Canonicalize domain/host names in nomulus tool commands (#1583)

* Canonicalize domain/host names in nomulus tool commands

This helps prevent some common user errors.
This commit is contained in:
Ben McIlwain 2022-04-06 18:35:38 -04:00 committed by GitHub
parent 6314eb4ee7
commit 99b1e93cc4
36 changed files with 132 additions and 86 deletions

View file

@ -22,7 +22,7 @@ import static google.registry.beam.initsql.BackupPaths.getExportFilePatterns;
import static google.registry.model.ofy.ObjectifyService.auditedOfy; import static google.registry.model.ofy.ObjectifyService.auditedOfy;
import static google.registry.util.DateTimeUtils.START_OF_TIME; import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static google.registry.util.DateTimeUtils.isBeforeOrAt; import static google.registry.util.DateTimeUtils.isBeforeOrAt;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName; import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import static java.util.Comparator.comparing; import static java.util.Comparator.comparing;
import static org.apache.beam.sdk.values.TypeDescriptors.kvs; import static org.apache.beam.sdk.values.TypeDescriptors.kvs;
import static org.apache.beam.sdk.values.TypeDescriptors.strings; import static org.apache.beam.sdk.values.TypeDescriptors.strings;
@ -343,11 +343,11 @@ public final class Transforms {
// Canonicalize old domain/host names from 2016 and earlier before we were enforcing this. // Canonicalize old domain/host names from 2016 and earlier before we were enforcing this.
entity.setIndexedProperty( entity.setIndexedProperty(
"fullyQualifiedDomainName", "fullyQualifiedDomainName",
canonicalizeDomainName((String) entity.getProperty("fullyQualifiedDomainName"))); canonicalizeHostname((String) entity.getProperty("fullyQualifiedDomainName")));
} else if (entity.getKind().equals("HostResource")) { } else if (entity.getKind().equals("HostResource")) {
entity.setIndexedProperty( entity.setIndexedProperty(
"fullyQualifiedHostName", "fullyQualifiedHostName",
canonicalizeDomainName((String) entity.getProperty("fullyQualifiedHostName"))); canonicalizeHostname((String) entity.getProperty("fullyQualifiedHostName")));
} }
return entity; return entity;
} }

View file

