Don't validate RDAP nameserver names using validateDomainName

The nameserver may be external, in which case its TLD will not appear in our
list of valid TLDs, and the search will be rejected erroneously.

Tests for letter case canonicalizations also added at reviewer's suggestion.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=171985702
This commit is contained in:
mountford 2017-10-12 11:21:57 -07:00 committed by jianglai
parent 9d1eb0d429
commit 326cf698e0
15 changed files with 113 additions and 91 deletions

View file

@ -169,7 +169,7 @@ public class DomainFlowUtils {
*
* @see #validateDomainNameWithIdnTables(InternetDomainName)
*/
static InternetDomainName validateDomainName(String name)
public static InternetDomainName validateDomainName(String name)
throws EppException {
if (!ALLOWED_CHARS.matchesAllOf(name)) {
throw new BadDomainNameCharacterException();

View file

@ -39,7 +39,7 @@ import org.joda.time.DateTime;
public class HostFlowUtils {
/** Checks that a host name is valid. */
static InternetDomainName validateHostName(String name) throws EppException {
public static InternetDomainName validateHostName(String name) throws EppException {
checkArgumentNotNull(name, "Must specify host name to validate");
if (name.length() > 253) {
throw new HostNameTooLongException();

View file

@ -9,6 +9,7 @@ java_library(
srcs = glob(["*.java"]),
deps = [
"//java/google/registry/config",
"//java/google/registry/flows",
"//java/google/registry/model",
"//java/google/registry/request",
"//java/google/registry/request/auth",

View file

@ -19,8 +19,6 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.net.HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.registry.Registries.findTldForName;
import static google.registry.model.registry.Registries.getTlds;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
@ -28,7 +26,6 @@ import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import com.google.common.collect.ImmutableMap;
import com.google.common.net.InternetDomainName;
import com.google.common.net.MediaType;
import com.google.re2j.Pattern;
import com.googlecode.objectify.Key;
@ -39,8 +36,6 @@ import google.registry.model.registrar.Registrar;
import google.registry.rdap.RdapSearchResults.IncompletenessWarningType;
import google.registry.request.Action;
import google.registry.request.HttpException;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.HttpException.NotFoundException;
import google.registry.request.HttpException.UnprocessableEntityException;
import google.registry.request.Parameter;
import google.registry.request.RequestMethod;
@ -247,18 +242,6 @@ public abstract class RdapActionBase implements Runnable {
&& (!registrarParam.isPresent() || registrarParam.get().equals(registrar.getClientId()));
}
void validateDomainName(String name) {
try {
Optional<InternetDomainName> tld = findTldForName(InternetDomainName.from(name));
if (!tld.isPresent() || !getTlds().contains(tld.get().toString())) {
throw new NotFoundException(name + " not found");
}
} catch (IllegalArgumentException e) {
throw new BadRequestException(
name + " is not a valid " + getHumanReadableObjectTypeName());
}
}
String canonicalizeName(String name) {
name = canonicalizeDomainName(name);
if (name.endsWith(".")) {

View file

@ -14,14 +14,17 @@
package google.registry.rdap;
import static google.registry.flows.domain.DomainFlowUtils.validateDomainName;
import static google.registry.model.EppResourceUtils.loadByForeignKey;
import static google.registry.request.Action.Method.GET;
import static google.registry.request.Action.Method.HEAD;
import com.google.common.collect.ImmutableMap;
import google.registry.flows.EppException;
import google.registry.model.domain.DomainResource;
import google.registry.rdap.RdapJsonFormatter.OutputDataType;
import google.registry.request.Action;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.HttpException.NotFoundException;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
@ -57,7 +60,14 @@ public class RdapDomainAction extends RdapActionBase {
String pathSearchString, boolean isHeadRequest, String linkBase) {
DateTime now = clock.nowUtc();
pathSearchString = canonicalizeName(pathSearchString);
try {
validateDomainName(pathSearchString);
} catch (EppException e) {
throw new BadRequestException(
String.format(
"%s is not a valid %s: %s",
pathSearchString, getHumanReadableObjectTypeName(), e.getMessage()));
}
// The query string is not used; the RDAP syntax is /rdap/domain/mydomain.com.
DomainResource domainResource = loadByForeignKey(DomainResource.class, pathSearchString, now);
if (domainResource == null) {

View file

@ -14,15 +14,18 @@
package google.registry.rdap;
import static google.registry.flows.host.HostFlowUtils.validateHostName;
import static google.registry.model.EppResourceUtils.loadByForeignKey;
import static google.registry.request.Action.Method.GET;
import static google.registry.request.Action.Method.HEAD;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import com.google.common.collect.ImmutableMap;
import google.registry.flows.EppException;
import google.registry.model.host.HostResource;
import google.registry.rdap.RdapJsonFormatter.OutputDataType;
import google.registry.request.Action;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.HttpException.NotFoundException;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
@ -59,7 +62,14 @@ public class RdapNameserverAction extends RdapActionBase {
DateTime now = clock.nowUtc();
pathSearchString = canonicalizeName(pathSearchString);
// The RDAP syntax is /rdap/nameserver/ns1.mydomain.com.
validateDomainName(pathSearchString);
try {
validateHostName(pathSearchString);
} catch (EppException e) {
throw new BadRequestException(
String.format(
"%s is not a valid %s: %s",
pathSearchString, getHumanReadableObjectTypeName(), e.getMessage()));
}
// If there are no undeleted nameservers with the given name, the foreign key should point to
// the most recently deleted one.
HostResource hostResource =

View file

@ -15,7 +15,6 @@
package google.registry.rdap;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.DatastoreHelper.persistSimpleResources;
@ -322,21 +321,44 @@ public class RdapDomainActionTest {
}
}
private void assertProperResponseForCatLol(String queryString, String expectedOutputFile) {
assertJsonEqual(
generateActualJson(queryString),
generateExpectedJsonWithTopLevelEntries(
"cat.lol",
null,
"C-LOL",
expectedOutputFile.equals("rdap_domain.json")
? ImmutableList.of("4-ROID", "6-ROID", "2-ROID")
: null,
expectedOutputFile));
assertThat(response.getStatus()).isEqualTo(200);
}
@Test
public void testInvalidDomain_returns400() throws Exception {
assertJsonEqual(
generateActualJson("invalid/domain/name"),
generateExpectedJson(
"invalid/domain/name is not a valid domain name", null, "1", "rdap_error_400.json"));
"invalid/domain/name is not a valid domain name: Domain names can only contain a-z,"
+ " 0-9, '.' and '-'",
null,
"1",
"rdap_error_400.json"));
assertThat(response.getStatus()).isEqualTo(400);
}
@Test
public void testUnknownDomain_returns404() throws Exception {
public void testUnknownDomain_returns400() throws Exception {
assertJsonEqual(
generateActualJson("missingdomain.com"),
generateExpectedJson("missingdomain.com not found", null, "1", "rdap_error_404.json"));
assertThat(response.getStatus()).isEqualTo(404);
generateExpectedJson(
"missingdomain.com is not a valid domain name: Domain name is under tld com which"
+ " doesn't exist",
null,
"1",
"rdap_error_400.json"));
assertThat(response.getStatus()).isEqualTo(400);
}
@Test
@ -349,15 +371,7 @@ public class RdapDomainActionTest {
@Test
public void testValidDomain_works() throws Exception {
assertJsonEqual(
generateActualJson("cat.lol"),
generateExpectedJsonWithTopLevelEntries(
"cat.lol",
null,
"C-LOL",
ImmutableList.of("4-ROID", "6-ROID", "2-ROID"),
"rdap_domain.json"));
assertThat(response.getStatus()).isEqualTo(200);
assertProperResponseForCatLol("cat.lol", "rdap_domain.json");
}
@Test
@ -366,69 +380,34 @@ public class RdapDomainActionTest {
action.authResult = AuthResult.create(AuthLevel.USER, adminUserAuthInfo);
when(sessionUtils.checkRegistrarConsoleLogin(request, adminUserAuthInfo)).thenReturn(false);
when(sessionUtils.getRegistrarClientId(request)).thenReturn("noregistrar");
assertJsonEqual(
generateActualJson("cat.lol"),
generateExpectedJsonWithTopLevelEntries(
"cat.lol",
null,
"C-LOL",
ImmutableList.of("4-ROID", "6-ROID", "2-ROID"),
"rdap_domain.json"));
assertThat(response.getStatus()).isEqualTo(200);
assertProperResponseForCatLol("cat.lol", "rdap_domain.json");
}
@Test
public void testValidDomain_notLoggedIn_noContacts() throws Exception {
when(sessionUtils.checkRegistrarConsoleLogin(request, userAuthInfo)).thenReturn(false);
assertJsonEqual(
generateActualJson("cat.lol"),
generateExpectedJsonWithTopLevelEntries(
"cat.lol",
null,
"C-LOL",
null,
"rdap_domain_no_contacts.json"));
assertThat(response.getStatus()).isEqualTo(200);
assertProperResponseForCatLol("cat.lol", "rdap_domain_no_contacts.json");
}
@Test
public void testValidDomain_loggedInAsOtherRegistrar_noContacts() throws Exception {
when(sessionUtils.getRegistrarClientId(request)).thenReturn("otherregistrar");
assertJsonEqual(
generateActualJson("cat.lol"),
generateExpectedJsonWithTopLevelEntries(
"cat.lol",
null,
"C-LOL",
null,
"rdap_domain_no_contacts.json"));
assertThat(response.getStatus()).isEqualTo(200);
assertProperResponseForCatLol("cat.lol", "rdap_domain_no_contacts.json");
}
@Test
public void testUpperCase_ignored() throws Exception {
assertProperResponseForCatLol("CaT.lOl", "rdap_domain.json");
}
@Test
public void testTrailingDot_ignored() throws Exception {
assertJsonEqual(
generateActualJson("cat.lol."),
generateExpectedJsonWithTopLevelEntries(
"cat.lol",
null,
"C-LOL",
ImmutableList.of("4-ROID", "6-ROID", "2-ROID"),
"rdap_domain.json"));
assertThat(response.getStatus()).isEqualTo(200);
assertProperResponseForCatLol("cat.lol.", "rdap_domain.json");
}
@Test
public void testQueryParameter_ignored() throws Exception {
assertJsonEqual(
generateActualJson("cat.lol?key=value"),
generateExpectedJsonWithTopLevelEntries(
"cat.lol",
null,
"C-LOL",
ImmutableList.of("4-ROID", "6-ROID", "2-ROID"),
"rdap_domain.json"));
assertThat(response.getStatus()).isEqualTo(200);
assertProperResponseForCatLol("cat.lol?key=value", "rdap_domain.json");
}
@Test

View file

@ -16,7 +16,6 @@ package google.registry.rdap;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistDomainAsDeleted;
import static google.registry.testing.DatastoreHelper.persistResource;
@ -586,6 +585,11 @@ public class RdapDomainSearchActionTest {
runSuccessfulTestWithCatLol(RequestType.NAME, "cat.lol", "rdap_domain.json");
}
@Test
public void testDomainMatch_foundWithUpperCase() throws Exception {
runSuccessfulTestWithCatLol(RequestType.NAME, "CaT.lOl", "rdap_domain.json");
}
@Test
public void testDomainMatch_found_asAdministrator() throws Exception {
UserAuthInfo adminUserAuthInfo = UserAuthInfo.create(user, true);

View file

@ -15,7 +15,6 @@
package google.registry.rdap;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.DatastoreHelper.persistSimpleResources;

View file

@ -16,7 +16,6 @@ package google.registry.rdap;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.DatastoreHelper.persistResources;

View file

@ -15,7 +15,6 @@
package google.registry.rdap;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static google.registry.testing.TestDataHelper.loadFileWithSubstitutions;
import com.google.common.collect.ImmutableMap;

View file

@ -15,7 +15,6 @@
package google.registry.rdap;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.DatastoreHelper.persistSimpleResources;

View file

@ -15,7 +15,6 @@
package google.registry.rdap;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.FullFieldsTestEntityHelper.makeAndPersistHostResource;
@ -94,6 +93,8 @@ public class RdapNameserverActionTest {
// other registrar
persistResource(
makeRegistrar("otherregistrar", "Yes Virginia <script>", Registrar.State.ACTIVE, 102L));
// external
makeAndPersistHostResource("ns1.domain.external", "9.10.11.12", clock.nowUtc().minusYears(1));
}
private RdapNameserverAction newRdapNameserverAction(
@ -180,9 +181,12 @@ public class RdapNameserverActionTest {
@Test
public void testInvalidNameserver_returns400() throws Exception {
assertThat(generateActualJson("invalid/host/name")).isEqualTo(
assertThat(generateActualJson("invalid/host/name"))
.isEqualTo(
generateExpectedJson(
"invalid/host/name is not a valid nameserver", null, "rdap_error_400.json"));
"invalid/host/name is not a valid nameserver: Invalid host name",
null,
"rdap_error_400.json"));
assertThat(response.getStatus()).isEqualTo(400);
}
@ -221,6 +225,20 @@ public class RdapNameserverActionTest {
assertThat(response.getStatus()).isEqualTo(200);
}
@Test
public void testUpperCase_getsCanonicalized() throws Exception {
assertThat(generateActualJson("Ns1.CaT.lOl."))
.isEqualTo(generateExpectedJsonWithTopLevelEntries(
"ns1.cat.lol",
ImmutableMap.of(
"HANDLE", "2-ROID",
"ADDRESSTYPE", "v4",
"ADDRESS", "1.2.3.4",
"STATUS", "active"),
"rdap_host.json"));
assertThat(response.getStatus()).isEqualTo(200);
}
@Test
public void testQueryParameter_getsIgnored() throws Exception {
assertThat(generateActualJson("ns1.cat.lol?key=value"))
@ -279,6 +297,20 @@ public class RdapNameserverActionTest {
assertThat(response.getStatus()).isEqualTo(200);
}
@Test
public void testExternalNameserver_works() throws Exception {
assertThat(generateActualJson("ns1.domain.external"))
.isEqualTo(generateExpectedJsonWithTopLevelEntries(
"ns1.domain.external",
ImmutableMap.of(
"HANDLE", "C-ROID",
"ADDRESSTYPE", "v4",
"ADDRESS", "9.10.11.12",
"STATUS", "active"),
"rdap_host.json"));
assertThat(response.getStatus()).isEqualTo(200);
}
@Test
public void testNameserver_found_sameRegistrarRequested() throws Exception {
assertThat(

View file

@ -15,7 +15,6 @@
package google.registry.rdap;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.DatastoreHelper.persistResources;
@ -309,6 +308,15 @@ public class RdapNameserverSearchActionTest {
assertThat(response.getStatus()).isEqualTo(200);
}
@Test
public void testNameMatch_ns1_cat_lol_foundWithUpperCase() throws Exception {
assertThat(generateActualJsonWithName("Ns1.CaT.lOl"))
.isEqualTo(
generateExpectedJsonForNameserver(
"ns1.cat.lol", null, "2-ROID", "v4", "1.2.3.4", "rdap_host_linked.json"));
assertThat(response.getStatus()).isEqualTo(200);
}
@Test
public void testNameMatch_ns1_cat_lol_found_sameRegistrarRequested() throws Exception {
action.registrarParam = Optional.of("TheRegistrar");

View file

@ -15,7 +15,6 @@
package google.registry.rdap;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import google.registry.request.HttpException.UnprocessableEntityException;
import google.registry.testing.ExceptionRule;