From ac822053cc0db6650ef7af402a7685839cb37b24 Mon Sep 17 00:00:00 2001 From: mountford Date: Thu, 19 Oct 2017 11:51:03 -0700 Subject: [PATCH] Change behavior when searching contacts by name We no longer find contacts by name if the request is not authorized to see the name. Several changes cascade from this. Previously, the code assumed that deleted contacts might still have full names, and therefore be searchable. This is not possible in all cases, because Datastore doesn't have the right index to find deleted contacts by name with a matching registrar. However, luckily, this situation can never occur, because contacts always have their name fields nulled out when they are deleted. So instead, we simply ignore deleted records when searching by name, knowing that none can ever match. The tests were then changed so that deleted records look the way the really will, meaning devoid of personal information. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=172776926 --- .../registry/rdap/RdapEntitySearchAction.java | 52 ++++--- .../registry/rdap/RdapEntityActionTest.java | 18 ++- .../rdap/RdapEntitySearchActionTest.java | 128 +++++++++++++++--- .../rdap/testdata/rdap_contact_deleted.json | 18 +-- .../testing/FullFieldsTestEntityHelper.java | 33 +++++ 5 files changed, 182 insertions(+), 67 deletions(-) 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