Derive RDAP link paths from the received query

The former method -- a config string -- was cumbersome, as each Nomulus system would have to configure the link base to its own URL.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=173567021
This commit is contained in:
mountford 2017-10-26 11:36:40 -07:00 committed by jianglai
parent 30bfcf9c55
commit 11a218f9c3
25 changed files with 88 additions and 64 deletions

View file

@ -761,17 +761,6 @@ public final class RegistryConfig {
return 100;
}
/**
* Base for RDAP link paths.
*
* @see google.registry.rdap.RdapActionBase
*/
@Provides
@Config("rdapLinkBase")
public static String provideRdapLinkBase(RegistryConfigSettings config) {
return config.rdap.baseUrl;
}
/**
* WHOIS server displayed in RDAP query responses. As per Gustavo Lozano of ICANN, this should
* be omitted, but the ICANN operational profile doesn't actually say that, so it's good to have

View file

@ -32,7 +32,6 @@ public class RegistryConfigSettings {
public RegistrarConsole registrarConsole;
public Monitoring monitoring;
public Misc misc;
public Rdap rdap;
public Braintree braintree;
public Kms kms;
public RegistryTool registryTool;
@ -148,11 +147,6 @@ public class RegistryConfigSettings {
public String sheetExportId;
}
/** Configuration for RDAP. */
public static class Rdap {
public String baseUrl;
}
/** Configuration for Braintree credit card payment processing. */
public static class Braintree {
public String merchantId;

View file

@ -215,10 +215,6 @@ misc:
# to. Leave this null to disable syncing.
sheetExportId: null
rdap:
# Base URL (with trailing slash) for RDAP links.
baseUrl: http://domain-registry.example/rdap/
# Braintree is a credit card payment processor that is used on the registrar
# console to allow registrars to pay their invoices.
braintree:

View file

@ -58,9 +58,6 @@ registrarConsole:
misc:
sheetExportId: placeholder
rdap:
baseUrl: placeholder
# You only need to specify this section if using Braintree.
braintree:
merchantId: placeholder

View file

@ -35,6 +35,7 @@ import google.registry.model.EppResource;
import google.registry.model.registrar.Registrar;
import google.registry.rdap.RdapSearchResults.IncompletenessWarningType;
import google.registry.request.Action;
import google.registry.request.FullServletPath;
import google.registry.request.HttpException;
import google.registry.request.HttpException.UnprocessableEntityException;
import google.registry.request.Parameter;
@ -79,12 +80,12 @@ public abstract class RdapActionBase implements Runnable {
@Inject Response response;
@Inject @RequestMethod Action.Method requestMethod;
@Inject @RequestPath String requestPath;
@Inject @FullServletPath String fullServletPath;
@Inject AuthResult authResult;
@Inject SessionUtils sessionUtils;
@Inject RdapJsonFormatter rdapJsonFormatter;
@Inject @Parameter("registrar") Optional<String> registrarParam;
@Inject @Parameter("includeDeleted") Optional<Boolean> includeDeletedParam;
@Inject @Config("rdapLinkBase") String rdapLinkBase;
@Inject @Config("rdapWhoisServer") @Nullable String rdapWhoisServer;
@Inject @Config("rdapResultSetMaxSize") int rdapResultSetMaxSize;
@ -103,11 +104,10 @@ public abstract class RdapActionBase implements Runnable {
* building a map, to make sure that the request would return a 500 status if it were
* invoked using GET. So this field should usually be ignored, unless there's some
* expensive task required to create the map which will never result in a request failure.
* @param linkBase the base URL for RDAP link structures
* @return A map (probably containing nested maps and lists) with the final JSON response data.
*/
abstract ImmutableMap<String, Object> getJsonObjectForResource(
String pathSearchString, boolean isHeadRequest, String linkBase);
String pathSearchString, boolean isHeadRequest);
@Override
public void run() {
@ -124,9 +124,7 @@ public abstract class RdapActionBase implements Runnable {
"%s doesn't start with %s", pathProper, getActionPath());
ImmutableMap<String, Object> rdapJson =
getJsonObjectForResource(
pathProper.substring(getActionPath().length()),
requestMethod == Action.Method.HEAD,
rdapLinkBase);
pathProper.substring(getActionPath().length()), requestMethod == Action.Method.HEAD);
response.setStatus(SC_OK);
response.setContentType(RESPONSE_MEDIA_TYPE);
if (requestMethod != Action.Method.HEAD) {

View file

@ -53,7 +53,7 @@ public class RdapAutnumAction extends RdapActionBase {
@Override
public ImmutableMap<String, Object> getJsonObjectForResource(
String pathSearchString, boolean isHeadRequest, String linkBase) {
String pathSearchString, boolean isHeadRequest) {
throw new NotImplementedException("Domain Name Registry information only");
}
}

View file

@ -58,7 +58,7 @@ public class RdapDomainAction extends RdapActionBase {
@Override
public ImmutableMap<String, Object> getJsonObjectForResource(
String pathSearchString, boolean isHeadRequest, String linkBase) {
String pathSearchString, boolean isHeadRequest) {
DateTime now = clock.nowUtc();
pathSearchString = canonicalizeName(pathSearchString);
try {
@ -79,7 +79,7 @@ public class RdapDomainAction extends RdapActionBase {
return rdapJsonFormatter.makeRdapJsonForDomain(
domainResource,
true,
rdapLinkBase,
fullServletPath,
rdapWhoisServer,
now,
OutputDataType.FULL,

View file

@ -100,7 +100,7 @@ public class RdapDomainSearchAction extends RdapActionBase {
*/
@Override
public ImmutableMap<String, Object> getJsonObjectForResource(
String pathSearchString, boolean isHeadRequest, String linkBase) {
String pathSearchString, boolean isHeadRequest) {
DateTime now = clock.nowUtc();
// RDAP syntax example: /rdap/domains?name=exam*.com.
// The pathSearchString is not used by search commands.
@ -151,7 +151,7 @@ public class RdapDomainSearchAction extends RdapActionBase {
BoilerplateType.DOMAIN,
results.getIncompletenessWarnings(),
ImmutableList.<ImmutableMap<String, Object>>of(),
rdapLinkBase);
fullServletPath);
return builder.build();
}
@ -497,7 +497,7 @@ public class RdapDomainSearchAction extends RdapActionBase {
for (DomainResource domain : domains) {
jsonList.add(
rdapJsonFormatter.makeRdapJsonForDomain(
domain, false, rdapLinkBase, rdapWhoisServer, now, outputDataType, authorization));
domain, false, fullServletPath, rdapWhoisServer, now, outputDataType, authorization));
if (jsonList.size() >= rdapResultSetMaxSize) {
break;
}

View file

@ -73,7 +73,7 @@ public class RdapEntityAction extends RdapActionBase {
@Override
public ImmutableMap<String, Object> getJsonObjectForResource(
String pathSearchString, boolean isHeadRequest, String linkBase) {
String pathSearchString, boolean isHeadRequest) {
DateTime now = clock.nowUtc();
// The query string is not used; the RDAP syntax is /rdap/entity/handle (the handle is the roid
// for contacts and the client identifier for registrars). Since RDAP's concept of an entity
@ -90,7 +90,7 @@ public class RdapEntityAction extends RdapActionBase {
contactResource,
true,
Optional.<DesignatedContact.Type>empty(),
rdapLinkBase,
fullServletPath,
rdapWhoisServer,
now,
OutputDataType.FULL,
@ -103,7 +103,7 @@ public class RdapEntityAction extends RdapActionBase {
Optional<Registrar> registrar = getRegistrarByIanaIdentifier(ianaIdentifier);
if (registrar.isPresent() && shouldBeVisible(registrar.get())) {
return rdapJsonFormatter.makeRdapJsonForRegistrar(
registrar.get(), true, rdapLinkBase, rdapWhoisServer, now, OutputDataType.FULL);
registrar.get(), true, fullServletPath, rdapWhoisServer, now, OutputDataType.FULL);
}
}
// At this point, we have failed to find either a contact or a registrar.

View file

@ -84,7 +84,7 @@ public class RdapEntitySearchAction extends RdapActionBase {
/** Parses the parameters and calls the appropriate search function. */
@Override
public ImmutableMap<String, Object> getJsonObjectForResource(
String pathSearchString, boolean isHeadRequest, String linkBase) {
String pathSearchString, boolean isHeadRequest) {
DateTime now = clock.nowUtc();
// RDAP syntax example: /rdap/entities?fn=Bobby%20Joe*.
// The pathSearchString is not used by search commands.
@ -114,7 +114,7 @@ public class RdapEntitySearchAction extends RdapActionBase {
BoilerplateType.ENTITY,
results.getIncompletenessWarnings(),
ImmutableList.<ImmutableMap<String, Object>>of(),
rdapLinkBase);
fullServletPath);
return jsonBuilder.build();
}
@ -313,7 +313,7 @@ public class RdapEntitySearchAction extends RdapActionBase {
contact,
false,
Optional.<DesignatedContact.Type>empty(),
rdapLinkBase,
fullServletPath,
rdapWhoisServer,
now,
outputDataType,
@ -325,7 +325,7 @@ public class RdapEntitySearchAction extends RdapActionBase {
ImmutableList.copyOf(jsonOutputList), IncompletenessWarningType.TRUNCATED);
}
jsonOutputList.add(rdapJsonFormatter.makeRdapJsonForRegistrar(
registrar, false, rdapLinkBase, rdapWhoisServer, now, outputDataType));
registrar, false, fullServletPath, rdapWhoisServer, now, outputDataType));
}
return RdapSearchResults.create(
ImmutableList.copyOf(jsonOutputList),

View file

@ -51,16 +51,16 @@ public class RdapHelpAction extends RdapActionBase {
@Override
public ImmutableMap<String, Object> getJsonObjectForResource(
String pathSearchString, boolean isHeadRequest, String linkBase) {
String pathSearchString, boolean isHeadRequest) {
// We rely on addTopLevelEntries to notice if we are sending the TOS notice, and not add a
// duplicate boilerplate entry.
ImmutableMap.Builder<String, Object> builder = new ImmutableMap.Builder<>();
rdapJsonFormatter.addTopLevelEntries(
builder,
BoilerplateType.OTHER,
ImmutableList.of(rdapJsonFormatter.getJsonHelpNotice(pathSearchString, rdapLinkBase)),
ImmutableList.of(rdapJsonFormatter.getJsonHelpNotice(pathSearchString, fullServletPath)),
ImmutableList.<ImmutableMap<String, Object>>of(),
rdapLinkBase);
fullServletPath);
return builder.build();
}
}

View file

@ -53,7 +53,7 @@ public class RdapIpAction extends RdapActionBase {
@Override
public ImmutableMap<String, Object> getJsonObjectForResource(
String pathSearchString, boolean isHeadRequest, String linkBase) {
String pathSearchString, boolean isHeadRequest) {
throw new NotImplementedException("Domain Name Registry information only");
}
}

View file

@ -410,8 +410,14 @@ public class RdapJsonFormatter {
if (parameters.getTypeString() != null) {
jsonBuilder.put("typeString", parameters.getTypeString());
}
String linkBaseNotNull = nullToEmpty(linkBase);
String linkValueSuffixNotNull = nullToEmpty(parameters.getLinkValueSuffix());
String linkValueString =
nullToEmpty(linkBase) + nullToEmpty(parameters.getLinkValueSuffix());
String.format(
"%s%s%s",
linkBaseNotNull,
(linkBaseNotNull.endsWith("/") || linkValueSuffixNotNull.startsWith("/")) ? "" : "/",
linkValueSuffixNotNull);
if (parameters.getLinkHrefUrlString() == null) {
jsonBuilder.put("links", ImmutableList.of(ImmutableMap.of(
"value", linkValueString,

View file

@ -58,7 +58,7 @@ public class RdapNameserverAction extends RdapActionBase {
@Override
public ImmutableMap<String, Object> getJsonObjectForResource(
String pathSearchString, boolean isHeadRequest, String linkBase) {
String pathSearchString, boolean isHeadRequest) {
DateTime now = clock.nowUtc();
pathSearchString = canonicalizeName(pathSearchString);
// The RDAP syntax is /rdap/nameserver/ns1.mydomain.com.
@ -79,6 +79,6 @@ public class RdapNameserverAction extends RdapActionBase {
throw new NotFoundException(pathSearchString + " not found");
}
return rdapJsonFormatter.makeRdapJsonForHost(
hostResource, true, rdapLinkBase, rdapWhoisServer, now, OutputDataType.FULL);
hostResource, true, fullServletPath, rdapWhoisServer, now, OutputDataType.FULL);
}
}

View file

@ -87,7 +87,7 @@ public class RdapNameserverSearchAction extends RdapActionBase {
*/
@Override
public ImmutableMap<String, Object> getJsonObjectForResource(
String pathSearchString, boolean isHeadRequest, String linkBase) {
String pathSearchString, boolean isHeadRequest) {
DateTime now = clock.nowUtc();
// RDAP syntax example: /rdap/nameservers?name=ns*.example.com.
// The pathSearchString is not used by search commands.
@ -126,7 +126,7 @@ public class RdapNameserverSearchAction extends RdapActionBase {
BoilerplateType.NAMESERVER,
results.getIncompletenessWarnings(),
ImmutableList.<ImmutableMap<String, Object>>of(),
rdapLinkBase);
fullServletPath);
return jsonBuilder.build();
}
@ -175,7 +175,7 @@ public class RdapNameserverSearchAction extends RdapActionBase {
return RdapSearchResults.create(
ImmutableList.of(
rdapJsonFormatter.makeRdapJsonForHost(
hostResource, false, rdapLinkBase, rdapWhoisServer, now, OutputDataType.FULL)));
hostResource, false, fullServletPath, rdapWhoisServer, now, OutputDataType.FULL)));
}
/** Searches for nameservers by name using the superordinate domain as a suffix. */
@ -267,7 +267,7 @@ public class RdapNameserverSearchAction extends RdapActionBase {
for (HostResource host : Iterables.limit(hosts, rdapResultSetMaxSize)) {
jsonListBuilder.add(
rdapJsonFormatter.makeRdapJsonForHost(
host, false, rdapLinkBase, rdapWhoisServer, now, outputDataType));
host, false, fullServletPath, rdapWhoisServer, now, outputDataType));
}
ImmutableList<ImmutableMap<String, Object>> jsonList = jsonListBuilder.build();
return RdapSearchResults.create(

View file

@ -0,0 +1,30 @@
// 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.request;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Qualifier;
/**
* Dagger qualifier for the HTTP servlet path, prepended with scheme, host and port.
*
* See {@link javax.servlet.http.HttpServletRequest#getServletPath}
*/
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface FullServletPath {}

View file

@ -90,6 +90,20 @@ public final class RequestModule {
return req.getRequestURI();
}
@Provides
@FullServletPath
static String provideFullServletPath(HttpServletRequest req) {
// Include the port only if it differs from the default for the scheme.
if ((req.getScheme().equals("http") && (req.getServerPort() == 80))
|| (req.getScheme().equals("https") && (req.getServerPort() == 443))) {
return String.format("%s://%s%s", req.getScheme(), req.getServerName(), req.getServletPath());
} else {
return String.format(
"%s://%s:%d%s",
req.getScheme(), req.getServerName(), req.getServerPort(), req.getServletPath());
}
}
@Provides
@RequestMethod
static Action.Method provideRequestMethod(HttpServletRequest req) {

View file

@ -71,7 +71,7 @@ public class RdapActionBaseTest {
@Override
public ImmutableMap<String, Object> getJsonObjectForResource(
String pathSearchString, boolean isHeadRequest, String linkBase) {
String pathSearchString, boolean isHeadRequest) {
if (pathSearchString.equals("IllegalArgumentException")) {
throw new IllegalArgumentException();
}
@ -103,16 +103,16 @@ public class RdapActionBaseTest {
private Object generateActualJson(String domainName) {
action.requestPath = RdapTestAction.PATH + domainName;
action.fullServletPath = "http://myserver.example.com" + RdapTestAction.PATH;
action.requestMethod = GET;
action.rdapLinkBase = "http://myserver.example.com/";
action.run();
return JSONValue.parse(response.getPayload());
}
private String generateHeadPayload(String domainName) {
action.requestPath = RdapTestAction.PATH + domainName;
action.fullServletPath = "http://myserver.example.com" + RdapTestAction.PATH;
action.requestMethod = HEAD;
action.rdapLinkBase = "http://myserver.example.com/";
action.run();
return response.getPayload();
}

View file

@ -256,11 +256,11 @@ public class RdapDomainActionTest {
action = new RdapDomainAction();
action.clock = clock;
action.request = request;
action.fullServletPath = "https://example.com/rdap";
action.response = response;
action.registrarParam = Optional.empty();
action.includeDeletedParam = Optional.empty();
action.rdapJsonFormatter = RdapTestHelper.getTestRdapJsonFormatter();
action.rdapLinkBase = "https://example.com/rdap/";
action.rdapWhoisServer = null;
action.sessionUtils = sessionUtils;
action.authResult = AuthResult.create(AuthLevel.USER, userAuthInfo);

View file

@ -362,11 +362,11 @@ public class RdapDomainSearchActionTest {
action.clock = clock;
action.request = request;
action.fullServletPath = "https://example.com/rdap";
action.response = response;
action.registrarParam = Optional.empty();
action.includeDeletedParam = Optional.empty();
action.rdapJsonFormatter = RdapTestHelper.getTestRdapJsonFormatter();
action.rdapLinkBase = "https://example.com/rdap/";
action.rdapWhoisServer = null;
action.sessionUtils = sessionUtils;
action.authResult = AuthResult.create(AuthLevel.USER, userAuthInfo);

View file

@ -157,11 +157,11 @@ public class RdapEntityActionTest {
action = new RdapEntityAction();
action.clock = clock;
action.request = request;
action.fullServletPath = "https://example.com/rdap";
action.response = response;
action.registrarParam = Optional.<String>empty();
action.includeDeletedParam = Optional.<Boolean>empty();
action.rdapJsonFormatter = RdapTestHelper.getTestRdapJsonFormatter();
action.rdapLinkBase = "https://example.com/rdap/";
action.rdapWhoisServer = null;
action.sessionUtils = sessionUtils;
action.authResult = AuthResult.create(AuthLevel.USER, userAuthInfo);

View file

@ -140,11 +140,11 @@ public class RdapEntitySearchActionTest {
action.clock = clock;
action.request = request;
action.fullServletPath = "https://example.com/rdap";
action.requestPath = RdapEntitySearchAction.PATH;
action.response = response;
action.rdapJsonFormatter = RdapTestHelper.getTestRdapJsonFormatter();
action.rdapResultSetMaxSize = 4;
action.rdapLinkBase = "https://example.com/rdap/";
action.rdapWhoisServer = null;
action.fnParam = Optional.empty();
action.handleParam = Optional.empty();

View file

@ -48,9 +48,9 @@ public class RdapHelpActionTest {
action = new RdapHelpAction();
action.clock = clock;
action.fullServletPath = "https://example.tld/rdap";
action.response = response;
action.rdapJsonFormatter = RdapTestHelper.getTestRdapJsonFormatter();
action.rdapLinkBase = "https://example.tld/rdap/";
action.rdapWhoisServer = null;
}

View file

@ -111,12 +111,12 @@ public class RdapNameserverActionTest {
RdapNameserverAction action = new RdapNameserverAction();
action.clock = clock;
action.request = request;
action.fullServletPath = "https://example.tld/rdap";
action.response = response;
action.requestPath = RdapNameserverAction.PATH.concat(input);
action.registrarParam = desiredRegistrar;
action.includeDeletedParam = includeDeleted;
action.rdapJsonFormatter = RdapTestHelper.getTestRdapJsonFormatter();
action.rdapLinkBase = "https://example.tld/rdap/";
action.rdapWhoisServer = null;
action.authResult = authResult;
action.sessionUtils = sessionUtils;

View file

@ -141,12 +141,12 @@ public class RdapNameserverSearchActionTest {
inject.setStaticField(Ofy.class, "clock", clock);
action.clock = clock;
action.fullServletPath = "https://example.tld/rdap";
action.requestPath = RdapNameserverSearchAction.PATH;
action.request = request;
action.response = response;
action.rdapJsonFormatter = RdapTestHelper.getTestRdapJsonFormatter();
action.rdapResultSetMaxSize = 4;
action.rdapLinkBase = "https://example.tld/rdap/";
action.rdapWhoisServer = null;
action.ipParam = Optional.empty();
action.nameParam = Optional.empty();