google-nomulus/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java
mcilwain 4813ed392b Rename "clientIdentifier" to "clientId" almost everywhere
It's best to be consistent and use the same thing everywhere.  "clientId" was
already used in more places and is shorter and no more ambiguous, so it's the
logical one to win out.

Note that this CL is almost solely a big Eclipse-assisted refactoring. There are
two places that I did not change clientIdentifier -- the actual entity field on
Registrar (though I did change all getters and setters), and the name of a
column on the exported registrar spreadsheet. Both would require data
migrations.

Also fixes a few minor nits discovered in touched files, including an incorrect
test in OfyFilterTest.java and some superfluous uses of String.format() when
calling checkArgument().

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=133956465
2016-09-22 14:30:05 -04:00

404 lines
14 KiB
Java

// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.tools;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Predicates.isNull;
import static com.google.common.base.Strings.isNullOrEmpty;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
import static google.registry.util.RegistrarUtils.normalizeRegistrarName;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static org.joda.time.DateTimeZone.UTC;
import com.beust.jcommander.Parameter;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import google.registry.model.billing.RegistrarBillingUtils;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.Registrar.BillingMethod;
import google.registry.model.registrar.RegistrarAddress;
import google.registry.tools.params.OptionalLongParameter;
import google.registry.tools.params.OptionalPhoneNumberParameter;
import google.registry.tools.params.OptionalStringParameter;
import google.registry.tools.params.PathParameter;
import google.registry.util.CidrAddressBlock;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import org.joda.money.CurrencyUnit;
import org.joda.money.Money;
import org.joda.time.DateTime;
/** Shared base class for commands to create or update a {@link Registrar}. */
abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
@Parameter(
description = "Client identifier of the registrar account",
required = true)
List<String> mainParameters;
@Parameter(
names = "--registrar_type",
description = "Type of the registrar")
Registrar.Type registrarType;
@Nullable
@Parameter(
names = "--registrar_state",
description = "Initial state of the registrar")
Registrar.State registrarState;
@Parameter(
names = "--allowed_tlds",
description = "Comma-delimited list of TLDs which the registrar is allowed to use")
List<String> allowedTlds = new ArrayList<>();
@Parameter(
names = "--add_allowed_tlds",
description = "Comma-delimited list of TLDs to add to TLDs a registrar is allowed to use")
List<String> addAllowedTlds = new ArrayList<>();
@Nullable
@Parameter(
names = "--password",
description = "Password for the registrar account")
String password;
@Nullable
@Parameter(
names = "--name",
description = "Name of the registrar")
String registrarName;
@Nullable
@Parameter(
names = "--email",
description = "Email address of registrar",
converter = OptionalStringParameter.class,
validateWith = OptionalStringParameter.class)
Optional<String> email;
@Nullable
@Parameter(
names = "--icann_referral_email",
description = "ICANN referral email, as specified in registrar contract")
String icannReferralEmail;
@Nullable
@Parameter(
names = "--url",
description = "URL of registrar's website",
converter = OptionalStringParameter.class,
validateWith = OptionalStringParameter.class)
private Optional<String> url;
@Nullable
@Parameter(
names = "--phone",
description = "E.164 phone number, e.g. +1.2125650666",
converter = OptionalPhoneNumberParameter.class,
validateWith = OptionalPhoneNumberParameter.class)
Optional<String> phone;
@Nullable
@Parameter(
names = "--fax",
description = "E.164 fax number, e.g. +1.2125650666",
converter = OptionalPhoneNumberParameter.class,
validateWith = OptionalPhoneNumberParameter.class)
Optional<String> fax;
@Nullable
@Parameter(
names = "--cert_file",
description = "File containing client certificate (X.509 PEM)",
validateWith = PathParameter.InputFile.class)
Path clientCertificateFilename;
@Nullable
@Parameter(
names = "--cert_hash",
description = "Hash of client certificate (SHA256 base64 no padding). Do not use this unless "
+ "you want to store ONLY the hash and not the full certificate")
private String clientCertificateHash;
@Nullable
@Parameter(
names = "--failover_cert_file",
description = "File containing failover client certificate (X.509 PEM)",
validateWith = PathParameter.InputFile.class)
Path failoverClientCertificateFilename;
@Parameter(
names = "--ip_whitelist",
description = "Comma-delimited list of IP ranges")
List<String> ipWhitelist = new ArrayList<>();
@Nullable
@Parameter(
names = "--iana_id",
description = "Registrar IANA ID",
converter = OptionalLongParameter.class,
validateWith = OptionalLongParameter.class)
Optional<Long> ianaId;
@Nullable
@Parameter(
names = "--billing_id",
description = "Registrar Billing ID (i.e. Oracle #)",
converter = OptionalLongParameter.class,
validateWith = OptionalLongParameter.class)
private Optional<Long> billingId;
@Nullable
@Parameter(
names = "--billing_method",
description = "Method by which registry bills this registrar customer")
private BillingMethod billingMethod;
@Nullable
@Parameter(
names = "--street",
variableArity = true,
description = "Street lines of address. Can take up to 3 lines.")
List<String> street;
@Nullable
@Parameter(
names = "--city",
description = "City of address")
String city;
@Nullable
@Parameter(
names = "--state",
description = "State/Province of address. The value \"null\" clears this field.")
String state;
@Nullable
@Parameter(
names = "--zip",
description = "Postal code of address. The value \"null\" clears this field.")
String zip;
@Nullable
@Parameter(
names = "--cc",
description = "Country code of address")
String countryCode;
@Nullable
@Parameter(
names = "--block_premium",
description = "Whether premium name registration should be blocked on this registrar",
arity = 1)
private Boolean blockPremiumNames;
@Nullable
@Parameter(
names = "--sync_groups",
description = "Whether this registrar's groups should be updated at the next scheduled sync",
arity = 1)
private Boolean contactsRequireSyncing;
@Nullable
@Parameter(
names = "--drive_id",
description = "Id of this registrar's folder in Drive",
converter = OptionalStringParameter.class,
validateWith = OptionalStringParameter.class)
Optional<String> driveFolderId;
@Nullable
@Parameter(
names = "--passcode",
description = "Telephone support passcode")
String phonePasscode;
@Nullable
@Parameter(
names = "--whois",
description = "Hostname of registrar WHOIS server. (Default: whois.nic.google)")
String whoisServer;
/** Returns the existing registrar (for update) or null (for creates). */
@Nullable
abstract Registrar getOldRegistrar(String clientId);
protected void initRegistrarCommand() throws Exception {}
@Override
protected final void init() throws Exception {
initRegistrarCommand();
DateTime now = DateTime.now(UTC);
for (String clientId : mainParameters) {
Registrar oldRegistrar = getOldRegistrar(clientId);
Registrar.Builder builder = (oldRegistrar == null)
? new Registrar.Builder().setClientId(clientId)
: oldRegistrar.asBuilder();
if (!isNullOrEmpty(password)) {
builder.setPassword(password);
}
if (!isNullOrEmpty(registrarName)) {
builder.setRegistrarName(registrarName);
}
if (email != null) {
builder.setEmailAddress(email.orNull());
}
if (url != null) {
builder.setUrl(url.orNull());
}
if (phone != null) {
builder.setPhoneNumber(phone.orNull());
}
if (fax != null) {
builder.setFaxNumber(fax.orNull());
}
if (registrarType != null) {
builder.setType(registrarType);
}
if (registrarState != null) {
builder.setState(registrarState);
}
if (driveFolderId != null) {
builder.setDriveFolderId(driveFolderId.orNull());
}
if (!allowedTlds.isEmpty()) {
checkArgument(addAllowedTlds.isEmpty(),
"Can't specify both --allowedTlds and --addAllowedTlds");
ImmutableSet.Builder<String> allowedTldsBuilder = new ImmutableSet.Builder<>();
for (String allowedTld : allowedTlds) {
allowedTldsBuilder.add(canonicalizeDomainName(allowedTld));
}
builder.setAllowedTlds(allowedTldsBuilder.build());
}
if (!addAllowedTlds.isEmpty()) {
ImmutableSet.Builder<String> allowedTldsBuilder = new ImmutableSet.Builder<>();
if (oldRegistrar != null) {
allowedTldsBuilder.addAll(oldRegistrar.getAllowedTlds());
}
for (String allowedTld : addAllowedTlds) {
allowedTldsBuilder.add(canonicalizeDomainName(allowedTld));
}
builder.setAllowedTlds(allowedTldsBuilder.build());
}
if (!ipWhitelist.isEmpty()) {
ImmutableList.Builder<CidrAddressBlock> ipWhitelistBuilder = new ImmutableList.Builder<>();
if (!(ipWhitelist.size() == 1 && ipWhitelist.get(0).contains("null"))) {
for (String ipRange : ipWhitelist) {
ipWhitelistBuilder.add(CidrAddressBlock.create(ipRange));
}
}
builder.setIpAddressWhitelist(ipWhitelistBuilder.build());
}
if (clientCertificateFilename != null) {
String asciiCert = new String(Files.readAllBytes(clientCertificateFilename), US_ASCII);
builder.setClientCertificate(asciiCert, now);
}
if (failoverClientCertificateFilename != null) {
String asciiCert =
new String(Files.readAllBytes(failoverClientCertificateFilename), US_ASCII);
builder.setFailoverClientCertificate(asciiCert, now);
}
if (!isNullOrEmpty(clientCertificateHash)) {
checkArgument(clientCertificateFilename == null,
"Can't specify both --cert_hash and --cert_file");
if ("null".equals(clientCertificateHash)) {
clientCertificateHash = null;
}
builder.setClientCertificateHash(clientCertificateHash);
}
if (ianaId != null) {
builder.setIanaIdentifier(ianaId.orNull());
}
if (billingId != null) {
builder.setBillingIdentifier(billingId.orNull());
}
if (billingMethod != null) {
if (oldRegistrar != null && !billingMethod.equals(oldRegistrar.getBillingMethod())) {
Map<CurrencyUnit, Money> balances = RegistrarBillingUtils.loadBalance(oldRegistrar);
for (Money balance : balances.values()) {
checkState(balance.isZero(),
"Refusing to change billing method on Registrar '%s' from %s to %s"
+ " because current balance is non-zero: %s",
clientId, oldRegistrar.getBillingMethod(), billingMethod, balances);
}
}
builder.setBillingMethod(billingMethod);
}
List<Object> streetAddressFields = Arrays.asList(street, city, state, zip, countryCode);
checkArgument(Iterables.any(streetAddressFields, isNull())
== Iterables.all(streetAddressFields, isNull()),
"Must specify all fields of address");
if (street != null) {
// We always set the localized address for now. That should be safe to do since it supports
// unrestricted UTF-8.
builder.setLocalizedAddress(new RegistrarAddress.Builder()
.setStreet(ImmutableList.copyOf(street))
.setCity(city)
.setState("null".equals(state) ? null : state)
.setZip("null".equals(zip) ? null : zip)
.setCountryCode(countryCode)
.build());
}
if (blockPremiumNames != null) {
builder.setBlockPremiumNames(blockPremiumNames);
}
if (contactsRequireSyncing != null) {
builder.setContactsRequireSyncing(contactsRequireSyncing);
}
// When creating a new REAL registrar or changing the type to REAL, a passcode is required.
// Leave existing REAL registrars alone.
if (Registrar.Type.REAL.equals(registrarType)
&& (oldRegistrar == null || oldRegistrar.getPhonePasscode() == null)) {
checkArgument(phonePasscode != null, "--passcode is required for REAL registrars.");
}
if (phonePasscode != null) {
builder.setPhonePasscode(phonePasscode);
}
if (icannReferralEmail != null) {
builder.setIcannReferralEmail(icannReferralEmail);
}
if (whoisServer != null) {
builder.setWhoisServer(whoisServer);
}
// If the registrarName is being set, verify that it is either null or it normalizes uniquely.
String oldRegistrarName = (oldRegistrar == null) ? null : oldRegistrar.getRegistrarName();
if (registrarName != null && !registrarName.equals(oldRegistrarName)) {
String normalizedName = normalizeRegistrarName(registrarName);
for (Registrar registrar : Registrar.loadAll()) {
if (registrar.getRegistrarName() != null) {
checkArgument(
!normalizedName.equals(normalizeRegistrarName(registrar.getRegistrarName())),
"The registrar name %s normalizes identically to existing registrar name %s",
registrarName,
registrar.getRegistrarName());
}
}
}
stageEntityChange(oldRegistrar, builder.build());
}
}
}