mirror of
https://github.com/google/nomulus.git
synced 2025-04-30 20:17:51 +02:00
Tighten up restrictions on domain/host naming on EPP resources
All domain/host names should be stored in their canonical forms (puny- coded and lower-cased). This validation is already in the flows, but this adds protection against bad data from other sources, e.g. admin consoles or RDE imports. This also removes an old work-around that temporarily suspended this validation for superusers, because we used to have non-canonicalized data in the system. The non-canonicalized data has since all been cleaned up, so this work-around is no longer necessary. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=146799558
This commit is contained in:
parent
ec55aa5361
commit
d2bc569b4b
11 changed files with 49 additions and 31 deletions
|
@ -123,11 +123,7 @@ public final class HostUpdateFlow implements TransactionalFlow {
|
||||||
Change change = command.getInnerChange();
|
Change change = command.getInnerChange();
|
||||||
String suppliedNewHostName = change.getFullyQualifiedHostName();
|
String suppliedNewHostName = change.getFullyQualifiedHostName();
|
||||||
DateTime now = ofy().getTransactionTime();
|
DateTime now = ofy().getTransactionTime();
|
||||||
// Validation is disabled for superusers to allow renaming of existing invalid hostnames.
|
validateHostName(targetId);
|
||||||
// TODO(b/32328995): Remove superuser override once all bad data in prod has been fixed.
|
|
||||||
if (!isSuperuser) {
|
|
||||||
validateHostName(targetId);
|
|
||||||
}
|
|
||||||
HostResource existingHost = loadAndVerifyExistence(HostResource.class, targetId, now);
|
HostResource existingHost = loadAndVerifyExistence(HostResource.class, targetId, now);
|
||||||
boolean isHostRename = suppliedNewHostName != null;
|
boolean isHostRename = suppliedNewHostName != null;
|
||||||
String oldHostName = targetId;
|
String oldHostName = targetId;
|
||||||
|
|
|
@ -26,6 +26,7 @@ import static google.registry.util.CollectionUtils.nullToEmpty;
|
||||||
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
|
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
|
||||||
import static google.registry.util.CollectionUtils.nullToEmptyImmutableSortedCopy;
|
import static google.registry.util.CollectionUtils.nullToEmptyImmutableSortedCopy;
|
||||||
import static google.registry.util.CollectionUtils.union;
|
import static google.registry.util.CollectionUtils.union;
|
||||||
|
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||||
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;
|
||||||
|
|
||||||
|
@ -209,6 +210,9 @@ public abstract class DomainBase extends EppResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
public B setFullyQualifiedDomainName(String fullyQualifiedDomainName) {
|
public B setFullyQualifiedDomainName(String fullyQualifiedDomainName) {
|
||||||
|
checkArgument(
|
||||||
|
fullyQualifiedDomainName.equals(canonicalizeDomainName(fullyQualifiedDomainName)),
|
||||||
|
"Domain name must be in puny-coded, lower-case form");
|
||||||
getInstance().fullyQualifiedDomainName = fullyQualifiedDomainName;
|
getInstance().fullyQualifiedDomainName = fullyQualifiedDomainName;
|
||||||
return thisCastToDerived();
|
return thisCastToDerived();
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,12 +14,14 @@
|
||||||
|
|
||||||
package google.registry.model.host;
|
package google.registry.model.host;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import static com.google.common.collect.Sets.difference;
|
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.model.ofy.ObjectifyService.ofy;
|
import static google.registry.model.ofy.ObjectifyService.ofy;
|
||||||
import static google.registry.model.ofy.Ofy.RECOMMENDED_MEMCACHE_EXPIRATION;
|
import static google.registry.model.ofy.Ofy.RECOMMENDED_MEMCACHE_EXPIRATION;
|
||||||
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 com.google.common.base.Optional;
|
import com.google.common.base.Optional;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
@ -164,6 +166,9 @@ public class HostResource extends EppResource implements ForeignKeyedEppResource
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setFullyQualifiedHostName(String fullyQualifiedHostName) {
|
public Builder setFullyQualifiedHostName(String fullyQualifiedHostName) {
|
||||||
|
checkArgument(
|
||||||
|
fullyQualifiedHostName.equals(canonicalizeDomainName(fullyQualifiedHostName)),
|
||||||
|
"Host name must be in puny-coded, lower-case form");
|
||||||
getInstance().fullyQualifiedHostName = fullyQualifiedHostName;
|
getInstance().fullyQualifiedHostName = fullyQualifiedHostName;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -360,19 +360,6 @@ public class HostUpdateFlowTest extends ResourceFlowTestCase<HostUpdateFlow, Hos
|
||||||
.hasStatusValue(StatusValue.SERVER_UPDATE_PROHIBITED);
|
.hasStatusValue(StatusValue.SERVER_UPDATE_PROHIBITED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSuccess_superuserCanSpecifyInvalidExistingHostName() throws Exception {
|
|
||||||
persistActiveHost("NS1.çiça199.tld.");
|
|
||||||
clock.advanceOneMilli();
|
|
||||||
setEppHostUpdateInput("NS1.çiça199.tld.", "ns1.xn--ia199-xrab.tld", null, null);
|
|
||||||
runFlowAssertResponse(
|
|
||||||
CommitMode.LIVE, UserPrivileges.SUPERUSER, readFile("host_update_response.xml"));
|
|
||||||
clock.advanceOneMilli();
|
|
||||||
assertThat(loadByForeignKey(HostResource.class, "NS1.çiça199.tld.", clock.nowUtc())).isNull();
|
|
||||||
assertThat(loadByForeignKey(HostResource.class, "ns1.xn--ia199-xrab.tld", clock.nowUtc()))
|
|
||||||
.isNotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuccess_subordToSubord_lastTransferTimeFromPreviousSuperordinateWinsOut()
|
public void testSuccess_subordToSubord_lastTransferTimeFromPreviousSuperordinateWinsOut()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
@ -720,7 +707,6 @@ public class HostUpdateFlowTest extends ResourceFlowTestCase<HostUpdateFlow, Hos
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_referToNonLowerCaseHostname() throws Exception {
|
public void testFailure_referToNonLowerCaseHostname() throws Exception {
|
||||||
persistActiveHost("ns1.EXAMPLE.tld");
|
|
||||||
setEppHostUpdateInput("ns1.EXAMPLE.tld", "ns2.example.tld", null, null);
|
setEppHostUpdateInput("ns1.EXAMPLE.tld", "ns2.example.tld", null, null);
|
||||||
thrown.expect(HostNameNotLowerCaseException.class);
|
thrown.expect(HostNameNotLowerCaseException.class);
|
||||||
runFlow();
|
runFlow();
|
||||||
|
@ -736,7 +722,6 @@ public class HostUpdateFlowTest extends ResourceFlowTestCase<HostUpdateFlow, Hos
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure_referToNonPunyCodedHostname() throws Exception {
|
public void testFailure_referToNonPunyCodedHostname() throws Exception {
|
||||||
persistActiveHost("ns1.çauçalito.tld");
|
|
||||||
setEppHostUpdateInput("ns1.çauçalito.tld", "ns1.sausalito.tld", null, null);
|
setEppHostUpdateInput("ns1.çauçalito.tld", "ns1.sausalito.tld", null, null);
|
||||||
thrown.expect(HostNameNotPunyCodedException.class, "expected ns1.xn--aualito-txac.tld");
|
thrown.expect(HostNameNotPunyCodedException.class, "expected ns1.xn--aualito-txac.tld");
|
||||||
runFlow();
|
runFlow();
|
||||||
|
|
|
@ -422,4 +422,18 @@ public class DomainResourceTest extends EntityTestCase {
|
||||||
public void testToHydratedString_notCircular() {
|
public void testToHydratedString_notCircular() {
|
||||||
domain.toHydratedString(); // If there are circular references, this will overflow the stack.
|
domain.toHydratedString(); // If there are circular references, this will overflow the stack.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFailure_uppercaseDomainName() {
|
||||||
|
thrown.expect(
|
||||||
|
IllegalArgumentException.class, "Domain name must be in puny-coded, lower-case form");
|
||||||
|
domain.asBuilder().setFullyQualifiedDomainName("AAA.BBB");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFailure_utf8DomainName() {
|
||||||
|
thrown.expect(
|
||||||
|
IllegalArgumentException.class, "Domain name must be in puny-coded, lower-case form");
|
||||||
|
domain.asBuilder().setFullyQualifiedDomainName("みんな.みんな");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -264,4 +264,18 @@ public class HostResourceTest extends EntityTestCase {
|
||||||
// If there are circular references, this will overflow the stack.
|
// If there are circular references, this will overflow the stack.
|
||||||
hostResource.toHydratedString();
|
hostResource.toHydratedString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFailure_uppercaseHostName() {
|
||||||
|
thrown.expect(
|
||||||
|
IllegalArgumentException.class, "Host name must be in puny-coded, lower-case form");
|
||||||
|
hostResource.asBuilder().setFullyQualifiedHostName("AAA.BBB.CCC");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFailure_utf8HostName() {
|
||||||
|
thrown.expect(
|
||||||
|
IllegalArgumentException.class, "Host name must be in puny-coded, lower-case form");
|
||||||
|
hostResource.asBuilder().setFullyQualifiedHostName("みんな.みんな.みんな");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,12 +71,12 @@ public class DomainWhoisResponseTest {
|
||||||
createTld("tld");
|
createTld("tld");
|
||||||
|
|
||||||
hostResource1 = persistResource(new HostResource.Builder()
|
hostResource1 = persistResource(new HostResource.Builder()
|
||||||
.setFullyQualifiedHostName("NS01.EXAMPLEREGISTRAR.tld")
|
.setFullyQualifiedHostName("ns01.exampleregistrar.tld")
|
||||||
.setRepoId("1-TLD")
|
.setRepoId("1-TLD")
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
hostResource2 = persistResource(new HostResource.Builder()
|
hostResource2 = persistResource(new HostResource.Builder()
|
||||||
.setFullyQualifiedHostName("NS02.EXAMPLEREGISTRAR.tld")
|
.setFullyQualifiedHostName("ns02.exampleregistrar.tld")
|
||||||
.setRepoId("2-TLD")
|
.setRepoId("2-TLD")
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ public class DomainWhoisResponseTest {
|
||||||
Key<ContactResource> techResourceKey = Key.create(techContact);
|
Key<ContactResource> techResourceKey = Key.create(techContact);
|
||||||
|
|
||||||
domainResource = persistResource(new DomainResource.Builder()
|
domainResource = persistResource(new DomainResource.Builder()
|
||||||
.setFullyQualifiedDomainName("EXAMPLE.tld")
|
.setFullyQualifiedDomainName("example.tld")
|
||||||
.setRepoId("3-TLD")
|
.setRepoId("3-TLD")
|
||||||
.setLastEppUpdateTime(DateTime.parse("2009-05-29T20:13:00Z"))
|
.setLastEppUpdateTime(DateTime.parse("2009-05-29T20:13:00Z"))
|
||||||
.setCreationTimeForTest(DateTime.parse("2000-10-08T00:45:00Z"))
|
.setCreationTimeForTest(DateTime.parse("2000-10-08T00:45:00Z"))
|
||||||
|
|
|
@ -59,7 +59,7 @@ public class NameserverWhoisResponseTest {
|
||||||
createTld("tld");
|
createTld("tld");
|
||||||
|
|
||||||
hostResource1 = new HostResource.Builder()
|
hostResource1 = new HostResource.Builder()
|
||||||
.setFullyQualifiedHostName("NS1.EXAMPLE.tld")
|
.setFullyQualifiedHostName("ns1.example.tld")
|
||||||
.setCurrentSponsorClientId("example")
|
.setCurrentSponsorClientId("example")
|
||||||
.setInetAddresses(ImmutableSet.of(
|
.setInetAddresses(ImmutableSet.of(
|
||||||
InetAddresses.forString("192.0.2.123"),
|
InetAddresses.forString("192.0.2.123"),
|
||||||
|
@ -68,7 +68,7 @@ public class NameserverWhoisResponseTest {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
hostResource2 = new HostResource.Builder()
|
hostResource2 = new HostResource.Builder()
|
||||||
.setFullyQualifiedHostName("NS2.EXAMPLE.tld")
|
.setFullyQualifiedHostName("ns2.example.tld")
|
||||||
.setCurrentSponsorClientId("example")
|
.setCurrentSponsorClientId("example")
|
||||||
.setInetAddresses(ImmutableSet.of(
|
.setInetAddresses(ImmutableSet.of(
|
||||||
InetAddresses.forString("192.0.2.123"),
|
InetAddresses.forString("192.0.2.123"),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
Domain Name: EXAMPLE.tld
|
Domain Name: example.tld
|
||||||
Domain ID: 3-TLD
|
Domain ID: 3-TLD
|
||||||
WHOIS Server: whois.nic.fakewhois.example
|
WHOIS Server: whois.nic.fakewhois.example
|
||||||
Referral URL: http://www.referral.example/path
|
Referral URL: http://www.referral.example/path
|
||||||
|
@ -52,8 +52,8 @@ Tech Phone Ext: 1234
|
||||||
Tech Fax: +1.5555551213
|
Tech Fax: +1.5555551213
|
||||||
Tech Fax Ext: 93
|
Tech Fax Ext: 93
|
||||||
Tech Email: EMAIL@EXAMPLE.tld
|
Tech Email: EMAIL@EXAMPLE.tld
|
||||||
Name Server: NS01.EXAMPLEREGISTRAR.tld
|
Name Server: ns01.exampleregistrar.tld
|
||||||
Name Server: NS02.EXAMPLEREGISTRAR.tld
|
Name Server: ns02.exampleregistrar.tld
|
||||||
DNSSEC: signedDelegation
|
DNSSEC: signedDelegation
|
||||||
>>> Last update of WHOIS database: 2009-05-29T20:15:00Z <<<
|
>>> Last update of WHOIS database: 2009-05-29T20:15:00Z <<<
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
Server Name: NS1.EXAMPLE.tld
|
Server Name: ns1.example.tld
|
||||||
IP Address: 192.0.2.123
|
IP Address: 192.0.2.123
|
||||||
IP Address: 2001:db8::1
|
IP Address: 2001:db8::1
|
||||||
Registrar: Example Registrar, Inc.
|
Registrar: Example Registrar, Inc.
|
||||||
WHOIS Server: whois.nic.fakewhois.example
|
WHOIS Server: whois.nic.fakewhois.example
|
||||||
Referral URL: http://www.referral.example/path
|
Referral URL: http://www.referral.example/path
|
||||||
|
|
||||||
Server Name: NS2.EXAMPLE.tld
|
Server Name: ns2.example.tld
|
||||||
IP Address: 192.0.2.123
|
IP Address: 192.0.2.123
|
||||||
IP Address: 2001:db8::1
|
IP Address: 2001:db8::1
|
||||||
Registrar: Example Registrar, Inc.
|
Registrar: Example Registrar, Inc.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
Server Name: NS1.EXAMPLE.tld
|
Server Name: ns1.example.tld
|
||||||
IP Address: 192.0.2.123
|
IP Address: 192.0.2.123
|
||||||
IP Address: 2001:db8::1
|
IP Address: 2001:db8::1
|
||||||
Registrar: Example Registrar, Inc.
|
Registrar: Example Registrar, Inc.
|
||||||
|
|
Loading…
Add table
Reference in a new issue