Java tutorial
// Copyright 2016 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.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()); } } }