mirror of
https://github.com/google/nomulus.git
synced 2025-05-02 13:07:50 +02:00
This does not change existing functionality but will allow us to use this common code in the yet-to-be-created web console action as well. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=227159346
377 lines
14 KiB
Java
377 lines
14 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 landrushTld;
|
|
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);
|
|
landrushTld =
|
|
createTld(baseClientId + "-landrush", TldState.LANDRUSH, null, null, null, false, 1);
|
|
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, landrushTld, 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. */
|
|
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")
|
|
.put(baseClientId + "-2", baseClientId + "-landrush")
|
|
.put(baseClientId + "-3", baseClientId + "-ga")
|
|
.put(baseClientId + "-4", baseClientId + "-ga")
|
|
.put(baseClientId + "-5", baseClientId + "-eap")
|
|
.build();
|
|
}
|
|
}
|