mirror of
https://github.com/google/nomulus.git
synced 2025-05-03 21:47:51 +02:00
Now that transfers are always restricted to 1 year, it's unnecessary to store extendedRegistrationYears on TransferData - it will always be equal to 1. This simplifies logic in a few other places, e.g. RdeDomainImportAction. I verified in BigQuery that no DomainBases exist with extendedRegistrationYears values that aren't either null or equal to 1. At some point we should remove the persisted fields from datastore via e.g. resaving all those domains, but it's low priority and can wait until we have some more pressing migration. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=150373897
284 lines
12 KiB
Java
284 lines
12 KiB
Java
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package google.registry.flows.domain;
|
|
|
|
import static com.google.common.collect.Iterables.filter;
|
|
import static com.google.common.collect.Iterables.getOnlyElement;
|
|
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
|
|
|
import com.google.common.base.Optional;
|
|
import com.google.common.collect.ImmutableList;
|
|
import com.google.common.collect.ImmutableSet;
|
|
import com.googlecode.objectify.Key;
|
|
import google.registry.model.billing.BillingEvent;
|
|
import google.registry.model.billing.BillingEvent.Flag;
|
|
import google.registry.model.billing.BillingEvent.Reason;
|
|
import google.registry.model.domain.DomainResource;
|
|
import google.registry.model.domain.GracePeriod;
|
|
import google.registry.model.domain.rgp.GracePeriodStatus;
|
|
import google.registry.model.eppcommon.Trid;
|
|
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
|
|
import google.registry.model.poll.PollMessage;
|
|
import google.registry.model.registry.Registry;
|
|
import google.registry.model.reporting.HistoryEntry;
|
|
import google.registry.model.transfer.TransferData;
|
|
import google.registry.model.transfer.TransferData.Builder;
|
|
import google.registry.model.transfer.TransferData.TransferServerApproveEntity;
|
|
import google.registry.model.transfer.TransferResponse.DomainTransferResponse;
|
|
import google.registry.model.transfer.TransferStatus;
|
|
import javax.annotation.Nullable;
|
|
import org.joda.money.Money;
|
|
import org.joda.time.DateTime;
|
|
|
|
/**
|
|
* Utility logic for facilitating domain transfers.
|
|
*/
|
|
public final class DomainTransferUtils {
|
|
|
|
/**
|
|
* Sets up {@link TransferData} for a domain with links to entities for server approval.
|
|
*/
|
|
public static TransferData createPendingTransferData(
|
|
TransferData.Builder transferDataBuilder,
|
|
ImmutableSet<TransferServerApproveEntity> serverApproveEntities) {
|
|
ImmutableSet.Builder<Key<? extends TransferServerApproveEntity>> serverApproveEntityKeys =
|
|
new ImmutableSet.Builder<>();
|
|
for (TransferServerApproveEntity entity : serverApproveEntities) {
|
|
serverApproveEntityKeys.add(Key.create(entity));
|
|
}
|
|
return transferDataBuilder
|
|
.setTransferStatus(TransferStatus.PENDING)
|
|
.setServerApproveBillingEvent(Key.create(
|
|
getOnlyElement(filter(serverApproveEntities, BillingEvent.OneTime.class))))
|
|
.setServerApproveAutorenewEvent(Key.create(
|
|
getOnlyElement(filter(serverApproveEntities, BillingEvent.Recurring.class))))
|
|
.setServerApproveAutorenewPollMessage(Key.create(
|
|
getOnlyElement(filter(serverApproveEntities, PollMessage.Autorenew.class))))
|
|
.setServerApproveEntities(serverApproveEntityKeys.build())
|
|
.build();
|
|
}
|
|
|
|
/**
|
|
* Returns a set of entities created speculatively in anticipation of a server approval.
|
|
*
|
|
* <p>This set consists of:
|
|
* <ul>
|
|
* <li>The one-time billing event charging the gaining registrar for the transfer
|
|
* <li>A cancellation of an autorenew charge for the losing registrar, if the autorenew grace
|
|
* period will apply at transfer time
|
|
* <li>A new post-transfer autorenew billing event for the domain (and gaining registrar)
|
|
* <li>A new post-transfer autorenew poll message for the domain (and gaining registrar)
|
|
* <li>A poll message for the gaining registrar
|
|
* <li>A poll message for the losing registrar
|
|
* </ul>
|
|
*/
|
|
public static ImmutableSet<TransferServerApproveEntity> createTransferServerApproveEntities(
|
|
DateTime automaticTransferTime,
|
|
DateTime serverApproveNewExpirationTime,
|
|
HistoryEntry historyEntry,
|
|
DomainResource existingDomain,
|
|
Trid trid,
|
|
String gainingClientId,
|
|
Money transferCost,
|
|
DateTime now) {
|
|
String targetId = existingDomain.getFullyQualifiedDomainName();
|
|
// Create a TransferData for the server-approve case to use for the speculative poll messages.
|
|
TransferData serverApproveTransferData =
|
|
createTransferDataBuilder(
|
|
existingDomain, trid, gainingClientId, automaticTransferTime, now)
|
|
.setTransferStatus(TransferStatus.SERVER_APPROVED)
|
|
.build();
|
|
Registry registry = Registry.get(existingDomain.getTld());
|
|
return new ImmutableSet.Builder<TransferServerApproveEntity>()
|
|
.add(
|
|
createTransferBillingEvent(
|
|
automaticTransferTime,
|
|
historyEntry,
|
|
targetId,
|
|
gainingClientId,
|
|
registry,
|
|
transferCost))
|
|
.addAll(
|
|
createOptionalAutorenewCancellation(
|
|
automaticTransferTime, historyEntry, targetId, existingDomain)
|
|
.asSet())
|
|
.add(
|
|
createGainingClientAutorenewEvent(
|
|
serverApproveNewExpirationTime, historyEntry, targetId, gainingClientId))
|
|
.add(
|
|
createGainingClientAutorenewPollMessage(
|
|
serverApproveNewExpirationTime, historyEntry, targetId, gainingClientId))
|
|
.add(
|
|
createGainingTransferPollMessage(
|
|
targetId, serverApproveTransferData, serverApproveNewExpirationTime, historyEntry))
|
|
.add(
|
|
createLosingTransferPollMessage(
|
|
targetId, serverApproveTransferData, serverApproveNewExpirationTime, historyEntry))
|
|
.build();
|
|
}
|
|
|
|
/** Create a poll message for the gaining client in a transfer. */
|
|
public static PollMessage createGainingTransferPollMessage(
|
|
String targetId,
|
|
TransferData transferData,
|
|
@Nullable DateTime extendedRegistrationExpirationTime,
|
|
HistoryEntry historyEntry) {
|
|
return new PollMessage.OneTime.Builder()
|
|
.setClientId(transferData.getGainingClientId())
|
|
.setEventTime(transferData.getPendingTransferExpirationTime())
|
|
.setMsg(transferData.getTransferStatus().getMessage())
|
|
.setResponseData(ImmutableList.of(
|
|
createTransferResponse(targetId, transferData, extendedRegistrationExpirationTime),
|
|
DomainPendingActionNotificationResponse.create(
|
|
targetId,
|
|
transferData.getTransferStatus().isApproved(),
|
|
transferData.getTransferRequestTrid(),
|
|
historyEntry.getModificationTime())))
|
|
.setParent(historyEntry)
|
|
.build();
|
|
}
|
|
|
|
/** Create a poll message for the losing client in a transfer. */
|
|
public static PollMessage createLosingTransferPollMessage(
|
|
String targetId,
|
|
TransferData transferData,
|
|
@Nullable DateTime extendedRegistrationExpirationTime,
|
|
HistoryEntry historyEntry) {
|
|
return new PollMessage.OneTime.Builder()
|
|
.setClientId(transferData.getLosingClientId())
|
|
.setEventTime(transferData.getPendingTransferExpirationTime())
|
|
.setMsg(transferData.getTransferStatus().getMessage())
|
|
.setResponseData(ImmutableList.of(
|
|
createTransferResponse(targetId, transferData, extendedRegistrationExpirationTime)))
|
|
.setParent(historyEntry)
|
|
.build();
|
|
}
|
|
|
|
/** Create a {@link DomainTransferResponse} off of the info in a {@link TransferData}. */
|
|
static DomainTransferResponse createTransferResponse(
|
|
String targetId,
|
|
TransferData transferData,
|
|
@Nullable DateTime extendedRegistrationExpirationTime) {
|
|
return new DomainTransferResponse.Builder()
|
|
.setFullyQualifiedDomainNameName(targetId)
|
|
.setGainingClientId(transferData.getGainingClientId())
|
|
.setLosingClientId(transferData.getLosingClientId())
|
|
.setPendingTransferExpirationTime(transferData.getPendingTransferExpirationTime())
|
|
.setTransferRequestTime(transferData.getTransferRequestTime())
|
|
.setTransferStatus(transferData.getTransferStatus())
|
|
.setExtendedRegistrationExpirationTime(extendedRegistrationExpirationTime)
|
|
.build();
|
|
}
|
|
|
|
private static PollMessage.Autorenew createGainingClientAutorenewPollMessage(
|
|
DateTime serverApproveNewExpirationTime,
|
|
HistoryEntry historyEntry,
|
|
String targetId,
|
|
String gainingClientId) {
|
|
return new PollMessage.Autorenew.Builder()
|
|
.setTargetId(targetId)
|
|
.setClientId(gainingClientId)
|
|
.setEventTime(serverApproveNewExpirationTime)
|
|
.setAutorenewEndTime(END_OF_TIME)
|
|
.setMsg("Domain was auto-renewed.")
|
|
.setParent(historyEntry)
|
|
.build();
|
|
}
|
|
|
|
private static BillingEvent.Recurring createGainingClientAutorenewEvent(
|
|
DateTime serverApproveNewExpirationTime,
|
|
HistoryEntry historyEntry,
|
|
String targetId,
|
|
String gainingClientId) {
|
|
return new BillingEvent.Recurring.Builder()
|
|
.setReason(Reason.RENEW)
|
|
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
|
.setTargetId(targetId)
|
|
.setClientId(gainingClientId)
|
|
.setEventTime(serverApproveNewExpirationTime)
|
|
.setRecurrenceEndTime(END_OF_TIME)
|
|
.setParent(historyEntry)
|
|
.build();
|
|
}
|
|
|
|
/**
|
|
* Creates an optional autorenew cancellation if one would apply to the server-approved transfer.
|
|
*
|
|
* <p>If the domain will be in the auto-renew grace period at the automatic transfer time, then
|
|
* the transfer will subsume the autorenew. This means that we "cancel" the 1-year extension of
|
|
* the autorenew before applying the extra transfer years, which in effect means reducing the
|
|
* transfer extended registration years by one. Since the gaining registrar will still be billed
|
|
* for the full extended registration years, we must issue a cancellation for the autorenew, so
|
|
* that the losing registrar will not be charged (essentially, the gaining registrar takes on the
|
|
* cost of the year of registration that the autorenew just added).
|
|
*
|
|
* <p>For details on the policy justification, see b/19430703#comment17 and
|
|
* <a href="https://www.icann.org/news/advisory-2002-06-06-en">this ICANN advisory</a>.
|
|
*/
|
|
private static Optional<BillingEvent.Cancellation> createOptionalAutorenewCancellation(
|
|
DateTime automaticTransferTime,
|
|
HistoryEntry historyEntry,
|
|
String targetId,
|
|
DomainResource existingDomain) {
|
|
DomainResource domainAtTransferTime =
|
|
existingDomain.cloneProjectedAtTime(automaticTransferTime);
|
|
GracePeriod autorenewGracePeriod =
|
|
getOnlyElement(
|
|
domainAtTransferTime.getGracePeriodsOfType(GracePeriodStatus.AUTO_RENEW), null);
|
|
if (autorenewGracePeriod != null) {
|
|
return Optional.of(
|
|
BillingEvent.Cancellation.forGracePeriod(autorenewGracePeriod, historyEntry, targetId)
|
|
.asBuilder()
|
|
.setEventTime(automaticTransferTime)
|
|
.build());
|
|
}
|
|
return Optional.absent();
|
|
}
|
|
|
|
private static BillingEvent.OneTime createTransferBillingEvent(
|
|
DateTime automaticTransferTime,
|
|
HistoryEntry historyEntry,
|
|
String targetId,
|
|
String gainingClientId,
|
|
Registry registry,
|
|
Money transferCost) {
|
|
return new BillingEvent.OneTime.Builder()
|
|
.setReason(Reason.TRANSFER)
|
|
.setTargetId(targetId)
|
|
.setClientId(gainingClientId)
|
|
.setCost(transferCost)
|
|
.setPeriodYears(1)
|
|
.setEventTime(automaticTransferTime)
|
|
.setBillingTime(automaticTransferTime.plus(registry.getTransferGracePeriodLength()))
|
|
.setParent(historyEntry)
|
|
.build();
|
|
}
|
|
|
|
private static Builder createTransferDataBuilder(
|
|
DomainResource existingDomain,
|
|
Trid trid,
|
|
String gainingClientId,
|
|
DateTime automaticTransferTime,
|
|
DateTime now) {
|
|
return new TransferData.Builder()
|
|
.setTransferRequestTrid(trid)
|
|
.setTransferRequestTime(now)
|
|
.setGainingClientId(gainingClientId)
|
|
.setLosingClientId(existingDomain.getCurrentSponsorClientId())
|
|
.setPendingTransferExpirationTime(automaticTransferTime);
|
|
}
|
|
|
|
private DomainTransferUtils() {}
|
|
}
|