diff --git a/java/google/registry/rdap/RdapEntitySearchAction.java b/java/google/registry/rdap/RdapEntitySearchAction.java index 16e70e334..079996f80 100644 --- a/java/google/registry/rdap/RdapEntitySearchAction.java +++ b/java/google/registry/rdap/RdapEntitySearchAction.java @@ -130,17 +130,20 @@ public class RdapEntitySearchAction extends RdapActionBase { *

According to RFC 7482 section 6.1, punycode is only used for domain name labels, so we can * assume that entity names are regular unicode. * - *

Searches for deleted entities are treated like wildcard searches, because they can return - * multiple entities. + *

The includeDeleted flag is ignored when searching for contacts, because contact names are + * set to null when the contact is deleted, so a deleted contact can never have a name. + * + *

Since we are restricting access to contact names, we don't want name searches to return + * contacts whose names are not visible. That would allow unscrupulous users to query by name + * and infer that all returned contacts contain that name string. So we check the authorization + * level to determine what to do. * * @see 1.6 * of Section 4 of the Base Registry Agreement */ private RdapSearchResults searchByName(final RdapSearchPattern partialStringQuery, DateTime now) { - // For wildcard searches, and searches that include deleted items, make sure the initial string - // is long enough, and don't allow suffixes. - if ((partialStringQuery.getHasWildcard() || shouldIncludeDeleted()) - && (partialStringQuery.getSuffix() != null)) { + // For wildcard searches, make sure the initial string is long enough, and don't allow suffixes. + if (partialStringQuery.getHasWildcard() && (partialStringQuery.getSuffix() != null)) { throw new UnprocessableEntityException( partialStringQuery.getHasWildcard() ? "Suffixes not allowed in wildcard entity name searches" @@ -164,19 +167,28 @@ public class RdapEntitySearchAction extends RdapActionBase { .limit(rdapResultSetMaxSize + 1) .collect(toImmutableList()); // Get the contact matches and return the results, fetching an additional contact to detect - // truncation. If we are including deleted entries, we must fetch more entries, in case some - // get excluded due to permissioning. - Query query = - queryItems( - ContactResource.class, - "searchName", - partialStringQuery, - shouldIncludeDeleted(), - shouldIncludeDeleted() - ? (RESULT_SET_SIZE_SCALING_FACTOR * (rdapResultSetMaxSize + 1)) - : (rdapResultSetMaxSize + 1)); - return makeSearchResults( - getMatchingResources(query, shouldIncludeDeleted(), now), registrars, now); + // truncation. Don't bother searching for contacts by name if the request would not be able to + // see any names anyway. + RdapResourcesAndIncompletenessWarningType + resourcesAndIncompletenessWarningType; + RdapAuthorization authorization = getAuthorization(); + if (authorization.role() == RdapAuthorization.Role.PUBLIC) { + resourcesAndIncompletenessWarningType = + RdapResourcesAndIncompletenessWarningType.create(ImmutableList.of()); + } else { + Query query = + queryItems( + ContactResource.class, + "searchName", + partialStringQuery, + false, + rdapResultSetMaxSize + 1); + if (authorization.role() != RdapAuthorization.Role.ADMINISTRATOR) { + query = query.filter("currentSponsorClientId in", authorization.clientIds()); + } + resourcesAndIncompletenessWarningType = getMatchingResources(query, false, now); + } + return makeSearchResults(resourcesAndIncompletenessWarningType, registrars, now); } /** @@ -185,7 +197,7 @@ public class RdapEntitySearchAction extends RdapActionBase { *

Searches for deleted entities are treated like wildcard searches. * *

We don't allow suffixes after a wildcard in entity searches. Suffixes are used in domain - * searches to specify a TLD, and in nameserver searches to specify an in-bailiwick domain name. + * searches to specify a TLD, and in nameserver searches to specify a locally managed domain name. * In both cases, the suffix can be turned into an additional query filter field. For contacts, * there is no equivalent string suffix that can be used as a query filter, so we disallow use. */ diff --git a/javatests/google/registry/rdap/RdapEntityActionTest.java b/javatests/google/registry/rdap/RdapEntityActionTest.java index 8aca601bf..31a4d9f12 100644 --- a/javatests/google/registry/rdap/RdapEntityActionTest.java +++ b/javatests/google/registry/rdap/RdapEntityActionTest.java @@ -19,6 +19,7 @@ import static google.registry.testing.DatastoreHelper.createTld; import static google.registry.testing.DatastoreHelper.persistResource; import static google.registry.testing.DatastoreHelper.persistSimpleResources; import static google.registry.testing.FullFieldsTestEntityHelper.makeAndPersistContactResource; +import static google.registry.testing.FullFieldsTestEntityHelper.makeAndPersistDeletedContactResource; import static google.registry.testing.FullFieldsTestEntityHelper.makeDomainResource; import static google.registry.testing.FullFieldsTestEntityHelper.makeHostResource; import static google.registry.testing.FullFieldsTestEntityHelper.makeRegistrar; @@ -148,11 +149,8 @@ public class RdapEntityActionTest { clock.nowUtc(), registrarLol); deletedContact = - makeAndPersistContactResource( + makeAndPersistDeletedContactResource( "8372808-DEL", - "(◕‿◕)", - "lol@cat.みんな", - ImmutableList.of("2 Smiley Row"), clock.nowUtc().minusYears(1), registrarLol, clock.nowUtc().minusMonths(6)); @@ -400,9 +398,9 @@ public class RdapEntityActionTest { action.includeDeletedParam = Optional.of(true); runSuccessfulTest( deletedContact.getRepoId(), - "(◕‿◕)", - "active", - "\"2 Smiley Row\"", + "", + "removed", + "", false, "rdap_contact_deleted.json"); } @@ -413,9 +411,9 @@ public class RdapEntityActionTest { action.includeDeletedParam = Optional.of(true); runSuccessfulTest( deletedContact.getRepoId(), - "(◕‿◕)", - "active", - "\"2 Smiley Row\"", + "", + "removed", + "", false, "rdap_contact_deleted.json"); } diff --git a/javatests/google/registry/rdap/RdapEntitySearchActionTest.java b/javatests/google/registry/rdap/RdapEntitySearchActionTest.java index 686517fb7..fc208cd03 100644 --- a/javatests/google/registry/rdap/RdapEntitySearchActionTest.java +++ b/javatests/google/registry/rdap/RdapEntitySearchActionTest.java @@ -21,6 +21,7 @@ import static google.registry.testing.DatastoreHelper.persistResource; import static google.registry.testing.DatastoreHelper.persistResources; import static google.registry.testing.DatastoreHelper.persistSimpleResources; import static google.registry.testing.FullFieldsTestEntityHelper.makeAndPersistContactResource; +import static google.registry.testing.FullFieldsTestEntityHelper.makeAndPersistDeletedContactResource; import static google.registry.testing.FullFieldsTestEntityHelper.makeContactResource; import static google.registry.testing.FullFieldsTestEntityHelper.makeRegistrar; import static google.registry.testing.FullFieldsTestEntityHelper.makeRegistrarContacts; @@ -48,6 +49,7 @@ import java.util.Optional; import javax.annotation.Nullable; import javax.servlet.http.HttpServletRequest; import org.joda.time.DateTime; +import org.json.simple.JSONObject; import org.json.simple.JSONValue; import org.junit.Before; import org.junit.Rule; @@ -130,11 +132,8 @@ public class RdapEntitySearchActionTest { clock.nowUtc(), registrarTest); - makeAndPersistContactResource( + makeAndPersistDeletedContactResource( "clyde", - "Clyde (愚図た)", - "clyde@c.tld", - ImmutableList.of("123 Example Blvd