mirror of
https://github.com/google/nomulus.git
synced 2025-07-11 21:48:18 +02:00
mv com/google/domain/registry google/registry
This change renames directories in preparation for the great package rename. The repository is now in a broken state because the code itself hasn't been updated. However this should ensure that git correctly preserves history for each file.
This commit is contained in:
parent
a41677aea1
commit
5012893c1d
2396 changed files with 0 additions and 0 deletions
57
java/google/registry/flows/domain/BaseDomainCheckFlow.java
Normal file
57
java/google/registry/flows/domain/BaseDomainCheckFlow.java
Normal file
|
@ -0,0 +1,57 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.validateDomainName;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.validateDomainNameWithIdnTables;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.flows.ResourceCheckFlow;
|
||||
import com.google.domain.registry.model.domain.DomainCommand.Check;
|
||||
import com.google.domain.registry.model.domain.DomainResource;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/** An EPP flow that checks whether a domain can be provisioned. */
|
||||
public abstract class BaseDomainCheckFlow extends ResourceCheckFlow<DomainResource, Check> {
|
||||
|
||||
protected Map<String, InternetDomainName> domainNames;
|
||||
|
||||
@Override
|
||||
protected final void initCheckResourceFlow() throws EppException {
|
||||
ImmutableMap.Builder<String, InternetDomainName> domains = new ImmutableMap.Builder<>();
|
||||
ImmutableSet.Builder<String> tlds = new ImmutableSet.Builder<>();
|
||||
for (String targetId : ImmutableSet.copyOf(targetIds)) {
|
||||
// This validation is moderately expensive, so cache the results for getCheckData to use too.
|
||||
InternetDomainName domainName = validateDomainName(targetId);
|
||||
tlds.add(domainName.parent().toString());
|
||||
validateDomainNameWithIdnTables(domainName);
|
||||
domains.put(targetId, domainName);
|
||||
}
|
||||
for (String tld : tlds.build()) {
|
||||
checkAllowedAccessToTld(getAllowedTlds(), tld);
|
||||
checkRegistryStateForTld(tld);
|
||||
}
|
||||
domainNames = domains.build();
|
||||
initDomainCheckFlow();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
protected void initDomainCheckFlow() throws EppException {}
|
||||
}
|
378
java/google/registry/flows/domain/BaseDomainCreateFlow.java
Normal file
378
java/google/registry/flows/domain/BaseDomainCreateFlow.java
Normal file
|
@ -0,0 +1,378 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.validateContactsHaveTypes;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.validateDomainName;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.validateDomainNameWithIdnTables;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.validateDsData;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.validateNameservers;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.validateNoDuplicateContacts;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.validateRegistrantAllowedOnTld;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.validateRequiredContactsPresent;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.verifyLaunchPhase;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.verifyNotInPendingDelete;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.verifyNotReserved;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.verifyPremiumNameIsNotBlocked;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.verifySignedMarks;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears;
|
||||
import static com.google.domain.registry.model.EppResourceUtils.createDomainRoid;
|
||||
import static com.google.domain.registry.model.EppResourceUtils.loadByUniqueId;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.model.registry.Registries.findTldForName;
|
||||
import static com.google.domain.registry.model.registry.label.ReservedList.matchesAnchorTenantReservation;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.flows.EppException.ParameterValuePolicyErrorException;
|
||||
import com.google.domain.registry.flows.EppException.ParameterValueRangeErrorException;
|
||||
import com.google.domain.registry.flows.EppException.ParameterValueSyntaxErrorException;
|
||||
import com.google.domain.registry.flows.EppException.StatusProhibitsOperationException;
|
||||
import com.google.domain.registry.flows.EppException.UnimplementedOptionException;
|
||||
import com.google.domain.registry.flows.ResourceCreateFlow;
|
||||
import com.google.domain.registry.model.domain.DomainBase;
|
||||
import com.google.domain.registry.model.domain.DomainBase.Builder;
|
||||
import com.google.domain.registry.model.domain.DomainCommand.Create;
|
||||
import com.google.domain.registry.model.domain.DomainResource;
|
||||
import com.google.domain.registry.model.domain.fee.FeeCreateExtension;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchCreateExtension;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchNotice;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchNotice.InvalidChecksumException;
|
||||
import com.google.domain.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import com.google.domain.registry.model.domain.secdns.SecDnsCreateExtension;
|
||||
import com.google.domain.registry.model.ofy.ObjectifyService;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.model.registry.Registry.TldState;
|
||||
import com.google.domain.registry.model.smd.SignedMark;
|
||||
import com.google.domain.registry.model.tmch.ClaimsListShard;
|
||||
|
||||
import com.googlecode.objectify.Work;
|
||||
|
||||
import org.joda.money.Money;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An EPP flow that creates a new domain resource or application.
|
||||
*
|
||||
* @param <R> the resource type being created
|
||||
* @param <B> a builder for the resource
|
||||
*/
|
||||
public abstract class BaseDomainCreateFlow<R extends DomainBase, B extends Builder<R, ?>>
|
||||
extends ResourceCreateFlow<R, B, Create> {
|
||||
|
||||
private SecDnsCreateExtension secDnsCreate;
|
||||
|
||||
protected LaunchCreateExtension launchCreate;
|
||||
protected String domainLabel;
|
||||
protected InternetDomainName domainName;
|
||||
protected String idnTableName;
|
||||
protected FeeCreateExtension feeCreate;
|
||||
protected Money createCost;
|
||||
protected boolean hasSignedMarks;
|
||||
protected SignedMark signedMark;
|
||||
protected boolean isAnchorTenantViaReservation;
|
||||
protected TldState tldState;
|
||||
|
||||
@Override
|
||||
public final void initResourceCreateOrMutateFlow() throws EppException {
|
||||
command = cloneAndLinkReferences(command, now);
|
||||
registerExtensions(SecDnsCreateExtension.class);
|
||||
secDnsCreate = eppInput.getSingleExtension(SecDnsCreateExtension.class);
|
||||
launchCreate = eppInput.getSingleExtension(LaunchCreateExtension.class);
|
||||
feeCreate = eppInput.getSingleExtension(FeeCreateExtension.class);
|
||||
hasSignedMarks = launchCreate != null && !launchCreate.getSignedMarks().isEmpty();
|
||||
initDomainCreateFlow();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected String createFlowRepoId() {
|
||||
// The domain name hasn't been validated yet, so if it's invalid, instead of throwing an error,
|
||||
// simply leave the repoId blank (it won't be needed anyway as the flow will fail when
|
||||
// validation fails later).
|
||||
try {
|
||||
Optional<InternetDomainName> tldParsed =
|
||||
findTldForName(InternetDomainName.from(command.getFullyQualifiedDomainName()));
|
||||
return tldParsed.isPresent()
|
||||
? createDomainRoid(ObjectifyService.allocateId(), tldParsed.get().toString())
|
||||
: null;
|
||||
} catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Subclasses may override this to do more specific initialization. */
|
||||
protected void initDomainCreateFlow() {}
|
||||
|
||||
/**
|
||||
* Returns the tld of the domain being created.
|
||||
*
|
||||
* <p>Update/delete domain-related flows can simply grab the tld using existingResource.getTld(),
|
||||
* but in the create flows, the resource doesn't exist yet. So we grab it off the domain name
|
||||
* that the flow is attempting to create.
|
||||
*
|
||||
* <p>Note that it's not always safe to call this until after the domain name has been validated
|
||||
* in verifyCreateIsAllowed().
|
||||
*/
|
||||
protected String getTld() {
|
||||
return domainName.parent().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fail the domain or application create very fast if the domain is already registered.
|
||||
* <p>
|
||||
* Try to load the domain non-transactionally, since this can hit memcache. If we succeed, and the
|
||||
* domain is not in the ADD grace period (the only state that allows instantaneous transition to
|
||||
* being deleted), we can assume that the domain will not be deleted (and therefore won't be
|
||||
* creatable) until its deletion time. For repeated failed creates this means we can avoid the
|
||||
* datastore lookup, which is very expensive (and first-seen failed creates are no worse than they
|
||||
* otherwise would be). This comes at the cost of the extra lookup for successful creates (or
|
||||
* rather, those that don't fail due to the domain existing) and also for failed creates within
|
||||
* the existing domain's ADD grace period.
|
||||
*/
|
||||
@Override
|
||||
protected final void failfast() throws EppException {
|
||||
// Enter a transactionless context briefly.
|
||||
DomainResource domain = ofy().doTransactionless(new Work<DomainResource>() {
|
||||
@Override
|
||||
public DomainResource run() {
|
||||
// This is cacheable because we are outside of a transaction.
|
||||
return loadByUniqueId(DomainResource.class, targetId, now);
|
||||
}});
|
||||
// If the domain exists already and isn't in the ADD grace period then there is no way it will
|
||||
// be suddenly deleted and therefore the create must fail.
|
||||
if (domain != null
|
||||
&& !domain.getGracePeriodStatuses().contains(GracePeriodStatus.ADD)) {
|
||||
throw new ResourceAlreadyExistsException(targetId, true);
|
||||
}
|
||||
}
|
||||
|
||||
/** Fail if the create command is somehow invalid. */
|
||||
@Override
|
||||
protected final void verifyCreateIsAllowed() throws EppException {
|
||||
// Validate that this is actually a legal domain name on a TLD that the registrar has access to.
|
||||
domainName = validateDomainName(command.getFullyQualifiedDomainName());
|
||||
idnTableName = validateDomainNameWithIdnTables(domainName);
|
||||
String tld = getTld();
|
||||
checkAllowedAccessToTld(getAllowedTlds(), tld);
|
||||
Registry registry = Registry.get(tld);
|
||||
tldState = registry.getTldState(now);
|
||||
checkRegistryStateForTld(tld);
|
||||
domainLabel = domainName.parts().get(0);
|
||||
createCost = registry.getDomainCreateCost(targetId, command.getPeriod().getValue());
|
||||
// The TLD should always be the parent of the requested domain name.
|
||||
isAnchorTenantViaReservation = matchesAnchorTenantReservation(
|
||||
domainLabel, tld, command.getAuthInfo().getPw().getValue());
|
||||
// Superusers can create reserved domains, force creations on domains that require a claims
|
||||
// notice without specifying a claims key, and override blocks on registering premium domains.
|
||||
if (!superuser) {
|
||||
boolean isSunriseApplication =
|
||||
launchCreate != null && !launchCreate.getSignedMarks().isEmpty();
|
||||
if (!isAnchorTenantViaReservation) {
|
||||
verifyNotReserved(domainName, isSunriseApplication);
|
||||
}
|
||||
boolean isClaimsPeriod = now.isBefore(registry.getClaimsPeriodEnd());
|
||||
boolean isClaimsCreate = launchCreate != null && launchCreate.getNotice() != null;
|
||||
if (isClaimsPeriod) {
|
||||
boolean labelOnClaimsList = ClaimsListShard.get().getClaimKey(domainLabel) != null;
|
||||
if (labelOnClaimsList && !isSunriseApplication && !isClaimsCreate) {
|
||||
throw new MissingClaimsNoticeException(domainName.toString());
|
||||
}
|
||||
if (!labelOnClaimsList && isClaimsCreate) {
|
||||
throw new UnexpectedClaimsNoticeException(domainName.toString());
|
||||
}
|
||||
} else if (isClaimsCreate) {
|
||||
throw new ClaimsPeriodEndedException(tld);
|
||||
}
|
||||
verifyPremiumNameIsNotBlocked(targetId, tld, getClientId());
|
||||
}
|
||||
verifyUnitIsYears(command.getPeriod());
|
||||
verifyNotInPendingDelete(
|
||||
command.getContacts(),
|
||||
command.getRegistrant(),
|
||||
command.getNameservers());
|
||||
validateContactsHaveTypes(command.getContacts());
|
||||
validateRegistrantAllowedOnTld(tld, command.getRegistrant());
|
||||
validateNoDuplicateContacts(command.getContacts());
|
||||
validateRequiredContactsPresent(command.getRegistrant(), command.getContacts());
|
||||
validateNameservers(tld, command.getNameservers());
|
||||
validateLaunchCreateExtension();
|
||||
// If a signed mark was provided, then it must match the desired domain label.
|
||||
// We do this after validating the launch create extension so that flows which don't allow any
|
||||
// signed marks throw a more useful error message rather than complaining about specific issues
|
||||
// with the signed marks.
|
||||
if (hasSignedMarks) {
|
||||
signedMark = verifySignedMarks(launchCreate.getSignedMarks(), domainLabel, now);
|
||||
}
|
||||
validateSecDnsExtension();
|
||||
verifyDomainCreateIsAllowed();
|
||||
}
|
||||
|
||||
/** Validate the secDNS extension, if present. */
|
||||
private void validateSecDnsExtension() throws EppException {
|
||||
if (secDnsCreate != null) {
|
||||
if (secDnsCreate.getDsData() == null) {
|
||||
throw new DsDataRequiredException();
|
||||
}
|
||||
if (secDnsCreate.getMaxSigLife() != null) {
|
||||
throw new MaxSigLifeNotSupportedException();
|
||||
}
|
||||
validateDsData(secDnsCreate.getDsData());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If a launch create extension was given (always present for application creates, optional for
|
||||
* domain creates) then validate it.
|
||||
*/
|
||||
private void validateLaunchCreateExtension() throws EppException {
|
||||
if (launchCreate == null) {
|
||||
return;
|
||||
}
|
||||
if (!superuser) { // Superusers can ignore the phase.
|
||||
verifyLaunchPhase(getTld(), launchCreate, now);
|
||||
}
|
||||
if (launchCreate.hasCodeMarks()) {
|
||||
throw new UnsupportedMarkTypeException();
|
||||
}
|
||||
validateDomainLaunchCreateExtension();
|
||||
LaunchNotice notice = launchCreate.getNotice();
|
||||
if (notice == null) {
|
||||
return;
|
||||
}
|
||||
if (!notice.getNoticeId().getValidatorId().equals("tmch")) {
|
||||
throw new InvalidTrademarkValidatorException();
|
||||
}
|
||||
// Superuser can force domain creations regardless of the current date.
|
||||
if (!superuser) {
|
||||
if (notice.getExpirationTime().isBefore(now)) {
|
||||
throw new ExpiredClaimException();
|
||||
}
|
||||
// An acceptance within the past 48 hours is mandated by the TMCH Functional Spec.
|
||||
if (notice.getAcceptedTime().isBefore(now.minusHours(48))) {
|
||||
throw new AcceptedTooLongAgoException();
|
||||
}
|
||||
}
|
||||
try {
|
||||
notice.validate(domainLabel);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new MalformedTcnIdException();
|
||||
} catch (InvalidChecksumException e) {
|
||||
throw new InvalidTcnIdChecksumException();
|
||||
}
|
||||
}
|
||||
|
||||
/** Subclasses may override this to do more specific checks. */
|
||||
@SuppressWarnings("unused")
|
||||
protected void verifyDomainCreateIsAllowed() throws EppException {}
|
||||
|
||||
/** Subclasses may override this to do more specific validation of the launchCreate extension. */
|
||||
@SuppressWarnings("unused")
|
||||
protected void validateDomainLaunchCreateExtension() throws EppException {}
|
||||
|
||||
/** Handle the secDNS extension */
|
||||
@Override
|
||||
protected final void setCreateProperties(B builder) throws EppException {
|
||||
if (secDnsCreate != null) {
|
||||
builder.setDsData(secDnsCreate.getDsData());
|
||||
}
|
||||
builder.setLaunchNotice(launchCreate == null ? null : launchCreate.getNotice());
|
||||
setDomainCreateProperties(builder);
|
||||
builder.setIdnTableName(idnTableName);
|
||||
}
|
||||
|
||||
protected abstract void setDomainCreateProperties(B builder) throws EppException;
|
||||
|
||||
/** Requested domain requires a claims notice. */
|
||||
static class MissingClaimsNoticeException extends StatusProhibitsOperationException {
|
||||
public MissingClaimsNoticeException(String domainName) {
|
||||
super(String.format("%s requires a claims notice", domainName));
|
||||
}
|
||||
}
|
||||
|
||||
/** Requested domain does not require a claims notice. */
|
||||
static class UnexpectedClaimsNoticeException extends StatusProhibitsOperationException {
|
||||
public UnexpectedClaimsNoticeException(String domainName) {
|
||||
super(String.format("%s does not require a claims notice", domainName));
|
||||
}
|
||||
}
|
||||
|
||||
/** The claims period for this TLD has ended. */
|
||||
static class ClaimsPeriodEndedException extends StatusProhibitsOperationException {
|
||||
public ClaimsPeriodEndedException(String tld) {
|
||||
super(String.format("The claims period for %s has ended", tld));
|
||||
}
|
||||
}
|
||||
|
||||
/** The specified trademark validator is not supported. */
|
||||
static class InvalidTrademarkValidatorException extends ParameterValuePolicyErrorException {
|
||||
public InvalidTrademarkValidatorException() {
|
||||
super("The only supported validationID is 'tmch' for the ICANN Trademark Clearinghouse.");
|
||||
}
|
||||
}
|
||||
|
||||
/** At least one dsData is required when using the secDNS extension. */
|
||||
static class DsDataRequiredException extends ParameterValuePolicyErrorException {
|
||||
public DsDataRequiredException() {
|
||||
super("At least one dsData is required when using the secDNS extension");
|
||||
}
|
||||
}
|
||||
|
||||
/** Only encoded signed marks are supported. */
|
||||
static class UnsupportedMarkTypeException extends ParameterValuePolicyErrorException {
|
||||
public UnsupportedMarkTypeException() {
|
||||
super("Only encoded signed marks are supported");
|
||||
}
|
||||
}
|
||||
|
||||
/** The 'maxSigLife' setting is not supported. */
|
||||
static class MaxSigLifeNotSupportedException extends UnimplementedOptionException {
|
||||
public MaxSigLifeNotSupportedException() {
|
||||
super("The 'maxSigLife' setting is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
/** The expiration time specified in the claim notice has elapsed. */
|
||||
static class ExpiredClaimException extends ParameterValueRangeErrorException {
|
||||
public ExpiredClaimException() {
|
||||
super("The expiration time specified in the claim notice has elapsed");
|
||||
}
|
||||
}
|
||||
|
||||
/** The acceptance time specified in the claim notice is more than 48 hours in the past. */
|
||||
static class AcceptedTooLongAgoException extends ParameterValueRangeErrorException {
|
||||
public AcceptedTooLongAgoException() {
|
||||
super("The acceptance time specified in the claim notice is more than 48 hours in the past");
|
||||
}
|
||||
}
|
||||
|
||||
/** The specified TCNID is invalid. */
|
||||
static class MalformedTcnIdException extends ParameterValueSyntaxErrorException {
|
||||
public MalformedTcnIdException() {
|
||||
super("The specified TCNID is malformed");
|
||||
}
|
||||
}
|
||||
|
||||
/** The checksum in the specified TCNID does not validate. */
|
||||
static class InvalidTcnIdChecksumException extends ParameterValueRangeErrorException {
|
||||
public InvalidTcnIdChecksumException() {
|
||||
super("The checksum in the specified TCNID does not validate");
|
||||
}
|
||||
}
|
||||
}
|
51
java/google/registry/flows/domain/BaseDomainInfoFlow.java
Normal file
51
java/google/registry/flows/domain/BaseDomainInfoFlow.java
Normal file
|
@ -0,0 +1,51 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
import static com.google.domain.registry.util.CollectionUtils.forceEmptyToNull;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.flows.ResourceInfoFlow;
|
||||
import com.google.domain.registry.model.domain.DomainBase;
|
||||
import com.google.domain.registry.model.domain.DomainBase.Builder;
|
||||
import com.google.domain.registry.model.domain.DomainCommand;
|
||||
import com.google.domain.registry.model.domain.secdns.SecDnsInfoExtension;
|
||||
import com.google.domain.registry.model.eppoutput.Response.ResponseExtension;
|
||||
|
||||
/**
|
||||
* An EPP flow that reads a domain resource or application.
|
||||
*
|
||||
* @param <R> the resource type being manipulated
|
||||
* @param <B> a builder for the resource
|
||||
*/
|
||||
public abstract class BaseDomainInfoFlow<R extends DomainBase, B extends Builder<R, B>>
|
||||
extends ResourceInfoFlow<R, DomainCommand.Info> {
|
||||
@Override
|
||||
protected final ImmutableList<ResponseExtension> getResponseExtensions() throws EppException {
|
||||
ImmutableList.Builder<ResponseExtension> builder = new ImmutableList.Builder<>();
|
||||
// According to RFC 5910 section 2, we should only return this if the client specified the
|
||||
// "urn:ietf:params:xml:ns:secDNS-1.1" when logging in. However, this is a "SHOULD" not a "MUST"
|
||||
// and we are going to ignore it; clients who don't care about secDNS can just ignore it.
|
||||
if (!existingResource.getDsData().isEmpty()) {
|
||||
builder.add(SecDnsInfoExtension.create(existingResource.getDsData()));
|
||||
}
|
||||
return forceEmptyToNull(builder.addAll(getDomainResponseExtensions()).build());
|
||||
}
|
||||
|
||||
/** Subclasses should override this to add their extensions. */
|
||||
protected abstract ImmutableList<? extends ResponseExtension> getDomainResponseExtensions()
|
||||
throws EppException;
|
||||
}
|
156
java/google/registry/flows/domain/BaseDomainUpdateFlow.java
Normal file
156
java/google/registry/flows/domain/BaseDomainUpdateFlow.java
Normal file
|
@ -0,0 +1,156 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static com.google.common.collect.Sets.union;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.validateContactsHaveTypes;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.validateDsData;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.validateNameservers;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.validateNoDuplicateContacts;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.validateRegistrantAllowedOnTld;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.validateRequiredContactsPresent;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.verifyNotInPendingDelete;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.flows.EppException.ParameterValuePolicyErrorException;
|
||||
import com.google.domain.registry.flows.EppException.RequiredParameterMissingException;
|
||||
import com.google.domain.registry.flows.EppException.UnimplementedOptionException;
|
||||
import com.google.domain.registry.flows.ResourceUpdateFlow;
|
||||
import com.google.domain.registry.model.domain.DomainBase;
|
||||
import com.google.domain.registry.model.domain.DomainBase.Builder;
|
||||
import com.google.domain.registry.model.domain.DomainCommand.Update;
|
||||
import com.google.domain.registry.model.domain.secdns.DelegationSignerData;
|
||||
import com.google.domain.registry.model.domain.secdns.SecDnsUpdateExtension;
|
||||
import com.google.domain.registry.model.domain.secdns.SecDnsUpdateExtension.Add;
|
||||
import com.google.domain.registry.model.domain.secdns.SecDnsUpdateExtension.Remove;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 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> {
|
||||
|
||||
@Override
|
||||
public final void initResourceCreateOrMutateFlow() throws EppException {
|
||||
command = cloneAndLinkReferences(command, now);
|
||||
initDomainUpdateFlow();
|
||||
}
|
||||
|
||||
@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 5901 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. */
|
||||
protected B setDomainUpdateProperties(B builder) {
|
||||
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());
|
||||
}
|
||||
|
||||
/** 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());
|
||||
validateRegistrantAllowedOnTld(newResource.getTld(), newResource.getRegistrant());
|
||||
validateNameservers(newResource.getTld(), newResource.getNameservers());
|
||||
}
|
||||
|
||||
/** 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");
|
||||
}
|
||||
}
|
||||
}
|
80
java/google/registry/flows/domain/ClaimsCheckFlow.java
Normal file
80
java/google/registry/flows/domain/ClaimsCheckFlow.java
Normal file
|
@ -0,0 +1,80 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
import static com.google.domain.registry.model.domain.launch.LaunchPhase.CLAIMS;
|
||||
import static com.google.domain.registry.util.DateTimeUtils.isAtOrAfter;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchCheckExtension;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchCheckResponseExtension;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchCheckResponseExtension.LaunchCheck;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchCheckResponseExtension.LaunchCheckName;
|
||||
import com.google.domain.registry.model.eppoutput.CheckData;
|
||||
import com.google.domain.registry.model.eppoutput.Response.ResponseExtension;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.model.registry.Registry.TldState;
|
||||
import com.google.domain.registry.model.tmch.ClaimsListShard;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* An EPP flow that checks whether strings are trademarked.
|
||||
*
|
||||
* @error {@link com.google.domain.registry.flows.ResourceCheckFlow.TooManyResourceChecksException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceFlow.BadCommandForRegistryPhaseException}
|
||||
* @error {@link com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link DomainFlowUtils.TldDoesNotExistException}
|
||||
*/
|
||||
public class ClaimsCheckFlow extends BaseDomainCheckFlow {
|
||||
|
||||
public static final ImmutableSet<TldState> DISALLOWED_TLD_STATES = Sets.immutableEnumSet(
|
||||
TldState.PREDELEGATION, TldState.SUNRISE);
|
||||
|
||||
@Override
|
||||
protected void initDomainCheckFlow() throws EppException {
|
||||
registerExtensions(LaunchCheckExtension.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CheckData getCheckData() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImmutableList<? extends ResponseExtension> getResponseExtensions() throws EppException {
|
||||
ImmutableList.Builder<LaunchCheck> launchChecksBuilder = new ImmutableList.Builder<>();
|
||||
for (Entry<String, InternetDomainName> entry : domainNames.entrySet()) {
|
||||
InternetDomainName domainName = entry.getValue();
|
||||
if (isAtOrAfter(now, Registry.get(domainName.parent().toString()).getClaimsPeriodEnd())) {
|
||||
throw new BadCommandForRegistryPhaseException();
|
||||
}
|
||||
String claimKey = ClaimsListShard.get().getClaimKey(domainName.parts().get(0));
|
||||
launchChecksBuilder.add(LaunchCheck.create(
|
||||
LaunchCheckName.create(claimKey != null, entry.getKey()), claimKey));
|
||||
}
|
||||
return ImmutableList.of(
|
||||
LaunchCheckResponseExtension.create(CLAIMS, launchChecksBuilder.build()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final ImmutableSet<TldState> getDisallowedTldStates() {
|
||||
return DISALLOWED_TLD_STATES;
|
||||
}
|
||||
}
|
223
java/google/registry/flows/domain/DomainAllocateFlow.java
Normal file
223
java/google/registry/flows/domain/DomainAllocateFlow.java
Normal file
|
@ -0,0 +1,223 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.getReservationType;
|
||||
import static com.google.domain.registry.model.EppResourceUtils.loadByUniqueId;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.util.CollectionUtils.isNullOrEmpty;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.flows.EppException.AuthorizationErrorException;
|
||||
import com.google.domain.registry.flows.EppException.ObjectDoesNotExistException;
|
||||
import com.google.domain.registry.flows.EppException.RequiredParameterMissingException;
|
||||
import com.google.domain.registry.flows.EppException.StatusProhibitsOperationException;
|
||||
import com.google.domain.registry.model.billing.BillingEvent;
|
||||
import com.google.domain.registry.model.billing.BillingEvent.Flag;
|
||||
import com.google.domain.registry.model.billing.BillingEvent.Reason;
|
||||
import com.google.domain.registry.model.domain.DomainApplication;
|
||||
import com.google.domain.registry.model.domain.DomainResource.Builder;
|
||||
import com.google.domain.registry.model.domain.GracePeriod;
|
||||
import com.google.domain.registry.model.domain.allocate.AllocateCreateExtension;
|
||||
import com.google.domain.registry.model.domain.launch.ApplicationStatus;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchInfoResponseExtension;
|
||||
import com.google.domain.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import com.google.domain.registry.model.eppcommon.StatusValue;
|
||||
import com.google.domain.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
|
||||
import com.google.domain.registry.model.poll.PollMessage;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.model.registry.label.ReservationType;
|
||||
import com.google.domain.registry.model.reporting.HistoryEntry;
|
||||
import com.google.domain.registry.tmch.LordnTask;
|
||||
|
||||
import com.googlecode.objectify.Ref;
|
||||
|
||||
/**
|
||||
* An EPP flow that allocates a new domain resource from a domain application.
|
||||
*
|
||||
* @error {@link com.google.domain.registry.flows.EppException.UnimplementedExtensionException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceCreateFlow.ResourceAlreadyExistsException}
|
||||
* @error {@link com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link DomainAllocateFlow.HasFinalStatusException}
|
||||
* @error {@link DomainAllocateFlow.MissingAllocateCreateExtensionException}
|
||||
* @error {@link DomainAllocateFlow.MissingApplicationException}
|
||||
* @error {@link DomainAllocateFlow.OnlySuperuserCanAllocateException}
|
||||
*/
|
||||
public class DomainAllocateFlow extends DomainCreateOrAllocateFlow {
|
||||
|
||||
protected AllocateCreateExtension allocateCreate;
|
||||
protected DomainApplication application;
|
||||
|
||||
@Override
|
||||
protected final void initDomainCreateOrAllocateFlow() {
|
||||
registerExtensions(AllocateCreateExtension.class);
|
||||
allocateCreate = eppInput.getSingleExtension(AllocateCreateExtension.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void verifyDomainCreateIsAllowed() throws EppException {
|
||||
if (!superuser) {
|
||||
throw new OnlySuperuserCanAllocateException();
|
||||
}
|
||||
if (allocateCreate == null) {
|
||||
throw new MissingAllocateCreateExtensionException();
|
||||
}
|
||||
String applicationRoid = allocateCreate.getApplicationRoid();
|
||||
application = loadByUniqueId(DomainApplication.class, applicationRoid, now);
|
||||
if (application == null) {
|
||||
throw new MissingApplicationException(applicationRoid);
|
||||
}
|
||||
if (application.getApplicationStatus().isFinalStatus()) {
|
||||
throw new HasFinalStatusException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void setDomainCreateOrAllocateProperties(Builder builder) {
|
||||
boolean sunrushAddGracePeriod = isNullOrEmpty(command.getNameservers());
|
||||
Registry registry = Registry.get(getTld());
|
||||
ImmutableSet.Builder<Flag> billingFlagsBuilder = ImmutableSet.builder();
|
||||
if (!application.getEncodedSignedMarks().isEmpty()) {
|
||||
billingFlagsBuilder.add(Flag.SUNRISE);
|
||||
} else {
|
||||
billingFlagsBuilder.add(Flag.LANDRUSH);
|
||||
}
|
||||
BillingEvent.OneTime billingEvent = new BillingEvent.OneTime.Builder()
|
||||
.setReason(Reason.CREATE)
|
||||
.setFlags(billingFlagsBuilder.add(Flag.ALLOCATION).build())
|
||||
.setTargetId(targetId)
|
||||
.setClientId(getClientId())
|
||||
.setCost(registry.getDomainCreateCost(targetId, command.getPeriod().getValue()))
|
||||
.setPeriodYears(command.getPeriod().getValue())
|
||||
.setEventTime(now)
|
||||
// If there are no nameservers on the domain, then they get the benefit of the sunrush add
|
||||
// grace period, which is longer than the standard add grace period.
|
||||
.setBillingTime(now.plus(sunrushAddGracePeriod
|
||||
? registry.getSunrushAddGracePeriodLength()
|
||||
: registry.getAddGracePeriodLength()))
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
ReservationType reservationType = getReservationType(domainName);
|
||||
ofy().save().<Object>entities(
|
||||
// Save the billing event
|
||||
billingEvent,
|
||||
// Update the application itself.
|
||||
application.asBuilder()
|
||||
.setApplicationStatus(ApplicationStatus.ALLOCATED)
|
||||
.removeStatusValue(StatusValue.PENDING_CREATE)
|
||||
.build(),
|
||||
// Create a poll message informing the registrar that the application status was updated.
|
||||
new PollMessage.OneTime.Builder()
|
||||
.setClientId(application.getCurrentSponsorClientId())
|
||||
.setEventTime(ofy().getTransactionTime())
|
||||
.setMsg(reservationType == ReservationType.NAME_COLLISION
|
||||
// Change the poll message to remind the registrar of the name collision policy.
|
||||
? "Domain on the name collision list was allocated. "
|
||||
+ "But by policy, the domain will not be delegated. "
|
||||
+ "Please visit https://www.icann.org/namecollision "
|
||||
+ "for more information on name collision."
|
||||
: "Domain was allocated")
|
||||
.setResponseData(ImmutableList.of(
|
||||
DomainPendingActionNotificationResponse.create(
|
||||
application.getFullyQualifiedDomainName(),
|
||||
true,
|
||||
// If the creation TRID is not present on the application (this can happen for
|
||||
// older applications written before this field was added), then we must read
|
||||
// the earliest history entry for the application to retrieve it.
|
||||
application.getCreationTrid() == null
|
||||
? checkNotNull(ofy()
|
||||
.load()
|
||||
.type(HistoryEntry.class)
|
||||
.ancestor(application)
|
||||
.order("modificationTime")
|
||||
.first()
|
||||
.now()
|
||||
.getTrid())
|
||||
: application.getCreationTrid(),
|
||||
now)))
|
||||
.setResponseExtensions(ImmutableList.of(
|
||||
new LaunchInfoResponseExtension.Builder()
|
||||
.setApplicationId(application.getForeignKey())
|
||||
.setPhase(application.getPhase())
|
||||
.setApplicationStatus(ApplicationStatus.ALLOCATED)
|
||||
.build()))
|
||||
.setParent(historyEntry)
|
||||
.build(),
|
||||
// Create a history entry (with no xml or trid) to record that we updated the application.
|
||||
new HistoryEntry.Builder()
|
||||
.setType(HistoryEntry.Type.DOMAIN_APPLICATION_STATUS_UPDATE)
|
||||
.setParent(application)
|
||||
.setModificationTime(now)
|
||||
.setClientId(application.getCurrentSponsorClientId())
|
||||
.setBySuperuser(true)
|
||||
.build());
|
||||
// Set the properties on the new domain.
|
||||
builder
|
||||
.addGracePeriod(GracePeriod.forBillingEvent(
|
||||
sunrushAddGracePeriod ? GracePeriodStatus.SUNRUSH_ADD : GracePeriodStatus.ADD,
|
||||
billingEvent))
|
||||
.setApplicationTime(allocateCreate.getApplicationTime())
|
||||
.setApplication(Ref.create(application))
|
||||
.setSmdId(allocateCreate.getSmdId())
|
||||
.setLaunchNotice(allocateCreate.getNotice());
|
||||
// Names on the collision list will not be delegated. Set server hold.
|
||||
if (ReservationType.NAME_COLLISION == reservationType) {
|
||||
builder.addStatusValue(StatusValue.SERVER_HOLD);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enqueueLordnTaskIfNeeded() {
|
||||
if (allocateCreate.getSmdId() != null || allocateCreate.getNotice() != null) {
|
||||
LordnTask.enqueueDomainResourceTask(newResource);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final HistoryEntry.Type getHistoryEntryType() {
|
||||
return HistoryEntry.Type.DOMAIN_ALLOCATE;
|
||||
}
|
||||
|
||||
/** The allocate create extension is required to allocate a domain. */
|
||||
static class MissingAllocateCreateExtensionException extends RequiredParameterMissingException {
|
||||
public MissingAllocateCreateExtensionException() {
|
||||
super("The allocate create extension is required to allocate a domain");
|
||||
}
|
||||
}
|
||||
|
||||
/** Domain application with specific ROID does not exist. */
|
||||
static class MissingApplicationException extends ObjectDoesNotExistException {
|
||||
public MissingApplicationException(String applicationRoid) {
|
||||
super(DomainApplication.class, applicationRoid);
|
||||
}
|
||||
}
|
||||
|
||||
/** Domain application already has a final status. */
|
||||
static class HasFinalStatusException extends StatusProhibitsOperationException {
|
||||
public HasFinalStatusException() {
|
||||
super("Domain application already has a final status");
|
||||
}
|
||||
}
|
||||
|
||||
/** Only a superuser can allocate domains. */
|
||||
static class OnlySuperuserCanAllocateException extends AuthorizationErrorException {
|
||||
public OnlySuperuserCanAllocateException() {
|
||||
super("Only a superuser can allocate domains");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,253 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.DISALLOWED_TLD_STATES_FOR_LAUNCH_FLOWS;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.validateFeeChallenge;
|
||||
import static com.google.domain.registry.model.eppoutput.Result.Code.Success;
|
||||
import static com.google.domain.registry.model.index.DomainApplicationIndex.loadActiveApplicationsByDomainName;
|
||||
import static com.google.domain.registry.model.index.ForeignKeyIndex.loadAndGetReference;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.flows.EppException.CommandUseErrorException;
|
||||
import com.google.domain.registry.flows.EppException.ObjectAlreadyExistsException;
|
||||
import com.google.domain.registry.flows.EppException.RequiredParameterMissingException;
|
||||
import com.google.domain.registry.model.domain.DomainApplication;
|
||||
import com.google.domain.registry.model.domain.DomainApplication.Builder;
|
||||
import com.google.domain.registry.model.domain.DomainResource;
|
||||
import com.google.domain.registry.model.domain.Period;
|
||||
import com.google.domain.registry.model.domain.fee.Fee;
|
||||
import com.google.domain.registry.model.domain.fee.FeeCreateExtension;
|
||||
import com.google.domain.registry.model.domain.fee.FeeCreateResponseExtension;
|
||||
import com.google.domain.registry.model.domain.launch.ApplicationStatus;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchCreateExtension;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchCreateResponseExtension;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchPhase;
|
||||
import com.google.domain.registry.model.eppcommon.StatusValue;
|
||||
import com.google.domain.registry.model.eppoutput.CreateData.DomainCreateData;
|
||||
import com.google.domain.registry.model.eppoutput.EppOutput;
|
||||
import com.google.domain.registry.model.eppoutput.Response.ResponseExtension;
|
||||
import com.google.domain.registry.model.registry.Registry.TldState;
|
||||
import com.google.domain.registry.model.reporting.HistoryEntry;
|
||||
import com.google.domain.registry.model.smd.AbstractSignedMark;
|
||||
import com.google.domain.registry.model.smd.EncodedSignedMark;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An EPP flow that creates a new application for a domain resource.
|
||||
*
|
||||
* @error {@link com.google.domain.registry.flows.EppException.UnimplementedExtensionException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceFlow.BadCommandForRegistryPhaseException}
|
||||
* @error {@link com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceCreateFlow.ResourceAlreadyExistsException}
|
||||
* @error {@link BaseDomainCreateFlow.AcceptedTooLongAgoException}
|
||||
* @error {@link BaseDomainCreateFlow.ClaimsPeriodEndedException}
|
||||
* @error {@link BaseDomainCreateFlow.ExpiredClaimException}
|
||||
* @error {@link BaseDomainCreateFlow.InvalidTcnIdChecksumException}
|
||||
* @error {@link BaseDomainCreateFlow.InvalidTrademarkValidatorException}
|
||||
* @error {@link BaseDomainCreateFlow.MalformedTcnIdException}
|
||||
* @error {@link BaseDomainCreateFlow.MaxSigLifeNotSupportedException}
|
||||
* @error {@link BaseDomainCreateFlow.MissingClaimsNoticeException}
|
||||
* @error {@link BaseDomainCreateFlow.UnexpectedClaimsNoticeException}
|
||||
* @error {@link BaseDomainCreateFlow.UnsupportedMarkTypeException}
|
||||
* @error {@link DomainApplicationCreateFlow.LandrushApplicationDisallowedDuringSunriseException}
|
||||
* @error {@link DomainApplicationCreateFlow.NoticeCannotBeUsedWithSignedMarkException}
|
||||
* @error {@link DomainApplicationCreateFlow.SunriseApplicationDisallowedDuringLandrushException}
|
||||
* @error {@link DomainApplicationCreateFlow.UncontestedSunriseApplicationBlockedInLandrushException}
|
||||
* @error {@link DomainFlowUtils.BadDomainNameCharacterException}
|
||||
* @error {@link DomainFlowUtils.BadDomainNamePartsCountException}
|
||||
* @error {@link DomainFlowUtils.BadPeriodUnitException}
|
||||
* @error {@link DomainFlowUtils.Base64RequiredForEncodedSignedMarksException}
|
||||
* @error {@link DomainFlowUtils.CurrencyUnitMismatchException}
|
||||
* @error {@link DomainFlowUtils.CurrencyValueScaleException}
|
||||
* @error {@link DomainFlowUtils.DashesInThirdAndFourthException}
|
||||
* @error {@link DomainFlowUtils.DomainLabelTooLongException}
|
||||
* @error {@link DomainFlowUtils.DomainReservedException}
|
||||
* @error {@link DomainFlowUtils.DuplicateContactForRoleException}
|
||||
* @error {@link DomainFlowUtils.EmptyDomainNamePartException}
|
||||
* @error {@link DomainFlowUtils.FeesMismatchException}
|
||||
* @error {@link DomainFlowUtils.FeesRequiredForPremiumNameException}
|
||||
* @error {@link DomainFlowUtils.InvalidIdnDomainLabelException}
|
||||
* @error {@link DomainFlowUtils.InvalidPunycodeException}
|
||||
* @error {@link DomainFlowUtils.LaunchPhaseMismatchException}
|
||||
* @error {@link DomainFlowUtils.LeadingDashException}
|
||||
* @error {@link DomainFlowUtils.LinkedResourceDoesNotExistException}
|
||||
* @error {@link DomainFlowUtils.MissingContactTypeException}
|
||||
* @error {@link DomainFlowUtils.NameserverNotAllowedException}
|
||||
* @error {@link DomainFlowUtils.NoMarksFoundMatchingDomainException}
|
||||
* @error {@link DomainFlowUtils.PremiumNameBlockedException}
|
||||
* @error {@link DomainFlowUtils.RegistrantNotAllowedException}
|
||||
* @error {@link DomainFlowUtils.SignedMarksMustBeEncodedException}
|
||||
* @error {@link DomainFlowUtils.SignedMarkCertificateExpiredException}
|
||||
* @error {@link DomainFlowUtils.SignedMarkCertificateInvalidException}
|
||||
* @error {@link DomainFlowUtils.SignedMarkCertificateNotYetValidException}
|
||||
* @error {@link DomainFlowUtils.SignedMarkCertificateRevokedException}
|
||||
* @error {@link DomainFlowUtils.SignedMarkCertificateSignatureException}
|
||||
* @error {@link DomainFlowUtils.SignedMarkEncodingErrorException}
|
||||
* @error {@link DomainFlowUtils.SignedMarkParsingErrorException}
|
||||
* @error {@link DomainFlowUtils.SignedMarkRevokedErrorException}
|
||||
* @error {@link DomainFlowUtils.SignedMarkSignatureException}
|
||||
* @error {@link DomainFlowUtils.TldDoesNotExistException}
|
||||
* @error {@link DomainFlowUtils.TooManyDsRecordsException}
|
||||
* @error {@link DomainFlowUtils.TooManyNameserversException}
|
||||
* @error {@link DomainFlowUtils.TooManySignedMarksException}
|
||||
* @error {@link DomainFlowUtils.TrailingDashException}
|
||||
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
|
||||
*/
|
||||
public class DomainApplicationCreateFlow extends BaseDomainCreateFlow<DomainApplication, Builder> {
|
||||
|
||||
@Override
|
||||
protected void initDomainCreateFlow() {
|
||||
registerExtensions(FeeCreateExtension.class, LaunchCreateExtension.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void validateDomainLaunchCreateExtension() throws EppException {
|
||||
if (launchCreate.getSignedMarks().isEmpty()) {
|
||||
// During sunrise, a signed mark is required since only trademark holders are allowed to
|
||||
// create an application. However, we found no marks (ie, this was a landrush application).
|
||||
if (tldState == TldState.SUNRISE) {
|
||||
throw new LandrushApplicationDisallowedDuringSunriseException();
|
||||
}
|
||||
} else {
|
||||
if (launchCreate.getNotice() != null) { // Can't use a claims notice id with a signed mark.
|
||||
throw new NoticeCannotBeUsedWithSignedMarkException();
|
||||
}
|
||||
if (tldState == TldState.LANDRUSH) {
|
||||
throw new SunriseApplicationDisallowedDuringLandrushException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void verifyDomainCreateIsAllowed() throws EppException {
|
||||
validateFeeChallenge(targetId, getTld(), feeCreate, createCost);
|
||||
if (tldState == TldState.LANDRUSH && !superuser) {
|
||||
// Prohibit creating a landrush application in LANDRUSH (but not in SUNRUSH) if there is
|
||||
// exactly one sunrise application for the same name.
|
||||
List<DomainApplication> applications = FluentIterable
|
||||
.from(loadActiveApplicationsByDomainName(targetId, now))
|
||||
.limit(2)
|
||||
.toList();
|
||||
if (applications.size() == 1 && applications.get(0).getPhase().equals(LaunchPhase.SUNRISE)) {
|
||||
throw new UncontestedSunriseApplicationBlockedInLandrushException();
|
||||
}
|
||||
}
|
||||
// Fail if the domain is already registered (e.g. this is a landrush application but the domain
|
||||
// was awarded at the end of sunrise).
|
||||
if (loadAndGetReference(DomainResource.class, targetId, now) != null) {
|
||||
throw new ResourceAlreadyExistsException(targetId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDomainCreateProperties(Builder builder) {
|
||||
builder
|
||||
.setCreationTrid(trid)
|
||||
.setPhase(launchCreate.getPhase())
|
||||
.setApplicationStatus(ApplicationStatus.VALIDATED)
|
||||
.addStatusValue(StatusValue.PENDING_CREATE);
|
||||
if (!launchCreate.getSignedMarks().isEmpty()) {
|
||||
builder.setEncodedSignedMarks(FluentIterable
|
||||
.from(launchCreate.getSignedMarks())
|
||||
.transform(new Function<AbstractSignedMark, EncodedSignedMark>() {
|
||||
@Override
|
||||
public EncodedSignedMark apply(AbstractSignedMark abstractSignedMark) {
|
||||
// We verified that this is the case in verifyDomainCreateIsAllowed().
|
||||
return (EncodedSignedMark) abstractSignedMark;
|
||||
}})
|
||||
.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final ImmutableSet<TldState> getDisallowedTldStates() {
|
||||
return DISALLOWED_TLD_STATES_FOR_LAUNCH_FLOWS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final HistoryEntry.Type getHistoryEntryType() {
|
||||
return HistoryEntry.Type.DOMAIN_APPLICATION_CREATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final Period getCommandPeriod() {
|
||||
return command.getPeriod();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean tryToLoadExisting() {
|
||||
// Multiple domain applications can be created for the same targetId (which is the fully
|
||||
// qualified domain name), so don't try to load an existing resource with the same target id.
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final EppOutput getOutput() {
|
||||
ImmutableList.Builder<ResponseExtension> responseExtensionsBuilder =
|
||||
new ImmutableList.Builder<>();
|
||||
responseExtensionsBuilder.add(new LaunchCreateResponseExtension.Builder()
|
||||
.setPhase(launchCreate.getPhase())
|
||||
.setApplicationId(newResource.getForeignKey())
|
||||
.build());
|
||||
if (feeCreate != null) {
|
||||
responseExtensionsBuilder.add(new FeeCreateResponseExtension.Builder()
|
||||
.setCurrency(createCost.getCurrencyUnit())
|
||||
.setFee(ImmutableList.of(Fee.create(createCost.getAmount(), "create")))
|
||||
.build());
|
||||
}
|
||||
|
||||
return createOutput(
|
||||
Success,
|
||||
DomainCreateData.create(newResource.getFullyQualifiedDomainName(), now, null),
|
||||
responseExtensionsBuilder.build());
|
||||
}
|
||||
|
||||
/** Landrush applications are disallowed during sunrise. */
|
||||
static class LandrushApplicationDisallowedDuringSunriseException
|
||||
extends RequiredParameterMissingException {
|
||||
public LandrushApplicationDisallowedDuringSunriseException() {
|
||||
super("Landrush applications are disallowed during sunrise");
|
||||
}
|
||||
}
|
||||
|
||||
/** A notice cannot be specified when using a signed mark. */
|
||||
static class NoticeCannotBeUsedWithSignedMarkException extends CommandUseErrorException {
|
||||
public NoticeCannotBeUsedWithSignedMarkException() {
|
||||
super("A notice cannot be specified when using a signed mark");
|
||||
}
|
||||
}
|
||||
|
||||
/** Sunrise applications are disallowed during landrush. */
|
||||
static class SunriseApplicationDisallowedDuringLandrushException
|
||||
extends CommandUseErrorException {
|
||||
public SunriseApplicationDisallowedDuringLandrushException() {
|
||||
super("Sunrise applications are disallowed during landrush");
|
||||
}
|
||||
}
|
||||
|
||||
/** This name has already been claimed by a sunrise applicant. */
|
||||
static class UncontestedSunriseApplicationBlockedInLandrushException
|
||||
extends ObjectAlreadyExistsException {
|
||||
public UncontestedSunriseApplicationBlockedInLandrushException() {
|
||||
super("This name has already been claimed by a sunrise applicant");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.DISALLOWED_TLD_STATES_FOR_LAUNCH_FLOWS;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.verifyLaunchApplicationIdMatchesDomain;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.verifyLaunchPhase;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.flows.EppException.StatusProhibitsOperationException;
|
||||
import com.google.domain.registry.flows.ResourceSyncDeleteFlow;
|
||||
import com.google.domain.registry.model.domain.DomainApplication;
|
||||
import com.google.domain.registry.model.domain.DomainApplication.Builder;
|
||||
import com.google.domain.registry.model.domain.DomainCommand.Delete;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchDeleteExtension;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchPhase;
|
||||
import com.google.domain.registry.model.eppcommon.StatusValue;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.model.registry.Registry.TldState;
|
||||
import com.google.domain.registry.model.reporting.HistoryEntry;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An EPP flow that deletes a domain application.
|
||||
*
|
||||
* @error {@link com.google.domain.registry.flows.EppException.UnimplementedExtensionException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceFlow.BadCommandForRegistryPhaseException}
|
||||
* @error {@link com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException}
|
||||
* @error {@link DomainApplicationDeleteFlow.SunriseApplicationCannotBeDeletedInLandrushException}
|
||||
* @error {@link DomainFlowUtils.ApplicationDomainNameMismatchException}
|
||||
* @error {@link DomainFlowUtils.LaunchPhaseMismatchException}
|
||||
*/
|
||||
public class DomainApplicationDeleteFlow
|
||||
extends ResourceSyncDeleteFlow<DomainApplication, Builder, Delete> {
|
||||
|
||||
@Override
|
||||
protected void initResourceCreateOrMutateFlow() throws EppException {
|
||||
registerExtensions(LaunchDeleteExtension.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void verifyMutationOnOwnedResourceAllowed() throws EppException {
|
||||
String tld = existingResource.getTld();
|
||||
checkRegistryStateForTld(tld);
|
||||
checkAllowedAccessToTld(getAllowedTlds(), tld);
|
||||
verifyLaunchPhase(tld, eppInput.getSingleExtension(LaunchDeleteExtension.class), now);
|
||||
verifyLaunchApplicationIdMatchesDomain(command, existingResource);
|
||||
// Don't allow deleting a sunrise application during landrush.
|
||||
if (existingResource.getPhase().equals(LaunchPhase.SUNRISE)
|
||||
&& Registry.get(existingResource.getTld()).getTldState(now).equals(TldState.LANDRUSH)
|
||||
&& !superuser) {
|
||||
throw new SunriseApplicationCannotBeDeletedInLandrushException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final ImmutableSet<TldState> getDisallowedTldStates() {
|
||||
return DISALLOWED_TLD_STATES_FOR_LAUNCH_FLOWS;
|
||||
}
|
||||
|
||||
/** Domain applications do not respect status values that prohibit various operations. */
|
||||
@Override
|
||||
protected Set<StatusValue> getDisallowedStatuses() {
|
||||
return ImmutableSet.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final HistoryEntry.Type getHistoryEntryType() {
|
||||
return HistoryEntry.Type.DOMAIN_APPLICATION_DELETE;
|
||||
}
|
||||
|
||||
/** A sunrise application cannot be deleted during landrush. */
|
||||
static class SunriseApplicationCannotBeDeletedInLandrushException
|
||||
extends StatusProhibitsOperationException {
|
||||
public SunriseApplicationCannotBeDeletedInLandrushException() {
|
||||
super("A sunrise application cannot be deleted during landrush");
|
||||
}
|
||||
}
|
||||
}
|
117
java/google/registry/flows/domain/DomainApplicationInfoFlow.java
Normal file
117
java/google/registry/flows/domain/DomainApplicationInfoFlow.java
Normal file
|
@ -0,0 +1,117 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
import static com.google.domain.registry.flows.EppXmlTransformer.unmarshal;
|
||||
import static com.google.domain.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.verifyLaunchApplicationIdMatchesDomain;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.flows.EppException.ParameterValuePolicyErrorException;
|
||||
import com.google.domain.registry.flows.EppException.RequiredParameterMissingException;
|
||||
import com.google.domain.registry.model.domain.DomainApplication;
|
||||
import com.google.domain.registry.model.domain.DomainApplication.Builder;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchInfoExtension;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchInfoResponseExtension;
|
||||
import com.google.domain.registry.model.eppoutput.Response.ResponseExtension;
|
||||
import com.google.domain.registry.model.mark.Mark;
|
||||
import com.google.domain.registry.model.smd.EncodedSignedMark;
|
||||
import com.google.domain.registry.model.smd.SignedMark;
|
||||
|
||||
/**
|
||||
* An EPP flow that reads a domain application.
|
||||
*
|
||||
* @error {@link com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceQueryFlow.ResourceToQueryDoesNotExistException}
|
||||
* @error {@link DomainFlowUtils.ApplicationDomainNameMismatchException}
|
||||
* @error {@link DomainApplicationInfoFlow.ApplicationLaunchPhaseMismatchException}
|
||||
* @error {@link DomainApplicationInfoFlow.MissingApplicationIdException}
|
||||
*/
|
||||
public class DomainApplicationInfoFlow extends BaseDomainInfoFlow<DomainApplication, Builder> {
|
||||
|
||||
private boolean includeMarks;
|
||||
|
||||
@Override
|
||||
protected final void initSingleResourceFlow() throws EppException {
|
||||
registerExtensions(LaunchInfoExtension.class);
|
||||
// We need to do this in init rather than verify or we'll get the generic "object not found".
|
||||
LaunchInfoExtension extension = eppInput.getSingleExtension(LaunchInfoExtension.class);
|
||||
if (extension.getApplicationId() == null) {
|
||||
throw new MissingApplicationIdException();
|
||||
}
|
||||
includeMarks = Boolean.TRUE.equals(extension.getIncludeMark()); // Default to false.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void verifyQueryIsAllowed() throws EppException {
|
||||
verifyLaunchApplicationIdMatchesDomain(command, existingResource);
|
||||
if (!existingResource.getPhase().equals(
|
||||
eppInput.getSingleExtension(LaunchInfoExtension.class).getPhase())) {
|
||||
throw new ApplicationLaunchPhaseMismatchException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final DomainApplication getResourceInfo() throws EppException {
|
||||
// We don't support authInfo for applications, so if it's another registrar always fail.
|
||||
verifyResourceOwnership(getClientId(), existingResource);
|
||||
if (!command.getHostsRequest().requestDelegated()) {
|
||||
// Delegated hosts are present by default, so clear them out if they aren't wanted.
|
||||
// This requires overriding the implicit status values so that we don't get INACTIVE added due
|
||||
// to the missing nameservers.
|
||||
return existingResource.asBuilder()
|
||||
.setNameservers(null)
|
||||
.buildWithoutImplicitStatusValues();
|
||||
}
|
||||
return existingResource;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final ImmutableList<? extends ResponseExtension> getDomainResponseExtensions()
|
||||
throws EppException {
|
||||
ImmutableList.Builder<Mark> marksBuilder = new ImmutableList.Builder<>();
|
||||
if (includeMarks) {
|
||||
for (EncodedSignedMark encodedMark : existingResource.getEncodedSignedMarks()) {
|
||||
try {
|
||||
marksBuilder.add(((SignedMark) unmarshal(encodedMark.getBytes())).getMark());
|
||||
} catch (EppException e) {
|
||||
// This is a serious error; don't let the benign EppException propagate.
|
||||
throw new IllegalStateException("Could not decode a stored encoded signed mark");
|
||||
}
|
||||
}
|
||||
}
|
||||
return ImmutableList.of(new LaunchInfoResponseExtension.Builder()
|
||||
.setPhase(existingResource.getPhase())
|
||||
.setApplicationId(existingResource.getForeignKey())
|
||||
.setApplicationStatus(existingResource.getApplicationStatus())
|
||||
.setMarks(marksBuilder.build())
|
||||
.build());
|
||||
}
|
||||
|
||||
/** Application id is required. */
|
||||
static class MissingApplicationIdException extends RequiredParameterMissingException {
|
||||
public MissingApplicationIdException() {
|
||||
super("Application id is required");
|
||||
}
|
||||
}
|
||||
|
||||
/** Declared launch extension phase does not match phase of the application. */
|
||||
static class ApplicationLaunchPhaseMismatchException extends ParameterValuePolicyErrorException {
|
||||
public ApplicationLaunchPhaseMismatchException() {
|
||||
super("Declared launch extension phase does not match the phase of the application");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
import static com.google.common.base.CaseFormat.LOWER_CAMEL;
|
||||
import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.DISALLOWED_TLD_STATES_FOR_LAUNCH_FLOWS;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.flows.EppException.StatusProhibitsOperationException;
|
||||
import com.google.domain.registry.model.domain.DomainApplication;
|
||||
import com.google.domain.registry.model.domain.DomainApplication.Builder;
|
||||
import com.google.domain.registry.model.domain.launch.ApplicationStatus;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchUpdateExtension;
|
||||
import com.google.domain.registry.model.domain.secdns.SecDnsUpdateExtension;
|
||||
import com.google.domain.registry.model.registry.Registry.TldState;
|
||||
import com.google.domain.registry.model.reporting.HistoryEntry;
|
||||
|
||||
/**
|
||||
* An EPP flow that updates a domain resource.
|
||||
*
|
||||
* @error {@link com.google.domain.registry.flows.EppException.UnimplementedExtensionException}
|
||||
* @error {@link com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceUpdateFlow.AddRemoveSameValueEppException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceUpdateFlow.ResourceHasClientUpdateProhibitedException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceUpdateFlow.StatusNotClientSettableException}
|
||||
* @error {@link com.google.domain.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.LinkedResourceDoesNotExistException}
|
||||
* @error {@link DomainFlowUtils.MissingAdminContactException}
|
||||
* @error {@link DomainFlowUtils.MissingContactTypeException}
|
||||
* @error {@link DomainFlowUtils.MissingTechnicalContactException}
|
||||
* @error {@link DomainFlowUtils.NameserverNotAllowedException}
|
||||
* @error {@link DomainFlowUtils.RegistrantNotAllowedException}
|
||||
* @error {@link DomainFlowUtils.TooManyDsRecordsException}
|
||||
* @error {@link DomainFlowUtils.TooManyNameserversException}
|
||||
* @error {@link DomainApplicationUpdateFlow.ApplicationStatusProhibitsUpdateException}
|
||||
*/
|
||||
public class DomainApplicationUpdateFlow
|
||||
extends BaseDomainUpdateFlow<DomainApplication, Builder> {
|
||||
|
||||
@Override
|
||||
protected void initDomainUpdateFlow() throws EppException {
|
||||
registerExtensions(LaunchUpdateExtension.class, SecDnsUpdateExtension.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void verifyDomainUpdateIsAllowed() throws EppException {
|
||||
switch (existingResource.getApplicationStatus()) {
|
||||
case PENDING_ALLOCATION:
|
||||
case PENDING_VALIDATION:
|
||||
case VALIDATED:
|
||||
return;
|
||||
default:
|
||||
throw new ApplicationStatusProhibitsUpdateException(
|
||||
existingResource.getApplicationStatus());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final ImmutableSet<TldState> getDisallowedTldStates() {
|
||||
return DISALLOWED_TLD_STATES_FOR_LAUNCH_FLOWS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final HistoryEntry.Type getHistoryEntryType() {
|
||||
return HistoryEntry.Type.DOMAIN_APPLICATION_UPDATE;
|
||||
}
|
||||
|
||||
/** Application status prohibits this domain update. */
|
||||
static class ApplicationStatusProhibitsUpdateException extends StatusProhibitsOperationException {
|
||||
public ApplicationStatusProhibitsUpdateException(ApplicationStatus status) {
|
||||
super(String.format(
|
||||
"Applications in state %s can not be updated",
|
||||
UPPER_UNDERSCORE.to(LOWER_CAMEL, status.name())));
|
||||
}
|
||||
}
|
||||
}
|
128
java/google/registry/flows/domain/DomainCheckFlow.java
Normal file
128
java/google/registry/flows/domain/DomainCheckFlow.java
Normal file
|
@ -0,0 +1,128 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.getReservationType;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.handleFeeRequest;
|
||||
import static com.google.domain.registry.model.EppResourceUtils.checkResourcesExist;
|
||||
import static com.google.domain.registry.model.registry.label.ReservationType.UNRESERVED;
|
||||
import static com.google.domain.registry.util.CollectionUtils.nullToEmpty;
|
||||
import static com.google.domain.registry.util.DomainNameUtils.getTldFromDomainName;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.flows.EppException.ParameterValuePolicyErrorException;
|
||||
import com.google.domain.registry.model.domain.fee.FeeCheckExtension;
|
||||
import com.google.domain.registry.model.domain.fee.FeeCheckResponseExtension;
|
||||
import com.google.domain.registry.model.domain.fee.FeeCheckResponseExtension.FeeCheck;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchCheckExtension;
|
||||
import com.google.domain.registry.model.eppcommon.ProtocolDefinition.ServiceExtension;
|
||||
import com.google.domain.registry.model.eppoutput.CheckData;
|
||||
import com.google.domain.registry.model.eppoutput.CheckData.DomainCheck;
|
||||
import com.google.domain.registry.model.eppoutput.CheckData.DomainCheckData;
|
||||
import com.google.domain.registry.model.eppoutput.Response.ResponseExtension;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.model.registry.label.ReservationType;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An EPP flow that checks whether a domain can be provisioned.
|
||||
*
|
||||
* @error {@link com.google.domain.registry.flows.ResourceCheckFlow.TooManyResourceChecksException}
|
||||
* @error {@link com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link DomainFlowUtils.BadDomainNameCharacterException}
|
||||
* @error {@link DomainFlowUtils.BadDomainNamePartsCountException}
|
||||
* @error {@link DomainFlowUtils.BadPeriodUnitException}
|
||||
* @error {@link DomainFlowUtils.CurrencyUnitMismatchException}
|
||||
* @error {@link DomainFlowUtils.DashesInThirdAndFourthException}
|
||||
* @error {@link DomainFlowUtils.DomainLabelTooLongException}
|
||||
* @error {@link DomainFlowUtils.EmptyDomainNamePartException}
|
||||
* @error {@link DomainFlowUtils.FeeChecksDontSupportPhasesException}
|
||||
* @error {@link DomainFlowUtils.InvalidIdnDomainLabelException}
|
||||
* @error {@link DomainFlowUtils.InvalidPunycodeException}
|
||||
* @error {@link DomainFlowUtils.LeadingDashException}
|
||||
* @error {@link DomainFlowUtils.RestoresAreAlwaysForOneYearException}
|
||||
* @error {@link DomainFlowUtils.TldDoesNotExistException}
|
||||
* @error {@link DomainFlowUtils.TrailingDashException}
|
||||
* @error {@link DomainFlowUtils.UnknownFeeCommandException}
|
||||
* @error {@link DomainCheckFlow.OnlyCheckedNamesCanBeFeeCheckedException}
|
||||
*/
|
||||
public class DomainCheckFlow extends BaseDomainCheckFlow {
|
||||
|
||||
@Override
|
||||
protected void initDomainCheckFlow() throws EppException {
|
||||
registerExtensions(LaunchCheckExtension.class, FeeCheckExtension.class);
|
||||
}
|
||||
|
||||
private String getMessageForCheck(String targetId, Set<String> existingIds) {
|
||||
if (existingIds.contains(targetId)) {
|
||||
return "In use";
|
||||
}
|
||||
InternetDomainName domainName = domainNames.get(targetId);
|
||||
ReservationType reservationType = getReservationType(domainName);
|
||||
Registry registry = Registry.get(domainName.parent().toString());
|
||||
if (reservationType == UNRESERVED
|
||||
&& registry.isPremiumName(domainName)
|
||||
&& registry.getPremiumPriceAckRequired()
|
||||
&& !nullToEmpty(sessionMetadata.getServiceExtensionUris()).contains(
|
||||
ServiceExtension.FEE_0_6.getUri())) {
|
||||
return "Premium names require EPP ext.";
|
||||
}
|
||||
return reservationType.getMessageForCheck();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CheckData getCheckData() {
|
||||
Set<String> existingIds = checkResourcesExist(resourceClass, targetIds, now);
|
||||
ImmutableList.Builder<DomainCheck> checks = new ImmutableList.Builder<>();
|
||||
for (String id : targetIds) {
|
||||
String message = getMessageForCheck(id, existingIds);
|
||||
checks.add(DomainCheck.create(message == null, id, message));
|
||||
}
|
||||
return DomainCheckData.create(checks.build());
|
||||
}
|
||||
|
||||
/** Handle the fee check extension. */
|
||||
@Override
|
||||
protected ImmutableList<? extends ResponseExtension> getResponseExtensions() throws EppException {
|
||||
FeeCheckExtension feeCheck = eppInput.getSingleExtension(FeeCheckExtension.class);
|
||||
if (feeCheck == null) {
|
||||
return null; // No fee checks were requested.
|
||||
}
|
||||
ImmutableList.Builder<FeeCheck> feeChecksBuilder = new ImmutableList.Builder<>();
|
||||
for (FeeCheckExtension.DomainCheck domainCheck : feeCheck.getDomains()) {
|
||||
String domainName = domainCheck.getName();
|
||||
if (!domainNames.containsKey(domainName)) {
|
||||
// Although the fee extension explicitly says it's ok to fee check a domain name that you
|
||||
// aren't also availability checking, we forbid it. This makes the experience simpler and
|
||||
// also means we can assume any domain names in the fee checks have been validated.
|
||||
throw new OnlyCheckedNamesCanBeFeeCheckedException();
|
||||
}
|
||||
FeeCheck.Builder builder = new FeeCheck.Builder();
|
||||
handleFeeRequest(domainCheck, builder, domainName, getTldFromDomainName(domainName), now);
|
||||
feeChecksBuilder.add(builder.setName(domainName).build());
|
||||
}
|
||||
return ImmutableList.of(FeeCheckResponseExtension.create(feeChecksBuilder.build()));
|
||||
}
|
||||
|
||||
/** By server policy, fee check names must be listed in the availability check. */
|
||||
static class OnlyCheckedNamesCanBeFeeCheckedException extends ParameterValuePolicyErrorException {
|
||||
OnlyCheckedNamesCanBeFeeCheckedException() {
|
||||
super("By server policy, fee check names must be listed in the availability check");
|
||||
}
|
||||
}
|
||||
}
|
199
java/google/registry/flows/domain/DomainCreateFlow.java
Normal file
199
java/google/registry/flows/domain/DomainCreateFlow.java
Normal file
|
@ -0,0 +1,199 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.validateFeeChallenge;
|
||||
import static com.google.domain.registry.model.index.DomainApplicationIndex.loadActiveApplicationsByDomainName;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.flows.EppException.CommandUseErrorException;
|
||||
import com.google.domain.registry.flows.EppException.StatusProhibitsOperationException;
|
||||
import com.google.domain.registry.model.billing.BillingEvent;
|
||||
import com.google.domain.registry.model.billing.BillingEvent.Reason;
|
||||
import com.google.domain.registry.model.domain.DomainApplication;
|
||||
import com.google.domain.registry.model.domain.DomainResource.Builder;
|
||||
import com.google.domain.registry.model.domain.GracePeriod;
|
||||
import com.google.domain.registry.model.domain.fee.FeeCreateExtension;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchCreateExtension;
|
||||
import com.google.domain.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.model.registry.Registry.TldState;
|
||||
import com.google.domain.registry.model.reporting.HistoryEntry;
|
||||
import com.google.domain.registry.tmch.LordnTask;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An EPP flow that creates a new domain resource.
|
||||
*
|
||||
* @error {@link com.google.domain.registry.flows.EppException.UnimplementedExtensionException}
|
||||
* @error {@link com.google.domain.registry.flows.LoggedInFlow.UndeclaredServiceExtensionException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceCreateFlow.ResourceAlreadyExistsException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceCreateOrMutateFlow.OnlyToolCanPassMetadataException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceFlow.BadCommandForRegistryPhaseException}
|
||||
* @error {@link com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link BaseDomainCreateFlow.AcceptedTooLongAgoException}
|
||||
* @error {@link BaseDomainCreateFlow.ClaimsPeriodEndedException}
|
||||
* @error {@link BaseDomainCreateFlow.ExpiredClaimException}
|
||||
* @error {@link BaseDomainCreateFlow.InvalidTcnIdChecksumException}
|
||||
* @error {@link BaseDomainCreateFlow.InvalidTrademarkValidatorException}
|
||||
* @error {@link BaseDomainCreateFlow.MalformedTcnIdException}
|
||||
* @error {@link BaseDomainCreateFlow.MaxSigLifeNotSupportedException}
|
||||
* @error {@link BaseDomainCreateFlow.MissingClaimsNoticeException}
|
||||
* @error {@link BaseDomainCreateFlow.UnexpectedClaimsNoticeException}
|
||||
* @error {@link BaseDomainCreateFlow.UnsupportedMarkTypeException}
|
||||
* @error {@link DomainCreateFlow.SignedMarksNotAcceptedInCurrentPhaseException}
|
||||
* @error {@link DomainFlowUtils.BadDomainNameCharacterException}
|
||||
* @error {@link DomainFlowUtils.BadDomainNamePartsCountException}
|
||||
* @error {@link DomainFlowUtils.BadPeriodUnitException}
|
||||
* @error {@link DomainFlowUtils.CurrencyUnitMismatchException}
|
||||
* @error {@link DomainFlowUtils.CurrencyValueScaleException}
|
||||
* @error {@link DomainFlowUtils.DashesInThirdAndFourthException}
|
||||
* @error {@link DomainFlowUtils.DomainLabelTooLongException}
|
||||
* @error {@link DomainFlowUtils.DomainReservedException}
|
||||
* @error {@link DomainFlowUtils.DuplicateContactForRoleException}
|
||||
* @error {@link DomainFlowUtils.EmptyDomainNamePartException}
|
||||
* @error {@link DomainFlowUtils.FeesMismatchException}
|
||||
* @error {@link DomainFlowUtils.FeesRequiredForPremiumNameException}
|
||||
* @error {@link DomainFlowUtils.InvalidIdnDomainLabelException}
|
||||
* @error {@link DomainFlowUtils.InvalidPunycodeException}
|
||||
* @error {@link DomainFlowUtils.LeadingDashException}
|
||||
* @error {@link DomainFlowUtils.LinkedResourceDoesNotExistException}
|
||||
* @error {@link DomainFlowUtils.LinkedResourceInPendingDeleteProhibitsOperationException}
|
||||
* @error {@link DomainFlowUtils.MissingAdminContactException}
|
||||
* @error {@link DomainFlowUtils.MissingContactTypeException}
|
||||
* @error {@link DomainFlowUtils.MissingRegistrantException}
|
||||
* @error {@link DomainFlowUtils.MissingTechnicalContactException}
|
||||
* @error {@link DomainFlowUtils.NameserverNotAllowedException}
|
||||
* @error {@link DomainFlowUtils.PremiumNameBlockedException}
|
||||
* @error {@link DomainFlowUtils.RegistrantNotAllowedException}
|
||||
* @error {@link DomainFlowUtils.TldDoesNotExistException}
|
||||
* @error {@link DomainFlowUtils.TooManyDsRecordsException}
|
||||
* @error {@link DomainFlowUtils.TooManyNameserversException}
|
||||
* @error {@link DomainFlowUtils.TrailingDashException}
|
||||
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
|
||||
* @error {@link DomainCreateFlow.DomainHasOpenApplicationsException}
|
||||
* @error {@link DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException}
|
||||
*/
|
||||
|
||||
public class DomainCreateFlow extends DomainCreateOrAllocateFlow {
|
||||
|
||||
private static final Set<TldState> QLP_SMD_ALLOWED_STATES =
|
||||
Sets.immutableEnumSet(TldState.SUNRISE, TldState.SUNRUSH);
|
||||
|
||||
private boolean isAnchorTenant() {
|
||||
return isAnchorTenantViaReservation || isAnchorTenantViaExtension;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void verifyDomainCreateIsAllowed() throws EppException {
|
||||
String tld = getTld();
|
||||
validateFeeChallenge(targetId, tld, feeCreate, createCost);
|
||||
if (!superuser) {
|
||||
// Prohibit creating a domain if there is an open application for the same name.
|
||||
for (DomainApplication application : loadActiveApplicationsByDomainName(targetId, now)) {
|
||||
if (!application.getApplicationStatus().isFinalStatus()) {
|
||||
throw new DomainHasOpenApplicationsException();
|
||||
}
|
||||
}
|
||||
// Prohibit registrations for non-qlp and non-superuser outside of GA.
|
||||
if (!isAnchorTenant()
|
||||
&& Registry.get(tld).getTldState(now) != TldState.GENERAL_AVAILABILITY) {
|
||||
throw new NoGeneralRegistrationsInCurrentPhaseException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void initDomainCreateOrAllocateFlow() {
|
||||
registerExtensions(FeeCreateExtension.class, LaunchCreateExtension.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void validateDomainLaunchCreateExtension() throws EppException {
|
||||
// We can assume launchCreate is not null here.
|
||||
// Only QLP domains can have a signed mark on a domain create, and only in sunrise or sunrush.
|
||||
if (hasSignedMarks) {
|
||||
if (isAnchorTenant() && QLP_SMD_ALLOWED_STATES.contains(
|
||||
Registry.get(getTld()).getTldState(now))) {
|
||||
return;
|
||||
}
|
||||
throw new SignedMarksNotAcceptedInCurrentPhaseException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void setDomainCreateOrAllocateProperties(Builder builder) throws EppException {
|
||||
Registry registry = Registry.get(getTld());
|
||||
// Bill for the create.
|
||||
BillingEvent.OneTime createEvent = new BillingEvent.OneTime.Builder()
|
||||
.setReason(Reason.CREATE)
|
||||
.setTargetId(targetId)
|
||||
.setClientId(getClientId())
|
||||
.setPeriodYears(command.getPeriod().getValue())
|
||||
.setCost(checkNotNull(createCost))
|
||||
.setEventTime(now)
|
||||
.setBillingTime(now.plus(isAnchorTenant()
|
||||
? registry.getAnchorTenantAddGracePeriodLength()
|
||||
: registry.getAddGracePeriodLength()))
|
||||
.setFlags(isAnchorTenant() ? ImmutableSet.of(BillingEvent.Flag.ANCHOR_TENANT) : null)
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
ofy().save().entity(createEvent);
|
||||
builder.addGracePeriod(GracePeriod.forBillingEvent(GracePeriodStatus.ADD, createEvent));
|
||||
if (launchCreate != null && (launchCreate.getNotice() != null || hasSignedMarks)) {
|
||||
builder
|
||||
.setLaunchNotice(launchCreate.getNotice())
|
||||
.setSmdId(signedMark == null ? null : signedMark.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enqueueLordnTaskIfNeeded() {
|
||||
if (launchCreate != null && (launchCreate.getNotice() != null || hasSignedMarks)) {
|
||||
LordnTask.enqueueDomainResourceTask(newResource);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final HistoryEntry.Type getHistoryEntryType() {
|
||||
return HistoryEntry.Type.DOMAIN_CREATE;
|
||||
}
|
||||
|
||||
/** There is an open application for this domain. */
|
||||
static class DomainHasOpenApplicationsException extends StatusProhibitsOperationException {
|
||||
public DomainHasOpenApplicationsException() {
|
||||
super("There is an open application for this domain");
|
||||
}
|
||||
}
|
||||
|
||||
/** Signed marks are not accepted in the current registry phase. */
|
||||
static class SignedMarksNotAcceptedInCurrentPhaseException extends CommandUseErrorException {
|
||||
public SignedMarksNotAcceptedInCurrentPhaseException() {
|
||||
super("Signed marks are not accepted in the current registry phase");
|
||||
}
|
||||
}
|
||||
|
||||
/** The current registry phase does not allow for general registrations. */
|
||||
static class NoGeneralRegistrationsInCurrentPhaseException extends CommandUseErrorException {
|
||||
public NoGeneralRegistrationsInCurrentPhaseException() {
|
||||
super("The current registry phase does not allow for general registrations");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static com.google.domain.registry.util.DateTimeUtils.leapSafeAddYears;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.dns.DnsQueue;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.model.billing.BillingEvent;
|
||||
import com.google.domain.registry.model.billing.BillingEvent.Flag;
|
||||
import com.google.domain.registry.model.billing.BillingEvent.Reason;
|
||||
import com.google.domain.registry.model.domain.DomainResource;
|
||||
import com.google.domain.registry.model.domain.DomainResource.Builder;
|
||||
import com.google.domain.registry.model.domain.Period;
|
||||
import com.google.domain.registry.model.domain.fee.Fee;
|
||||
import com.google.domain.registry.model.domain.fee.FeeCreateResponseExtension;
|
||||
import com.google.domain.registry.model.domain.metadata.MetadataExtension;
|
||||
import com.google.domain.registry.model.eppoutput.CreateData.DomainCreateData;
|
||||
import com.google.domain.registry.model.eppoutput.EppOutput;
|
||||
import com.google.domain.registry.model.eppoutput.Result;
|
||||
import com.google.domain.registry.model.poll.PollMessage;
|
||||
|
||||
import com.googlecode.objectify.Ref;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** An EPP flow that creates or allocates a new domain resource. */
|
||||
public abstract class DomainCreateOrAllocateFlow
|
||||
extends BaseDomainCreateFlow<DomainResource, Builder> {
|
||||
|
||||
protected boolean isAnchorTenantViaExtension;
|
||||
|
||||
@Override
|
||||
protected final void initDomainCreateFlow() {
|
||||
registerExtensions(MetadataExtension.class);
|
||||
isAnchorTenantViaExtension =
|
||||
(metadataExtension != null && metadataExtension.getIsAnchorTenant());
|
||||
initDomainCreateOrAllocateFlow();
|
||||
}
|
||||
|
||||
protected abstract void initDomainCreateOrAllocateFlow();
|
||||
|
||||
@Override
|
||||
protected final void setDomainCreateProperties(Builder builder) throws EppException {
|
||||
DateTime registrationExpirationTime = leapSafeAddYears(now, command.getPeriod().getValue());
|
||||
// Create a new autorenew billing event and poll message starting at the expiration time.
|
||||
BillingEvent.Recurring autorenewEvent = new BillingEvent.Recurring.Builder()
|
||||
.setReason(Reason.RENEW)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setTargetId(targetId)
|
||||
.setClientId(getClientId())
|
||||
.setEventTime(registrationExpirationTime)
|
||||
.setRecurrenceEndTime(END_OF_TIME)
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
PollMessage.Autorenew autorenewPollMessage = new PollMessage.Autorenew.Builder()
|
||||
.setTargetId(targetId)
|
||||
.setClientId(getClientId())
|
||||
.setEventTime(registrationExpirationTime)
|
||||
.setMsg("Domain was auto-renewed.")
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
ofy().save().<Object>entities(autorenewEvent, autorenewPollMessage);
|
||||
|
||||
builder
|
||||
.setRegistrationExpirationTime(registrationExpirationTime)
|
||||
.setAutorenewBillingEvent(Ref.create(autorenewEvent))
|
||||
.setAutorenewPollMessage(Ref.create(autorenewPollMessage));
|
||||
setDomainCreateOrAllocateProperties(builder);
|
||||
}
|
||||
|
||||
/** Subclasses must override this to set more fields, like any grace period. */
|
||||
protected abstract void setDomainCreateOrAllocateProperties(Builder builder) throws EppException;
|
||||
|
||||
@Override
|
||||
protected final void enqueueTasks() {
|
||||
if (newResource.shouldPublishToDns()) {
|
||||
DnsQueue.create().addDomainRefreshTask(newResource.getFullyQualifiedDomainName());
|
||||
}
|
||||
enqueueLordnTaskIfNeeded();
|
||||
}
|
||||
|
||||
/** Subclasses must override this to enqueue any additional tasks. */
|
||||
protected abstract void enqueueLordnTaskIfNeeded();
|
||||
|
||||
@Override
|
||||
protected final Period getCommandPeriod() {
|
||||
return command.getPeriod();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final EppOutput getOutput() {
|
||||
return createOutput(
|
||||
Result.Code.Success,
|
||||
DomainCreateData.create(
|
||||
newResource.getFullyQualifiedDomainName(),
|
||||
now,
|
||||
newResource.getRegistrationExpirationTime()),
|
||||
(feeCreate == null) ? null : ImmutableList.of(
|
||||
new FeeCreateResponseExtension.Builder()
|
||||
.setCurrency(createCost.getCurrencyUnit())
|
||||
.setFee(ImmutableList.of(Fee.create(createCost.getAmount(), "create")))
|
||||
.build()));
|
||||
}
|
||||
}
|
197
java/google/registry/flows/domain/DomainDeleteFlow.java
Normal file
197
java/google/registry/flows/domain/DomainDeleteFlow.java
Normal file
|
@ -0,0 +1,197 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurrenceEndTime;
|
||||
import static com.google.domain.registry.model.eppoutput.Result.Code.Success;
|
||||
import static com.google.domain.registry.model.eppoutput.Result.Code.SuccessWithActionPending;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.util.CollectionUtils.nullToEmpty;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.dns.DnsQueue;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.flows.EppException.AssociationProhibitsOperationException;
|
||||
import com.google.domain.registry.flows.ResourceSyncDeleteFlow;
|
||||
import com.google.domain.registry.model.billing.BillingEvent;
|
||||
import com.google.domain.registry.model.common.TimeOfYear;
|
||||
import com.google.domain.registry.model.domain.DomainCommand.Delete;
|
||||
import com.google.domain.registry.model.domain.DomainResource;
|
||||
import com.google.domain.registry.model.domain.DomainResource.Builder;
|
||||
import com.google.domain.registry.model.domain.GracePeriod;
|
||||
import com.google.domain.registry.model.domain.fee.Credit;
|
||||
import com.google.domain.registry.model.domain.fee.FeeDeleteResponseExtension;
|
||||
import com.google.domain.registry.model.domain.metadata.MetadataExtension;
|
||||
import com.google.domain.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import com.google.domain.registry.model.domain.secdns.SecDnsUpdateExtension;
|
||||
import com.google.domain.registry.model.eppcommon.ProtocolDefinition.ServiceExtension;
|
||||
import com.google.domain.registry.model.eppcommon.StatusValue;
|
||||
import com.google.domain.registry.model.eppoutput.Response.ResponseExtension;
|
||||
import com.google.domain.registry.model.eppoutput.Result.Code;
|
||||
import com.google.domain.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
|
||||
import com.google.domain.registry.model.poll.PollMessage;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.model.reporting.HistoryEntry;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* An EPP flow that deletes a domain resource.
|
||||
*
|
||||
* @error {@link com.google.domain.registry.flows.ResourceCreateOrMutateFlow.OnlyToolCanPassMetadataException}
|
||||
* @error {@link com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException}
|
||||
* @error {@link com.google.domain.registry.flows.SingleResourceFlow.ResourceStatusProhibitsOperationException}
|
||||
* @error {@link DomainDeleteFlow.DomainToDeleteHasHostsException}
|
||||
*/
|
||||
public class DomainDeleteFlow extends ResourceSyncDeleteFlow<DomainResource, Builder, Delete> {
|
||||
|
||||
PollMessage.OneTime deletePollMessage;
|
||||
|
||||
CurrencyUnit creditsCurrencyUnit;
|
||||
|
||||
ImmutableList<Credit> credits;
|
||||
|
||||
@Override
|
||||
protected void initResourceCreateOrMutateFlow() throws EppException {
|
||||
registerExtensions(SecDnsUpdateExtension.class, MetadataExtension.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void verifyMutationOnOwnedResourceAllowed() throws EppException {
|
||||
checkRegistryStateForTld(existingResource.getTld());
|
||||
checkAllowedAccessToTld(getAllowedTlds(), existingResource.getTld());
|
||||
if (!existingResource.getSubordinateHosts().isEmpty()) {
|
||||
throw new DomainToDeleteHasHostsException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void setDeleteProperties(Builder builder) {
|
||||
// Only set to PENDING_DELETE if this domain is not in the Add Grace Period. If domain is in Add
|
||||
// Grace Period, we delete it immediately.
|
||||
// The base class code already handles the immediate delete case, so we only have to handle the
|
||||
// pending delete case here.
|
||||
if (!existingResource.getGracePeriodStatuses().contains(GracePeriodStatus.ADD)) {
|
||||
Registry registry = Registry.get(existingResource.getTld());
|
||||
// By default, this should be 30 days of grace, and 5 days of pending delete. */
|
||||
DateTime deletionTime = now
|
||||
.plus(registry.getRedemptionGracePeriodLength())
|
||||
.plus(registry.getPendingDeleteLength());
|
||||
deletePollMessage = new PollMessage.OneTime.Builder()
|
||||
.setClientId(existingResource.getCurrentSponsorClientId())
|
||||
.setEventTime(deletionTime)
|
||||
.setMsg("Domain deleted.")
|
||||
.setResponseData(ImmutableList.of(DomainPendingActionNotificationResponse.create(
|
||||
existingResource.getFullyQualifiedDomainName(), true, trid, deletionTime)))
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
builder.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
|
||||
.setDeletionTime(deletionTime)
|
||||
// Clear out all old grace periods and add REDEMPTION, which does not include a ref
|
||||
// to a billing event because there isn't one for a domain delete.
|
||||
.setGracePeriods(ImmutableSet.of(GracePeriod.createWithoutBillingEvent(
|
||||
GracePeriodStatus.REDEMPTION,
|
||||
now.plus(registry.getRedemptionGracePeriodLength()),
|
||||
getClientId())))
|
||||
.setDeletePollMessage(Key.create(deletePollMessage));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void enqueueTasks() {
|
||||
DnsQueue.create().addDomainRefreshTask(existingResource.getFullyQualifiedDomainName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void modifySyncDeleteRelatedResources() {
|
||||
// Cancel any grace periods that were still active.
|
||||
ImmutableList.Builder<Credit> creditsBuilder = new ImmutableList.Builder<>();
|
||||
for (GracePeriod gracePeriod : existingResource.getGracePeriods()) {
|
||||
// No cancellation is written if the grace period was not for a billable event.
|
||||
if (gracePeriod.hasBillingEvent()) {
|
||||
ofy().save().entity(
|
||||
BillingEvent.Cancellation.forGracePeriod(gracePeriod, historyEntry, targetId));
|
||||
|
||||
Money cost;
|
||||
if (gracePeriod.getType() == GracePeriodStatus.AUTO_RENEW) {
|
||||
TimeOfYear recurrenceTimeOfYear =
|
||||
checkNotNull(gracePeriod.getRecurringBillingEvent()).get().getRecurrenceTimeOfYear();
|
||||
DateTime autoRenewTime = recurrenceTimeOfYear.beforeOrAt(now);
|
||||
cost = Registry.get(existingResource.getTld())
|
||||
.getDomainRenewCost(targetId, 1, autoRenewTime);
|
||||
} else {
|
||||
cost = checkNotNull(gracePeriod.getOneTimeBillingEvent()).get().getCost();
|
||||
}
|
||||
creditsBuilder.add(Credit.create(
|
||||
cost.negated().getAmount(),
|
||||
String.format("%s credit", gracePeriod.getType().getXmlName())));
|
||||
creditsCurrencyUnit = cost.getCurrencyUnit();
|
||||
}
|
||||
}
|
||||
credits = creditsBuilder.build();
|
||||
|
||||
// If the delete isn't immediate, save the poll message for when the delete will happen.
|
||||
if (deletePollMessage != null) {
|
||||
ofy().save().entity(deletePollMessage);
|
||||
}
|
||||
// Close the autorenew billing event and poll message. This may delete the poll message.
|
||||
updateAutorenewRecurrenceEndTime(existingResource, now);
|
||||
|
||||
// If there's a pending transfer, the gaining client's autorenew billing
|
||||
// event and poll message will already have been deleted in
|
||||
// ResourceDeleteFlow since it's listed in serverApproveEntities.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final Code getDeleteResultCode() {
|
||||
return newResource.getDeletionTime().isAfter(now)
|
||||
? SuccessWithActionPending : Success;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final ImmutableList<? extends ResponseExtension> getDeleteResponseExtensions() {
|
||||
if (!credits.isEmpty()
|
||||
&& nullToEmpty(sessionMetadata.getServiceExtensionUris()).contains(
|
||||
ServiceExtension.FEE_0_6.getUri())) {
|
||||
return ImmutableList.of(new FeeDeleteResponseExtension.Builder()
|
||||
.setCurrency(checkNotNull(creditsCurrencyUnit))
|
||||
.setCredits(credits)
|
||||
.build());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final HistoryEntry.Type getHistoryEntryType() {
|
||||
return HistoryEntry.Type.DOMAIN_DELETE;
|
||||
}
|
||||
|
||||
/** Domain to be deleted has subordinate hosts. */
|
||||
static class DomainToDeleteHasHostsException extends AssociationProhibitsOperationException {
|
||||
public DomainToDeleteHasHostsException() {
|
||||
super("Domain to be deleted has subordinate hosts");
|
||||
}
|
||||
}
|
||||
}
|
1006
java/google/registry/flows/domain/DomainFlowUtils.java
Normal file
1006
java/google/registry/flows/domain/DomainFlowUtils.java
Normal file
File diff suppressed because it is too large
Load diff
94
java/google/registry/flows/domain/DomainInfoFlow.java
Normal file
94
java/google/registry/flows/domain/DomainInfoFlow.java
Normal file
|
@ -0,0 +1,94 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.handleFeeRequest;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.model.domain.DomainResource;
|
||||
import com.google.domain.registry.model.domain.DomainResource.Builder;
|
||||
import com.google.domain.registry.model.domain.fee.FeeInfoExtension;
|
||||
import com.google.domain.registry.model.domain.fee.FeeInfoResponseExtension;
|
||||
import com.google.domain.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import com.google.domain.registry.model.domain.rgp.RgpInfoExtension;
|
||||
import com.google.domain.registry.model.eppoutput.Response.ResponseExtension;
|
||||
|
||||
/**
|
||||
* An EPP flow that reads a domain.
|
||||
*
|
||||
* @error {@link com.google.domain.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceQueryFlow.ResourceToQueryDoesNotExistException}
|
||||
* @error {@link DomainFlowUtils.BadPeriodUnitException}
|
||||
* @error {@link DomainFlowUtils.CurrencyUnitMismatchException}
|
||||
* @error {@link DomainFlowUtils.FeeChecksDontSupportPhasesException}
|
||||
* @error {@link DomainFlowUtils.RestoresAreAlwaysForOneYearException}
|
||||
*/
|
||||
public class DomainInfoFlow extends BaseDomainInfoFlow<DomainResource, Builder> {
|
||||
|
||||
@Override
|
||||
protected void initSingleResourceFlow() throws EppException {
|
||||
registerExtensions(FeeInfoExtension.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final DomainResource getResourceInfo() {
|
||||
// If authInfo is non-null, then the caller is authorized to see the full information since we
|
||||
// will have already verified the authInfo is valid in ResourceQueryFlow.verifyIsAllowed().
|
||||
if (!getClientId().equals(existingResource.getCurrentSponsorClientId())
|
||||
&& command.getAuthInfo() == null) {
|
||||
// Registrars can only see a few fields on unauthorized domains.
|
||||
// This is a policy decision that is left up to us by the rfcs.
|
||||
return new DomainResource.Builder()
|
||||
.setFullyQualifiedDomainName(existingResource.getFullyQualifiedDomainName())
|
||||
.setRepoId(existingResource.getRepoId())
|
||||
.setCurrentSponsorClientId(existingResource.getCurrentSponsorClientId())
|
||||
// If we didn't do this, we'd get implicit status values.
|
||||
.buildWithoutImplicitStatusValues();
|
||||
}
|
||||
Builder info = existingResource.asBuilder();
|
||||
if (!command.getHostsRequest().requestSubordinate()) {
|
||||
info.setSubordinateHosts(null);
|
||||
}
|
||||
if (!command.getHostsRequest().requestDelegated()) {
|
||||
// Delegated hosts are present by default, so clear them out if they aren't wanted.
|
||||
// This requires overriding the implicit status values so that we don't get INACTIVE added due
|
||||
// to the missing nameservers.
|
||||
return info.setNameservers(null).buildWithoutImplicitStatusValues();
|
||||
}
|
||||
return info.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final ImmutableList<ResponseExtension> getDomainResponseExtensions()
|
||||
throws EppException {
|
||||
ImmutableList.Builder<ResponseExtension> extensions = new ImmutableList.Builder<>();
|
||||
// According to RFC 5910 section 2, we should only return this if the client specified the
|
||||
// "urn:ietf:params:xml:ns:rgp-1.0" when logging in. However, this is a "SHOULD" not a "MUST"
|
||||
// and we are going to ignore it; clients who don't care about rgp can just ignore it.
|
||||
ImmutableSet<GracePeriodStatus> gracePeriodStatuses = existingResource.getGracePeriodStatuses();
|
||||
if (!gracePeriodStatuses.isEmpty()) {
|
||||
extensions.add(RgpInfoExtension.create(gracePeriodStatuses));
|
||||
}
|
||||
FeeInfoExtension feeInfo = eppInput.getSingleExtension(FeeInfoExtension.class);
|
||||
if (feeInfo != null) { // Fee check was requested.
|
||||
FeeInfoResponseExtension.Builder builder = new FeeInfoResponseExtension.Builder();
|
||||
handleFeeRequest(feeInfo, builder, getTargetId(), existingResource.getTld(), now);
|
||||
extensions.add(builder.build());
|
||||
}
|
||||
return extensions.build();
|
||||
}
|
||||
}
|
206
java/google/registry/flows/domain/DomainRenewFlow.java
Normal file
206
java/google/registry/flows/domain/DomainRenewFlow.java
Normal file
|
@ -0,0 +1,206 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.newAutorenewBillingEvent;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.newAutorenewPollMessage;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurrenceEndTime;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.validateFeeChallenge;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears;
|
||||
import static com.google.domain.registry.model.domain.DomainResource.MAX_REGISTRATION_YEARS;
|
||||
import static com.google.domain.registry.model.eppoutput.Result.Code.Success;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.util.DateTimeUtils.leapSafeAddYears;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.flows.EppException.ObjectPendingTransferException;
|
||||
import com.google.domain.registry.flows.EppException.ParameterValueRangeErrorException;
|
||||
import com.google.domain.registry.flows.OwnedResourceMutateFlow;
|
||||
import com.google.domain.registry.model.billing.BillingEvent;
|
||||
import com.google.domain.registry.model.billing.BillingEvent.Reason;
|
||||
import com.google.domain.registry.model.domain.DomainCommand.Renew;
|
||||
import com.google.domain.registry.model.domain.DomainRenewData;
|
||||
import com.google.domain.registry.model.domain.DomainResource;
|
||||
import com.google.domain.registry.model.domain.GracePeriod;
|
||||
import com.google.domain.registry.model.domain.Period;
|
||||
import com.google.domain.registry.model.domain.fee.Fee;
|
||||
import com.google.domain.registry.model.domain.fee.FeeRenewExtension;
|
||||
import com.google.domain.registry.model.domain.fee.FeeRenewResponseExtension;
|
||||
import com.google.domain.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import com.google.domain.registry.model.eppcommon.StatusValue;
|
||||
import com.google.domain.registry.model.eppoutput.EppOutput;
|
||||
import com.google.domain.registry.model.poll.PollMessage;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.model.reporting.HistoryEntry;
|
||||
import com.google.domain.registry.model.transfer.TransferStatus;
|
||||
|
||||
import com.googlecode.objectify.Ref;
|
||||
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An EPP flow that updates a domain resource.
|
||||
*
|
||||
* @error {@link com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException}
|
||||
* @error {@link com.google.domain.registry.flows.SingleResourceFlow.ResourceStatusProhibitsOperationException}
|
||||
* @error {@link DomainFlowUtils.BadPeriodUnitException}
|
||||
* @error {@link DomainFlowUtils.CurrencyUnitMismatchException}
|
||||
* @error {@link DomainFlowUtils.CurrencyValueScaleException}
|
||||
* @error {@link DomainFlowUtils.FeesMismatchException}
|
||||
* @error {@link DomainFlowUtils.FeesRequiredForPremiumNameException}
|
||||
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
|
||||
* @error {@link DomainRenewFlow.DomainHasPendingTransferException}
|
||||
* @error {@link DomainRenewFlow.ExceedsMaxRegistrationYearsException}
|
||||
* @error {@link DomainRenewFlow.IncorrectCurrentExpirationDateException}
|
||||
*/
|
||||
public class DomainRenewFlow extends OwnedResourceMutateFlow<DomainResource, Renew> {
|
||||
|
||||
private static final Set<StatusValue> RENEW_DISALLOWED_STATUSES = ImmutableSet.of(
|
||||
StatusValue.CLIENT_RENEW_PROHIBITED,
|
||||
StatusValue.PENDING_DELETE,
|
||||
StatusValue.SERVER_RENEW_PROHIBITED);
|
||||
|
||||
protected FeeRenewExtension feeRenew;
|
||||
protected Money renewCost;
|
||||
|
||||
@Override
|
||||
protected Set<StatusValue> getDisallowedStatuses() {
|
||||
return RENEW_DISALLOWED_STATUSES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void initResourceCreateOrMutateFlow() throws EppException {
|
||||
registerExtensions(FeeRenewExtension.class);
|
||||
feeRenew = eppInput.getSingleExtension(FeeRenewExtension.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void verifyMutationOnOwnedResourceAllowed() throws EppException {
|
||||
checkAllowedAccessToTld(getAllowedTlds(), existingResource.getTld());
|
||||
// Verify that the resource does not have a pending transfer on it.
|
||||
if (existingResource.getTransferData().getTransferStatus() == TransferStatus.PENDING) {
|
||||
throw new DomainHasPendingTransferException(targetId);
|
||||
}
|
||||
verifyUnitIsYears(command.getPeriod());
|
||||
// If the date they specify doesn't match the expiration, fail. (This is an idempotence check).
|
||||
if (!command.getCurrentExpirationDate().equals(
|
||||
existingResource.getRegistrationExpirationTime().toLocalDate())) {
|
||||
throw new IncorrectCurrentExpirationDateException();
|
||||
}
|
||||
renewCost = Registry.get(existingResource.getTld())
|
||||
.getDomainRenewCost(targetId, command.getPeriod().getValue(), now);
|
||||
validateFeeChallenge(targetId, existingResource.getTld(), feeRenew, renewCost);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DomainResource createOrMutateResource() {
|
||||
DateTime newExpirationTime = leapSafeAddYears(
|
||||
existingResource.getRegistrationExpirationTime(), command.getPeriod().getValue());
|
||||
// Bill for this explicit renew itself.
|
||||
BillingEvent.OneTime explicitRenewEvent = new BillingEvent.OneTime.Builder()
|
||||
.setReason(Reason.RENEW)
|
||||
.setTargetId(targetId)
|
||||
.setClientId(getClientId())
|
||||
.setPeriodYears(command.getPeriod().getValue())
|
||||
.setCost(checkNotNull(renewCost))
|
||||
.setEventTime(now)
|
||||
.setBillingTime(
|
||||
now.plus(Registry.get(existingResource.getTld()).getRenewGracePeriodLength()))
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
// End the old autorenew billing event and poll message now. This may delete the poll message.
|
||||
updateAutorenewRecurrenceEndTime(existingResource, now);
|
||||
// Create a new autorenew billing event and poll message starting at the new expiration time.
|
||||
BillingEvent.Recurring newAutorenewEvent = newAutorenewBillingEvent(existingResource)
|
||||
.setEventTime(newExpirationTime)
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
PollMessage.Autorenew newAutorenewPollMessage = newAutorenewPollMessage(existingResource)
|
||||
.setEventTime(newExpirationTime)
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
ofy().save().<Object>entities(explicitRenewEvent, newAutorenewEvent, newAutorenewPollMessage);
|
||||
return existingResource.asBuilder()
|
||||
.setRegistrationExpirationTime(newExpirationTime)
|
||||
.setAutorenewBillingEvent(Ref.create(newAutorenewEvent))
|
||||
.setAutorenewPollMessage(Ref.create(newAutorenewPollMessage))
|
||||
.addGracePeriod(GracePeriod.forBillingEvent(GracePeriodStatus.RENEW, explicitRenewEvent))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void verifyNewStateIsAllowed() throws EppException {
|
||||
if (leapSafeAddYears(now, MAX_REGISTRATION_YEARS)
|
||||
.isBefore(newResource.getRegistrationExpirationTime())) {
|
||||
throw new ExceedsMaxRegistrationYearsException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final HistoryEntry.Type getHistoryEntryType() {
|
||||
return HistoryEntry.Type.DOMAIN_RENEW;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final Period getCommandPeriod() {
|
||||
return command.getPeriod();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final EppOutput getOutput() {
|
||||
return createOutput(
|
||||
Success,
|
||||
DomainRenewData.create(
|
||||
newResource.getFullyQualifiedDomainName(),
|
||||
newResource.getRegistrationExpirationTime()),
|
||||
(feeRenew == null) ? null : ImmutableList.of(
|
||||
new FeeRenewResponseExtension.Builder()
|
||||
.setCurrency(renewCost.getCurrencyUnit())
|
||||
.setFee(ImmutableList.of(Fee.create(renewCost.getAmount(), "renew")))
|
||||
.build()));
|
||||
}
|
||||
|
||||
/** The domain has a pending transfer on it and so can't be explicitly renewed. */
|
||||
public static class DomainHasPendingTransferException extends ObjectPendingTransferException {
|
||||
public DomainHasPendingTransferException(String targetId) {
|
||||
super(targetId);
|
||||
}
|
||||
}
|
||||
|
||||
/** The current expiration date is incorrect. */
|
||||
static class IncorrectCurrentExpirationDateException extends ParameterValueRangeErrorException {
|
||||
public IncorrectCurrentExpirationDateException() {
|
||||
super("The current expiration date is incorrect");
|
||||
}
|
||||
}
|
||||
|
||||
/** New registration period exceeds maximum number of years. */
|
||||
static class ExceedsMaxRegistrationYearsException extends ParameterValueRangeErrorException {
|
||||
public ExceedsMaxRegistrationYearsException() {
|
||||
super(String.format(
|
||||
"Registrations cannot extend for more than %d years into the future",
|
||||
MAX_REGISTRATION_YEARS));
|
||||
}
|
||||
}
|
||||
}
|
208
java/google/registry/flows/domain/DomainRestoreRequestFlow.java
Normal file
208
java/google/registry/flows/domain/DomainRestoreRequestFlow.java
Normal file
|
@ -0,0 +1,208 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.newAutorenewBillingEvent;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.newAutorenewPollMessage;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.validateFeeChallenge;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.verifyNotReserved;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.verifyPremiumNameIsNotBlocked;
|
||||
import static com.google.domain.registry.model.eppoutput.Result.Code.Success;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.google.domain.registry.dns.DnsQueue;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.flows.EppException.CommandUseErrorException;
|
||||
import com.google.domain.registry.flows.EppException.StatusProhibitsOperationException;
|
||||
import com.google.domain.registry.flows.OwnedResourceMutateFlow;
|
||||
import com.google.domain.registry.model.billing.BillingEvent;
|
||||
import com.google.domain.registry.model.billing.BillingEvent.Reason;
|
||||
import com.google.domain.registry.model.domain.DomainCommand.Update;
|
||||
import com.google.domain.registry.model.domain.DomainResource;
|
||||
import com.google.domain.registry.model.domain.fee.Fee;
|
||||
import com.google.domain.registry.model.domain.fee.FeeUpdateExtension;
|
||||
import com.google.domain.registry.model.domain.fee.FeeUpdateResponseExtension;
|
||||
import com.google.domain.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import com.google.domain.registry.model.domain.rgp.RgpUpdateExtension;
|
||||
import com.google.domain.registry.model.eppcommon.StatusValue;
|
||||
import com.google.domain.registry.model.eppoutput.EppOutput;
|
||||
import com.google.domain.registry.model.index.ForeignKeyIndex;
|
||||
import com.google.domain.registry.model.poll.PollMessage;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.model.reporting.HistoryEntry;
|
||||
|
||||
import com.googlecode.objectify.Ref;
|
||||
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* An EPP flow that requests that a deleted domain be restored.
|
||||
*
|
||||
* @error {@link com.google.domain.registry.flows.EppException.UnimplementedExtensionException}
|
||||
* @error {@link com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException}
|
||||
* @error {@link DomainFlowUtils.CurrencyUnitMismatchException}
|
||||
* @error {@link DomainFlowUtils.CurrencyValueScaleException}
|
||||
* @error {@link DomainFlowUtils.DomainReservedException}
|
||||
* @error {@link DomainFlowUtils.FeesMismatchException}
|
||||
* @error {@link DomainFlowUtils.FeesRequiredForPremiumNameException}
|
||||
* @error {@link DomainFlowUtils.PremiumNameBlockedException}
|
||||
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
|
||||
* @error {@link DomainRestoreRequestFlow.DomainNotEligibleForRestoreException}
|
||||
* @error {@link DomainRestoreRequestFlow.RestoreCommandIncludesChangesException}
|
||||
*/
|
||||
public class DomainRestoreRequestFlow extends OwnedResourceMutateFlow<DomainResource, Update> {
|
||||
|
||||
protected FeeUpdateExtension feeUpdate;
|
||||
protected Money restoreCost;
|
||||
protected Money renewCost;
|
||||
|
||||
@Override
|
||||
protected final void initResourceCreateOrMutateFlow() throws EppException {
|
||||
registerExtensions(FeeUpdateExtension.class, RgpUpdateExtension.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void verifyMutationOnOwnedResourceAllowed() throws EppException {
|
||||
// No other changes can be specified on a restore request.
|
||||
if (!command.noChangesPresent()) {
|
||||
throw new RestoreCommandIncludesChangesException();
|
||||
}
|
||||
|
||||
// Domain must be in pendingDelete and within the redemptionPeriod to be eligible for restore.
|
||||
if (!existingResource.getStatusValues().contains(StatusValue.PENDING_DELETE)
|
||||
|| !existingResource.getGracePeriodStatuses().contains(GracePeriodStatus.REDEMPTION)) {
|
||||
throw new DomainNotEligibleForRestoreException();
|
||||
}
|
||||
|
||||
String tld = existingResource.getTld();
|
||||
checkAllowedAccessToTld(getAllowedTlds(), tld);
|
||||
if (!superuser) {
|
||||
verifyNotReserved(InternetDomainName.from(targetId), false);
|
||||
verifyPremiumNameIsNotBlocked(targetId, tld, getClientId());
|
||||
}
|
||||
feeUpdate = eppInput.getSingleExtension(FeeUpdateExtension.class);
|
||||
restoreCost = Registry.get(tld).getStandardRestoreCost();
|
||||
renewCost = Registry.get(tld).getDomainRenewCost(targetId, 1, now);
|
||||
validateFeeChallenge(targetId, tld, feeUpdate, restoreCost, renewCost);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final DomainResource createOrMutateResource() throws EppException {
|
||||
// We don't preserve the original expiration time of the domain when we restore, since doing so
|
||||
// would require us to know if they received a grace period refund when they deleted the domain,
|
||||
// and to charge them for that again. Instead, we just say that all restores get a fresh year of
|
||||
// registration and bill them for that accordingly.
|
||||
DateTime newExpirationTime = now.plusYears(1);
|
||||
|
||||
// Bill for the restore.
|
||||
BillingEvent.OneTime restoreEvent = new BillingEvent.OneTime.Builder()
|
||||
.setReason(Reason.RESTORE)
|
||||
.setTargetId(targetId)
|
||||
.setClientId(getClientId())
|
||||
.setCost(restoreCost)
|
||||
.setEventTime(now)
|
||||
.setBillingTime(now)
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
// Create a new autorenew billing event and poll message starting at the new expiration time.
|
||||
BillingEvent.Recurring autorenewEvent = newAutorenewBillingEvent(existingResource)
|
||||
.setEventTime(newExpirationTime)
|
||||
.setRecurrenceEndTime(END_OF_TIME)
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
PollMessage.Autorenew autorenewPollMessage = newAutorenewPollMessage(existingResource)
|
||||
.setEventTime(newExpirationTime)
|
||||
.setAutorenewEndTime(END_OF_TIME)
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
|
||||
// Also bill for the 1 year cost of a domain renew. This is to avoid registrants being able to
|
||||
// game the system for premium names by renewing, deleting, and then restoring to get a free
|
||||
// year. Note that this billing event has no grace period; it is effective immediately.
|
||||
BillingEvent.OneTime renewEvent = new BillingEvent.OneTime.Builder()
|
||||
.setReason(Reason.RENEW)
|
||||
.setTargetId(targetId)
|
||||
.setClientId(getClientId())
|
||||
.setPeriodYears(1)
|
||||
.setCost(renewCost)
|
||||
.setEventTime(now)
|
||||
.setBillingTime(now)
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
|
||||
ofy().save().<Object>entities(restoreEvent, autorenewEvent, autorenewPollMessage, renewEvent);
|
||||
return existingResource.asBuilder()
|
||||
.setRegistrationExpirationTime(newExpirationTime)
|
||||
.setDeletionTime(END_OF_TIME)
|
||||
.setStatusValues(null)
|
||||
.setGracePeriods(null)
|
||||
.setDeletePollMessage(null)
|
||||
.setAutorenewBillingEvent(Ref.create(autorenewEvent))
|
||||
.setAutorenewPollMessage(Ref.create(autorenewPollMessage))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void modifyRelatedResources() {
|
||||
// Update the relevant {@link ForeignKey} to cache the new deletion time.
|
||||
ofy().save().entity(ForeignKeyIndex.create(newResource, newResource.getDeletionTime()));
|
||||
ofy().delete().key(existingResource.getDeletePollMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enqueueTasks() {
|
||||
DnsQueue.create().addDomainRefreshTask(existingResource.getFullyQualifiedDomainName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final HistoryEntry.Type getHistoryEntryType() {
|
||||
return HistoryEntry.Type.DOMAIN_RESTORE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final EppOutput getOutput() {
|
||||
return createOutput(
|
||||
Success,
|
||||
null,
|
||||
(feeUpdate == null) ? null : ImmutableList.of(
|
||||
new FeeUpdateResponseExtension.Builder()
|
||||
.setCurrency(restoreCost.getCurrencyUnit())
|
||||
.setFee(ImmutableList.of(
|
||||
Fee.create(restoreCost.getAmount(), "restore"),
|
||||
Fee.create(renewCost.getAmount(), "renew")))
|
||||
.build()));
|
||||
}
|
||||
|
||||
/** Restore command cannot have other changes specified. */
|
||||
static class RestoreCommandIncludesChangesException extends CommandUseErrorException {
|
||||
public RestoreCommandIncludesChangesException() {
|
||||
super("Restore command cannot have other changes specified");
|
||||
}
|
||||
}
|
||||
|
||||
/** Domain is not eligible for restore. */
|
||||
static class DomainNotEligibleForRestoreException extends StatusProhibitsOperationException {
|
||||
public DomainNotEligibleForRestoreException() {
|
||||
super("Domain is not eligible for restore");
|
||||
}
|
||||
}
|
||||
}
|
140
java/google/registry/flows/domain/DomainTransferApproveFlow.java
Normal file
140
java/google/registry/flows/domain/DomainTransferApproveFlow.java
Normal file
|
@ -0,0 +1,140 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurrenceEndTime;
|
||||
import static com.google.domain.registry.model.domain.DomainResource.extendRegistrationWithCap;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.flows.ResourceTransferApproveFlow;
|
||||
import com.google.domain.registry.model.billing.BillingEvent;
|
||||
import com.google.domain.registry.model.billing.BillingEvent.Flag;
|
||||
import com.google.domain.registry.model.billing.BillingEvent.Reason;
|
||||
import com.google.domain.registry.model.domain.DomainCommand.Transfer;
|
||||
import com.google.domain.registry.model.domain.DomainResource;
|
||||
import com.google.domain.registry.model.domain.DomainResource.Builder;
|
||||
import com.google.domain.registry.model.domain.GracePeriod;
|
||||
import com.google.domain.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import com.google.domain.registry.model.poll.PollMessage;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.model.reporting.HistoryEntry;
|
||||
import com.google.domain.registry.model.transfer.TransferData;
|
||||
|
||||
import com.googlecode.objectify.Ref;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* An EPP flow that approves a pending transfer on a {@link DomainResource}.
|
||||
* <p>
|
||||
* The logic in this flow, which handles client approvals, very closely parallels the logic in
|
||||
* {@link DomainResource#cloneProjectedAtTime} which handles implicit server approvals.
|
||||
*
|
||||
* @error {@link com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceMutatePendingTransferFlow.NotPendingTransferException}
|
||||
*/
|
||||
public class DomainTransferApproveFlow extends
|
||||
ResourceTransferApproveFlow<DomainResource, Builder, Transfer> {
|
||||
|
||||
@Override
|
||||
protected void verifyOwnedResourcePendingTransferMutationAllowed() throws EppException {
|
||||
checkAllowedAccessToTld(getAllowedTlds(), existingResource.getTld());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void setTransferApproveProperties(Builder builder) {
|
||||
TransferData transferData = existingResource.getTransferData();
|
||||
String gainingClientId = transferData.getGainingClientId();
|
||||
String tld = existingResource.getTld();
|
||||
int extraYears = transferData.getExtendedRegistrationYears();
|
||||
// Bill for the transfer.
|
||||
BillingEvent.OneTime billingEvent = new BillingEvent.OneTime.Builder()
|
||||
.setReason(Reason.TRANSFER)
|
||||
.setTargetId(targetId)
|
||||
.setClientId(gainingClientId)
|
||||
.setPeriodYears(extraYears)
|
||||
.setCost(Registry.get(tld).getDomainRenewCost(
|
||||
targetId,
|
||||
extraYears,
|
||||
transferData.getTransferRequestTime()))
|
||||
.setEventTime(now)
|
||||
.setBillingTime(now.plus(Registry.get(tld).getTransferGracePeriodLength()))
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
ofy().save().entity(billingEvent);
|
||||
// If we are within an autorenew grace period, cancel the autorenew billing event and reduce
|
||||
// the number of years to extend the registration by one.
|
||||
GracePeriod autorenewGrace = Iterables.getOnlyElement(FluentIterable
|
||||
.from(existingResource.getGracePeriods())
|
||||
.filter(new Predicate<GracePeriod>(){
|
||||
@Override
|
||||
public boolean apply(GracePeriod gracePeriod) {
|
||||
return GracePeriodStatus.AUTO_RENEW.equals(gracePeriod.getType());
|
||||
}}), null);
|
||||
if (autorenewGrace != null) {
|
||||
extraYears--;
|
||||
ofy().save().entity(
|
||||
BillingEvent.Cancellation.forGracePeriod(autorenewGrace, historyEntry, targetId));
|
||||
}
|
||||
// Close the old autorenew event and poll message at the transfer time (aka now). This may end
|
||||
// up deleting the poll message.
|
||||
updateAutorenewRecurrenceEndTime(existingResource, now);
|
||||
DateTime newExpirationTime = extendRegistrationWithCap(
|
||||
now, existingResource.getRegistrationExpirationTime(), extraYears);
|
||||
// Create a new autorenew event starting at the expiration time.
|
||||
BillingEvent.Recurring autorenewEvent = new BillingEvent.Recurring.Builder()
|
||||
.setReason(Reason.RENEW)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setTargetId(targetId)
|
||||
.setClientId(gainingClientId)
|
||||
.setEventTime(newExpirationTime)
|
||||
.setRecurrenceEndTime(END_OF_TIME)
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
ofy().save().entity(autorenewEvent);
|
||||
// Create a new autorenew poll message.
|
||||
PollMessage.Autorenew gainingClientAutorenewPollMessage = new PollMessage.Autorenew.Builder()
|
||||
.setTargetId(targetId)
|
||||
.setClientId(gainingClientId)
|
||||
.setEventTime(newExpirationTime)
|
||||
.setAutorenewEndTime(END_OF_TIME)
|
||||
.setMsg("Domain was auto-renewed.")
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
ofy().save().entity(gainingClientAutorenewPollMessage);
|
||||
builder
|
||||
.setRegistrationExpirationTime(newExpirationTime)
|
||||
.setAutorenewBillingEvent(Ref.create(autorenewEvent))
|
||||
.setAutorenewPollMessage(Ref.create(gainingClientAutorenewPollMessage))
|
||||
// Remove all the old grace periods and add a new one for the transfer.
|
||||
.setGracePeriods(ImmutableSet.of(
|
||||
GracePeriod.forBillingEvent(GracePeriodStatus.TRANSFER, billingEvent)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final HistoryEntry.Type getHistoryEntryType() {
|
||||
return HistoryEntry.Type.DOMAIN_TRANSFER_APPROVE;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurrenceEndTime;
|
||||
import static com.google.domain.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.flows.ResourceTransferCancelFlow;
|
||||
import com.google.domain.registry.model.domain.DomainCommand.Transfer;
|
||||
import com.google.domain.registry.model.domain.DomainResource;
|
||||
import com.google.domain.registry.model.domain.DomainResource.Builder;
|
||||
import com.google.domain.registry.model.reporting.HistoryEntry;
|
||||
|
||||
/**
|
||||
* An EPP flow that cancels a pending transfer on a {@link DomainResource}.
|
||||
*
|
||||
* @error {@link com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceMutatePendingTransferFlow.NotPendingTransferException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceTransferCancelFlow.NotTransferInitiatorException}
|
||||
*/
|
||||
public class DomainTransferCancelFlow
|
||||
extends ResourceTransferCancelFlow<DomainResource, Builder, Transfer> {
|
||||
|
||||
/**
|
||||
* Reopen the autorenew event and poll message that we closed for the implicit transfer.
|
||||
* This may end up recreating the autorenew poll message if it was deleted when the transfer
|
||||
* request was made.
|
||||
*/
|
||||
@Override
|
||||
protected final void modifyRelatedResourcesForTransferCancel() {
|
||||
updateAutorenewRecurrenceEndTime(existingResource, END_OF_TIME);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void verifyTransferCancelMutationAllowed() throws EppException {
|
||||
checkAllowedAccessToTld(getAllowedTlds(), existingResource.getTld());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final HistoryEntry.Type getHistoryEntryType() {
|
||||
return HistoryEntry.Type.DOMAIN_TRANSFER_CANCEL;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
import com.google.domain.registry.flows.ResourceTransferQueryFlow;
|
||||
import com.google.domain.registry.model.domain.DomainCommand.Transfer;
|
||||
import com.google.domain.registry.model.domain.DomainResource;
|
||||
|
||||
/**
|
||||
* An EPP flow that queries a pending transfer on a {@link DomainResource}.
|
||||
*
|
||||
* @error {@link com.google.domain.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceQueryFlow.ResourceToQueryDoesNotExistException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceTransferQueryFlow.NoTransferHistoryToQueryException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceTransferQueryFlow.NotAuthorizedToViewTransferException}
|
||||
*/
|
||||
public class DomainTransferQueryFlow extends ResourceTransferQueryFlow<DomainResource, Transfer> {}
|
|
@ -0,0 +1,58 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurrenceEndTime;
|
||||
import static com.google.domain.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.flows.ResourceTransferRejectFlow;
|
||||
import com.google.domain.registry.model.domain.DomainCommand.Transfer;
|
||||
import com.google.domain.registry.model.domain.DomainResource;
|
||||
import com.google.domain.registry.model.domain.DomainResource.Builder;
|
||||
import com.google.domain.registry.model.reporting.HistoryEntry;
|
||||
|
||||
/**
|
||||
* An EPP flow that rejects a pending transfer on a {@link DomainResource}.
|
||||
*
|
||||
* @error {@link com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceMutatePendingTransferFlow.NotPendingTransferException}
|
||||
*/
|
||||
public class DomainTransferRejectFlow
|
||||
extends ResourceTransferRejectFlow<DomainResource, Builder, Transfer> {
|
||||
|
||||
@Override
|
||||
protected void verifyOwnedResourcePendingTransferMutationAllowed() throws EppException {
|
||||
checkAllowedAccessToTld(getAllowedTlds(), existingResource.getTld());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reopen the autorenew event and poll message that we closed for the implicit transfer.
|
||||
* This may end up recreating the poll message if it was deleted upon the transfer request.
|
||||
*/
|
||||
@Override
|
||||
protected final void modifyRelatedResourcesForTransferReject() {
|
||||
updateAutorenewRecurrenceEndTime(existingResource, END_OF_TIME);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final HistoryEntry.Type getHistoryEntryType() {
|
||||
return HistoryEntry.Type.DOMAIN_TRANSFER_REJECT;
|
||||
}
|
||||
}
|
242
java/google/registry/flows/domain/DomainTransferRequestFlow.java
Normal file
242
java/google/registry/flows/domain/DomainTransferRequestFlow.java
Normal file
|
@ -0,0 +1,242 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.updateAutorenewRecurrenceEndTime;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.validateFeeChallenge;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.verifyPremiumNameIsNotBlocked;
|
||||
import static com.google.domain.registry.flows.domain.DomainFlowUtils.verifyUnitIsYears;
|
||||
import static com.google.domain.registry.model.domain.DomainResource.extendRegistrationWithCap;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.flows.ResourceTransferRequestFlow;
|
||||
import com.google.domain.registry.model.billing.BillingEvent;
|
||||
import com.google.domain.registry.model.billing.BillingEvent.Flag;
|
||||
import com.google.domain.registry.model.billing.BillingEvent.Reason;
|
||||
import com.google.domain.registry.model.domain.DomainCommand.Transfer;
|
||||
import com.google.domain.registry.model.domain.DomainResource;
|
||||
import com.google.domain.registry.model.domain.Period;
|
||||
import com.google.domain.registry.model.domain.fee.Fee;
|
||||
import com.google.domain.registry.model.domain.fee.FeeTransferExtension;
|
||||
import com.google.domain.registry.model.domain.fee.FeeTransferResponseExtension;
|
||||
import com.google.domain.registry.model.eppoutput.Response.ResponseExtension;
|
||||
import com.google.domain.registry.model.poll.PollMessage;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.model.reporting.HistoryEntry;
|
||||
import com.google.domain.registry.model.transfer.TransferData;
|
||||
import com.google.domain.registry.model.transfer.TransferData.TransferServerApproveEntity;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.Ref;
|
||||
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An EPP flow that requests a transfer on a {@link DomainResource}.
|
||||
*
|
||||
* @error {@link com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceFlowUtils.BadAuthInfoForResourceException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceTransferRequestFlow.AlreadyPendingTransferException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceTransferRequestFlow.MissingTransferRequestAuthInfoException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceTransferRequestFlow.ObjectAlreadySponsoredException}
|
||||
* @error {@link com.google.domain.registry.flows.SingleResourceFlow.ResourceStatusProhibitsOperationException}
|
||||
* @error {@link DomainFlowUtils.BadPeriodUnitException}
|
||||
* @error {@link DomainFlowUtils.CurrencyUnitMismatchException}
|
||||
* @error {@link DomainFlowUtils.CurrencyValueScaleException}
|
||||
* @error {@link DomainFlowUtils.FeesMismatchException}
|
||||
* @error {@link DomainFlowUtils.FeesRequiredForPremiumNameException}
|
||||
* @error {@link DomainFlowUtils.PremiumNameBlockedException}
|
||||
* @error {@link DomainFlowUtils.UnsupportedFeeAttributeException}
|
||||
*/
|
||||
public class DomainTransferRequestFlow
|
||||
extends ResourceTransferRequestFlow<DomainResource, Transfer> {
|
||||
|
||||
/** The time when the transfer will be server approved if no other action happens first. */
|
||||
private DateTime automaticTransferTime;
|
||||
|
||||
/** A new one-time billing event for the renewal packaged as part of this transfer. */
|
||||
private BillingEvent.OneTime transferBillingEvent;
|
||||
|
||||
/** A new autorenew billing event starting at the transfer time. */
|
||||
private BillingEvent.Recurring gainingClientAutorenewEvent;
|
||||
|
||||
/** A new autorenew poll message starting at the transfer time. */
|
||||
private PollMessage.Autorenew gainingClientAutorenewPollMessage;
|
||||
|
||||
/** The amount that this transfer will cost due to the implied renew. */
|
||||
private Money renewCost;
|
||||
|
||||
/**
|
||||
* An optional extension from the client specifying how much they think the transfer should cost.
|
||||
*/
|
||||
private FeeTransferExtension feeTransfer;
|
||||
|
||||
@Override
|
||||
protected Duration getAutomaticTransferLength() {
|
||||
return Registry.get(existingResource.getTld()).getAutomaticTransferLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void initResourceTransferRequestFlow() {
|
||||
registerExtensions(FeeTransferExtension.class);
|
||||
feeTransfer = eppInput.getSingleExtension(FeeTransferExtension.class);
|
||||
// The "existingResource" field is loaded before this function is called, but it may be null if
|
||||
// the domain name specified is invalid or doesn't exist. If that's the case, simply exit
|
||||
// early, and ResourceMutateFlow will later throw ResourceToMutateDoesNotExistException.
|
||||
if (existingResource == null) {
|
||||
return;
|
||||
}
|
||||
Registry registry = Registry.get(existingResource.getTld());
|
||||
automaticTransferTime = now.plus(registry.getAutomaticTransferLength());
|
||||
renewCost = registry.getDomainRenewCost(targetId, command.getPeriod().getValue(), now);
|
||||
transferBillingEvent = new BillingEvent.OneTime.Builder()
|
||||
.setReason(Reason.TRANSFER)
|
||||
.setTargetId(targetId)
|
||||
.setClientId(getClientId())
|
||||
.setCost(renewCost)
|
||||
.setPeriodYears(command.getPeriod().getValue())
|
||||
.setEventTime(automaticTransferTime)
|
||||
.setBillingTime(automaticTransferTime.plus(registry.getTransferGracePeriodLength()))
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
DateTime newExpirationTime = extendRegistrationWithCap(
|
||||
automaticTransferTime,
|
||||
existingResource.getRegistrationExpirationTime(),
|
||||
command.getPeriod().getValue());
|
||||
gainingClientAutorenewEvent = new BillingEvent.Recurring.Builder()
|
||||
.setReason(Reason.RENEW)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setTargetId(targetId)
|
||||
.setClientId(gainingClient.getId())
|
||||
.setEventTime(newExpirationTime)
|
||||
.setRecurrenceEndTime(END_OF_TIME)
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
gainingClientAutorenewPollMessage = new PollMessage.Autorenew.Builder()
|
||||
.setTargetId(targetId)
|
||||
.setClientId(gainingClient.getId())
|
||||
.setEventTime(newExpirationTime)
|
||||
.setAutorenewEndTime(END_OF_TIME)
|
||||
.setMsg("Domain was auto-renewed.")
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void verifyTransferRequestIsAllowed() throws EppException {
|
||||
verifyUnitIsYears(command.getPeriod());
|
||||
if (!superuser) {
|
||||
verifyPremiumNameIsNotBlocked(targetId, existingResource.getTld(), getClientId());
|
||||
}
|
||||
validateFeeChallenge(targetId, existingResource.getTld(), feeTransfer, renewCost);
|
||||
checkAllowedAccessToTld(getAllowedTlds(), existingResource.getTld());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImmutableList<? extends ResponseExtension> getTransferResponseExtensions() {
|
||||
if (feeTransfer != null) {
|
||||
return ImmutableList.of(new FeeTransferResponseExtension.Builder()
|
||||
.setCurrency(renewCost.getCurrencyUnit())
|
||||
.setFee(ImmutableList.of(Fee.create(renewCost.getAmount(), "renew")))
|
||||
.build());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setTransferDataProperties(TransferData.Builder builder) {
|
||||
builder
|
||||
.setServerApproveBillingEvent(Ref.create(transferBillingEvent))
|
||||
.setServerApproveAutorenewEvent(Ref.create(gainingClientAutorenewEvent))
|
||||
.setServerApproveAutorenewPollMessage(Ref.create(gainingClientAutorenewPollMessage))
|
||||
.setExtendedRegistrationYears(command.getPeriod().getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* When a transfer is requested, schedule a billing event and poll message for the automatic
|
||||
* approval case.
|
||||
* <p>
|
||||
* Note that the action time is AUTOMATIC_TRANSFER_DAYS in the future, matching the server policy
|
||||
* on automated approval of transfers. There is no equivalent grace period added; if the transfer
|
||||
* is implicitly approved, the resource will project a grace period on itself.
|
||||
*/
|
||||
@Override
|
||||
protected Set<Key<? extends TransferServerApproveEntity>> getTransferServerApproveEntities() {
|
||||
ofy().save().<Object>entities(
|
||||
transferBillingEvent, gainingClientAutorenewEvent, gainingClientAutorenewPollMessage);
|
||||
// If there will be an autorenew between now and the automatic transfer time, and if the
|
||||
// autorenew grace period length is long enough that the domain will still be within it at the
|
||||
// automatic transfer time, then the transfer will subsume the autorenew so we need to write out
|
||||
// a cancellation for it.
|
||||
Set<Key<? extends TransferServerApproveEntity>> serverApproveEntities = new HashSet<>();
|
||||
DateTime expirationTime = existingResource.getRegistrationExpirationTime();
|
||||
Registry registry = Registry.get(existingResource.getTld());
|
||||
if (automaticTransferTime.isAfter(expirationTime) && automaticTransferTime.isBefore(
|
||||
expirationTime.plus(registry.getAutoRenewGracePeriodLength()))) {
|
||||
BillingEvent.Cancellation autorenewCancellation = new BillingEvent.Cancellation.Builder()
|
||||
.setReason(Reason.RENEW)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setTargetId(targetId)
|
||||
.setClientId(existingResource.getCurrentSponsorClientId())
|
||||
.setEventTime(automaticTransferTime)
|
||||
.setBillingTime(expirationTime.plus(registry.getAutoRenewGracePeriodLength()))
|
||||
.setRecurringEventRef(existingResource.getAutorenewBillingEvent())
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
ofy().save().entity(autorenewCancellation);
|
||||
serverApproveEntities.add(Key.create(autorenewCancellation));
|
||||
}
|
||||
serverApproveEntities.add(Key.create(transferBillingEvent));
|
||||
serverApproveEntities.add(Key.create(gainingClientAutorenewEvent));
|
||||
serverApproveEntities.add(Key.create(gainingClientAutorenewPollMessage));
|
||||
return serverApproveEntities;
|
||||
}
|
||||
|
||||
/** Close the old autorenew billing event and save a new one. */
|
||||
@Override
|
||||
protected final void modifyRelatedResources() throws EppException {
|
||||
// End the old autorenew event and poll message at the implicit transfer time. This may delete
|
||||
// the poll message if it has no events left.
|
||||
//
|
||||
// Note that this is still left on the domain as the autorenewBillingEvent because it is still
|
||||
// the current autorenew event until the transfer happens. If you read the domain after the
|
||||
// transfer occurs, then the logic in cloneProjectedAtTime() will move the
|
||||
// serverApproveAutoRenewEvent into the autoRenewEvent field.
|
||||
updateAutorenewRecurrenceEndTime(existingResource, automaticTransferTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final HistoryEntry.Type getHistoryEntryType() {
|
||||
return HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final Period getCommandPeriod() {
|
||||
return command.getPeriod();
|
||||
}
|
||||
}
|
167
java/google/registry/flows/domain/DomainUpdateFlow.java
Normal file
167
java/google/registry/flows/domain/DomainUpdateFlow.java
Normal file
|
@ -0,0 +1,167 @@
|
|||
// 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 com.google.domain.registry.flows.domain;
|
||||
|
||||
import static com.google.common.collect.Sets.symmetricDifference;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.util.DateTimeUtils.earliestOf;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.domain.registry.dns.DnsQueue;
|
||||
import com.google.domain.registry.model.billing.BillingEvent;
|
||||
import com.google.domain.registry.model.billing.BillingEvent.Reason;
|
||||
import com.google.domain.registry.model.domain.DomainResource;
|
||||
import com.google.domain.registry.model.domain.DomainResource.Builder;
|
||||
import com.google.domain.registry.model.domain.GracePeriod;
|
||||
import com.google.domain.registry.model.domain.metadata.MetadataExtension;
|
||||
import com.google.domain.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import com.google.domain.registry.model.domain.secdns.SecDnsUpdateExtension;
|
||||
import com.google.domain.registry.model.eppcommon.StatusValue;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.model.reporting.HistoryEntry;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An EPP flow that updates a domain resource.
|
||||
*
|
||||
* @error {@link com.google.domain.registry.flows.EppException.UnimplementedExtensionException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceCreateOrMutateFlow.OnlyToolCanPassMetadataException}
|
||||
* @error {@link com.google.domain.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceFlowUtils.ResourceNotOwnedException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceMutateFlow.ResourceToMutateDoesNotExistException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceUpdateFlow.AddRemoveSameValueEppException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceUpdateFlow.ResourceHasClientUpdateProhibitedException}
|
||||
* @error {@link com.google.domain.registry.flows.ResourceUpdateFlow.StatusNotClientSettableException}
|
||||
* @error {@link com.google.domain.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.LinkedResourceDoesNotExistException}
|
||||
* @error {@link DomainFlowUtils.LinkedResourceInPendingDeleteProhibitsOperationException}
|
||||
* @error {@link DomainFlowUtils.MissingAdminContactException}
|
||||
* @error {@link DomainFlowUtils.MissingContactTypeException}
|
||||
* @error {@link DomainFlowUtils.MissingTechnicalContactException}
|
||||
* @error {@link DomainFlowUtils.NameserverNotAllowedException}
|
||||
* @error {@link DomainFlowUtils.RegistrantNotAllowedException}
|
||||
* @error {@link DomainFlowUtils.TooManyDsRecordsException}
|
||||
* @error {@link DomainFlowUtils.TooManyNameserversException}
|
||||
*/
|
||||
public class DomainUpdateFlow extends BaseDomainUpdateFlow<DomainResource, Builder> {
|
||||
|
||||
@Override
|
||||
protected void initDomainUpdateFlow() {
|
||||
registerExtensions(SecDnsUpdateExtension.class, MetadataExtension.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Builder setDomainUpdateProperties(Builder builder) {
|
||||
// Check if the domain is currently in the sunrush add grace period.
|
||||
Optional<GracePeriod> sunrushAddGracePeriod = Iterables.tryFind(
|
||||
existingResource.getGracePeriods(),
|
||||
new Predicate<GracePeriod>() {
|
||||
@Override
|
||||
public boolean apply(GracePeriod gracePeriod) {
|
||||
return gracePeriod.isSunrushAddGracePeriod();
|
||||
}});
|
||||
|
||||
// If this domain is currently in the sunrush add grace period, and we're updating it in a way
|
||||
// that will cause it to now get delegated (either by setting nameservers, or by removing a
|
||||
// clientHold or serverHold), then that will remove the sunrush add grace period and convert
|
||||
// that to a standard add grace period.
|
||||
DomainResource updatedDomain = builder.build();
|
||||
builder = updatedDomain.asBuilder();
|
||||
if (sunrushAddGracePeriod.isPresent() && updatedDomain.shouldPublishToDns()) {
|
||||
// Remove the sunrush grace period and write a billing event cancellation for it.
|
||||
builder.removeGracePeriod(sunrushAddGracePeriod.get());
|
||||
BillingEvent.Cancellation billingEventCancellation = BillingEvent.Cancellation
|
||||
.forGracePeriod(sunrushAddGracePeriod.get(), historyEntry, targetId);
|
||||
|
||||
// Compute the expiration time of the add grace period. We will not allow it to be after the
|
||||
// sunrush add grace period expiration time (i.e. you can't get extra add grace period by
|
||||
// setting a nameserver).
|
||||
DateTime addGracePeriodExpirationTime = earliestOf(
|
||||
now.plus(Registry.get(existingResource.getTld()).getAddGracePeriodLength()),
|
||||
sunrushAddGracePeriod.get().getExpirationTime());
|
||||
|
||||
// Create a new billing event for the add grace period. Note that we do this even if it would
|
||||
// occur at the same time as the sunrush add grace period, as the event time will differ
|
||||
// between them.
|
||||
BillingEvent.OneTime originalAddEvent =
|
||||
sunrushAddGracePeriod.get().getOneTimeBillingEvent().get();
|
||||
BillingEvent.OneTime billingEvent = new BillingEvent.OneTime.Builder()
|
||||
.setReason(Reason.CREATE)
|
||||
.setTargetId(targetId)
|
||||
.setFlags(originalAddEvent.getFlags())
|
||||
.setClientId(sunrushAddGracePeriod.get().getClientId())
|
||||
.setCost(originalAddEvent.getCost())
|
||||
.setPeriodYears(originalAddEvent.getPeriodYears())
|
||||
.setEventTime(now)
|
||||
.setBillingTime(addGracePeriodExpirationTime)
|
||||
.setParent(historyEntry)
|
||||
.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);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void modifyRelatedResources() {
|
||||
// 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()) {
|
||||
Set<StatusValue> statusDifferences =
|
||||
symmetricDifference(existingResource.getStatusValues(), newResource.getStatusValues());
|
||||
if (Iterables.any(statusDifferences, new Predicate<StatusValue>() {
|
||||
@Override
|
||||
public boolean apply(StatusValue statusValue) {
|
||||
return statusValue.isChargedStatus();
|
||||
}})) {
|
||||
BillingEvent.OneTime billingEvent = new BillingEvent.OneTime.Builder()
|
||||
.setReason(Reason.SERVER_STATUS)
|
||||
.setTargetId(targetId)
|
||||
.setClientId(getClientId())
|
||||
.setCost(Registry.get(existingResource.getTld()).getServerStatusChangeCost())
|
||||
.setEventTime(now)
|
||||
.setBillingTime(now)
|
||||
.setParent(historyEntry)
|
||||
.build();
|
||||
ofy().save().entity(billingEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enqueueTasks() {
|
||||
DnsQueue.create().addDomainRefreshTask(existingResource.getFullyQualifiedDomainName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final HistoryEntry.Type getHistoryEntryType() {
|
||||
return HistoryEntry.Type.DOMAIN_UPDATE;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue