// Copyright 2017 The Nomulus Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package google.registry.rdap; import static com.google.common.collect.ImmutableList.toImmutableList; import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.rdap.RdapUtils.getRegistrarByIanaIdentifier; import static google.registry.request.Action.Method.GET; import static google.registry.request.Action.Method.HEAD; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Streams; import com.google.common.primitives.Booleans; import com.google.common.primitives.Longs; import com.googlecode.objectify.cmd.Query; import google.registry.model.contact.ContactResource; import google.registry.model.registrar.Registrar; import google.registry.rdap.RdapJsonFormatter.BoilerplateType; import google.registry.rdap.RdapJsonFormatter.OutputDataType; import google.registry.rdap.RdapMetrics.EndpointType; import google.registry.rdap.RdapMetrics.SearchType; import google.registry.rdap.RdapSearchResults.IncompletenessWarningType; import google.registry.request.Action; 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.auth.Auth; import google.registry.util.Clock; import java.util.ArrayList; import java.util.List; import java.util.Optional; import javax.inject.Inject; import org.joda.time.DateTime; /** * RDAP (new WHOIS) action for entity (contact and registrar) search requests. * *
All commands and responses conform to the RDAP spec as defined in RFCs 7480 through 7485.
*
* @see RFC 7482: Registration Data Access Protocol
* (RDAP) Query Format
* @see RFC 7483: JSON Responses for the Registration
* Data Access Protocol (RDAP)
*/
@Action(
path = RdapEntitySearchAction.PATH,
method = {GET, HEAD},
auth = Auth.AUTH_PUBLIC
)
public class RdapEntitySearchAction extends RdapActionBase {
public static final String PATH = "/rdap/entities";
@Inject Clock clock;
@Inject @Parameter("fn") Optional As per Gustavo Lozano of ICANN, registrar name search should be by registrar name only, not
* by registrar contact name:
*
* The search is by registrar name only. The profile is supporting the functionality defined
* in the Base Registry Agreement.
*
* 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.
*
* 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, 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"
: "Suffixes not allowed when searching for deleted entities");
}
if (partialStringQuery.getHasWildcard()
&& (partialStringQuery.getInitialString().length()
< RdapSearchPattern.MIN_INITIAL_STRING_LENGTH)) {
throw new UnprocessableEntityException(
partialStringQuery.getHasWildcard()
? "Initial search string required in wildcard entity name searches"
: "Initial search string required when searching for deleted entities");
}
// Get the registrar matches.
ImmutableList 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 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.
*/
private RdapSearchResults searchByHandle(
final RdapSearchPattern partialStringQuery, DateTime now) {
if (partialStringQuery.getSuffix() != null) {
throw new UnprocessableEntityException("Suffixes not allowed in entity handle searches");
}
// Handle queries without a wildcard (and not including deleted) -- load by ID.
if (!partialStringQuery.getHasWildcard() && !shouldIncludeDeleted()) {
ContactResource contactResource = ofy().load()
.type(ContactResource.class)
.id(partialStringQuery.getInitialString())
.now();
ImmutableList This is a convenience wrapper for the four-argument makeSearchResults; it unpacks the
* properties of the {@link RdapResultSet} structure and passes them as separate arguments.
*/
private RdapSearchResults makeSearchResults(
RdapResultSet The number of contacts retrieved is recorded for use by the metrics.
*
* @param contacts the list of contacts which can be returned
* @param incompletenessWarningType MIGHT_BE_INCOMPLETE if the list of contacts might be
* incomplete; this only matters if the total count of contacts and registrars combined is
* less than a full result set's worth
* @param numContactsRetrieved the number of contacts retrieved in the process of generating the
* results
* @param registrars the list of registrars which can be returned
* @param now the current date and time
* @return an {@link RdapSearchResults} object
*/
private RdapSearchResults makeSearchResults(
List