mirror of
https://github.com/google/nomulus.git
synced 2025-05-09 16:28:21 +02:00
This also deletes the associated commands and domain application specific entities. We haven't used any of these TLD phases since early 2015 and have no intent to do so in the future, so it makes sense to delete them now so we don't have to carry them through the Registry 3.0 migration. Note that, while there are data model changes, there should be no required data migrations. The fields and entities being removed will simply remain as orphans. I confirmed that the removed types (such as the SUNRUSH_ADD GracePeriodType) are no longer used in production data, and left types that are still used, e.g. BillingEvent.Flag.LANDRUSH or HistoryEntry.Type.ALLOCATE. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=228752843
387 lines
15 KiB
Java
387 lines
15 KiB
Java
// Copyright 2018 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.model;
|
|
|
|
import static com.google.common.base.Preconditions.checkArgument;
|
|
import static com.google.common.base.Preconditions.checkNotNull;
|
|
import static com.google.common.base.Preconditions.checkState;
|
|
import static com.google.common.collect.ImmutableList.toImmutableList;
|
|
import static google.registry.model.ofy.ObjectifyService.ofy;
|
|
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
|
|
|
import com.google.common.collect.ImmutableList;
|
|
import com.google.common.collect.ImmutableMap;
|
|
import com.google.common.collect.ImmutableSet;
|
|
import com.google.common.collect.ImmutableSortedMap;
|
|
import com.google.common.collect.Sets;
|
|
import com.google.common.collect.Streams;
|
|
import com.googlecode.objectify.Key;
|
|
import google.registry.config.RegistryEnvironment;
|
|
import google.registry.model.common.GaeUserIdConverter;
|
|
import google.registry.model.pricing.StaticPremiumListPricingEngine;
|
|
import google.registry.model.registrar.Registrar;
|
|
import google.registry.model.registrar.RegistrarAddress;
|
|
import google.registry.model.registrar.RegistrarContact;
|
|
import google.registry.model.registry.Registry;
|
|
import google.registry.model.registry.Registry.TldState;
|
|
import google.registry.model.registry.label.PremiumList;
|
|
import google.registry.util.CidrAddressBlock;
|
|
import java.util.Collection;
|
|
import java.util.Optional;
|
|
import java.util.Set;
|
|
import java.util.function.Function;
|
|
import java.util.regex.Pattern;
|
|
import org.joda.money.CurrencyUnit;
|
|
import org.joda.money.Money;
|
|
import org.joda.time.DateTime;
|
|
import org.joda.time.Duration;
|
|
|
|
/**
|
|
* Class to help build and persist all the OT&E entities in Datastore.
|
|
*
|
|
* <p>This includes the TLDs (Registries), Registrars, and the RegistrarContacts that can access the
|
|
* web console.
|
|
*
|
|
* This class is basically a "builder" for the parameters needed to generate the OT&E entities.
|
|
* Nothing is created until you call {@link #buildAndPersist}.
|
|
*
|
|
* Usage example:
|
|
*
|
|
* <pre> {@code
|
|
* OteAccountBuilder.forClientId("example")
|
|
* .addContact("contact@email.com") // OPTIONAL
|
|
* .setPassword("password") // OPTIONAL
|
|
* .setCertificateHash(certificateHash) // OPTIONAL
|
|
* .setIpWhitelist(ImmutableList.of("1.1.1.1", "2.2.2.0/24")) // OPTIONAL
|
|
* .buildAndPersist();
|
|
* }</pre>
|
|
*/
|
|
public final class OteAccountBuilder {
|
|
|
|
// Regex: 3-14 lower-case alphanumeric characters or hyphens, the first of which must be a letter.
|
|
private static final Pattern REGISTRAR_PATTERN = Pattern.compile("^[a-z][-a-z0-9]{2,13}$");
|
|
|
|
// Durations are short so that registrars can test with quick transfer (etc.) turnaround.
|
|
private static final Duration SHORT_ADD_GRACE_PERIOD = Duration.standardMinutes(60);
|
|
private static final Duration SHORT_REDEMPTION_GRACE_PERIOD = Duration.standardMinutes(10);
|
|
private static final Duration SHORT_PENDING_DELETE_LENGTH = Duration.standardMinutes(5);
|
|
|
|
private static final String DEFAULT_PREMIUM_LIST = "default_sandbox_list";
|
|
|
|
private static final RegistrarAddress DEFAULT_ADDRESS =
|
|
new RegistrarAddress.Builder()
|
|
.setStreet(ImmutableList.of("e-street"))
|
|
.setCity("Neverland")
|
|
.setState("NY")
|
|
.setCountryCode("US")
|
|
.setZip("55555")
|
|
.build();
|
|
|
|
private static final ImmutableSortedMap<DateTime, Money> EAP_FEE_SCHEDULE =
|
|
ImmutableSortedMap.of(
|
|
new DateTime(0),
|
|
Money.of(CurrencyUnit.USD, 0),
|
|
DateTime.parse("2018-03-01T00:00:00Z"),
|
|
Money.of(CurrencyUnit.USD, 100),
|
|
DateTime.parse("2030-03-01T00:00:00Z"),
|
|
Money.of(CurrencyUnit.USD, 0));
|
|
|
|
private final ImmutableMap<String, String> clientIdToTld;
|
|
private final Registry sunriseTld;
|
|
private final Registry gaTld;
|
|
private final Registry eapTld;
|
|
private final ImmutableList.Builder<RegistrarContact> contactsBuilder =
|
|
new ImmutableList.Builder<>();
|
|
|
|
private ImmutableList<Registrar> registrars;
|
|
private boolean replaceExisting = false;
|
|
|
|
private OteAccountBuilder(String baseClientId) {
|
|
checkState(
|
|
RegistryEnvironment.get() != RegistryEnvironment.PRODUCTION,
|
|
"Can't setup OT&E in production");
|
|
clientIdToTld = createClientIdToTldMap(baseClientId);
|
|
sunriseTld =
|
|
createTld(
|
|
baseClientId + "-sunrise", TldState.START_DATE_SUNRISE, null, null, null, false, 0);
|
|
gaTld =
|
|
createTld(
|
|
baseClientId + "-ga",
|
|
TldState.GENERAL_AVAILABILITY,
|
|
SHORT_ADD_GRACE_PERIOD,
|
|
SHORT_REDEMPTION_GRACE_PERIOD,
|
|
SHORT_PENDING_DELETE_LENGTH,
|
|
false,
|
|
2);
|
|
eapTld =
|
|
createTld(
|
|
baseClientId + "-eap",
|
|
TldState.GENERAL_AVAILABILITY,
|
|
SHORT_ADD_GRACE_PERIOD,
|
|
SHORT_REDEMPTION_GRACE_PERIOD,
|
|
SHORT_PENDING_DELETE_LENGTH,
|
|
true,
|
|
3);
|
|
registrars =
|
|
clientIdToTld.keySet().stream()
|
|
.map(OteAccountBuilder::createRegistrar)
|
|
.collect(toImmutableList());
|
|
}
|
|
|
|
/**
|
|
* Creates an OteAccountBuilder for the given base client ID.
|
|
*
|
|
* @param baseClientId the base clientId which will help name all the entities we create. Normally
|
|
* is the same as the "prod" clientId designated for this registrar.
|
|
*/
|
|
public static OteAccountBuilder forClientId(String baseClientId) {
|
|
return new OteAccountBuilder(baseClientId);
|
|
}
|
|
|
|
/**
|
|
* Set whether to replace any conflicting existing entities.
|
|
*
|
|
* <p>If true, any existing entity that conflicts with the entities we want to create will be
|
|
* replaced with the newly created data.
|
|
*
|
|
* <p>If false, encountering an existing entity that conflicts with one we want to create will
|
|
* throw an exception during {@link #buildAndPersist}.
|
|
*
|
|
* <p>NOTE that if we fail, no entities are created (the creation is atomic).
|
|
*
|
|
* <p>Default is false (failing if entities exist)
|
|
*/
|
|
public OteAccountBuilder setReplaceExisting(boolean replaceExisting) {
|
|
this.replaceExisting = replaceExisting;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Adds a RegistrarContact with Web Console access.
|
|
*
|
|
* <p>NOTE: can be called more than once, adding multiple contacts. Each contact will have access
|
|
* to all OT&E Registrars.
|
|
*
|
|
* @param email the contact email that will have web-console access to all the Registrars. Must be
|
|
* from "our G Suite domain" (we have to be able to get its GaeUserId)
|
|
*/
|
|
public OteAccountBuilder addContact(String email) {
|
|
String gaeUserId =
|
|
checkNotNull(
|
|
GaeUserIdConverter.convertEmailAddressToGaeUserId(email),
|
|
"Email address %s is not associated with any GAE ID",
|
|
email);
|
|
registrars.forEach(
|
|
registrar -> contactsBuilder.add(createRegistrarContact(email, gaeUserId, registrar)));
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Apply a function on all the OT&E Registrars.
|
|
*
|
|
* <p>Use this to set up registrar fields.
|
|
*
|
|
* <p>NOTE: DO NOT change anything that would affect the {@link Key#create} result on Registrars.
|
|
* If you want to make this function public, add a check that the Key.create on the registrars
|
|
* hasn't changed.
|
|
*
|
|
* @param func a function setting the requested fields on Registrar Builders. Will be applied to
|
|
* all the Registrars.
|
|
*/
|
|
private OteAccountBuilder transformRegistrars(
|
|
Function<Registrar.Builder, Registrar.Builder> func) {
|
|
registrars =
|
|
registrars.stream()
|
|
.map(Registrar::asBuilder)
|
|
.map(func)
|
|
.map(Registrar.Builder::build)
|
|
.collect(toImmutableList());
|
|
return this;
|
|
}
|
|
|
|
/** Sets the EPP login password for all the OT&E Registrars. */
|
|
public OteAccountBuilder setPassword(String password) {
|
|
return transformRegistrars(builder -> builder.setPassword(password));
|
|
}
|
|
|
|
/** Sets the client certificate hash to all the OT&E Registrars. */
|
|
public OteAccountBuilder setCertificateHash(String certHash) {
|
|
return transformRegistrars(builder -> builder.setClientCertificateHash(certHash));
|
|
}
|
|
|
|
/** Sets the client certificate to all the OT&E Registrars. */
|
|
public OteAccountBuilder setCertificate(String asciiCert, DateTime now) {
|
|
return transformRegistrars(builder -> builder.setClientCertificate(asciiCert, now));
|
|
}
|
|
|
|
/** Sets the IP whitelist to all the OT&E Registrars. */
|
|
public OteAccountBuilder setIpWhitelist(Collection<String> ipWhitelist) {
|
|
ImmutableList<CidrAddressBlock> ipAddressWhitelist =
|
|
ipWhitelist.stream().map(CidrAddressBlock::create).collect(toImmutableList());
|
|
return transformRegistrars(builder -> builder.setIpAddressWhitelist(ipAddressWhitelist));
|
|
}
|
|
|
|
/**
|
|
* Persists all the OT&E entities to datastore.
|
|
*
|
|
* @return map from the new clientIds created to the new TLDs they have access to. Can be used to
|
|
* go over all the newly created Registrars / Registries / RegistrarContacts if any
|
|
* post-creation work is needed.
|
|
*/
|
|
public ImmutableMap<String, String> buildAndPersist() {
|
|
// save all the entitiesl in a single transaction
|
|
ofy().transact(this::saveAllEntities);
|
|
return clientIdToTld;
|
|
}
|
|
|
|
/**
|
|
* Return map from the OT&E clientIds we will create to the new TLDs they will have access to.
|
|
*/
|
|
public ImmutableMap<String, String> getClientIdToTldMap() {
|
|
return clientIdToTld;
|
|
}
|
|
|
|
/** Saves all the OT&E entities we created. */
|
|
private void saveAllEntities() {
|
|
ofy().assertInTransaction();
|
|
|
|
ImmutableList<Registry> registries = ImmutableList.of(sunriseTld, gaTld, eapTld);
|
|
ImmutableList<RegistrarContact> contacts = contactsBuilder.build();
|
|
|
|
if (!replaceExisting) {
|
|
ImmutableList<Key<ImmutableObject>> keys =
|
|
Streams.concat(registries.stream(), registrars.stream(), contacts.stream())
|
|
.map(Key::create)
|
|
.collect(toImmutableList());
|
|
Set<Key<ImmutableObject>> existingKeys = ofy().load().keys(keys).keySet();
|
|
checkState(
|
|
existingKeys.isEmpty(),
|
|
"Found existing object(s) conflicting with OT&E objects: %s",
|
|
existingKeys);
|
|
}
|
|
// Save the Registries (TLDs) first
|
|
ofy().save().entities(registries).now();
|
|
// Now we can set the allowedTlds for the registrars
|
|
registrars = registrars.stream().map(this::addAllowedTld).collect(toImmutableList());
|
|
// and we can save the registrars and contacts!
|
|
ofy().save().entities(registrars);
|
|
ofy().save().entities(contacts);
|
|
}
|
|
|
|
private Registrar addAllowedTld(Registrar registrar) {
|
|
String tld = clientIdToTld.get(registrar.getClientId());
|
|
if (registrar.getAllowedTlds().contains(tld)) {
|
|
return registrar;
|
|
}
|
|
return registrar
|
|
.asBuilder()
|
|
.setAllowedTldsUncached(Sets.union(registrar.getAllowedTlds(), ImmutableSet.of(tld)))
|
|
.build();
|
|
}
|
|
|
|
private static Registry createTld(
|
|
String tldName,
|
|
TldState initialTldState,
|
|
Duration addGracePeriod,
|
|
Duration redemptionGracePeriod,
|
|
Duration pendingDeleteLength,
|
|
boolean isEarlyAccess,
|
|
int roidSuffix) {
|
|
String tldNameAlphaNumerical = tldName.replaceAll("[^a-z0-9]", "");
|
|
Optional<PremiumList> premiumList = PremiumList.getUncached(DEFAULT_PREMIUM_LIST);
|
|
checkState(premiumList.isPresent(), "Couldn't find premium list %s.", DEFAULT_PREMIUM_LIST);
|
|
Registry.Builder builder =
|
|
new Registry.Builder()
|
|
.setTldStr(tldName)
|
|
.setPremiumPricingEngine(StaticPremiumListPricingEngine.NAME)
|
|
.setTldStateTransitions(ImmutableSortedMap.of(START_OF_TIME, initialTldState))
|
|
.setDnsWriters(ImmutableSet.of("VoidDnsWriter"))
|
|
.setPremiumList(premiumList.get())
|
|
.setRoidSuffix(
|
|
String.format(
|
|
"%S%X",
|
|
tldNameAlphaNumerical.substring(0, Math.min(tldNameAlphaNumerical.length(), 7)),
|
|
roidSuffix));
|
|
if (addGracePeriod != null) {
|
|
builder.setAddGracePeriodLength(addGracePeriod);
|
|
}
|
|
if (pendingDeleteLength != null) {
|
|
builder.setPendingDeleteLength(pendingDeleteLength);
|
|
}
|
|
if (redemptionGracePeriod != null) {
|
|
builder.setRedemptionGracePeriodLength(redemptionGracePeriod);
|
|
}
|
|
if (isEarlyAccess) {
|
|
builder.setEapFeeSchedule(EAP_FEE_SCHEDULE);
|
|
}
|
|
return builder.build();
|
|
}
|
|
|
|
/**
|
|
* Creates the Registrar without the allowedTlds set - because we can't set allowedTlds before the
|
|
* TLD is saved.
|
|
*/
|
|
private static Registrar createRegistrar(String registrarName) {
|
|
return new Registrar.Builder()
|
|
.setClientId(registrarName)
|
|
.setRegistrarName(registrarName)
|
|
.setType(Registrar.Type.OTE)
|
|
.setLocalizedAddress(DEFAULT_ADDRESS)
|
|
.setEmailAddress("foo@neverland.com")
|
|
.setFaxNumber("+1.2125550100")
|
|
.setPhoneNumber("+1.2125550100")
|
|
.setIcannReferralEmail("nightmare@registrar.test")
|
|
.setState(Registrar.State.ACTIVE)
|
|
.build();
|
|
}
|
|
|
|
private static RegistrarContact createRegistrarContact(
|
|
String email, String gaeUserId, Registrar registrar) {
|
|
return new RegistrarContact.Builder()
|
|
.setParent(registrar)
|
|
.setName(email)
|
|
.setEmailAddress(email)
|
|
.setGaeUserId(gaeUserId)
|
|
.build();
|
|
}
|
|
|
|
/** Returns the ClientIds of the OT&E, with the TLDs each has access to. */
|
|
public static ImmutableMap<String, String> createClientIdToTldMap(String baseClientId) {
|
|
checkArgument(
|
|
REGISTRAR_PATTERN.matcher(baseClientId).matches(),
|
|
"Invalid registrar name: %s",
|
|
baseClientId);
|
|
return new ImmutableMap.Builder<String, String>()
|
|
.put(baseClientId + "-1", baseClientId + "-sunrise")
|
|
// The -2 registrar no longer exists because landrush no longer exists.
|
|
.put(baseClientId + "-3", baseClientId + "-ga")
|
|
.put(baseClientId + "-4", baseClientId + "-ga")
|
|
.put(baseClientId + "-5", baseClientId + "-eap")
|
|
.build();
|
|
}
|
|
|
|
/** Returns the base client ID that correspond to a given OT&E client ID. */
|
|
public static String getBaseClientId(String oteClientId) {
|
|
int index = oteClientId.lastIndexOf('-');
|
|
checkArgument(index > 0, "Invalid OT&E client ID: %s", oteClientId);
|
|
String baseClientId = oteClientId.substring(0, index);
|
|
checkArgument(
|
|
createClientIdToTldMap(baseClientId).containsKey(oteClientId),
|
|
"ID %s is not one of the OT&E client IDs for base %s",
|
|
oteClientId,
|
|
baseClientId);
|
|
return baseClientId;
|
|
}
|
|
}
|