@ -31,7 +31,7 @@ import static google.registry.monitoring.whitebox.CheckApiMetric.Status.UNKNOWN_
import static google.registry.monitoring.whitebox.CheckApiMetric.Tier.PREMIUM; import static google.registry.monitoring.whitebox.CheckApiMetric.Tier.PREMIUM;
import static google.registry.monitoring.whitebox.CheckApiMetric.Tier.STANDARD; import static google.registry.monitoring.whitebox.CheckApiMetric.Tier.STANDARD;
import static google.registry.pricing.PricingEngineProxy.isDomainPremium; import static google.registry.pricing.PricingEngineProxy.isDomainPremium;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName; import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import static org.json.simple.JSONValue.toJSONString; import static org.json.simple.JSONValue.toJSONString;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@ -105,7 +105,7 @@ public class CheckApiAction implements Runnable {
String domainString; String domainString;
InternetDomainName domainName; InternetDomainName domainName;
try { try {
domainString = canonicalizeDomainName(nullToEmpty(domain)); domainString = canonicalizeHostname(nullToEmpty(domain));
domainName = validateDomainName(domainString); domainName = validateDomainName(domainString);
} catch (IllegalArgumentException | EppException e) { } catch (IllegalArgumentException | EppException e) {
metricBuilder.status(INVALID_NAME); metricBuilder.status(INVALID_NAME);

View file

@ -175,7 +175,7 @@ public class DomainBase extends DomainContent
@Override @Override
public void beforeSqlSaveOnReplay() { public void beforeSqlSaveOnReplay() {
fullyQualifiedDomainName = DomainNameUtils.canonicalizeDomainName(fullyQualifiedDomainName); fullyQualifiedDomainName = DomainNameUtils.canonicalizeHostname(fullyQualifiedDomainName);
dsData = dsData =
dsData.stream() dsData.stream()
.filter(datum -> datum.getDigest() != null && datum.getDigest().length > 0) .filter(datum -> datum.getDigest() != null && datum.getDigest().length > 0)

View file

@ -34,7 +34,7 @@ import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.earliestOf; import static google.registry.util.DateTimeUtils.earliestOf;
import static google.registry.util.DateTimeUtils.isBeforeOrAt; import static google.registry.util.DateTimeUtils.isBeforeOrAt;
import static google.registry.util.DateTimeUtils.leapSafeAddYears; import static google.registry.util.DateTimeUtils.leapSafeAddYears;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName; import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import static google.registry.util.DomainNameUtils.getTldFromDomainName; import static google.registry.util.DomainNameUtils.getTldFromDomainName;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
@ -890,7 +890,7 @@ public class DomainContent extends EppResource
public B setDomainName(String domainName) { public B setDomainName(String domainName) {
checkArgument( checkArgument(
domainName.equals(canonicalizeDomainName(domainName)), domainName.equals(canonicalizeHostname(domainName)),
"Domain name %s not in puny-coded, lower-case form", "Domain name %s not in puny-coded, lower-case form",
domainName); domainName);
getInstance().fullyQualifiedDomainName = domainName; getInstance().fullyQualifiedDomainName = domainName;

View file

@ -309,7 +309,7 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
if (domainContent == null) { if (domainContent == null) {
domainContent = jpaTm().getEntityManager().find(DomainBase.class, getDomainRepoId()); domainContent = jpaTm().getEntityManager().find(DomainBase.class, getDomainRepoId());
domainContent.fullyQualifiedDomainName = domainContent.fullyQualifiedDomainName =
DomainNameUtils.canonicalizeDomainName(domainContent.fullyQualifiedDomainName); DomainNameUtils.canonicalizeHostname(domainContent.fullyQualifiedDomainName);
fillAuxiliaryFieldsFromDomain(this); fillAuxiliaryFieldsFromDomain(this);
} }
} }

View file

@ -19,7 +19,7 @@ import static com.google.common.collect.Sets.difference;
import static com.google.common.collect.Sets.union; import static com.google.common.collect.Sets.union;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import static google.registry.util.DateTimeUtils.START_OF_TIME; import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName; import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.annotation.IgnoreSave; import com.googlecode.objectify.annotation.IgnoreSave;
@ -195,7 +195,7 @@ public class HostBase extends EppResource {
public B setHostName(String hostName) { public B setHostName(String hostName) {
checkArgument( checkArgument(
hostName.equals(canonicalizeDomainName(hostName)), hostName.equals(canonicalizeHostname(hostName)),
"Host name %s not in puny-coded, lower-case form", "Host name %s not in puny-coded, lower-case form",
hostName); hostName);
getInstance().fullyQualifiedHostName = hostName; getInstance().fullyQualifiedHostName = hostName;

View file

@ -143,7 +143,7 @@ public class HostHistory extends HistoryEntry implements SqlEntity, UnsafeSerial
if (hostBase == null) { if (hostBase == null) {
hostBase = jpaTm().getEntityManager().find(HostResource.class, getHostRepoId()); hostBase = jpaTm().getEntityManager().find(HostResource.class, getHostRepoId());
hostBase.fullyQualifiedHostName = hostBase.fullyQualifiedHostName =
DomainNameUtils.canonicalizeDomainName(hostBase.fullyQualifiedHostName); DomainNameUtils.canonicalizeHostname(hostBase.fullyQualifiedHostName);
} }
} }

View file

@ -73,7 +73,7 @@ public class HostResource extends HostBase
@Override @Override
public void beforeSqlSaveOnReplay() { public void beforeSqlSaveOnReplay() {
fullyQualifiedHostName = DomainNameUtils.canonicalizeDomainName(fullyQualifiedHostName); fullyQualifiedHostName = DomainNameUtils.canonicalizeHostname(fullyQualifiedHostName);
} }
@Override @Override

View file

@ -16,7 +16,7 @@ package google.registry.model.tld.label;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.emptyToNull; import static com.google.common.base.Strings.emptyToNull;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName; import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.google.common.net.InternetDomainName; import com.google.common.net.InternetDomainName;
@ -77,7 +77,7 @@ public abstract class DomainLabelEntry<T extends Comparable<?>, D extends Domain
public T build() { public T build() {
checkArgumentNotNull(emptyToNull(getInstance().domainLabel), "Label must be specified"); checkArgumentNotNull(emptyToNull(getInstance().domainLabel), "Label must be specified");
checkArgument( checkArgument(
getInstance().domainLabel.equals(canonicalizeDomainName(getInstance().domainLabel)), getInstance().domainLabel.equals(canonicalizeHostname(getInstance().domainLabel)),
"Label '%s' must be in puny-coded, lower-case form", "Label '%s' must be in puny-coded, lower-case form",
getInstance().domainLabel); getInstance().domainLabel);
checkArgumentNotNull(getInstance().getValue(), "Value must be specified"); checkArgumentNotNull(getInstance().getValue(), "Value must be specified");

View file

@ -18,7 +18,7 @@ import static com.google.common.base.Charsets.UTF_8;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.net.HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN; import static com.google.common.net.HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN;
import static google.registry.request.Actions.getPathForAction; import static google.registry.request.Actions.getPathForAction;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName; import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import static javax.servlet.http.HttpServletResponse.SC_OK; import static javax.servlet.http.HttpServletResponse.SC_OK;
@ -245,7 +245,7 @@ public abstract class RdapActionBase implements Runnable {
} }
String canonicalizeName(String name) { String canonicalizeName(String name) {
name = canonicalizeDomainName(name); name = canonicalizeHostname(name);
if (name.endsWith(".")) { if (name.endsWith(".")) {
name = name.substring(0, name.length() - 1); name = name.substring(0, name.length() - 1);
} }

View file

@ -19,7 +19,7 @@ import static com.google.appengine.api.urlfetch.HTTPMethod.PUT;
import static com.google.common.io.BaseEncoding.base64; import static com.google.common.io.BaseEncoding.base64;
import static com.google.common.net.HttpHeaders.AUTHORIZATION; import static com.google.common.net.HttpHeaders.AUTHORIZATION;
import static com.google.common.net.HttpHeaders.CONTENT_TYPE; import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName; import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static javax.servlet.http.HttpServletResponse.SC_OK; import static javax.servlet.http.HttpServletResponse.SC_OK;
@ -123,7 +123,7 @@ public class RdeReporter {
private URL makeReportUrl(String tld, String id) { private URL makeReportUrl(String tld, String id) {
try { try {
return new URL(String.format("%s/%s/%s", reportUrlPrefix, canonicalizeDomainName(tld), id)); return new URL(String.format("%s/%s/%s", reportUrlPrefix, canonicalizeHostname(tld), id));
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View file

@ -14,7 +14,7 @@
package google.registry.tools; package google.registry.tools;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName; import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameter;
@ -80,7 +80,7 @@ final class CanonicalizeLabelsCommand implements Command {
private String canonicalize(String rawLabel) { private String canonicalize(String rawLabel) {
try { try {
return canonicalizeDomainName(rawLabel.replaceAll(" ", "")); return canonicalizeHostname(rawLabel.replaceAll(" ", ""));
} catch (Exception e) { } catch (Exception e) {
System.err.printf("Error canonicalizing %s: %s\n", rawLabel, e.getMessage()); System.err.printf("Error canonicalizing %s: %s\n", rawLabel, e.getMessage());
return ""; return "";

View file

@ -15,7 +15,7 @@
package google.registry.tools; package google.registry.tools;
import static google.registry.util.DomainNameUtils.ACE_PREFIX; import static google.registry.util.DomainNameUtils.ACE_PREFIX;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName; import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters; import com.beust.jcommander.Parameters;
@ -38,7 +38,7 @@ final class ConvertIdnCommand implements Command {
if (label.startsWith(ACE_PREFIX)) { if (label.startsWith(ACE_PREFIX)) {
System.out.println(Idn.toUnicode(Ascii.toLowerCase(label))); System.out.println(Idn.toUnicode(Ascii.toLowerCase(label)));
} else { } else {
System.out.println(canonicalizeDomainName(label)); System.out.println(canonicalizeHostname(label));
} }
} }
} }

View file

@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.net.InetAddresses; import com.google.common.net.InetAddresses;
import com.google.template.soy.data.SoyMapData; import com.google.template.soy.data.SoyMapData;
import google.registry.tools.soy.HostCreateSoyInfo; import google.registry.tools.soy.HostCreateSoyInfo;
import google.registry.util.DomainNameUtils;
import java.net.Inet4Address; import java.net.Inet4Address;
import java.net.Inet6Address; import java.net.Inet6Address;
import java.net.InetAddress; import java.net.InetAddress;
@ -68,7 +69,7 @@ final class CreateHostCommand extends MutatingEppToolCommand {
addSoyRecord( addSoyRecord(
clientId, clientId,
new SoyMapData( new SoyMapData(
"hostname", hostName, "hostname", DomainNameUtils.canonicalizeHostname(hostName),
"ipv4addresses", ipv4Addresses.build(), "ipv4addresses", ipv4Addresses.build(),
"ipv6addresses", ipv6Addresses.build())); "ipv6addresses", ipv6Addresses.build()));
} }

View file

@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Predicates.isNull; import static com.google.common.base.Predicates.isNull;
import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName; import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import static google.registry.util.RegistrarUtils.normalizeRegistrarName; import static google.registry.util.RegistrarUtils.normalizeRegistrarName;
import static java.nio.charset.StandardCharsets.US_ASCII; import static java.nio.charset.StandardCharsets.US_ASCII;
import static org.joda.time.DateTimeZone.UTC; import static org.joda.time.DateTimeZone.UTC;
@ -319,7 +319,7 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
addAllowedTlds.isEmpty(), "Can't specify both --allowedTlds and --addAllowedTlds"); addAllowedTlds.isEmpty(), "Can't specify both --allowedTlds and --addAllowedTlds");
ImmutableSet.Builder<String> allowedTldsBuilder = new ImmutableSet.Builder<>(); ImmutableSet.Builder<String> allowedTldsBuilder = new ImmutableSet.Builder<>();
for (String allowedTld : allowedTlds) { for (String allowedTld : allowedTlds) {
allowedTldsBuilder.add(canonicalizeDomainName(allowedTld)); allowedTldsBuilder.add(canonicalizeHostname(allowedTld));
} }
builder.setAllowedTlds(allowedTldsBuilder.build()); builder.setAllowedTlds(allowedTldsBuilder.build());
} }
@ -329,7 +329,7 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
allowedTldsBuilder.addAll(oldRegistrar.getAllowedTlds()); allowedTldsBuilder.addAll(oldRegistrar.getAllowedTlds());
} }
for (String allowedTld : addAllowedTlds) { for (String allowedTld : addAllowedTlds) {
allowedTldsBuilder.add(canonicalizeDomainName(allowedTld)); allowedTldsBuilder.add(canonicalizeHostname(allowedTld));
} }
builder.setAllowedTlds(allowedTldsBuilder.build()); builder.setAllowedTlds(allowedTldsBuilder.build());
} }

View file

@ -16,7 +16,7 @@ package google.registry.tools;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.util.CollectionUtils.findDuplicates; import static google.registry.util.CollectionUtils.findDuplicates;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName; import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameter;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
@ -263,10 +263,10 @@ abstract class CreateOrUpdateTldCommand extends MutatingCommand {
"Can't update roid suffixes on multiple TLDs simultaneously"); "Can't update roid suffixes on multiple TLDs simultaneously");
for (String tld : tlds) { for (String tld : tlds) {
checkArgument( checkArgument(
tld.equals(canonicalizeDomainName(tld)), tld.equals(canonicalizeHostname(tld)),
"TLD '%s' should be given in the canonical form '%s'", "TLD '%s' should be given in the canonical form '%s'",
tld, tld,
canonicalizeDomainName(tld)); canonicalizeHostname(tld));
checkArgument( checkArgument(
!Character.isDigit(tld.charAt(0)), !Character.isDigit(tld.charAt(0)),
"TLDs cannot begin with a number"); "TLDs cannot begin with a number");

View file

@ -18,6 +18,7 @@ import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters; import com.beust.jcommander.Parameters;
import com.google.template.soy.data.SoyMapData; import com.google.template.soy.data.SoyMapData;
import google.registry.tools.soy.HostDeleteSoyInfo; import google.registry.tools.soy.HostDeleteSoyInfo;
import google.registry.util.DomainNameUtils;
/** A command to delete a host via EPP. */ /** A command to delete a host via EPP. */
@Parameters(separators = " =", commandDescription = "Delete host") @Parameters(separators = " =", commandDescription = "Delete host")
@ -50,8 +51,10 @@ final class DeleteHostCommand extends MutatingEppToolCommand {
@Override @Override
protected void initMutatingEppToolCommand() { protected void initMutatingEppToolCommand() {
setSoyTemplate(HostDeleteSoyInfo.getInstance(), HostDeleteSoyInfo.DELETEHOST); setSoyTemplate(HostDeleteSoyInfo.getInstance(), HostDeleteSoyInfo.DELETEHOST);
addSoyRecord(clientId, new SoyMapData( addSoyRecord(
"hostName", hostName, clientId,
new SoyMapData(
"hostName", DomainNameUtils.canonicalizeHostname(hostName),
"reason", reason, "reason", reason,
"requestedByRegistrar", requestedByRegistrar)); "requestedByRegistrar", requestedByRegistrar));
} }

View file

@ -14,7 +14,7 @@
package google.registry.tools; package google.registry.tools;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName; import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters; import com.beust.jcommander.Parameters;
@ -61,6 +61,6 @@ class EncryptEscrowDepositCommand implements CommandWithRemoteApi {
@Override @Override
public final void run() throws Exception { public final void run() throws Exception {
encryptor.encrypt(mode, canonicalizeDomainName(tld), revision, input, outdir); encryptor.encrypt(mode, canonicalizeHostname(tld), revision, input, outdir);
} }
} }

View file

@ -30,6 +30,7 @@ import google.registry.model.domain.DomainHistory;
import google.registry.model.poll.PollMessage; import google.registry.model.poll.PollMessage;
import google.registry.model.registrar.Registrar; import google.registry.model.registrar.Registrar;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import google.registry.util.DomainNameUtils;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import javax.inject.Inject; import javax.inject.Inject;
@ -79,6 +80,7 @@ class EnqueuePollMessageCommand extends MutatingCommand {
@Override @Override
protected final void init() { protected final void init() {
domainName = DomainNameUtils.canonicalizeHostname(domainName);
checkArgument( checkArgument(
!sendToAll || isNullOrEmpty(clientIds), "Cannot specify both --all and --clients"); !sendToAll || isNullOrEmpty(clientIds), "Cannot specify both --all and --clients");
tm().transact( tm().transact(

View file

@ -20,6 +20,7 @@ import static com.google.common.collect.Maps.filterValues;
import static com.google.common.io.Resources.getResource; import static com.google.common.io.Resources.getResource;
import static google.registry.model.tld.Registries.findTldForNameOrThrow; import static google.registry.model.tld.Registries.findTldForNameOrThrow;
import static google.registry.tools.CommandUtilities.addHeader; import static google.registry.tools.CommandUtilities.addHeader;
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import static google.registry.util.PreconditionsUtils.checkArgumentPresent; import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
import static google.registry.xml.XmlTransformer.prettyPrint; import static google.registry.xml.XmlTransformer.prettyPrint;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
@ -83,8 +84,9 @@ abstract class EppToolCommand extends ConfirmingCommand
protected static Multimap<String, String> validateAndGroupDomainNamesByTld(List<String> names) { protected static Multimap<String, String> validateAndGroupDomainNamesByTld(List<String> names) {
ImmutableMultimap.Builder<String, String> builder = new ImmutableMultimap.Builder<>(); ImmutableMultimap.Builder<String, String> builder = new ImmutableMultimap.Builder<>();
for (String name : names) { for (String name : names) {
InternetDomainName tld = findTldForNameOrThrow(InternetDomainName.from(name)); String canonicalDomain = canonicalizeHostname(name);
builder.put(tld.toString(), name); InternetDomainName tld = findTldForNameOrThrow(InternetDomainName.from(canonicalDomain));
builder.put(tld.toString(), canonicalDomain);
} }
return builder.build(); return builder.build();
} }

View file

@ -16,7 +16,6 @@ package google.registry.tools;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Queues.newArrayDeque;
import static com.google.common.collect.Sets.difference; import static com.google.common.collect.Sets.difference;
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE; import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE; import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
@ -44,15 +43,18 @@ import google.registry.model.domain.token.AllocationToken.TokenType;
import google.registry.persistence.VKey; import google.registry.persistence.VKey;
import google.registry.tools.params.TransitionListParameter.TokenStatusTransitions; import google.registry.tools.params.TransitionListParameter.TokenStatusTransitions;
import google.registry.util.CollectionUtils; import google.registry.util.CollectionUtils;
import google.registry.util.DomainNameUtils;
import google.registry.util.NonFinalForTesting; import google.registry.util.NonFinalForTesting;
import google.registry.util.Retrier; import google.registry.util.Retrier;
import google.registry.util.StringGenerator; import google.registry.util.StringGenerator;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Collection; import java.util.Collection;
import java.util.Deque; import java.util.Deque;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
@ -164,11 +166,12 @@ class GenerateAllocationTokensCommand implements CommandWithRemoteApi {
domainNames = null; domainNames = null;
} else { } else {
domainNames = domainNames =
newArrayDeque(
Splitter.on('\n') Splitter.on('\n')
.omitEmptyStrings() .omitEmptyStrings()
.trimResults() .trimResults()
.split(Files.asCharSource(new File(domainNamesFile), UTF_8).read())); .splitToStream(Files.asCharSource(new File(domainNamesFile), UTF_8).read())
.map(DomainNameUtils::canonicalizeHostname)
.collect(Collectors.toCollection(ArrayDeque::new));
numTokens = domainNames.size(); numTokens = domainNames.size();
} }

View file

@ -19,6 +19,7 @@ import static google.registry.model.EppResourceUtils.loadByForeignKey;
import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters; import com.beust.jcommander.Parameters;
import google.registry.model.domain.DomainBase; import google.registry.model.domain.DomainBase;
import google.registry.util.DomainNameUtils;
import java.util.List; import java.util.List;
/** Command to show a domain resource. */ /** Command to show a domain resource. */
@ -33,8 +34,11 @@ final class GetDomainCommand extends GetEppResourceCommand {
@Override @Override
public void runAndPrint() { public void runAndPrint() {
for (String domainName : mainParameters) { for (String domainName : mainParameters) {
String canonicalDomain = DomainNameUtils.canonicalizeHostname(domainName);
printResource( printResource(
"Domain", domainName, loadByForeignKey(DomainBase.class, domainName, readTimestamp)); "Domain",
canonicalDomain,
loadByForeignKey(DomainBase.class, canonicalDomain, readTimestamp));
} }
} }
} }

View file

@ -19,6 +19,7 @@ import static google.registry.model.EppResourceUtils.loadByForeignKey;
import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters; import com.beust.jcommander.Parameters;
import google.registry.model.host.HostResource; import google.registry.model.host.HostResource;
import google.registry.util.DomainNameUtils;
import java.util.List; import java.util.List;
/** Command to show one or more host resources. */ /** Command to show one or more host resources. */
@ -32,7 +33,9 @@ final class GetHostCommand extends GetEppResourceCommand {
@Override @Override
public void runAndPrint() { public void runAndPrint() {
mainParameters.forEach( mainParameters.stream()
.map(DomainNameUtils::canonicalizeHostname)
.forEach(
h -> printResource("Host", h, loadByForeignKey(HostResource.class, h, readTimestamp))); h -> printResource("Host", h, loadByForeignKey(HostResource.class, h, readTimestamp)));
} }
} }

View file

@ -14,6 +14,8 @@
package google.registry.tools; package google.registry.tools;
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import com.beust.jcommander.Parameters; import com.beust.jcommander.Parameters;
/** /**
@ -26,6 +28,6 @@ public class LockDomainCommand extends LockOrUnlockDomainCommand {
@Override @Override
protected void createAndApplyRequest(String domain) { protected void createAndApplyRequest(String domain) {
domainLockUtils.administrativelyApplyLock(domain, clientId, null, true); domainLockUtils.administrativelyApplyLock(canonicalizeHostname(domain), clientId, null, true);
} }
} }

View file

@ -38,6 +38,7 @@ import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.HostResource; import google.registry.model.host.HostResource;
import google.registry.tools.soy.DomainRenewSoyInfo; import google.registry.tools.soy.DomainRenewSoyInfo;
import google.registry.tools.soy.UniformRapidSuspensionSoyInfo; import google.registry.tools.soy.UniformRapidSuspensionSoyInfo;
import google.registry.util.DomainNameUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -122,12 +123,14 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand {
protected void initMutatingEppToolCommand() { protected void initMutatingEppToolCommand() {
superuser = true; superuser = true;
DateTime now = DateTime.now(UTC); DateTime now = DateTime.now(UTC);
ImmutableSet<String> newHostsSet = ImmutableSet.copyOf(newHosts); ImmutableList<String> newCanonicalHosts =
newHosts.stream().map(DomainNameUtils::canonicalizeHostname).collect(toImmutableList());
ImmutableSet<String> newHostsSet = ImmutableSet.copyOf(newCanonicalHosts);
Optional<DomainBase> domainOpt = loadByForeignKey(DomainBase.class, domainName, now); Optional<DomainBase> domainOpt = loadByForeignKey(DomainBase.class, domainName, now);
checkArgumentPresent(domainOpt, "Domain '%s' does not exist or is deleted", domainName); checkArgumentPresent(domainOpt, "Domain '%s' does not exist or is deleted", domainName);
DomainBase domain = domainOpt.get(); DomainBase domain = domainOpt.get();
Set<String> missingHosts = Set<String> missingHosts =
difference(newHostsSet, checkResourcesExist(HostResource.class, newHosts, now)); difference(newHostsSet, checkResourcesExist(HostResource.class, newCanonicalHosts, now));
checkArgument(missingHosts.isEmpty(), "Hosts do not exist: %s", missingHosts); checkArgument(missingHosts.isEmpty(), "Hosts do not exist: %s", missingHosts);
checkArgument( checkArgument(
locksToPreserve.isEmpty() || undo, locksToPreserve.isEmpty() || undo,

View file

@ -14,6 +14,8 @@
package google.registry.tools; package google.registry.tools;
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import com.beust.jcommander.Parameters; import com.beust.jcommander.Parameters;
import java.util.Optional; import java.util.Optional;
@ -27,6 +29,7 @@ public class UnlockDomainCommand extends LockOrUnlockDomainCommand {
@Override @Override
protected void createAndApplyRequest(String domain) { protected void createAndApplyRequest(String domain) {
domainLockUtils.administrativelyApplyUnlock(domain, clientId, true, Optional.empty()); domainLockUtils.administrativelyApplyUnlock(
canonicalizeHostname(domain), clientId, true, Optional.empty());
} }
} }

View file

@ -15,6 +15,7 @@
package google.registry.tools.params; package google.registry.tools.params;
import com.google.common.net.InternetDomainName; import com.google.common.net.InternetDomainName;
import google.registry.util.DomainNameUtils;
/** InternetDomainName CLI parameter converter/validator. */ /** InternetDomainName CLI parameter converter/validator. */
public final class InternetDomainNameParameter public final class InternetDomainNameParameter
@ -26,6 +27,6 @@ public final class InternetDomainNameParameter
@Override @Override
public InternetDomainName convert(String value) { public InternetDomainName convert(String value) {
return InternetDomainName.from(value); return InternetDomainName.from(DomainNameUtils.canonicalizeHostname(value));
} }
} }

View file

@ -17,7 +17,7 @@ package google.registry.ui.server;
import static com.google.common.collect.Range.atLeast; import static com.google.common.collect.Range.atLeast;
import static com.google.common.collect.Range.atMost; import static com.google.common.collect.Range.atMost;
import static com.google.common.collect.Range.closed; import static com.google.common.collect.Range.closed;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName; import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import com.google.common.base.Ascii; import com.google.common.base.Ascii;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
@ -334,7 +334,7 @@ public final class RegistrarFormFields {
if (!InternetDomainName.isValid(input)) { if (!InternetDomainName.isValid(input)) {
throw new FormFieldException("Not a valid hostname."); throw new FormFieldException("Not a valid hostname.");
} }
return canonicalizeDomainName(input); return canonicalizeHostname(input);
} }
public static @Nullable DateTime parseDateTime(@Nullable String input) { public static @Nullable DateTime parseDateTime(@Nullable String input) {

View file

@ -17,7 +17,7 @@ package google.registry.whois;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.common.base.Strings.isNullOrEmpty;
import static google.registry.model.tld.Registries.findTldForName; import static google.registry.model.tld.Registries.findTldForName;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName; import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
@ -122,7 +122,7 @@ class WhoisReader {
logger.atInfo().log( logger.atInfo().log(
"Attempting domain lookup command using domain name '%s'.", tokens.get(1)); "Attempting domain lookup command using domain name '%s'.", tokens.get(1));
return commandFactory.domainLookup( return commandFactory.domainLookup(
InternetDomainName.from(canonicalizeDomainName(tokens.get(1))), InternetDomainName.from(canonicalizeHostname(tokens.get(1))),
fullOutput, fullOutput,
whoisRedactedEmailText); whoisRedactedEmailText);
} catch (IllegalArgumentException iae) { } catch (IllegalArgumentException iae) {
@ -152,8 +152,8 @@ class WhoisReader {
try { try {
logger.atInfo().log( logger.atInfo().log(
"Attempting nameserver lookup command using %s as a hostname.", tokens.get(1)); "Attempting nameserver lookup command using %s as a hostname.", tokens.get(1));
return commandFactory.nameserverLookupByHost(InternetDomainName.from( return commandFactory.nameserverLookupByHost(
canonicalizeDomainName(tokens.get(1)))); InternetDomainName.from(canonicalizeHostname(tokens.get(1))));
} catch (IllegalArgumentException iae) { } catch (IllegalArgumentException iae) {
// Silently ignore this exception. // Silently ignore this exception.
} }
@ -187,7 +187,7 @@ class WhoisReader {
// Try to parse it as a domain name or host name. // Try to parse it as a domain name or host name.
try { try {
final InternetDomainName targetName = InternetDomainName.from(canonicalizeDomainName(arg1)); final InternetDomainName targetName = InternetDomainName.from(canonicalizeHostname(arg1));
// We don't know at this point whether we have a domain name or a host name. We have to // We don't know at this point whether we have a domain name or a host name. We have to
// search through our configured TLDs to see if there's one that prefixes the name. // search through our configured TLDs to see if there's one that prefixes the name.

View file

@ -37,6 +37,12 @@ class CheckDomainClaimsCommandTest extends EppToolCommandTestCase<CheckDomainCla
eppVerifier.expectDryRun().verifySent("domain_check_claims.xml"); eppVerifier.expectDryRun().verifySent("domain_check_claims.xml");
} }
@Test
void testSuccess_canonicalizesInput() throws Exception {
runCommand("--client=NewRegistrar", "exaMPle.TLD");
eppVerifier.expectDryRun().verifySent("domain_check_claims.xml");
}
@Test @Test
void testSuccess_multipleTlds() throws Exception { void testSuccess_multipleTlds() throws Exception {
runCommand("--client=NewRegistrar", "example.tld", "example.tld2"); runCommand("--client=NewRegistrar", "example.tld", "example.tld2");

View file

@ -29,7 +29,7 @@ class CreateHostCommandTest extends EppToolCommandTestCase<CreateHostCommand> {
createTld("tld"); createTld("tld");
runCommandForced( runCommandForced(
"--client=NewRegistrar", "--client=NewRegistrar",
"--host=example.tld", "--host=example.TLD",
"--addresses=162.100.102.99,2001:0db8:85a3:0000:0000:8a2e:0370:7334,4.5.6.7"); "--addresses=162.100.102.99,2001:0db8:85a3:0000:0000:8a2e:0370:7334,4.5.6.7");
eppVerifier.verifySent("host_create_complete.xml"); eppVerifier.verifySent("host_create_complete.xml");
} }

View file

@ -55,7 +55,8 @@ class EnqueuePollMessageCommandTest extends CommandTestCase<EnqueuePollMessageCo
@TestOfyAndSql @TestOfyAndSql
void testSuccess_domainAndMessage() throws Exception { void testSuccess_domainAndMessage() throws Exception {
runCommandForced("--domain=example.tld", "--message=This domain is bad"); // Test canonicalization to lowercase example.tld as well.
runCommandForced("--domain=EXAMPLE.TLD", "--message=This domain is bad");
HistoryEntry synthetic = getOnlyHistoryEntryOfType(domain, SYNTHETIC); HistoryEntry synthetic = getOnlyHistoryEntryOfType(domain, SYNTHETIC);
assertAboutHistoryEntries() assertAboutHistoryEntries()

View file

@ -18,7 +18,7 @@ import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE; import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE; import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
import static google.registry.testing.DatabaseHelper.assertAllocationTokens; import static google.registry.testing.DatabaseHelper.assertAllocationTokens;
import static google.registry.testing.DatabaseHelper.createTld; import static google.registry.testing.DatabaseHelper.createTlds;
import static google.registry.testing.DatabaseHelper.loadAllOf; import static google.registry.testing.DatabaseHelper.loadAllOf;
import static google.registry.testing.DatabaseHelper.persistResource; import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.START_OF_TIME; import static google.registry.util.DateTimeUtils.START_OF_TIME;
@ -138,16 +138,18 @@ class GenerateAllocationTokensCommandTest extends CommandTestCase<GenerateAlloca
@TestOfyAndSql @TestOfyAndSql
void testSuccess_domainNames() throws Exception { void testSuccess_domainNames() throws Exception {
createTld("tld"); createTlds("tld", "xn--q9jyb4c");
File domainNamesFile = tmpDir.resolve("domain_names.txt").toFile(); File domainNamesFile = tmpDir.resolve("domain_names.txt").toFile();
Files.asCharSink(domainNamesFile, UTF_8).write("foo1.tld\nboo2.tld\nbaz9.tld\n"); Files.asCharSink(domainNamesFile, UTF_8).write("foo1.tld\nboo2.tld\nçauçalito.みんな\n");
runCommand("--domain_names_file", domainNamesFile.getPath()); runCommand("--domain_names_file", domainNamesFile.getPath());
assertAllocationTokens( assertAllocationTokens(
createToken("123456789ABCDEFG", null, "foo1.tld"), createToken("123456789ABCDEFG", null, "foo1.tld"),
createToken("HJKLMNPQRSTUVWXY", null, "boo2.tld"), createToken("HJKLMNPQRSTUVWXY", null, "boo2.tld"),
createToken("Zabcdefghijkmnop", null, "baz9.tld")); createToken("Zabcdefghijkmnop", null, "xn--aualito-txac.xn--q9jyb4c"));
assertInStdout( assertInStdout(
"foo1.tld,123456789ABCDEFG\nboo2.tld,HJKLMNPQRSTUVWXY\nbaz9.tld,Zabcdefghijkmnop"); "foo1.tld,123456789ABCDEFG",
"boo2.tld,HJKLMNPQRSTUVWXY",
"xn--aualito-txac.xn--q9jyb4c,Zabcdefghijkmnop");
} }
@TestOfyAndSql @TestOfyAndSql

View file

@ -64,6 +64,15 @@ class GetDomainCommandTest extends CommandTestCase<GetDomainCommand> {
assertNotInStdout("LiveRef"); assertNotInStdout("LiveRef");
} }
@Test
void testSuccess_canonicalizeDomainName() throws Exception {
createTld("xn--q9jyb4c");
persistActiveDomain("xn--aualito-txac.xn--q9jyb4c");
runCommand("çauçalito.みんな", "--expand");
assertInStdout("fullyQualifiedDomainName=xn--aualito-txac.xn--q9jyb4c");
assertInStdout("contactId=contact1234");
}
@Test @Test
void testSuccess_multipleArguments() throws Exception { void testSuccess_multipleArguments() throws Exception {
persistActiveDomain("example.tld"); persistActiveDomain("example.tld");

View file

@ -39,8 +39,8 @@ public final class DomainNameUtils {
.equals(potentialParent.parts()); .equals(potentialParent.parts());
} }
/** Canonicalizes a domain name by lowercasing and converting unicode to punycode. */ /** Canonicalizes a hostname/domain name by lowercasing and converting unicode to punycode. */
public static String canonicalizeDomainName(String label) { public static String canonicalizeHostname(String label) {
String labelLowercased = Ascii.toLowerCase(label); String labelLowercased = Ascii.toLowerCase(label);
try { try {
return Idn.toASCII(labelLowercased); return Idn.toASCII(labelLowercased);

View file

@ -15,7 +15,7 @@
package google.registry.util; package google.registry.util;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName; import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import static google.registry.util.DomainNameUtils.getSecondLevelDomain; import static google.registry.util.DomainNameUtils.getSecondLevelDomain;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
@ -25,37 +25,38 @@ import org.junit.jupiter.api.Test;
class DomainNameUtilsTest { class DomainNameUtilsTest {
@Test @Test
void testCanonicalizeDomainName_succeeds() { void testCanonicalizeHostname_succeeds() {
assertThat(canonicalizeDomainName("foo")).isEqualTo("foo"); assertThat(canonicalizeHostname("foo")).isEqualTo("foo");
assertThat(canonicalizeDomainName("FOO")).isEqualTo("foo"); assertThat(canonicalizeHostname("FOO")).isEqualTo("foo");
assertThat(canonicalizeDomainName("foo.tld")).isEqualTo("foo.tld"); assertThat(canonicalizeHostname("foo.tld")).isEqualTo("foo.tld");
assertThat(canonicalizeDomainName("xn--q9jyb4c")).isEqualTo("xn--q9jyb4c"); assertThat(canonicalizeHostname("xn--q9jyb4c")).isEqualTo("xn--q9jyb4c");
assertThat(canonicalizeDomainName("XN--Q9JYB4C")).isEqualTo("xn--q9jyb4c"); assertThat(canonicalizeHostname("XN--Q9JYB4C")).isEqualTo("xn--q9jyb4c");
assertThat(canonicalizeDomainName("みんな")).isEqualTo("xn--q9jyb4c"); assertThat(canonicalizeHostname("みんな")).isEqualTo("xn--q9jyb4c");
assertThat(canonicalizeDomainName("みんな.みんな")).isEqualTo("xn--q9jyb4c.xn--q9jyb4c"); assertThat(canonicalizeHostname("みんな.みんな")).isEqualTo("xn--q9jyb4c.xn--q9jyb4c");
assertThat(canonicalizeDomainName("みんな.foo")).isEqualTo("xn--q9jyb4c.foo"); assertThat(canonicalizeHostname("みんな.foo")).isEqualTo("xn--q9jyb4c.foo");
assertThat(canonicalizeDomainName("foo.みんな")).isEqualTo("foo.xn--q9jyb4c"); assertThat(canonicalizeHostname("foo.みんな")).isEqualTo("foo.xn--q9jyb4c");
assertThat(canonicalizeDomainName("ħ")).isEqualTo("xn--1ea"); assertThat(canonicalizeHostname("BAR.foo.みんな")).isEqualTo("bar.foo.xn--q9jyb4c");
assertThat(canonicalizeHostname("ħ")).isEqualTo("xn--1ea");
} }
@Test @Test
void testCanonicalizeDomainName_allowsRdnsNames() { void testCanonicalizeHostname_allowsRdnsNames() {
assertThat(canonicalizeDomainName("119.63.227.45-ns1.jhz-tt.uk")) assertThat(canonicalizeHostname("119.63.227.45-ns1.jhz-tt.uk"))
.isEqualTo("119.63.227.45-ns1.jhz-tt.uk"); .isEqualTo("119.63.227.45-ns1.jhz-tt.uk");
} }
@Test @Test
void testCanonicalizeDomainName_throwsOn34HyphenRule() { void testCanonicalizeHostname_throwsOn34HyphenRule() {
IllegalArgumentException thrown = IllegalArgumentException thrown =
assertThrows( assertThrows(
IllegalArgumentException.class, IllegalArgumentException.class,
() -> canonicalizeDomainName("119.63.227.45--ns1.jhz-tt.uk")); () -> canonicalizeHostname("119.63.227.45--ns1.jhz-tt.uk"));
assertThat(thrown).hasCauseThat().hasMessageThat().contains("HYPHEN_3_4"); assertThat(thrown).hasCauseThat().hasMessageThat().contains("HYPHEN_3_4");
} }
@Test @Test
void testCanonicalizeDomainName_acePrefixUnicodeChars() { void testCanonicalizeHostname_acePrefixUnicodeChars() {
assertThrows(IllegalArgumentException.class, () -> canonicalizeDomainName("xn--みんな")); assertThrows(IllegalArgumentException.class, () -> canonicalizeHostname("xn--みんな"));
} }
@Test @Test