diff --git a/core/build.gradle b/core/build.gradle index c40058ec8db..4d404694ceb 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -68,6 +68,7 @@ sourceSets { srcDirs += generatedDir } resources { + srcDirs = ['src/main/resources'] exclude '**/*.xjb' } } diff --git a/core/src/main/java/google/registry/rdap/RdapDomainAction.java b/core/src/main/java/google/registry/rdap/RdapDomainAction.java index 6ab9e5a37b9..93cfc4dba5a 100644 --- a/core/src/main/java/google/registry/rdap/RdapDomainAction.java +++ b/core/src/main/java/google/registry/rdap/RdapDomainAction.java @@ -19,6 +19,7 @@ import static google.registry.request.Action.Method.HEAD; import static google.registry.util.DateTimeUtils.START_OF_TIME; +import com.google.common.flogger.FluentLogger; import com.google.common.net.InternetDomainName; import google.registry.flows.EppException; import google.registry.flows.domain.DomainFlowUtils; @@ -45,12 +46,16 @@ auth = Auth.AUTH_PUBLIC) public class RdapDomainAction extends RdapActionBase { + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + @Inject public RdapDomainAction() { super("domain name", EndpointType.DOMAIN); } @Override public RdapDomain getJsonObjectForResource(String pathSearchString, boolean isHeadRequest) { + logger.atInfo().log("RDAP domain lookup for: %s", pathSearchString); + // RDAP Technical Implementation Guide 2.1.1 - we must support A-label (Punycode) and U-label // (Unicode) formats. canonicalizeName will transform Unicode to Punycode so we support both. pathSearchString = canonicalizeName(pathSearchString); @@ -72,7 +77,13 @@ public RdapDomain getJsonObjectForResource(String pathSearchString, boolean isHe Domain.class, pathSearchString, shouldIncludeDeleted() ? START_OF_TIME : rdapJsonFormatter.getRequestTime()); + + logger.atInfo().log("Domain lookup result: %s", domain.isPresent() ? "FOUND" : "NOT FOUND"); + if (domain.isEmpty() || !isAuthorized(domain.get())) { + if (domain.isPresent()) { + logger.atInfo().log("Authorization result for domain: %s", isAuthorized(domain.get())); + } handlePossibleBsaBlock(domainName); // RFC7480 5.3 - if the server wishes to respond that it doesn't have data satisfying the // query, it MUST reply with 404 response code. diff --git a/core/src/main/java/google/registry/tools/RdapQueryCommand.java b/core/src/main/java/google/registry/tools/RdapQueryCommand.java new file mode 100644 index 00000000000..b0963cfb4a8 --- /dev/null +++ b/core/src/main/java/google/registry/tools/RdapQueryCommand.java @@ -0,0 +1,88 @@ +package google.registry.tools; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; +import com.google.common.base.Ascii; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.flogger.GoogleLogger; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import google.registry.config.RegistryConfig.Config; +import google.registry.request.Action.GkeService; +import java.io.IOException; +import java.util.List; +import javax.inject.Inject; + +/** Command to manually perform an authenticated RDAP query. */ +@Parameters(separators = " =", commandDescription = "Manually perform an authenticated RDAP query") +public final class RdapQueryCommand implements CommandWithConnection { + + private static final GoogleLogger logger = GoogleLogger.forEnclosingClass(); + + private static final ImmutableSet VALID_TYPES = + ImmutableSet.of("domain", "registrar", "contact", "nameserver"); + + @Parameter(description = "The object type and query term.", required = true) + private List mainParameters; + + private ServiceConnection defaultConnection; + + @Inject + @Config("useCanary") + boolean useCanary; + + @Override + public void setConnection(ServiceConnection connection) { + this.defaultConnection = connection; + } + + @Override + public void run() { + checkArgument( + mainParameters != null && mainParameters.size() == 2, + "Usage: nomulus rdap_query \n" + + " must be one of " + + VALID_TYPES); + + String type = Ascii.toLowerCase(mainParameters.get(0)); + checkArgument( + VALID_TYPES.contains(type), + "Invalid object type '%s'. Must be one of %s", + type, + VALID_TYPES); + + String name = mainParameters.get(1); + String path = String.format("/rdap/%s/%s", type, name); + + logger.atInfo().log("Starting RDAP query for path: %s", path); + + try { + if (defaultConnection == null) { + throw new IllegalStateException("ServiceConnection was not set by RegistryCli."); + } + // Create a new ServiceConnection instance targeting GkeService.PUBAPI. + // This is necessary because the default connection provided by RegistryCli + // targets GkeService.BACKEND, but RDAP queries need to be routed to the + // public-facing API. + ServiceConnection pubapiConnection = + defaultConnection.withService(GkeService.PUBAPI, useCanary); + + String rdapResponse = pubapiConnection.sendGetRequest(path, ImmutableMap.of()); + JsonElement rdapJson = JsonParser.parseString(rdapResponse); + + // Pretty-print the JSON response. + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + System.out.println(gson.toJson(rdapJson)); + + logger.atInfo().log("Successfully completed RDAP query for path: %s", path); + } catch (IOException e) { + logger.atSevere().withCause(e).log("Request failed for path: %s", path); + System.err.println("Request failed for " + path + ": " + e.getMessage()); + } + } +} diff --git a/core/src/main/java/google/registry/tools/RegistryTool.java b/core/src/main/java/google/registry/tools/RegistryTool.java index c08c5cc490c..869e9a927ca 100644 --- a/core/src/main/java/google/registry/tools/RegistryTool.java +++ b/core/src/main/java/google/registry/tools/RegistryTool.java @@ -98,6 +98,7 @@ public final class RegistryTool { .put("login", LoginCommand.class) .put("logout", LogoutCommand.class) .put("pending_escrow", PendingEscrowCommand.class) + .put("rdap_query", RdapQueryCommand.class) .put("recreate_billing_recurrences", RecreateBillingRecurrencesCommand.class) .put("registrar_poc", RegistrarPocCommand.class) .put("renew_domain", RenewDomainCommand.class) diff --git a/core/src/main/java/google/registry/tools/RegistryToolComponent.java b/core/src/main/java/google/registry/tools/RegistryToolComponent.java index 4846e77b384..386ec2a5749 100644 --- a/core/src/main/java/google/registry/tools/RegistryToolComponent.java +++ b/core/src/main/java/google/registry/tools/RegistryToolComponent.java @@ -135,6 +135,8 @@ interface RegistryToolComponent { void inject(PendingEscrowCommand command); + void inject(RdapQueryCommand command); + void inject(RenewDomainCommand command); void inject(SaveSqlCredentialCommand command);