From be55b941ecd9a13c2e705f348cd7b5f3b5cf1538 Mon Sep 17 00:00:00 2001 From: Ben McIlwain Date: Sat, 9 May 2026 10:30:28 -0400 Subject: [PATCH] Migrate EPP/Email from Soy to JAXB/FreeMarker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace deprecated Soy templates for EPP XML with JAXB models and a refined Fluent DSL. - Migrate Spec11 and administrative emails to FreeMarker with HTML auto-escaping. - Remove Soy compiler, Gradle tasks, and library dependencies. - Address PR feedback regarding shadowing, version locking, and security warnings. - Enhance tests with comprehensive XML equality assertions using Java 15 text blocks. - Improve Javadocs and maintain strict temporal consistency using java.time. FreeMarker replaces Soy for email templating, providing native HTML auto-escaping and allowing the removal of the complex 'soyToJava' compilation step from the build process. This significantly simplifies the build system and reduces maintenance overhead. For EPP XML, migrating to JAXB allows tool-generated commands to use the same model classes as the server-side EPP flows. This ensures that tool-generated XML is always schema-compliant and eliminates the risk of divergence between tool templates and actual server-side implementation. This unified approach provides compile-time type safety and improves developer ergonomics via a refined fluent DSL. The base ImmutableObject class now provides a public clone() override that correctly resets the cached hashCode to null. This centralizes the custom cloning logic previously handled by a static helper and ensures that all subclasses—including the newly added JAXB models—satisfy CodeQL security requirements without needing redundant per-class overrides. The legacy static clone(T) helper has been updated to delegate to this instance method to maintain compatibility and architectural consistency. --- common/build.gradle | 1 + common/gradle.lockfile | 3 +- .../registry/util/TemplateRenderer.java | 67 +++++ .../registry/util/TemplateRendererTest.java | 83 ++++++ .../google/registry/util/test_template.ftl | 6 + config/presubmits.py | 33 +-- core/build.gradle | 63 +---- core/gradle.lockfile | 55 ++-- .../flows/domain/DomainFlowUtils.java | 78 ++++-- .../flows/domain/DomainRenewFlow.java | 8 +- .../flows/domain/DomainUpdateFlow.java | 43 +-- .../registry/flows/host/HostUpdateFlow.java | 9 +- .../registry/model/ImmutableObject.java | 21 +- .../registry/model/domain/DomainCommand.java | 246 +++++++++++++++--- .../google/registry/model/domain/fee/Fee.java | 46 +++- .../fee/FeeQueryCommandExtensionItem.java | 4 + .../fee/FeeTransformCommandExtension.java | 4 +- .../fee/FeeTransformResponseExtension.java | 2 +- .../FeeCheckCommandExtensionItemV06.java | 11 + .../fee06/FeeCheckCommandExtensionV06.java | 12 +- .../fee06/FeeCreateCommandExtensionV06.java | 28 +- .../fee06/FeeCreateResponseExtensionV06.java | 13 +- .../fee06/FeeDeleteResponseExtensionV06.java | 2 +- .../fee06/FeeRenewCommandExtensionV06.java | 7 +- .../fee06/FeeRenewResponseExtensionV06.java | 13 +- .../fee06/FeeTransferCommandExtensionV06.java | 7 +- .../FeeTransferResponseExtensionV06.java | 15 +- .../fee06/FeeUpdateCommandExtensionV06.java | 7 +- .../fee06/FeeUpdateResponseExtensionV06.java | 13 +- .../fee12/FeeCreateCommandExtensionV12.java | 21 ++ .../fee12/FeeRenewCommandExtensionV12.java | 23 +- .../fee12/FeeTransferCommandExtensionV12.java | 21 ++ .../fee12/FeeUpdateCommandExtensionV12.java | 21 ++ .../domain/launch/LaunchCheckExtension.java | 42 +-- .../domain/metadata/MetadataExtension.java | 53 ++-- .../model/domain/secdns/DomainDsDataBase.java | 2 + .../domain/secdns/SecDnsCreateExtension.java | 22 +- .../domain/secdns/SecDnsUpdateExtension.java | 71 ++++- .../DomainDeleteSuperuserExtension.java | 10 + .../DomainUpdateSuperuserExtension.java | 14 +- .../token/AllocationTokenExtension.java | 6 + .../model/eppinput/EppExtensions.java | 181 +++++++++++++ .../registry/model/eppinput/EppInput.java | 219 +++++++++++++--- .../model/eppinput/ResourceCommand.java | 67 ++--- .../registry/model/host/HostCommand.java | 142 ++++++++-- .../spec11/PublishSpec11ReportAction.java | 37 ++- .../spec11/RegistrarThreatMatches.java | 17 +- .../reporting/spec11/Spec11EmailUtils.java | 108 ++++---- .../tools/CheckDomainClaimsCommand.java | 18 +- .../registry/tools/CheckDomainCommand.java | 28 +- .../tools/CreateAnchorTenantCommand.java | 37 ++- .../registry/tools/CreateDomainCommand.java | 55 ++-- .../registry/tools/CreateHostCommand.java | 38 ++- .../registry/tools/DeleteDomainCommand.java | 23 +- .../registry/tools/DeleteHostCommand.java | 20 +- .../java/google/registry/tools/DsRecord.java | 16 +- .../google/registry/tools/EppToolCommand.java | 66 +++-- .../registry/tools/RenewDomainCommand.java | 49 ++-- .../tools/UniformRapidSuspensionCommand.java | 159 ++++++----- .../registry/tools/UpdateDomainCommand.java | 241 ++++++++++------- .../tools/UpdateServerLocksCommand.java | 109 +++++--- .../spec11/ftl/daily_spec11_email.ftl | 47 ++++ .../spec11/ftl/monthly_spec11_email.ftl | 46 ++++ .../reporting/spec11/soy/Spec11Email.soy | 130 --------- .../registry/tools/soy/CreateAnchorTenant.soy | 62 ----- .../google/registry/tools/soy/DomainCheck.soy | 53 ---- .../registry/tools/soy/DomainCheckClaims.soy | 42 --- .../registry/tools/soy/DomainCreate.soy | 108 -------- .../registry/tools/soy/DomainDelete.soy | 49 ---- .../google/registry/tools/soy/DomainRenew.soy | 52 ---- .../registry/tools/soy/DomainUpdate.soy | 137 ---------- .../google/registry/tools/soy/HostCreate.soy | 45 ---- .../google/registry/tools/soy/HostDelete.soy | 42 --- .../registry/tools/soy/RemoveIpAddress.soy | 47 ---- .../tools/soy/UniformRapidSuspension.soy | 90 ------- .../registry/tools/soy/UpdateServerLocks.soy | 56 ---- .../registry/flows/ResourceFlowTestCase.java | 7 +- .../model/domain/DomainCommandTest.java | 2 +- .../registry/model/eppinput/EppInputTest.java | 200 +++++++++++--- .../spec11/PublishSpec11ReportActionTest.java | 11 +- .../spec11/Spec11EmailUtilsTest.java | 224 ++++++++++++---- .../server/domain_create_anchor_tenant.xml | 7 +- ...omain_create_anchor_tenant_fee_premium.xml | 7 +- ...main_create_anchor_tenant_fee_standard.xml | 7 +- ...ate_anchor_tenant_multiple_word_reason.xml | 7 +- .../domain_create_anchor_tenant_no_reason.xml | 7 +- .../domain_create_anchor_tenant_password.xml | 7 +- .../tools/server/domain_create_palladium.xml | 5 +- .../server/domain_create_parajiumu_3yrs.xml | 5 +- .../server/update_server_locks_apply_all.xml | 11 +- .../server/update_server_locks_apply_one.xml | 3 +- ...date_server_locks_multiple_word_reason.xml | 3 +- .../server/update_server_locks_remove_all.xml | 11 +- .../server/update_server_locks_remove_one.xml | 3 +- .../xn--q9jyb4c_2010-10-17_full_S1_R0.xml | 2 +- db/buildscript-gradle.lockfile | 4 +- db/gradle.lockfile | 7 +- dependencies.gradle | 6 +- jetty/gradle.lockfile | 23 +- load-testing/gradle.lockfile | 16 +- networking/gradle.lockfile | 23 +- prober/gradle.lockfile | 23 +- proxy/gradle.lockfile | 21 +- util/gradle.lockfile | 7 +- 104 files changed, 2520 insertions(+), 1981 deletions(-) create mode 100644 common/src/main/java/google/registry/util/TemplateRenderer.java create mode 100644 common/src/test/java/google/registry/util/TemplateRendererTest.java create mode 100644 common/src/test/resources/google/registry/util/test_template.ftl create mode 100644 core/src/main/java/google/registry/model/eppinput/EppExtensions.java create mode 100644 core/src/main/resources/google/registry/reporting/spec11/ftl/daily_spec11_email.ftl create mode 100644 core/src/main/resources/google/registry/reporting/spec11/ftl/monthly_spec11_email.ftl delete mode 100644 core/src/main/resources/google/registry/reporting/spec11/soy/Spec11Email.soy delete mode 100644 core/src/main/resources/google/registry/tools/soy/CreateAnchorTenant.soy delete mode 100644 core/src/main/resources/google/registry/tools/soy/DomainCheck.soy delete mode 100644 core/src/main/resources/google/registry/tools/soy/DomainCheckClaims.soy delete mode 100644 core/src/main/resources/google/registry/tools/soy/DomainCreate.soy delete mode 100644 core/src/main/resources/google/registry/tools/soy/DomainDelete.soy delete mode 100644 core/src/main/resources/google/registry/tools/soy/DomainRenew.soy delete mode 100644 core/src/main/resources/google/registry/tools/soy/DomainUpdate.soy delete mode 100644 core/src/main/resources/google/registry/tools/soy/HostCreate.soy delete mode 100644 core/src/main/resources/google/registry/tools/soy/HostDelete.soy delete mode 100644 core/src/main/resources/google/registry/tools/soy/RemoveIpAddress.soy delete mode 100644 core/src/main/resources/google/registry/tools/soy/UniformRapidSuspension.soy delete mode 100644 core/src/main/resources/google/registry/tools/soy/UpdateServerLocks.soy diff --git a/common/build.gradle b/common/build.gradle index 6ce5873c6ff..d8cec296fec 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -58,6 +58,7 @@ dependencies { implementation deps['com.google.code.findbugs:jsr305'] implementation deps['com.google.guava:guava'] implementation deps['jakarta.inject:jakarta.inject-api'] + implementation deps['org.freemarker:freemarker'] implementation deps['com.google.flogger:flogger'] implementation deps['io.github.java-diff-utils:java-diff-utils'] implementation deps['com.google.truth:truth'] diff --git a/common/gradle.lockfile b/common/gradle.lockfile index eea2bea6bbd..040629b7b86 100644 --- a/common/gradle.lockfile +++ b/common/gradle.lockfile @@ -34,7 +34,7 @@ commons-collections:commons-collections:3.2.2=checkstyle info.picocli:picocli:4.7.7=checkstyle io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,testAnnotationProcessor,testingAnnotationProcessor io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,testAnnotationProcessor,testingAnnotationProcessor -io.github.java-diff-utils:java-diff-utils:4.16=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath +io.github.java-diff-utils:java-diff-utils:4.17=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath jakarta.inject:jakarta.inject-api:2.0.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath javax.inject:javax.inject:1=annotationProcessor,testAnnotationProcessor,testingAnnotationProcessor junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath @@ -60,6 +60,7 @@ org.codehaus.plexus:plexus-classworlds:2.6.0=checkstyle org.codehaus.plexus:plexus-component-annotations:2.1.0=checkstyle org.codehaus.plexus:plexus-container-default:2.1.0=checkstyle org.codehaus.plexus:plexus-utils:3.3.0=checkstyle +org.freemarker:freemarker:2.3.34=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath org.hamcrest:hamcrest-core:1.3=testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath org.jacoco:org.jacoco.agent:0.8.14=jacocoAgent,jacocoAnt org.jacoco:org.jacoco.ant:0.8.14=jacocoAnt diff --git a/common/src/main/java/google/registry/util/TemplateRenderer.java b/common/src/main/java/google/registry/util/TemplateRenderer.java new file mode 100644 index 00000000000..56dadfd6083 --- /dev/null +++ b/common/src/main/java/google/registry/util/TemplateRenderer.java @@ -0,0 +1,67 @@ +// Copyright 2026 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.util; + +import com.google.common.collect.ImmutableMap; +import freemarker.core.HTMLOutputFormat; +import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateExceptionHandler; +import jakarta.inject.Inject; +import java.io.StringWriter; + +/** + * A utility class for rendering FreeMarker templates. + * + *

This renderer is configured to use HTML as the default output format, which enables automatic + * escaping of all interpolated variables. It also uses the "computer" number format to ensure + * consistent formatting of numeric values across different locales. + */ +public class TemplateRenderer { + + private final Configuration configuration; + + @Inject + public TemplateRenderer() { + this.configuration = new Configuration(Configuration.VERSION_2_3_32); + this.configuration.setClassLoaderForTemplateLoading(getClass().getClassLoader(), ""); + this.configuration.setDefaultEncoding("UTF-8"); + this.configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); + this.configuration.setLogTemplateExceptions(false); + this.configuration.setWrapUncheckedExceptions(true); + this.configuration.setFallbackOnNullLoopVariable(false); + this.configuration.setOutputFormat(HTMLOutputFormat.INSTANCE); + this.configuration.setNumberFormat("computer"); + } + + /** + * Renders the specified template with the given data model. + * + * @param templatePath the path to the template file relative to the classpath root + * @param dataModel an immutable map containing the data to be used in the template + * @return the rendered template as a string + * @throws RuntimeException if the template cannot be found, parsed, or processed + */ + public String render(String templatePath, ImmutableMap dataModel) { + try { + Template template = configuration.getTemplate(templatePath); + StringWriter writer = new StringWriter(); + template.process(dataModel, writer); + return writer.toString(); + } catch (Exception e) { + throw new RuntimeException(String.format("Error rendering template %s", templatePath), e); + } + } +} diff --git a/common/src/test/java/google/registry/util/TemplateRendererTest.java b/common/src/test/java/google/registry/util/TemplateRendererTest.java new file mode 100644 index 00000000000..1b523b21423 --- /dev/null +++ b/common/src/test/java/google/registry/util/TemplateRendererTest.java @@ -0,0 +1,83 @@ +// Copyright 2026 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.util; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.google.common.collect.ImmutableMap; +import org.junit.jupiter.api.Test; + +/** Unit tests for {@link TemplateRenderer}. */ +class TemplateRendererTest { + + private final TemplateRenderer renderer = new TemplateRenderer(); + + @Test + void testRender_success() { + ImmutableMap data = + ImmutableMap.of( + "name", "World", "score", 42, "showMessage", true, "message", "Keep going!"); + String result = renderer.render("google/registry/util/test_template.ftl", data); + assertThat(result).isEqualTo("Hello World!\nYour score is 42.\nMessage: Keep going!\n"); + } + + @Test + void testRender_conditional_false() { + ImmutableMap data = + ImmutableMap.of("name", "User", "score", 0, "showMessage", false); + String result = renderer.render("google/registry/util/test_template.ftl", data); + assertThat(result).isEqualTo("Hello User!\nYour score is 0.\n"); + } + + @Test + void testRender_htmlEscaping() { + ImmutableMap data = + ImmutableMap.of("name", "World", "score", 42, "showMessage", false); + String result = renderer.render("google/registry/util/test_template.ftl", data); + assertThat(result).contains("Hello <b>World</b>!"); + } + + @Test + void testRender_missingTemplate_throwsException() { + assertThrows( + RuntimeException.class, + () -> renderer.render("non/existent/template.ftl", ImmutableMap.of())); + } + + @Test + void testRender_missingVariable_throwsException() { + // The template expects 'name', 'score', and 'showMessage', but the map is empty. + assertThrows( + RuntimeException.class, + () -> renderer.render("google/registry/util/test_template.ftl", ImmutableMap.of())); + } + + @Test + void testRender_unusedVariable_ignored() { + ImmutableMap data = + ImmutableMap.of( + "name", + "User", + "score", + 100, + "showMessage", + false, + "unusedKey", + "This should be ignored"); + String result = renderer.render("google/registry/util/test_template.ftl", data); + assertThat(result).isEqualTo("Hello User!\nYour score is 100.\n"); + } +} diff --git a/common/src/test/resources/google/registry/util/test_template.ftl b/common/src/test/resources/google/registry/util/test_template.ftl new file mode 100644 index 00000000000..f871f497081 --- /dev/null +++ b/common/src/test/resources/google/registry/util/test_template.ftl @@ -0,0 +1,6 @@ +<#-- Copyright 2026 The Nomulus Authors. All Rights Reserved. --> +Hello ${name}! +Your score is ${score}. +<#if showMessage> +Message: ${message} + diff --git a/config/presubmits.py b/config/presubmits.py index 2b500ad34ef..50b73167308 100644 --- a/config/presubmits.py +++ b/config/presubmits.py @@ -90,15 +90,15 @@ def fails(self, file): # License check PresubmitCheck( r".*Copyright 20\d{2} The Nomulus Authors\. All Rights Reserved\.", - ("java", "js", "soy", "sql", "py", "sh", "gradle", "ts"), { + ("java", "js", "sql", "py", "sh", "gradle", "ts", "ftl"), { ".git", "/build/", "node_modules/", "LoggerConfig.java", "registrar_bin.", "registrar_dbg.", "google-java-format-diff.py", - "nomulus.golden.sql", "soyutils_usegoog.js", "javascript/checks.js" + "nomulus.golden.sql", "javascript/checks.js" }, REQUIRED): "File did not include the license header.", # Files must end in a newline - PresubmitCheck(r".*\n$", ("java", "js", "soy", "sql", "py", "sh", "gradle", "ts", "xml"), + PresubmitCheck(r".*\n$", ("java", "js", "sql", "py", "sh", "gradle", "ts", "xml", "ftl"), {"node_modules/", ".idea"}, REQUIRED): "Source files must end in a newline.", @@ -127,33 +127,6 @@ def fails(self, file): "System.(out|err).println is only allowed in tools/ packages. Please " "use a logger instead.", - # Various Soy linting checks - PresubmitCheck( - r".* (/\*)?\* {?@param ", - "soy", - {}, - ): - "In SOY please use the ({@param name: string} /** User name. */) style" - " parameter passing instead of the ( * @param name User name.) style " - "parameter passing.", - PresubmitCheck( - r'.*\{[^}]+\w+:\s+"', - "soy", - {}, - ): - "Please don't use double-quoted string literals in Soy parameters", - PresubmitCheck( - r'.*autoescape\s*=\s*"[^s]', - "soy", - {}, - ): - "All soy templates must use strict autoescaping", - PresubmitCheck( - r".*noAutoescape", - "soy", - {}, - ): - "All soy templates must use strict autoescaping", PresubmitCheck( r".*\nimport\s+(?:static\s+)?.*\.shaded\..*", "java", diff --git a/core/build.gradle b/core/build.gradle index 3cb8cbaead3..13849341e91 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -94,7 +94,6 @@ processTestResources { configurations { jaxb - soy devtool nonprodImplementation.extendsFrom implementation @@ -120,7 +119,7 @@ configurations { // For reasons we do not understand, marking the following dependencies as // compileOnly instead of compile does not exclude them from runtimeClasspath. all { - // servlet-api:3.1 pulled in but not used by soy compiler + // servlet-api:3.1 pulled in but not used exclude group: 'javax.servlet', module: 'javax.servlet-api' } } @@ -181,7 +180,7 @@ dependencies { implementation deps['com.google.oauth-client:google-oauth-client-jetty'] implementation deps['com.google.oauth-client:google-oauth-client-servlet'] implementation deps['com.google.re2j:re2j'] - implementation deps['com.google.template:soy'] + implementation deps['org.freemarker:freemarker'] implementation deps['com.googlecode.json-simple:json-simple'] implementation deps['com.jcraft:jsch'] implementation deps['com.zaxxer:HikariCP'] @@ -299,9 +298,6 @@ dependencies { jaxb deps['org.glassfish.jaxb:jaxb-runtime'] jaxb deps['org.glassfish.jaxb:jaxb-xjc'] - // Dependency needed for soy to java compilation. - soy deps['com.google.template:soy'] - // Flyway classes needed to generate the golden file. implementation deps['org.flywaydb:flyway-core'] implementation deps['org.flywaydb:flyway-database-postgresql'] @@ -371,62 +367,7 @@ task jaxbToJava { } } -task soyToJava { - // Relative paths of soy directories. - def spec11SoyDir = "google/registry/reporting/spec11/soy" - def toolsSoyDir = "google/registry/tools/soy" - - def soyRelativeDirs = [spec11SoyDir, toolsSoyDir] - soyRelativeDirs.each { - inputs.dir "${resourcesSourceDir}/${it}" - outputs.dir "${generatedDir}/${it}" - } - - ext.soyToJava = { javaPackage, outputDirectory, soyFiles -> - project.services.get(ExecOperations).javaexec { - mainClass = "com.google.template.soy.SoyParseInfoGenerator" - classpath = configurations.soy - jvmArgs = ["--sun-misc-unsafe-memory-access=allow", "--enable-native-access=ALL-UNNAMED"] - args = ["--javaPackage", "${javaPackage}", - "--outputDirectory", "${outputDirectory}", - "--javaClassNameSource", "filename", - "--srcs", "${soyFiles.join(',')}"] - } - - // Replace the "@link" tags after the "@deprecated" tags in the generated - // files. The soy compiler doesn't generate imports for these, causing - // us to get warnings when we generate javadocs. - // TODO(b/200296387): To be fair, the deprecations are accurate: we're - // using the old "SoyInfo" classes instead of the new "Templates" files. - // When we convert to the new classes, this hack can go away. - def outputs = fileTree(outputDirectory) { - include '**/*.java' - } - - outputs.each { file -> - project.services.get(ExecOperations).exec { - commandLine = ['sed', '-i""', '-e', 's/@link/LINK/g', file.getCanonicalPath()] - } - } - } - - doLast { - soyToJava('google.registry.tools.soy', - "${generatedDir}/${toolsSoyDir}", - fileTree( - dir: "${resourcesSourceDir}/${toolsSoyDir}", - include: ['**/*.soy'])) - - soyToJava('google.registry.reporting.spec11.soy', - "${generatedDir}/${spec11SoyDir}", - fileTree( - dir: "${resourcesSourceDir}/${spec11SoyDir}", - include: ['**/*.soy'])) - } -} - compileJava.dependsOn jaxbToJava -compileJava.dependsOn soyToJava // Make testing artifacts available to be depended up on by other projects. // TODO: factor out google.registry.testing to be a separate project. diff --git a/core/gradle.lockfile b/core/gradle.lockfile index a86c04e0492..af60e9f1729 100644 --- a/core/gradle.lockfile +++ b/core/gradle.lockfile @@ -1,8 +1,7 @@ # This is a Gradle generated file for dependency locking. # Manual edits can break the build and are not advised. # This file is expected to be part of source control. -aopalliance:aopalliance:1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath -args4j:args4j:2.33=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath +args4j:args4j:2.33=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.charleskorn.kaml:kaml:0.20.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath com.fasterxml.jackson.core:jackson-annotations:2.21=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.fasterxml.jackson.core:jackson-core:2.20.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -81,13 +80,13 @@ com.google.api:gax-grpc:2.80.0=testCompileClasspath,testRuntimeClasspath com.google.api:gax-httpjson:2.74.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath com.google.api:gax-httpjson:2.80.0=testCompileClasspath,testRuntimeClasspath com.google.api:gax:2.74.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.apis:google-api-services-admin-directory:directory_v1-rev20260227-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.apis:google-api-services-admin-directory:directory_v1-rev20260421-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-bigquery:v2-rev20251012-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-cloudresourcemanager:v1-rev20250606-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.apis:google-api-services-dataflow:v1b3-rev20260405-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.apis:google-api-services-dataflow:v1b3-rev20260503-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-dns:v1-rev20260421-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-drive:v3-rev20260428-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.apis:google-api-services-gmail:v1-rev20260427-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.apis:google-api-services-gmail:v1-rev20260511-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-groupssettings:v1-rev20220614-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-healthcare:v1-rev20240130-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-iam:v2-rev20250502-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -138,33 +137,26 @@ com.google.cloud:google-cloud-tasks:2.51.0=compileClasspath,deploy_jar,nonprodCo com.google.cloud:grpc-gcp:1.9.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.cloud:libraries-bom:26.48.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath com.google.cloud:proto-google-cloud-firestore-bundle-v1:3.37.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath -com.google.code.gson:gson:2.10.1=soy +com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath com.google.code.gson:gson:2.13.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.common.html.types:types:1.0.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath com.google.dagger:dagger-compiler:2.59.2=annotationProcessor,testAnnotationProcessor com.google.dagger:dagger-spi:2.59.2=annotationProcessor,testAnnotationProcessor com.google.dagger:dagger:2.59.2=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath com.google.devtools.ksp:symbol-processing-api:2.2.20-2.0.3=annotationProcessor,testAnnotationProcessor com.google.errorprone:error_prone_annotation:2.48.0=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor -com.google.errorprone:error_prone_annotations:2.20.0=soy com.google.errorprone:error_prone_annotations:2.36.0=checkstyle com.google.errorprone:error_prone_annotations:2.48.0=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor com.google.errorprone:error_prone_annotations:2.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.errorprone:error_prone_check_api:2.48.0=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor com.google.errorprone:error_prone_core:2.48.0=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor -com.google.escapevelocity:escapevelocity:1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath com.google.flatbuffers:flatbuffers-java:24.3.25=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.flogger:flogger-system-backend:0.7.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath -com.google.flogger:flogger:0.7.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath -com.google.flogger:google-extensions:0.7.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath +com.google.flogger:flogger-system-backend:0.7.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.flogger:flogger:0.7.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.flogger:google-extensions:0.7.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.googlejavaformat:google-java-format:1.34.1=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor -com.google.guava:failureaccess:1.0.1=soy com.google.guava:failureaccess:1.0.3=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath -com.google.guava:guava-parent:32.1.1-jre=soy com.google.guava:guava-testlib:33.3.0-jre=testRuntimeClasspath com.google.guava:guava-testlib:33.6.0-jre=testCompileClasspath -com.google.guava:guava:32.1.1-jre=soy com.google.guava:guava:33.4.8-jre=checkstyle com.google.guava:guava:33.5.0-jre=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor com.google.guava:guava:33.6.0-jre=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -179,10 +171,8 @@ com.google.http-client:google-http-client-jackson2:1.46.3=compileClasspath,deplo com.google.http-client:google-http-client-jackson2:2.1.0=testCompileClasspath,testRuntimeClasspath com.google.http-client:google-http-client-protobuf:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.http-client:google-http-client:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.inject:guice:7.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath com.google.j2objc:j2objc-annotations:3.0.0=checkstyle com.google.j2objc:j2objc-annotations:3.1=annotationProcessor,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath -com.google.jsinterop:jsinterop-annotations:1.0.1=soy com.google.jsinterop:jsinterop-annotations:2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.monitoring-client:contrib:1.0.7=testCompileClasspath,testRuntimeClasspath com.google.monitoring-client:metrics:1.0.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -195,15 +185,13 @@ com.google.oauth-client:google-oauth-client-servlet:1.36.0=deploy_jar,nonprodRun com.google.oauth-client:google-oauth-client-servlet:1.39.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath com.google.oauth-client:google-oauth-client:1.39.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.protobuf:protobuf-java-util:4.33.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath -com.google.protobuf:protobuf-java-util:4.35.0-RC2=testCompileClasspath,testRuntimeClasspath -com.google.protobuf:protobuf-java:3.21.7=soy +com.google.protobuf:protobuf-java-util:4.35.0=testCompileClasspath,testRuntimeClasspath com.google.protobuf:protobuf-java:4.33.2=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor -com.google.protobuf:protobuf-java:4.35.0-RC2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.protobuf:protobuf-java:4.35.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.re2j:re2j:1.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.template:soy:2024-02-26=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath com.google.truth:truth:1.4.5=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.googlecode.json-simple:json-simple:1.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.ibm.icu:icu4j:73.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath +com.ibm.icu:icu4j:73.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.jcraft:jsch:0.1.55=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.lmax:disruptor:3.4.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.puppycrawl.tools:checkstyle:10.24.0=checkstyle @@ -252,7 +240,7 @@ io.apicurio:apicurio-registry-protobuf-schema-utilities:3.0.0.M2=compileClasspat io.github.classgraph:classgraph:4.8.162=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor -io.github.java-diff-utils:java-diff-utils:4.16=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.github.java-diff-utils:java-diff-utils:4.17=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.github.ss-bhatt:testcontainers-valkey:1.0.0=testCompileClasspath,testRuntimeClasspath io.grpc:grpc-alts:1.76.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath io.grpc:grpc-alts:1.81.0=testCompileClasspath,testRuntimeClasspath @@ -351,7 +339,7 @@ jakarta-regexp:jakarta-regexp:1.4=compileClasspath,deploy_jar,nonprodCompileClas jakarta.activation:jakarta.activation-api:2.1.4=jaxb jakarta.activation:jakarta.activation-api:2.2.0-M1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath jakarta.activation:jakarta.activation-api:2.2.0-M2=compileClasspath,nonprodCompileClasspath,testCompileClasspath -jakarta.inject:jakarta.inject-api:2.0.1=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath +jakarta.inject:jakarta.inject-api:2.0.1=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath jakarta.mail:jakarta.mail-api:2.2.0-M1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath jakarta.persistence:jakarta.persistence-api:3.2.0=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath jakarta.servlet:jakarta.servlet-api:6.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -360,8 +348,7 @@ jakarta.xml.bind:jakarta.xml.bind-api:4.0.4=deploy_jar,nonprodRuntimeClasspath,r jakarta.xml.bind:jakarta.xml.bind-api:4.0.5=jaxb jakarta.xml.bind:jakarta.xml.bind-api:4.1.0-M1=compileClasspath,nonprodCompileClasspath,testCompileClasspath javax.annotation:javax.annotation-api:1.3.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -javax.annotation:jsr250-api:1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath -javax.inject:javax.inject:1=annotationProcessor,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath +javax.inject:javax.inject:1=annotationProcessor,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath javax.jdo:jdo2-api:2.3-20090302111651=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath javax.validation:validation-api:1.0.0.GA=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath joda-time:joda-time:2.14.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -438,10 +425,9 @@ org.bouncycastle:bcpg-jdk18on:1.84=compileClasspath,deploy_jar,nonprodCompileCla org.bouncycastle:bcpkix-jdk18on:1.84=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.bouncycastle:bcprov-jdk18on:1.84=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.bouncycastle:bcutil-jdk18on:1.84=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.checkerframework:checker-compat-qual:2.5.3=annotationProcessor,compileClasspath,nonprodCompileClasspath,soy,testAnnotationProcessor,testCompileClasspath +org.checkerframework:checker-compat-qual:2.5.3=annotationProcessor,compileClasspath,nonprodCompileClasspath,testAnnotationProcessor,testCompileClasspath org.checkerframework:checker-compat-qual:2.5.6=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.checkerframework:checker-qual:3.19.0=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor -org.checkerframework:checker-qual:3.33.0=soy org.checkerframework:checker-qual:3.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.checkerframework:checker-qual:3.49.3=checkstyle org.codehaus.mojo:animal-sniffer-annotations:1.24=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath @@ -465,8 +451,9 @@ org.eclipse.jetty:jetty-server:12.1.9=testCompileClasspath,testRuntimeClasspath org.eclipse.jetty:jetty-session:12.1.9=testCompileClasspath,testRuntimeClasspath org.eclipse.jetty:jetty-util:12.1.9=testCompileClasspath,testRuntimeClasspath org.eclipse.jetty:jetty-xml:12.1.9=testCompileClasspath,testRuntimeClasspath -org.flywaydb:flyway-core:12.6.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.flywaydb:flyway-database-postgresql:12.6.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.flywaydb:flyway-core:12.6.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.flywaydb:flyway-database-postgresql:12.6.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.freemarker:freemarker:2.3.34=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.glassfish.jaxb:codemodel:4.0.8=jaxb org.glassfish.jaxb:jaxb-core:4.0.6=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath org.glassfish.jaxb:jaxb-core:4.0.8=jaxb @@ -514,7 +501,6 @@ org.jetbrains:annotations:13.0=annotationProcessor,testAnnotationProcessor org.jetbrains:annotations:17.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jline:jline:3.30.5=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.joda:joda-money:2.0.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.json:json:20230618=soy org.json:json:20251224=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jsoup:jsoup:1.22.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.jspecify:jspecify:1.0.0=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath @@ -535,17 +521,12 @@ org.mockito:mockito-junit-jupiter:5.23.0=testCompileClasspath,testRuntimeClasspa org.objenesis:objenesis:3.3=testRuntimeClasspath org.ogce:xpp3:1.1.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-analysis:9.5=soy org.ow2.asm:asm-analysis:9.7.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-commons:9.5=soy org.ow2.asm:asm-commons:9.7.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.ow2.asm:asm-commons:9.9=jacocoAnt -org.ow2.asm:asm-tree:9.5=soy org.ow2.asm:asm-tree:9.7.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.ow2.asm:asm-tree:9.9=jacocoAnt -org.ow2.asm:asm-util:9.5=soy org.ow2.asm:asm-util:9.7.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm:9.5=soy org.ow2.asm:asm:9.7.1=compileClasspath,nonprodCompileClasspath org.ow2.asm:asm:9.8=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.ow2.asm:asm:9.9=jacocoAnt diff --git a/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java b/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java index 3a1d010c811..935ee3de6d4 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java +++ b/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java @@ -43,8 +43,8 @@ import static google.registry.util.DateTimeUtils.END_INSTANT; import static google.registry.util.DateTimeUtils.isAtOrAfter; import static google.registry.util.DateTimeUtils.minusDays; +import static google.registry.util.DateTimeUtils.plusYears; import static google.registry.util.DomainNameUtils.ACE_PREFIX; -import static java.time.ZoneOffset.UTC; import static java.util.stream.Collectors.joining; import com.google.common.base.CharMatcher; @@ -154,7 +154,7 @@ public class DomainFlowUtils { /** Warning message for allocation of collision domains in sunrise. */ public static final String COLLISION_MESSAGE = "Domain on the name collision list was allocated. But by policy, the domain will not be " - + "delegated. Please visit https://www.icann.org/namecollision for more information on " + + "delegated. Please visit https://www.icann.org/namecollision for more information on " + "name collision."; /** Strict validator for ascii lowercase letters, digits, and "-", allowing "." as a separator */ @@ -581,13 +581,12 @@ static void handleFeeRequest( InternetDomainName domainName, Optional domain, @Nullable CurrencyUnit topLevelCurrency, - Instant currentDate, + Instant now, DomainPricingLogic pricingLogic, Optional allocationToken, boolean isAvailable, @Nullable BillingRecurrence billingRecurrence) throws EppException { - Instant now = currentDate; // Use the custom effective date specified in the fee check request, if there is one. if (feeRequest.getEffectiveDate().isPresent()) { now = feeRequest.getEffectiveDate().get(); @@ -816,7 +815,7 @@ private static FeeType getOrParseType(Fee fee) throws ParameterValuePolicyErrorE return fee.getType(); } ImmutableList types = fee.parseDescriptionForTypes(); - if (types.size() == 0) { + if (types.isEmpty()) { throw new FeeDescriptionParseException(fee.getDescription()); } else if (types.size() > 1) { throw new FeeDescriptionMultipleMatchesException(fee.getDescription(), types); @@ -848,7 +847,7 @@ private static ImmutableMap buildFeeMap(List fees, Currency */ public static void validateRegistrationPeriod(Instant now, Instant newExpirationTime) throws EppException { - if (now.atZone(UTC).plusYears(MAX_REGISTRATION_YEARS).toInstant().isBefore(newExpirationTime)) { + if (plusYears(now, MAX_REGISTRATION_YEARS).isBefore(newExpirationTime)) { throw new ExceedsMaxRegistrationYearsException(); } } @@ -907,7 +906,7 @@ static ImmutableSet updateDsData( return ImmutableSet.copyOf(union(difference(oldDsData, toRemove), toAdd)); } - /** If a domain "clientUpdateProhibited" set, updates must clear it or fail. */ + /** If a domain has "clientUpdateProhibited" set, updates must clear it or fail. */ static void verifyClientUpdateNotProhibited(Update command, Domain existingResource) throws ResourceHasClientUpdateProhibitedException { if (existingResource.getStatusValues().contains(StatusValue.CLIENT_UPDATE_PROHIBITED) @@ -996,7 +995,13 @@ static void validateLaunchCreateNotice( } } - /** Check that the claims period hasn't ended. */ + /** + * Check that the claims period hasn't ended. + * + * @param tld the {@link Tld} to check + * @param now the current {@link Instant} + * @throws ClaimsPeriodEndedException if the claims period has ended + */ static void verifyClaimsPeriodNotEnded(Tld tld, Instant now) throws ClaimsPeriodEndedException { if (!now.isBefore(tld.getClaimsPeriodEnd())) { throw new ClaimsPeriodEndedException(tld.getTldStr()); @@ -1008,6 +1013,9 @@ static void verifyClaimsPeriodNotEnded(Tld tld, Instant now) throws ClaimsPeriod * *

{@link BigDecimal} has a concept of significant figures, so zero is not always zero. E.g. * zero in USD is 0.00, whereas zero in Yen is 0, and zero in Dinars is 0.000 (!). + * + * @param currencyUnit the {@link CurrencyUnit} + * @return zero in the given currency */ static BigDecimal zeroInCurrency(CurrencyUnit currencyUnit) { return Money.of(currencyUnit, BigDecimal.ZERO).getAmount(); @@ -1016,6 +1024,12 @@ static BigDecimal zeroInCurrency(CurrencyUnit currencyUnit) { /** * Check that if there's a claims notice it's on the claims list, and that if there's not one it's * not on the claims list. + * + * @param domainName the {@link InternetDomainName} to check + * @param claimsList the current {@link ClaimsList} + * @param hasSignedMarks whether signed marks are present + * @param hasClaimsNotice whether a claims notice is present + * @throws EppException if the claims notice status is incorrect */ static void verifyClaimsNoticeIfAndOnlyIfNeeded( InternetDomainName domainName, @@ -1032,7 +1046,12 @@ static void verifyClaimsNoticeIfAndOnlyIfNeeded( } } - /** Check that there are no code marks, which is a type of mark we don't support. */ + /** + * Check that there are no code marks, which is a type of mark we don't support. + * + * @param launchCreate the {@link LaunchCreateExtension} + * @throws UnsupportedMarkTypeException if code marks are present + */ static void verifyNoCodeMarks(LaunchCreateExtension launchCreate) throws UnsupportedMarkTypeException { if (launchCreate.hasCodeMarks()) { @@ -1040,7 +1059,13 @@ static void verifyNoCodeMarks(LaunchCreateExtension launchCreate) } } - /** Create a response extension listing the fees on a domain create. */ + /** + * Create a response extension listing the fees on a domain create. + * + * @param feeCreate the {@link FeeTransformCommandExtension} + * @param feesAndCredits the {@link FeesAndCredits} + * @return the {@link FeeTransformResponseExtension} + */ static FeeTransformResponseExtension createFeeCreateResponse( FeeTransformCommandExtension feeCreate, FeesAndCredits feesAndCredits) { return feeCreate @@ -1058,10 +1083,21 @@ static FeeTransformResponseExtension createFeeCreateResponse( * their flow. For example, if a grace period delete occurs, we must add -1 counters for the * associated NET_ADDS_#_YRS field, if it exists. * - *

The steps are as follows: 1. Find all HistoryEntries under the domain modified in the past, - * up to the maxSearchPeriod. 2. Only keep HistoryEntries with a DomainTransactionRecord that a) - * hasn't been reported yet and b) matches the predicate 3. Return the transactionRecords under - * the most recent HistoryEntry that fits the above criteria, with negated reportAmounts. + *

The steps are as follows: + * + *

    + *
  1. Find all HistoryEntries under the domain modified in the past, up to the maxSearchPeriod. + *
  2. Only keep HistoryEntries with a DomainTransactionRecord that a) hasn't been reported yet + * and b) matches the predicate + *
  3. Return the transactionRecords under the most recent HistoryEntry that fits the above + * criteria, with negated reportAmounts. + *
+ * + * @param domain the {@link Domain} to create records for + * @param now the current {@link Instant} + * @param maxSearchPeriod the {@link Duration} to search back + * @param cancelableFields the set of {@link TransactionReportField}s that can be canceled + * @return the set of canceling {@link DomainTransactionRecord}s */ public static ImmutableSet createCancelingRecords( Domain domain, @@ -1225,13 +1261,6 @@ public InvalidIdnDomainLabelException() { } } - /** Having a registrant is prohibited by registry policy. */ - public static class RegistrantProhibitedException extends ParameterValuePolicyErrorException { - public RegistrantProhibitedException() { - super("Having a registrant is prohibited by registry policy"); - } - } - /** Too many nameservers set on this domain. */ static class TooManyNameserversException extends ParameterValuePolicyErrorException { public TooManyNameserversException(String message) { @@ -1384,6 +1413,13 @@ public FeesMismatchException(FeeType type, Money correctFee) { } } + /** Having a registrant is prohibited by registry policy. */ + public static class RegistrantProhibitedException extends ParameterValuePolicyErrorException { + public RegistrantProhibitedException() { + super("Having a registrant is prohibited by registry policy"); + } + } + /** The fee description passed in the transform command cannot be parsed. */ public static class FeeDescriptionParseException extends ParameterValuePolicyErrorException { public FeeDescriptionParseException(String description) { diff --git a/core/src/main/java/google/registry/flows/domain/DomainRenewFlow.java b/core/src/main/java/google/registry/flows/domain/DomainRenewFlow.java index f561d2e6a95..780de360266 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainRenewFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainRenewFlow.java @@ -34,8 +34,8 @@ import static google.registry.flows.domain.token.AllocationTokenFlowUtils.verifyBulkTokenAllowedOnDomain; import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_RENEW; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; +import static google.registry.util.DateTimeUtils.plusYears; import static google.registry.util.DateTimeUtils.toLocalDate; -import static java.time.ZoneOffset.UTC; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -192,11 +192,7 @@ public EppResponse run() throws EppException { existingDomain = maybeApplyBulkPricingRemovalToken(existingDomain, allocationToken); Instant newExpirationTime = - existingDomain - .getRegistrationExpirationTime() - .atZone(UTC) - .plusYears(years) - .toInstant(); // Uncapped + plusYears(existingDomain.getRegistrationExpirationTime(), years); // Uncapped validateRegistrationPeriod(now, newExpirationTime); Optional feeRenew = eppInput.getSingleExtension(FeeRenewCommandExtension.class); diff --git a/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java b/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java index 92d65f86492..adb13b92570 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainUpdateFlow.java @@ -14,7 +14,6 @@ package google.registry.flows.domain; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet; import static com.google.common.collect.Sets.symmetricDifference; import static com.google.common.collect.Sets.union; @@ -58,17 +57,17 @@ import google.registry.flows.custom.EntityChanges; import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedForTldWithNameserverAllowListException; import google.registry.flows.domain.DomainFlowUtils.RegistrantProhibitedException; +import google.registry.flows.exceptions.ContactsProhibitedException; import google.registry.model.ImmutableObject; import google.registry.model.billing.BillingBase.Reason; import google.registry.model.billing.BillingEvent; import google.registry.model.domain.Domain; import google.registry.model.domain.DomainCommand.Update; -import google.registry.model.domain.DomainCommand.Update.AddRemove; import google.registry.model.domain.DomainCommand.Update.Change; +import google.registry.model.domain.DomainCommand.Update.DomainAddRemove; import google.registry.model.domain.DomainHistory; import google.registry.model.domain.fee.FeeUpdateCommandExtension; import google.registry.model.domain.metadata.MetadataExtension; -import google.registry.model.domain.secdns.DomainDsData; import google.registry.model.domain.secdns.SecDnsUpdateExtension; import google.registry.model.domain.secdns.SecDnsUpdateExtension.Add; import google.registry.model.domain.secdns.SecDnsUpdateExtension.Remove; @@ -118,6 +117,7 @@ * @error {@link NameserversNotSpecifiedForTldWithNameserverAllowListException} * @error {@link DomainFlowUtils.NotAuthorizedForTldException} * @error {@link RegistrantProhibitedException} + * @error {@link ContactsProhibitedException} * @error {@link DomainFlowUtils.SecDnsAllUsageException} * @error {@link DomainFlowUtils.TooManyDsRecordsException} * @error {@link DomainFlowUtils.TooManyNameserversException} @@ -214,8 +214,8 @@ private static boolean requiresDnsUpdate(Domain existingDomain, Domain newDomain private void verifyUpdateAllowed(Update command, Domain existingDomain, Instant now) throws EppException { verifyOptionalAuthInfo(authInfo, existingDomain); - AddRemove add = command.getInnerAdd(); - AddRemove remove = command.getInnerRemove(); + DomainAddRemove add = command.getInnerAdd(); + DomainAddRemove remove = command.getInnerRemove(); String tldStr = existingDomain.getTld(); if (!isSuperuser) { verifyNoDisallowedStatuses(existingDomain, UPDATE_DISALLOWED_STATUSES); @@ -234,8 +234,8 @@ private void verifyUpdateAllowed(Update command, Domain existingDomain, Instant } private Domain performUpdate(Update command, Domain domain, Instant now) throws EppException { - AddRemove add = command.getInnerAdd(); - AddRemove remove = command.getInnerRemove(); + DomainAddRemove add = command.getInnerAdd(); + DomainAddRemove remove = command.getInnerRemove(); Optional secDnsUpdate = eppInput.getSingleExtension(SecDnsUpdateExtension.class); verifyAddsAndRemoves(domain.getNameservers(), add.getNameservers(), remove.getNameservers()); @@ -251,28 +251,29 @@ private Domain performUpdate(Update command, Domain domain, Instant now) throws Domain.Builder domainBuilder = domain .asBuilder() - // Handle the secDNS extension. As dsData in secDnsUpdate is read from EPP input and - // does not have domainRepoId set, we create a copy of the existing dsData without - // domainRepoId for comparison. + // Handle the secDNS extension. .setDsData( secDnsUpdate.isPresent() - ? updateDsData( - domain.getDsData().stream() - .map(DomainDsData::cloneWithoutDomainRepoId) - .collect(toImmutableSet()), - secDnsUpdate.get()) + ? updateDsData(domain.getDsData(), secDnsUpdate.get()) : domain.getDsData()) .setLastEppUpdateTime(now) - .setLastEppUpdateRegistrarId(registrarId) - .addStatusValues(add.getStatusValues()) - .removeStatusValues(remove.getStatusValues()) - .setAuthInfo(Optional.ofNullable(change.getAuthInfo()).orElse(domain.getAuthInfo())); + .setLastEppUpdateRegistrarId(registrarId); + + if (!add.getStatusValues().isEmpty()) { + domainBuilder.addStatusValues(add.getStatusValues()); + } + if (!remove.getStatusValues().isEmpty()) { + domainBuilder.removeStatusValues(remove.getStatusValues()); + } + + domainBuilder.setAuthInfo( + Optional.ofNullable(change.getAuthInfo()).orElse(domain.getAuthInfo())); if (!add.getNameservers().isEmpty()) { - domainBuilder.addNameservers(add.getNameservers().stream().collect(toImmutableSet())); + domainBuilder.addNameservers(add.getNameservers()); } if (!remove.getNameservers().isEmpty()) { - domainBuilder.removeNameservers(remove.getNameservers().stream().collect(toImmutableSet())); + domainBuilder.removeNameservers(remove.getNameservers()); } Optional superuserExt = diff --git a/core/src/main/java/google/registry/flows/host/HostUpdateFlow.java b/core/src/main/java/google/registry/flows/host/HostUpdateFlow.java index 59171dc4c25..c5a2c243f26 100644 --- a/core/src/main/java/google/registry/flows/host/HostUpdateFlow.java +++ b/core/src/main/java/google/registry/flows/host/HostUpdateFlow.java @@ -36,7 +36,6 @@ import com.google.cloud.tasks.v2.Task; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; -import google.registry.batch.AsyncTaskEnqueuer; import google.registry.batch.CloudTasksUtils; import google.registry.dns.RefreshDnsOnHostRenameAction; import google.registry.flows.EppException; @@ -59,8 +58,8 @@ import google.registry.model.eppoutput.EppResponse; import google.registry.model.host.Host; import google.registry.model.host.HostCommand.Update; -import google.registry.model.host.HostCommand.Update.AddRemove; import google.registry.model.host.HostCommand.Update.Change; +import google.registry.model.host.HostCommand.Update.HostAddRemove; import google.registry.model.host.HostHistory; import google.registry.model.reporting.IcannReportingTypes.ActivityReportField; import google.registry.persistence.VKey; @@ -122,7 +121,6 @@ public final class HostUpdateFlow implements MutatingFlow { @Inject @TargetId String targetId; @Inject @Superuser boolean isSuperuser; @Inject HostHistory.Builder historyBuilder; - @Inject AsyncTaskEnqueuer asyncTaskEnqueuer; @Inject EppResponse.Builder responseBuilder; @Inject CloudTasksUtils cloudTasksUtils; @@ -148,6 +146,7 @@ public EppResponse run() throws EppException { ? tm().loadByKey(existingHost.getSuperordinateDomain()).cloneProjectedAtTime(now) : null; // Note that lookupSuperordinateDomain calls cloneProjectedAtTime on the domain for us. + Optional newSuperordinateDomain = lookupSuperordinateDomain(validateHostName(newHostName), now); verifySuperordinateDomainNotInPendingDelete(newSuperordinateDomain.orElse(null)); @@ -157,8 +156,8 @@ public EppResponse run() throws EppException { if (isHostRename && ForeignKeyUtils.loadKey(Host.class, newHostName, now).isPresent()) { throw new HostAlreadyExistsException(newHostName); } - AddRemove add = command.getInnerAdd(); - AddRemove remove = command.getInnerRemove(); + HostAddRemove add = command.getInnerAdd(); + HostAddRemove remove = command.getInnerRemove(); verifyAddsAndRemoves( existingHost.getStatusValues(), add.getStatusValues(), remove.getStatusValues()); verifyAddsAndRemoves( diff --git a/core/src/main/java/google/registry/model/ImmutableObject.java b/core/src/main/java/google/registry/model/ImmutableObject.java index 576144ff2f4..fe4775c6d17 100644 --- a/core/src/main/java/google/registry/model/ImmutableObject.java +++ b/core/src/main/java/google/registry/model/ImmutableObject.java @@ -106,19 +106,24 @@ public int hashCode() { return hashCode; } - /** Returns a clone of the given object. */ - @SuppressWarnings("unchecked") - protected static T clone(T t) { + @Override + @SuppressWarnings("AmbiguousMethodReference") + public ImmutableObject clone() { try { - T clone = (T) t.clone(); - // Clear the hashCode since we often mutate clones before handing them out. + ImmutableObject clone = (ImmutableObject) super.clone(); clone.hashCode = null; return clone; - } catch (CloneNotSupportedException e) { // Yes it is. - throw new IllegalStateException(); + } catch (CloneNotSupportedException e) { + throw new AssertionError(); } } + /** Returns a clone of the given object. */ + @SuppressWarnings({"unchecked", "AmbiguousMethodReference"}) + protected static T clone(T t) { + return (T) t.clone(); + } + /** Returns a clone of the given object with empty fields set to null. */ protected static T cloneEmptyToNull(T t) { return ModelUtils.cloneEmptyToNull(t); @@ -233,7 +238,7 @@ private static Object toMapRecursive(Object o) { } } - /** Marker to indicate that this filed should be ignored by {@link #toDiffableFieldMap}. */ + /** Marker to indicate that this field should be ignored by {@link #toDiffableFieldMap}. */ @Documented @Retention(RUNTIME) @Target(FIELD) diff --git a/core/src/main/java/google/registry/model/domain/DomainCommand.java b/core/src/main/java/google/registry/model/domain/DomainCommand.java index d85235b6237..9f59502ca74 100644 --- a/core/src/main/java/google/registry/model/domain/DomainCommand.java +++ b/core/src/main/java/google/registry/model/domain/DomainCommand.java @@ -17,7 +17,6 @@ import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Sets.difference; -import static google.registry.util.CollectionUtils.difference; import static google.registry.util.CollectionUtils.isNullOrEmpty; import static google.registry.util.CollectionUtils.nullSafeImmutableCopy; import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; @@ -28,8 +27,11 @@ import google.registry.flows.EppException.ParameterValuePolicyErrorException; import google.registry.flows.domain.DomainFlowUtils.RegistrantProhibitedException; import google.registry.flows.exceptions.ContactsProhibitedException; +import google.registry.model.Buildable; import google.registry.model.ForeignKeyUtils; import google.registry.model.ImmutableObject; +import google.registry.model.eppcommon.AuthInfo; +import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppinput.ResourceCommand.AbstractSingleResourceCommand; import google.registry.model.eppinput.ResourceCommand.ResourceCheck; import google.registry.model.eppinput.ResourceCommand.ResourceCreateOrChange; @@ -37,6 +39,8 @@ import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand; import google.registry.model.host.Host; import google.registry.persistence.VKey; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAttribute; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlElementWrapper; @@ -68,10 +72,10 @@ T cloneAndLinkReferences(Instant now) throws InvalidReferencesException, ParameterValuePolicyErrorException; } - /** The fields on "chgType" from RFC5731. */ + /** The fields on "chgType" from RFC5731. */ @XmlTransient - public static class DomainCreateOrChange extends ImmutableObject - implements ResourceCreateOrChange { + public abstract static class DomainCreateOrChange + extends ImmutableObject implements ResourceCreateOrChange { /** The contactId of the registrant who registered this domain. */ @XmlElement(name = "registrant") @@ -92,9 +96,10 @@ public DomainAuthInfo getAuthInfo() { /** * A create command for a {@link Domain}, mapping "createType" from RFC5731. + * href="https://tools.ietf.org/html/rfc5731">RFC5731. */ @XmlRootElement + @XmlAccessorType(XmlAccessType.FIELD) @XmlType( propOrder = { "domainName", @@ -147,17 +152,12 @@ public ImmutableSet> getNameservers() { return nullToEmptyImmutableCopy(nameservers); } - @Override - public DomainAuthInfo getAuthInfo() { - return authInfo; - } - /** Creates a copy of this {@link Create} with hard links to hosts and contacts. */ @Override public Create cloneAndLinkReferences(Instant now) throws InvalidReferencesException, ParameterValuePolicyErrorException { Create clone = clone(this); - clone.nameservers = linkHosts(clone.nameserverHostNames, now); + clone.nameservers = linkHosts(nullSafeImmutableCopy(clone.nameserverHostNames), now); if (registrantContactId != null) { throw new RegistrantProhibitedException(); } @@ -166,14 +166,65 @@ public Create cloneAndLinkReferences(Instant now) } return clone; } + + /** Builder for {@link Create}. */ + public static class Builder extends Buildable.Builder { + public Builder setDomainName(String domainName) { + getInstance().domainName = domainName; + return this; + } + + public Builder setPeriod(Period period) { + getInstance().period = period; + return this; + } + + public Builder setNameserverHostNames(ImmutableSet nameserverHostNames) { + getInstance().nameserverHostNames = + isNullOrEmpty(nameserverHostNames) ? null : nameserverHostNames; + return this; + } + + public Builder setForeignKeyedDesignatedContacts( + ImmutableSet foreignKeyedDesignatedContacts) { + getInstance().foreignKeyedDesignatedContacts = + isNullOrEmpty(foreignKeyedDesignatedContacts) ? null : foreignKeyedDesignatedContacts; + return this; + } + + public Builder setRegistrant(String registrant) { + getInstance().registrantContactId = registrant; + return this; + } + + public Builder setAuthInfo(DomainAuthInfo authInfo) { + getInstance().authInfo = authInfo; + return this; + } + } } /** A delete command for a {@link Domain}. */ @XmlRootElement - public static class Delete extends AbstractSingleResourceCommand {} + @XmlAccessorType(XmlAccessType.FIELD) + public static class Delete extends AbstractSingleResourceCommand { + @XmlElement(name = "name") + String name; + + @Override + public String getTargetId() { + return name; + } + + @Override + public void setTargetId(String targetId) { + this.name = targetId; + } + } /** An info request for a {@link Domain}. */ @XmlRootElement + @XmlAccessorType(XmlAccessType.FIELD) public static class Info extends ImmutableObject implements SingleResourceCommand { /** The name of the domain to look up, and an attribute specifying the host lookup type. */ @@ -226,7 +277,7 @@ public String getTargetId() { } @Override - public DomainAuthInfo getAuthInfo() { + public AuthInfo getAuthInfo() { return authInfo; } } @@ -237,12 +288,27 @@ public static class Check extends ResourceCheck {} /** A renew command for a {@link Domain}. */ @XmlRootElement + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(propOrder = {"name", "currentExpirationDate", "period"}) public static class Renew extends AbstractSingleResourceCommand { + @XmlElement(name = "name") + String name; + + @Override + public String getTargetId() { + return name; + } + + @Override + public void setTargetId(String targetId) { + this.name = targetId; + } + @XmlElement(name = "curExpDate") LocalDate currentExpirationDate; /** The period that this domain's state was set to last for. */ - Period period; + @XmlElement Period period; public LocalDate getCurrentExpirationDate() { return currentExpirationDate; @@ -251,13 +317,46 @@ public LocalDate getCurrentExpirationDate() { public Period getPeriod() { return firstNonNull(period, DEFAULT_PERIOD); } + + /** Builder for {@link Renew}. */ + public static class Builder extends Buildable.Builder { + public Builder setTargetId(String targetId) { + getInstance().setTargetId(targetId); + return this; + } + + public Builder setCurrentExpirationDate(LocalDate currentExpirationDate) { + getInstance().currentExpirationDate = currentExpirationDate; + return this; + } + + public Builder setPeriod(Period period) { + getInstance().period = period; + return this; + } + } } /** A transfer operation for a {@link Domain}. */ @XmlRootElement + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(propOrder = {"name", "period", "authInfo"}) public static class Transfer extends AbstractSingleResourceCommand { + @XmlElement(name = "name") + String name; + + @Override + public String getTargetId() { + return name; + } + + @Override + public void setTargetId(String targetId) { + this.name = targetId; + } + /** The period to extend this domain's registration upon completion of the transfer. */ - Period period; + @XmlElement Period period; /** Authorization info used to validate if client has permissions to perform this operation. */ DomainAuthInfo authInfo; @@ -267,25 +366,40 @@ public Period getPeriod() { } @Override - public DomainAuthInfo getAuthInfo() { + public AuthInfo getAuthInfo() { return authInfo; } } /** An update to a {@link Domain}. */ @XmlRootElement - @XmlType(propOrder = {"targetId", "innerAdd", "innerRemove", "innerChange"}) - public static class Update extends ResourceUpdate + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(propOrder = {"name", "innerAdd", "innerRemove", "innerChange"}) + public static class Update + extends ResourceUpdate implements CreateOrUpdate { + @XmlElement(name = "name") + String name; + + @Override + public String getTargetId() { + return name; + } + + @Override + public void setTargetId(String targetId) { + this.name = targetId; + } + @XmlElement(name = "chg") protected Change innerChange; @XmlElement(name = "add") - protected AddRemove innerAdd; + protected DomainAddRemove innerAdd; @XmlElement(name = "rem") - protected AddRemove innerRemove; + protected DomainAddRemove innerRemove; @Override protected Change getNullableInnerChange() { @@ -293,25 +407,49 @@ protected Change getNullableInnerChange() { } @Override - protected AddRemove getNullableInnerAdd() { + protected DomainAddRemove getNullableInnerAdd() { return innerAdd; } @Override - protected AddRemove getNullableInnerRemove() { + protected DomainAddRemove getNullableInnerRemove() { return innerRemove; } public boolean noChangesPresent() { - AddRemove emptyAddRemove = new AddRemove(); + DomainAddRemove emptyAddRemove = new DomainAddRemove(); return emptyAddRemove.equals(getInnerAdd()) && emptyAddRemove.equals(getInnerRemove()) && new Change().equals(getInnerChange()); } + /** Builder for {@link Update}. */ + public static class Builder extends Buildable.Builder { + public Builder setTargetId(String targetId) { + getInstance().setTargetId(targetId); + return this; + } + + public Builder setInnerAdd(DomainAddRemove innerAdd) { + getInstance().innerAdd = innerAdd; + return this; + } + + public Builder setInnerRemove(DomainAddRemove innerRemove) { + getInstance().innerRemove = innerRemove; + return this; + } + + public Builder setInnerChange(Change innerChange) { + getInstance().innerChange = innerChange; + return this; + } + } + /** The inner change type on a domain update command. */ + @XmlAccessorType(XmlAccessType.FIELD) @XmlType(propOrder = {"nameserverHostNames", "foreignKeyedDesignatedContacts", "statusValues"}) - public static class AddRemove extends ResourceUpdate.AddRemove { + public static class DomainAddRemove extends ResourceUpdate.AddRemove { /** Fully qualified host names of the hosts that are the nameservers for the domain. */ @XmlElementWrapper(name = "ns") @XmlElement(name = "hostObj") @@ -324,6 +462,25 @@ public static class AddRemove extends ResourceUpdate.AddRemove { @XmlElement(name = "contact") Set foreignKeyedDesignatedContacts; + @XmlElement(name = "status") + Set statusValues; + + public boolean isEmpty() { + return isNullOrEmpty(nameserverHostNames) + && isNullOrEmpty(foreignKeyedDesignatedContacts) + && isNullOrEmpty(statusValues); + } + + @Override + public void setStatusValues(ImmutableSet statusValues) { + this.statusValues = statusValues; + } + + @Override + public ImmutableSet getStatusValues() { + return nullToEmptyImmutableCopy(statusValues); + } + public ImmutableSet getNameserverHostNames() { return nullSafeImmutableCopy(nameserverHostNames); } @@ -332,11 +489,25 @@ public ImmutableSet> getNameservers() { return nullToEmptyImmutableCopy(nameservers); } - /** Creates a copy of this {@link AddRemove} with hard links to hosts and contacts. */ - private AddRemove cloneAndLinkReferences(Instant now) + /** Builder for {@link DomainAddRemove}. */ + public static class Builder extends Buildable.Builder { + public Builder setNameserverHostNames(ImmutableSet nameserverHostNames) { + getInstance().nameserverHostNames = + isNullOrEmpty(nameserverHostNames) ? null : nameserverHostNames; + return this; + } + + public Builder setStatusValues(ImmutableSet statusValues) { + getInstance().statusValues = isNullOrEmpty(statusValues) ? null : statusValues; + return this; + } + } + + /** Creates a copy of this {@link DomainAddRemove} with hard links to hosts and contacts. */ + private DomainAddRemove cloneAndLinkReferences(Instant now) throws InvalidReferencesException, ContactsProhibitedException { - AddRemove clone = clone(this); - clone.nameservers = linkHosts(clone.nameserverHostNames, now); + DomainAddRemove clone = clone(this); + clone.nameservers = linkHosts(nullSafeImmutableCopy(clone.nameserverHostNames), now); if (!isNullOrEmpty(foreignKeyedDesignatedContacts)) { throw new ContactsProhibitedException(); } @@ -345,8 +516,17 @@ private AddRemove cloneAndLinkReferences(Instant now) } /** The inner change type on a domain update command. */ + @XmlAccessorType(XmlAccessType.FIELD) @XmlType(propOrder = {"registrantContactId", "authInfo"}) public static class Change extends DomainCreateOrChange { + /** Builder for {@link Change}. */ + public static class Builder extends Buildable.Builder { + public Builder setAuthInfo(DomainAuthInfo authInfo) { + getInstance().authInfo = authInfo; + return this; + } + } + Change cloneAndLinkReferences() throws RegistrantProhibitedException { Change clone = clone(this); if (clone.registrantContactId != null) { @@ -373,7 +553,7 @@ public Update cloneAndLinkReferences(Instant now) } } - private static Set> linkHosts(Set hostNames, Instant now) + private static ImmutableSet> linkHosts(ImmutableSet hostNames, Instant now) throws InvalidReferencesException { if (hostNames == null) { return null; @@ -383,7 +563,7 @@ private static Set> linkHosts(Set hostNames, Instant now) /** Loads host keys to cached EPP resources by their foreign keys. */ private static ImmutableMap> loadByForeignKeysCached( - Set foreignKeys, Instant now) throws InvalidReferencesException { + ImmutableSet foreignKeys, Instant now) throws InvalidReferencesException { ImmutableMap> fks = ForeignKeyUtils.loadKeysByCacheIfEnabled(Host.class, foreignKeys, now); if (!fks.keySet().equals(foreignKeys)) { @@ -394,14 +574,14 @@ private static ImmutableMap> loadByForeignKeysCached( } /** Exception to throw when referenced objects don't exist. */ - public static class InvalidReferencesException extends Exception { + public static class InvalidReferencesException extends ParameterValuePolicyErrorException { private final ImmutableSet foreignKeys; private final Class type; - InvalidReferencesException(Class type, ImmutableSet foreignKeys) { + public InvalidReferencesException(Class type, Set foreignKeys) { super(String.format("Invalid %s reference IDs: %s", type.getSimpleName(), foreignKeys)); this.type = checkNotNull(type); - this.foreignKeys = foreignKeys; + this.foreignKeys = nullToEmptyImmutableCopy(foreignKeys); } public ImmutableSet getForeignKeys() { diff --git a/core/src/main/java/google/registry/model/domain/fee/Fee.java b/core/src/main/java/google/registry/model/domain/fee/Fee.java index cefa86fccc3..a5ce862415b 100644 --- a/core/src/main/java/google/registry/model/domain/fee/Fee.java +++ b/core/src/main/java/google/registry/model/domain/fee/Fee.java @@ -20,16 +20,22 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Range; +import google.registry.model.Buildable; import google.registry.model.eppcommon.ProtocolDefinition.ServiceExtension; import java.math.BigDecimal; import java.time.Instant; +import java.time.Period; /** - * A fee, in currency units specified elsewhere in the xml, with type of the fee an optional fee - * description. + * A fee, in currency units specified elsewhere in the XML, with a type and an optional description. */ public class Fee extends BaseFee { + @Override + public Fee clone() { + return (Fee) super.clone(); + } + /** Creates a Fee for the given cost and type with the default description. */ public static Fee create( BigDecimal cost, FeeType type, boolean isPremium, Object... descriptionArgs) { @@ -55,7 +61,7 @@ private static Fee createWithCustomDescription( BigDecimal cost, FeeType type, boolean isPremium, String description) { Fee instance = new Fee(); instance.cost = checkNotNull(cost); - checkArgument(instance.cost.signum() >= 0, "Cost must be a positive number"); + checkArgument(instance.cost.signum() >= 0, "Cost must be a non-negative number"); instance.type = checkNotNull(type); instance.isPremium = isPremium; instance.description = description; @@ -68,4 +74,38 @@ private static Fee createWithCustomDescription( ServiceExtension.FEE_0_12.getUri(), ServiceExtension.FEE_0_11.getUri(), ServiceExtension.FEE_0_6.getUri()); + + /** Builder for {@link Fee}. */ + public static class Builder extends Buildable.Builder { + + /** Sets the cost of the fee. */ + public Builder setCost(BigDecimal cost) { + getInstance().cost = cost; + return this; + } + + /** Sets the description of the fee. */ + public Builder setDescription(String description) { + getInstance().description = description; + return this; + } + + /** Sets whether the fee is refundable. */ + public Builder setRefundable(Boolean refundable) { + getInstance().refundable = refundable; + return this; + } + + /** Sets the grace period of the fee. */ + public Builder setGracePeriod(Period gracePeriod) { + getInstance().gracePeriod = gracePeriod; + return this; + } + + /** Sets when the fee is applied. */ + public Builder setApplied(AppliedType applied) { + getInstance().applied = applied; + return this; + } + } } diff --git a/core/src/main/java/google/registry/model/domain/fee/FeeQueryCommandExtensionItem.java b/core/src/main/java/google/registry/model/domain/fee/FeeQueryCommandExtensionItem.java index 18bce08ccbd..d14f5e2f800 100644 --- a/core/src/main/java/google/registry/model/domain/fee/FeeQueryCommandExtensionItem.java +++ b/core/src/main/java/google/registry/model/domain/fee/FeeQueryCommandExtensionItem.java @@ -78,6 +78,10 @@ public boolean shouldLoadDomainForCheck() { /** The period for the command being checked. */ Period period; + public void setPeriod(Period period) { + this.period = period; + } + /** * Three-character ISO4217 currency code. * diff --git a/core/src/main/java/google/registry/model/domain/fee/FeeTransformCommandExtension.java b/core/src/main/java/google/registry/model/domain/fee/FeeTransformCommandExtension.java index 6245fc06287..df36b88cd5c 100644 --- a/core/src/main/java/google/registry/model/domain/fee/FeeTransformCommandExtension.java +++ b/core/src/main/java/google/registry/model/domain/fee/FeeTransformCommandExtension.java @@ -30,7 +30,7 @@ public abstract class FeeTransformCommandExtension extends ImmutableObject implements CommandExtension { /** The currency of the fee. */ - CurrencyUnit currency; + @XmlElement public CurrencyUnit currency; /** * The magnitude of the fee, in the specified units, with an optional description. @@ -38,7 +38,7 @@ public abstract class FeeTransformCommandExtension *

This is a list because a single operation can involve multiple fees. */ @XmlElement(name = "fee") - List fees; + public List fees; public CurrencyUnit getCurrency() { return currency; diff --git a/core/src/main/java/google/registry/model/domain/fee/FeeTransformResponseExtension.java b/core/src/main/java/google/registry/model/domain/fee/FeeTransformResponseExtension.java index b7afae101d7..9570790c83d 100644 --- a/core/src/main/java/google/registry/model/domain/fee/FeeTransformResponseExtension.java +++ b/core/src/main/java/google/registry/model/domain/fee/FeeTransformResponseExtension.java @@ -31,7 +31,7 @@ public class FeeTransformResponseExtension extends ImmutableObject implements ResponseExtension { /** The currency of the fee. */ - CurrencyUnit currency; + @XmlElement CurrencyUnit currency; /** * The magnitude of the fee, in the specified units, with an optional description. diff --git a/core/src/main/java/google/registry/model/domain/fee06/FeeCheckCommandExtensionItemV06.java b/core/src/main/java/google/registry/model/domain/fee06/FeeCheckCommandExtensionItemV06.java index 7719ce72444..d06afae28e9 100644 --- a/core/src/main/java/google/registry/model/domain/fee06/FeeCheckCommandExtensionItemV06.java +++ b/core/src/main/java/google/registry/model/domain/fee06/FeeCheckCommandExtensionItemV06.java @@ -14,6 +14,7 @@ package google.registry.model.domain.fee06; +import google.registry.model.domain.Period; import google.registry.model.domain.fee.FeeCheckCommandExtensionItem; import google.registry.model.domain.fee.FeeExtensionCommandDescriptor; import jakarta.xml.bind.annotation.XmlType; @@ -33,6 +34,16 @@ public class FeeCheckCommandExtensionItemV06 extends FeeCheckCommandExtensionIte /** The command being checked. */ FeeExtensionCommandDescriptor command; + public static FeeCheckCommandExtensionItemV06 create( + String name, CurrencyUnit currency, FeeExtensionCommandDescriptor command, Period period) { + FeeCheckCommandExtensionItemV06 instance = new FeeCheckCommandExtensionItemV06(); + instance.name = name; + instance.currency = currency; + instance.command = command; + instance.setPeriod(period); + return instance; + } + /** The name of the command being checked. */ @Override public CommandName getCommandName() { diff --git a/core/src/main/java/google/registry/model/domain/fee06/FeeCheckCommandExtensionV06.java b/core/src/main/java/google/registry/model/domain/fee06/FeeCheckCommandExtensionV06.java index 6d75de1d725..1a96fc24e9c 100644 --- a/core/src/main/java/google/registry/model/domain/fee06/FeeCheckCommandExtensionV06.java +++ b/core/src/main/java/google/registry/model/domain/fee06/FeeCheckCommandExtensionV06.java @@ -25,16 +25,22 @@ import java.util.List; import org.joda.money.CurrencyUnit; -/** Version 0.6 of the fee extension that may be present on domain check commands. */ +/** + * An XML data object that represents version 0.6 of the fee extension that may be present on EPP + * domain check commands. + */ @XmlRootElement(name = "check") public class FeeCheckCommandExtensionV06 extends ImmutableObject implements FeeCheckCommandExtension< - FeeCheckCommandExtensionItemV06, - FeeCheckResponseExtensionV06> { + FeeCheckCommandExtensionItemV06, FeeCheckResponseExtensionV06> { @XmlElement(name = "domain") List items; + public void setItems(ImmutableList items) { + this.items = items; + } + @Override public CurrencyUnit getCurrency() { return null; // This version of the fee extension doesn't specify a top-level currency. diff --git a/core/src/main/java/google/registry/model/domain/fee06/FeeCreateCommandExtensionV06.java b/core/src/main/java/google/registry/model/domain/fee06/FeeCreateCommandExtensionV06.java index 7b407149691..80eb5ad47c1 100644 --- a/core/src/main/java/google/registry/model/domain/fee06/FeeCreateCommandExtensionV06.java +++ b/core/src/main/java/google/registry/model/domain/fee06/FeeCreateCommandExtensionV06.java @@ -15,13 +15,20 @@ package google.registry.model.domain.fee06; import com.google.common.collect.ImmutableList; +import google.registry.model.Buildable; import google.registry.model.domain.fee.Credit; +import google.registry.model.domain.fee.Fee; import google.registry.model.domain.fee.FeeCreateCommandExtension; import google.registry.model.domain.fee.FeeTransformResponseExtension; import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlTransient; import jakarta.xml.bind.annotation.XmlType; +import org.joda.money.CurrencyUnit; -/** A fee extension that may be present on domain create commands. */ +/** + * An XML data object that represents a fee extension that may be present on EPP domain create + * commands. + */ @XmlRootElement(name = "create") @XmlType(propOrder = {"currency", "fees"}) public class FeeCreateCommandExtensionV06 extends FeeCreateCommandExtension { @@ -31,12 +38,23 @@ public FeeTransformResponseExtension.Builder createResponseBuilder() { return new FeeTransformResponseExtension.Builder(new FeeCreateResponseExtensionV06()); } - /** - * This method is overridden and not annotated for JAXB because this version of the extension - * doesn't support the "credit" field. - */ + /** This version of the extension doesn't support the "credit" field. */ @Override + @XmlTransient public ImmutableList getCredits() { return ImmutableList.of(); } + + /** Builder for {@link FeeCreateCommandExtensionV06}. */ + public static class Builder extends Buildable.Builder { + public Builder setCurrency(CurrencyUnit currency) { + getInstance().currency = currency; + return this; + } + + public Builder setFees(ImmutableList fees) { + getInstance().fees = fees; + return this; + } + } } diff --git a/core/src/main/java/google/registry/model/domain/fee06/FeeCreateResponseExtensionV06.java b/core/src/main/java/google/registry/model/domain/fee06/FeeCreateResponseExtensionV06.java index b58dde263ba..410c2e2f269 100644 --- a/core/src/main/java/google/registry/model/domain/fee06/FeeCreateResponseExtensionV06.java +++ b/core/src/main/java/google/registry/model/domain/fee06/FeeCreateResponseExtensionV06.java @@ -14,8 +14,6 @@ package google.registry.model.domain.fee06; -import com.google.common.collect.ImmutableList; -import google.registry.model.domain.fee.Credit; import google.registry.model.domain.fee.FeeTransformResponseExtension; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlType; @@ -25,12 +23,5 @@ * domain create commands. */ @XmlRootElement(name = "creData") -@XmlType(propOrder = {"currency", "fees"}) -public class FeeCreateResponseExtensionV06 extends FeeTransformResponseExtension { - - /** This version of the extension doesn't support the "credit" field. */ - @Override - public ImmutableList getCredits() { - return ImmutableList.of(); - } -} +@XmlType(propOrder = {"currency", "fees", "credits"}) +public class FeeCreateResponseExtensionV06 extends FeeTransformResponseExtension {} diff --git a/core/src/main/java/google/registry/model/domain/fee06/FeeDeleteResponseExtensionV06.java b/core/src/main/java/google/registry/model/domain/fee06/FeeDeleteResponseExtensionV06.java index 7498656104b..e7cbf4a10d8 100644 --- a/core/src/main/java/google/registry/model/domain/fee06/FeeDeleteResponseExtensionV06.java +++ b/core/src/main/java/google/registry/model/domain/fee06/FeeDeleteResponseExtensionV06.java @@ -20,7 +20,7 @@ /** * An XML data object that represents a fee extension that may be present on the response to EPP - * domain create commands. + * domain delete commands. */ @XmlRootElement(name = "delData") @XmlType(propOrder = {"currency", "fees", "credits"}) diff --git a/core/src/main/java/google/registry/model/domain/fee06/FeeRenewCommandExtensionV06.java b/core/src/main/java/google/registry/model/domain/fee06/FeeRenewCommandExtensionV06.java index 73e593913f7..8a43d77fac4 100644 --- a/core/src/main/java/google/registry/model/domain/fee06/FeeRenewCommandExtensionV06.java +++ b/core/src/main/java/google/registry/model/domain/fee06/FeeRenewCommandExtensionV06.java @@ -19,9 +19,13 @@ import google.registry.model.domain.fee.FeeRenewCommandExtension; import google.registry.model.domain.fee.FeeTransformResponseExtension; import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlTransient; import jakarta.xml.bind.annotation.XmlType; -/** A fee extension that may be present on domain renew commands. */ +/** + * An XML data object that represents a fee extension that may be present on EPP domain renew + * commands. + */ @XmlRootElement(name = "renew") @XmlType(propOrder = {"currency", "fees"}) public class FeeRenewCommandExtensionV06 extends FeeRenewCommandExtension { @@ -33,6 +37,7 @@ public FeeTransformResponseExtension.Builder createResponseBuilder() { /** This version of the extension doesn't support the "credit" field. */ @Override + @XmlTransient public ImmutableList getCredits() { return ImmutableList.of(); } diff --git a/core/src/main/java/google/registry/model/domain/fee06/FeeRenewResponseExtensionV06.java b/core/src/main/java/google/registry/model/domain/fee06/FeeRenewResponseExtensionV06.java index 7f11aa5bc9f..07c58c3b993 100644 --- a/core/src/main/java/google/registry/model/domain/fee06/FeeRenewResponseExtensionV06.java +++ b/core/src/main/java/google/registry/model/domain/fee06/FeeRenewResponseExtensionV06.java @@ -14,8 +14,6 @@ package google.registry.model.domain.fee06; -import com.google.common.collect.ImmutableList; -import google.registry.model.domain.fee.Credit; import google.registry.model.domain.fee.FeeTransformResponseExtension; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlType; @@ -25,12 +23,5 @@ * domain renew commands. */ @XmlRootElement(name = "renData") -@XmlType(propOrder = {"currency", "fees"}) -public class FeeRenewResponseExtensionV06 extends FeeTransformResponseExtension { - - /** This version of the extension doesn't support the "credit" field. */ - @Override - public ImmutableList getCredits() { - return super.getCredits(); - } -} +@XmlType(propOrder = {"currency", "fees", "credits"}) +public class FeeRenewResponseExtensionV06 extends FeeTransformResponseExtension {} diff --git a/core/src/main/java/google/registry/model/domain/fee06/FeeTransferCommandExtensionV06.java b/core/src/main/java/google/registry/model/domain/fee06/FeeTransferCommandExtensionV06.java index e3c1ec1f726..194586f2597 100644 --- a/core/src/main/java/google/registry/model/domain/fee06/FeeTransferCommandExtensionV06.java +++ b/core/src/main/java/google/registry/model/domain/fee06/FeeTransferCommandExtensionV06.java @@ -19,9 +19,13 @@ import google.registry.model.domain.fee.FeeTransferCommandExtension; import google.registry.model.domain.fee.FeeTransformResponseExtension; import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlTransient; import jakarta.xml.bind.annotation.XmlType; -/** A fee extension that may be present on domain transfer requests. */ +/** + * An XML data object that represents a fee extension that may be present on EPP domain transfer + * commands. + */ @XmlRootElement(name = "transfer") @XmlType(propOrder = {"currency", "fees"}) public class FeeTransferCommandExtensionV06 extends FeeTransferCommandExtension { @@ -33,6 +37,7 @@ public FeeTransformResponseExtension.Builder createResponseBuilder() { /** This version of the extension doesn't support the "credit" field. */ @Override + @XmlTransient public ImmutableList getCredits() { return ImmutableList.of(); } diff --git a/core/src/main/java/google/registry/model/domain/fee06/FeeTransferResponseExtensionV06.java b/core/src/main/java/google/registry/model/domain/fee06/FeeTransferResponseExtensionV06.java index 1d92391b25f..b632d43ac9b 100644 --- a/core/src/main/java/google/registry/model/domain/fee06/FeeTransferResponseExtensionV06.java +++ b/core/src/main/java/google/registry/model/domain/fee06/FeeTransferResponseExtensionV06.java @@ -14,23 +14,14 @@ package google.registry.model.domain.fee06; -import com.google.common.collect.ImmutableList; -import google.registry.model.domain.fee.Credit; import google.registry.model.domain.fee.FeeTransformResponseExtension; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlType; /** * An XML data object that represents a fee extension that may be present on the response to EPP - * domain transfer requests. + * domain transfer commands. */ @XmlRootElement(name = "trnData") -@XmlType(propOrder = {"currency", "fees"}) -public class FeeTransferResponseExtensionV06 extends FeeTransformResponseExtension { - - /** This version of the extension doesn't support the "credit" field. */ - @Override - public ImmutableList getCredits() { - return super.getCredits(); - } -} +@XmlType(propOrder = {"currency", "fees", "credits"}) +public class FeeTransferResponseExtensionV06 extends FeeTransformResponseExtension {} diff --git a/core/src/main/java/google/registry/model/domain/fee06/FeeUpdateCommandExtensionV06.java b/core/src/main/java/google/registry/model/domain/fee06/FeeUpdateCommandExtensionV06.java index 3fce12cbada..d6327098dc5 100644 --- a/core/src/main/java/google/registry/model/domain/fee06/FeeUpdateCommandExtensionV06.java +++ b/core/src/main/java/google/registry/model/domain/fee06/FeeUpdateCommandExtensionV06.java @@ -19,9 +19,13 @@ import google.registry.model.domain.fee.FeeTransformResponseExtension; import google.registry.model.domain.fee.FeeUpdateCommandExtension; import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlTransient; import jakarta.xml.bind.annotation.XmlType; -/** A fee extension that may be present on domain update commands. */ +/** + * An XML data object that represents a fee extension that may be present on EPP domain update + * commands. + */ @XmlRootElement(name = "update") @XmlType(propOrder = {"currency", "fees"}) public class FeeUpdateCommandExtensionV06 extends FeeUpdateCommandExtension { @@ -33,6 +37,7 @@ public FeeTransformResponseExtension.Builder createResponseBuilder() { /** This version of the extension doesn't support the "credit" field. */ @Override + @XmlTransient public ImmutableList getCredits() { return ImmutableList.of(); } diff --git a/core/src/main/java/google/registry/model/domain/fee06/FeeUpdateResponseExtensionV06.java b/core/src/main/java/google/registry/model/domain/fee06/FeeUpdateResponseExtensionV06.java index 154b78d76c9..9b62459291e 100644 --- a/core/src/main/java/google/registry/model/domain/fee06/FeeUpdateResponseExtensionV06.java +++ b/core/src/main/java/google/registry/model/domain/fee06/FeeUpdateResponseExtensionV06.java @@ -14,8 +14,6 @@ package google.registry.model.domain.fee06; -import com.google.common.collect.ImmutableList; -import google.registry.model.domain.fee.Credit; import google.registry.model.domain.fee.FeeTransformResponseExtension; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlType; @@ -25,12 +23,5 @@ * domain update commands. */ @XmlRootElement(name = "updData") -@XmlType(propOrder = {"currency", "fees"}) -public class FeeUpdateResponseExtensionV06 extends FeeTransformResponseExtension { - - /** This version of the extension doesn't support the "credit" field. */ - @Override - public ImmutableList getCredits() { - return super.getCredits(); - } -} +@XmlType(propOrder = {"currency", "fees", "credits"}) +public class FeeUpdateResponseExtensionV06 extends FeeTransformResponseExtension {} diff --git a/core/src/main/java/google/registry/model/domain/fee12/FeeCreateCommandExtensionV12.java b/core/src/main/java/google/registry/model/domain/fee12/FeeCreateCommandExtensionV12.java index 2ff8fcbe1d0..9aed6ef09c2 100644 --- a/core/src/main/java/google/registry/model/domain/fee12/FeeCreateCommandExtensionV12.java +++ b/core/src/main/java/google/registry/model/domain/fee12/FeeCreateCommandExtensionV12.java @@ -17,13 +17,16 @@ import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import com.google.common.collect.ImmutableList; +import google.registry.model.Buildable; import google.registry.model.domain.fee.Credit; +import google.registry.model.domain.fee.Fee; import google.registry.model.domain.fee.FeeCreateCommandExtension; import google.registry.model.domain.fee.FeeTransformResponseExtension; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlType; import java.util.List; +import org.joda.money.CurrencyUnit; /** A fee extension that may be present on domain create commands. */ @XmlRootElement(name = "create") @@ -42,4 +45,22 @@ public ImmutableList getCredits() { public FeeTransformResponseExtension.Builder createResponseBuilder() { return new FeeTransformResponseExtension.Builder(new FeeCreateResponseExtensionV12()); } + + /** Builder for {@link FeeCreateCommandExtensionV12}. */ + public static class Builder extends Buildable.Builder { + public Builder setCurrency(CurrencyUnit currency) { + getInstance().currency = currency; + return this; + } + + public Builder setFees(ImmutableList fees) { + getInstance().fees = fees; + return this; + } + + public Builder setCredits(ImmutableList credits) { + getInstance().credits = credits; + return this; + } + } } diff --git a/core/src/main/java/google/registry/model/domain/fee12/FeeRenewCommandExtensionV12.java b/core/src/main/java/google/registry/model/domain/fee12/FeeRenewCommandExtensionV12.java index d1a0b120f0d..f9f9e886c7f 100644 --- a/core/src/main/java/google/registry/model/domain/fee12/FeeRenewCommandExtensionV12.java +++ b/core/src/main/java/google/registry/model/domain/fee12/FeeRenewCommandExtensionV12.java @@ -17,18 +17,21 @@ import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import com.google.common.collect.ImmutableList; +import google.registry.model.Buildable; import google.registry.model.domain.fee.Credit; +import google.registry.model.domain.fee.Fee; import google.registry.model.domain.fee.FeeRenewCommandExtension; import google.registry.model.domain.fee.FeeTransformResponseExtension; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlType; import java.util.List; +import org.joda.money.CurrencyUnit; /** A fee extension that may be present on domain renew commands. */ @XmlRootElement(name = "renew") @XmlType(propOrder = {"currency", "fees", "credits"}) -public class FeeRenewCommandExtensionV12 extends FeeRenewCommandExtension { +public class FeeRenewCommandExtensionV12 extends FeeRenewCommandExtension { @XmlElement(name = "credit") List credits; @@ -42,4 +45,22 @@ public ImmutableList getCredits() { public FeeTransformResponseExtension.Builder createResponseBuilder() { return new FeeTransformResponseExtension.Builder(new FeeRenewResponseExtensionV12()); } + + /** Builder for {@link FeeRenewCommandExtensionV12}. */ + public static class Builder extends Buildable.Builder { + public Builder setCurrency(CurrencyUnit currency) { + getInstance().currency = currency; + return this; + } + + public Builder setFees(ImmutableList fees) { + getInstance().fees = fees; + return this; + } + + public Builder setCredits(ImmutableList credits) { + getInstance().credits = credits; + return this; + } + } } diff --git a/core/src/main/java/google/registry/model/domain/fee12/FeeTransferCommandExtensionV12.java b/core/src/main/java/google/registry/model/domain/fee12/FeeTransferCommandExtensionV12.java index 53049bb9d6d..f9800a1c350 100644 --- a/core/src/main/java/google/registry/model/domain/fee12/FeeTransferCommandExtensionV12.java +++ b/core/src/main/java/google/registry/model/domain/fee12/FeeTransferCommandExtensionV12.java @@ -17,13 +17,16 @@ import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import com.google.common.collect.ImmutableList; +import google.registry.model.Buildable; import google.registry.model.domain.fee.Credit; +import google.registry.model.domain.fee.Fee; import google.registry.model.domain.fee.FeeTransferCommandExtension; import google.registry.model.domain.fee.FeeTransformResponseExtension; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlType; import java.util.List; +import org.joda.money.CurrencyUnit; /** A fee extension that may be present on domain transfer requests. */ @XmlRootElement(name = "transfer") @@ -42,4 +45,22 @@ public ImmutableList getCredits() { public FeeTransformResponseExtension.Builder createResponseBuilder() { return new FeeTransformResponseExtension.Builder(new FeeTransferResponseExtensionV12()); } + + /** Builder for {@link FeeTransferCommandExtensionV12}. */ + public static class Builder extends Buildable.Builder { + public Builder setCurrency(CurrencyUnit currency) { + getInstance().currency = currency; + return this; + } + + public Builder setFees(ImmutableList fees) { + getInstance().fees = fees; + return this; + } + + public Builder setCredits(ImmutableList credits) { + getInstance().credits = credits; + return this; + } + } } diff --git a/core/src/main/java/google/registry/model/domain/fee12/FeeUpdateCommandExtensionV12.java b/core/src/main/java/google/registry/model/domain/fee12/FeeUpdateCommandExtensionV12.java index 71a430093c1..86a600487d8 100644 --- a/core/src/main/java/google/registry/model/domain/fee12/FeeUpdateCommandExtensionV12.java +++ b/core/src/main/java/google/registry/model/domain/fee12/FeeUpdateCommandExtensionV12.java @@ -17,13 +17,16 @@ import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import com.google.common.collect.ImmutableList; +import google.registry.model.Buildable; import google.registry.model.domain.fee.Credit; +import google.registry.model.domain.fee.Fee; import google.registry.model.domain.fee.FeeTransformResponseExtension; import google.registry.model.domain.fee.FeeUpdateCommandExtension; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlType; import java.util.List; +import org.joda.money.CurrencyUnit; /** A fee extension that may be present on domain update commands. */ @XmlRootElement(name = "update") @@ -42,4 +45,22 @@ public ImmutableList getCredits() { public FeeTransformResponseExtension.Builder createResponseBuilder() { return new FeeTransformResponseExtension.Builder(new FeeUpdateResponseExtensionV12()); } + + /** Builder for {@link FeeUpdateCommandExtensionV12}. */ + public static class Builder extends Buildable.Builder { + public Builder setCurrency(CurrencyUnit currency) { + getInstance().currency = currency; + return this; + } + + public Builder setFees(ImmutableList fees) { + getInstance().fees = fees; + return this; + } + + public Builder setCredits(ImmutableList credits) { + getInstance().credits = credits; + return this; + } + } } diff --git a/core/src/main/java/google/registry/model/domain/launch/LaunchCheckExtension.java b/core/src/main/java/google/registry/model/domain/launch/LaunchCheckExtension.java index 5c3d2d0b8db..b3072f7fb35 100644 --- a/core/src/main/java/google/registry/model/domain/launch/LaunchCheckExtension.java +++ b/core/src/main/java/google/registry/model/domain/launch/LaunchCheckExtension.java @@ -19,34 +19,37 @@ import google.registry.model.ImmutableObject; import google.registry.model.eppinput.EppInput.CommandExtension; import jakarta.xml.bind.annotation.XmlAttribute; +import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlEnumValue; import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlType; /** * An XML data object that represents a launch extension that may be present on EPP domain check * commands. * *

This object holds XML data which JAXB will unmarshal from an EPP domain check command - * extension. The XML will have the following enclosing structure: + * extension. The XML will have the following enclosing structure: * - *

 {@code
- *   
- *     
- *       
- *         
- *       
- *       
- *         
- *           
- *         
- *       
- *     
- *   
- * } 
+ *
{@code
+ * 
+ *   
+ *     
+ *       
+ *     
+ *     
+ *       
+ *         
+ *       
+ *     
+ *   
+ * 
+ * }
* * @see CommandExtension */ @XmlRootElement(name = "check") +@XmlType(propOrder = "phase") public class LaunchCheckExtension extends ImmutableObject implements CommandExtension { /** The default check type is "claims" if not specified. */ @@ -67,11 +70,18 @@ public enum CheckType { * The launch phase this command is intended to run against. If it does not match the server's * current launch phase, the command will be rejected. */ - LaunchPhase phase; + @XmlElement LaunchPhase phase; @XmlAttribute CheckType type; + public static LaunchCheckExtension create(CheckType type, LaunchPhase phase) { + LaunchCheckExtension instance = new LaunchCheckExtension(); + instance.type = type; + instance.phase = phase; + return instance; + } + public CheckType getCheckType() { return firstNonNull(type, DEFAULT_CHECK_TYPE); } diff --git a/core/src/main/java/google/registry/model/domain/metadata/MetadataExtension.java b/core/src/main/java/google/registry/model/domain/metadata/MetadataExtension.java index f098982c7c1..5d7f41fffdd 100644 --- a/core/src/main/java/google/registry/model/domain/metadata/MetadataExtension.java +++ b/core/src/main/java/google/registry/model/domain/metadata/MetadataExtension.java @@ -14,39 +14,62 @@ package google.registry.model.domain.metadata; +import static com.google.common.base.MoreObjects.firstNonNull; + +import google.registry.model.Buildable; import google.registry.model.ImmutableObject; import google.registry.model.eppinput.EppInput.CommandExtension; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlType; +import javax.annotation.Nullable; -/** A metadata extension that may be present on EPP create/mutate commands. */ +/** + * Extension for EPP commands that provides metadata. + * + * @see EPP Metadata Extension + */ @XmlRootElement(name = "metadata") +@XmlType(propOrder = {"reason", "requestedByRegistrar", "isAnchorTenant"}) public class MetadataExtension extends ImmutableObject implements CommandExtension { - /** The reason for the change. */ - @XmlElement(name = "reason") - String reason; + /** Reason for the command. */ + @XmlElement @Nullable String reason; - /** Whether a change was requested by a registrar. */ - @XmlElement(name = "requestedByRegistrar") - boolean requestedByRegistrar; + /** Whether the command was requested by a registrar. */ + @XmlElement Boolean requestedByRegistrar; - /** - * Whether a domain is being created for an anchor tenant. This field is only - * relevant for domain creates, and should be omitted for all other operations. - */ + /** Whether this is an anchor tenant. */ @XmlElement(name = "anchorTenant") - boolean isAnchorTenant; + Boolean isAnchorTenant; public String getReason() { return reason; } - public boolean getRequestedByRegistrar() { + public Boolean getRequestedByRegistrar() { return requestedByRegistrar; } - public boolean getIsAnchorTenant() { - return isAnchorTenant; + public Boolean getIsAnchorTenant() { + return firstNonNull(isAnchorTenant, false); + } + + /** Builder for {@link MetadataExtension}. */ + public static class Builder extends Buildable.Builder { + public Builder setReason(String reason) { + getInstance().reason = reason; + return this; + } + + public Builder setRequestedByRegistrar(Boolean requestedByRegistrar) { + getInstance().requestedByRegistrar = requestedByRegistrar; + return this; + } + + public Builder setAnchorTenant(Boolean isAnchorTenant) { + getInstance().isAnchorTenant = isAnchorTenant; + return this; + } } } diff --git a/core/src/main/java/google/registry/model/domain/secdns/DomainDsDataBase.java b/core/src/main/java/google/registry/model/domain/secdns/DomainDsDataBase.java index 82195b819a1..81509fac3d1 100644 --- a/core/src/main/java/google/registry/model/domain/secdns/DomainDsDataBase.java +++ b/core/src/main/java/google/registry/model/domain/secdns/DomainDsDataBase.java @@ -23,12 +23,14 @@ import jakarta.xml.bind.DatatypeConverter; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlTransient; +import jakarta.xml.bind.annotation.XmlType; import jakarta.xml.bind.annotation.adapters.HexBinaryAdapter; import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter; /** Base class for {@link DomainDsData} and {@link DomainDsDataHistory}. */ @MappedSuperclass @Access(AccessType.FIELD) +@XmlType(propOrder = {"keyTag", "algorithm", "digestType", "digest"}) public abstract class DomainDsDataBase extends ImmutableObject implements UnsafeSerializable { @XmlTransient @Transient @Insignificant String domainRepoId; diff --git a/core/src/main/java/google/registry/model/domain/secdns/SecDnsCreateExtension.java b/core/src/main/java/google/registry/model/domain/secdns/SecDnsCreateExtension.java index a7a12e79684..b3297915e12 100644 --- a/core/src/main/java/google/registry/model/domain/secdns/SecDnsCreateExtension.java +++ b/core/src/main/java/google/registry/model/domain/secdns/SecDnsCreateExtension.java @@ -14,11 +14,13 @@ package google.registry.model.domain.secdns; -import static google.registry.util.CollectionUtils.nullSafeImmutableCopy; +import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import com.google.common.collect.ImmutableSet; +import google.registry.model.Buildable; import google.registry.model.ImmutableObject; import google.registry.model.eppinput.EppInput.CommandExtension; +import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlType; import java.util.Set; @@ -33,9 +35,10 @@ public class SecDnsCreateExtension extends ImmutableObject implements CommandExt *

We do not support expirations, but we need this field to be able to return appropriate * errors. */ - Long maxSigLife; + @XmlElement Long maxSigLife; /** Signatures for this domain. */ + @XmlElement(name = "dsData") Set dsData; public Long getMaxSigLife() { @@ -43,6 +46,19 @@ public Long getMaxSigLife() { } public ImmutableSet getDsData() { - return nullSafeImmutableCopy(dsData); + return nullToEmptyImmutableCopy(dsData); + } + + /** Builder for {@link SecDnsCreateExtension}. */ + public static class Builder extends Buildable.Builder { + public Builder setDsData(ImmutableSet dsData) { + getInstance().dsData = dsData; + return this; + } + + public Builder setMaxSigLife(Long maxSigLife) { + getInstance().maxSigLife = maxSigLife; + return this; + } } } diff --git a/core/src/main/java/google/registry/model/domain/secdns/SecDnsUpdateExtension.java b/core/src/main/java/google/registry/model/domain/secdns/SecDnsUpdateExtension.java index a9032c27748..9001a086391 100644 --- a/core/src/main/java/google/registry/model/domain/secdns/SecDnsUpdateExtension.java +++ b/core/src/main/java/google/registry/model/domain/secdns/SecDnsUpdateExtension.java @@ -17,6 +17,7 @@ import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import com.google.common.collect.ImmutableSet; +import google.registry.model.Buildable; import google.registry.model.ImmutableObject; import google.registry.model.eppinput.EppInput.CommandExtension; import jakarta.xml.bind.annotation.XmlAttribute; @@ -46,7 +47,7 @@ public class SecDnsUpdateExtension extends ImmutableObject implements CommandExt Remove remove; /** Allows adding new delegations. */ - Add add; + @XmlElement Add add; /** Would allow changing maxSigLife except that we don't support it. */ @XmlElement(name = "chg") @@ -68,31 +69,88 @@ public Optional getChange() { return Optional.ofNullable(change); } + /** Builder for {@link SecDnsUpdateExtension}. */ + public static class Builder extends Buildable.Builder { + public Builder setUrgent(Boolean urgent) { + getInstance().urgent = urgent; + return this; + } + + public Builder setRemove(Remove remove) { + getInstance().remove = remove; + return this; + } + + public Builder setAdd(Add add) { + getInstance().add = add; + return this; + } + } + @XmlTransient abstract static class AddRemoveBase extends ImmutableObject { - /** Delegations to add or remove. */ + abstract static class Builder> + extends Buildable.Builder { + public abstract B setDsData(ImmutableSet dsData); + } + } + + /** The inner add type on the update extension. */ + @XmlType(propOrder = "dsData") + public static class Add extends AddRemoveBase { + /** Delegations to add. */ + @XmlElement(name = "dsData") Set dsData; public ImmutableSet getDsData() { return nullToEmptyImmutableCopy(dsData); } - } - /** The inner add type on the update extension. */ - public static class Add extends AddRemoveBase {} + /** Builder for {@link Add}. */ + public static class Builder extends AddRemoveBase.Builder { + @Override + public Builder setDsData(ImmutableSet dsData) { + getInstance().dsData = dsData; + return this; + } + } + } /** The inner remove type on the update extension. */ @XmlType(propOrder = {"all", "dsData"}) public static class Remove extends AddRemoveBase { /** Whether to remove all delegations. */ - Boolean all; + @XmlElement Boolean all; + + /** Delegations to remove. */ + @XmlElement(name = "dsData") + Set dsData; public Boolean getAll() { return all; } + + public ImmutableSet getDsData() { + return nullToEmptyImmutableCopy(dsData); + } + + /** Builder for {@link Remove}. */ + public static class Builder extends AddRemoveBase.Builder { + public Builder setAll(Boolean all) { + getInstance().all = all; + return this; + } + + @Override + public Builder setDsData(ImmutableSet dsData) { + getInstance().dsData = dsData; + return this; + } + } } /** The inner change type on the update extension, though we don't actually support changes. */ + @XmlType(propOrder = "maxSigLife") public static class Change extends ImmutableObject { /** * Time in seconds until the signature should expire. @@ -100,6 +158,7 @@ public static class Change extends ImmutableObject { *

We do not support expirations, but we need this field to be able to return appropriate * errors. */ + @XmlElement(name = "maxSigLife") Long maxSigLife; } } diff --git a/core/src/main/java/google/registry/model/domain/superuser/DomainDeleteSuperuserExtension.java b/core/src/main/java/google/registry/model/domain/superuser/DomainDeleteSuperuserExtension.java index e63d895b0fe..2b7151f6838 100644 --- a/core/src/main/java/google/registry/model/domain/superuser/DomainDeleteSuperuserExtension.java +++ b/core/src/main/java/google/registry/model/domain/superuser/DomainDeleteSuperuserExtension.java @@ -16,9 +16,11 @@ import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlType; /** A superuser extension that may be present on domain delete commands. */ @XmlRootElement(name = "domainDelete") +@XmlType(propOrder = {"redemptionGracePeriodDays", "pendingDeleteDays"}) public class DomainDeleteSuperuserExtension extends SuperuserExtension { @XmlElement(name = "redemptionGracePeriodDays") @@ -27,6 +29,14 @@ public class DomainDeleteSuperuserExtension extends SuperuserExtension { @XmlElement(name = "pendingDeleteDays") int pendingDeleteDays; + public static DomainDeleteSuperuserExtension create( + int redemptionGracePeriodDays, int pendingDeleteDays) { + DomainDeleteSuperuserExtension instance = new DomainDeleteSuperuserExtension(); + instance.redemptionGracePeriodDays = redemptionGracePeriodDays; + instance.pendingDeleteDays = pendingDeleteDays; + return instance; + } + public int getRedemptionGracePeriodDays() { return redemptionGracePeriodDays; } diff --git a/core/src/main/java/google/registry/model/domain/superuser/DomainUpdateSuperuserExtension.java b/core/src/main/java/google/registry/model/domain/superuser/DomainUpdateSuperuserExtension.java index ac715d90cb2..54552bd3a7c 100644 --- a/core/src/main/java/google/registry/model/domain/superuser/DomainUpdateSuperuserExtension.java +++ b/core/src/main/java/google/registry/model/domain/superuser/DomainUpdateSuperuserExtension.java @@ -14,22 +14,28 @@ package google.registry.model.domain.superuser; -import static com.google.common.base.Strings.isNullOrEmpty; - import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlType; import java.util.Optional; import javax.annotation.Nullable; /** A superuser extension that may be present on domain update commands. */ @XmlRootElement(name = "domainUpdate") +@XmlType(propOrder = "autorenews") public class DomainUpdateSuperuserExtension extends SuperuserExtension { @XmlElement(name = "autorenews") @Nullable - String autorenews; + Boolean autorenews; + + public static DomainUpdateSuperuserExtension create(@Nullable Boolean autorenews) { + DomainUpdateSuperuserExtension instance = new DomainUpdateSuperuserExtension(); + instance.autorenews = autorenews; + return instance; + } public Optional getAutorenews() { - return Optional.ofNullable(isNullOrEmpty(autorenews) ? null : Boolean.valueOf(autorenews)); + return Optional.ofNullable(autorenews); } } diff --git a/core/src/main/java/google/registry/model/domain/token/AllocationTokenExtension.java b/core/src/main/java/google/registry/model/domain/token/AllocationTokenExtension.java index fdeee628593..327d1d7eaf2 100644 --- a/core/src/main/java/google/registry/model/domain/token/AllocationTokenExtension.java +++ b/core/src/main/java/google/registry/model/domain/token/AllocationTokenExtension.java @@ -35,6 +35,12 @@ public class AllocationTokenExtension extends ImmutableObject implements Command @XmlJavaTypeAdapter(TrimWhitespaceAdapter.class) String allocationToken; + public static AllocationTokenExtension create(String allocationToken) { + AllocationTokenExtension instance = new AllocationTokenExtension(); + instance.allocationToken = allocationToken; + return instance; + } + public String getAllocationToken() { return allocationToken; } diff --git a/core/src/main/java/google/registry/model/eppinput/EppExtensions.java b/core/src/main/java/google/registry/model/eppinput/EppExtensions.java new file mode 100644 index 00000000000..43891fbfe3d --- /dev/null +++ b/core/src/main/java/google/registry/model/eppinput/EppExtensions.java @@ -0,0 +1,181 @@ +// Copyright 2026 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.model.eppinput; + +import static com.google.common.base.Strings.isNullOrEmpty; +import static google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName.CREATE; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import google.registry.model.domain.Period; +import google.registry.model.domain.fee.Fee; +import google.registry.model.domain.fee.FeeExtensionCommandDescriptor; +import google.registry.model.domain.fee06.FeeCheckCommandExtensionItemV06; +import google.registry.model.domain.fee06.FeeCheckCommandExtensionV06; +import google.registry.model.domain.fee06.FeeCreateCommandExtensionV06; +import google.registry.model.domain.fee12.FeeCreateCommandExtensionV12; +import google.registry.model.domain.launch.LaunchCheckExtension; +import google.registry.model.domain.launch.LaunchCheckExtension.CheckType; +import google.registry.model.domain.launch.LaunchPhase; +import google.registry.model.domain.metadata.MetadataExtension; +import google.registry.model.domain.secdns.DomainDsData; +import google.registry.model.domain.secdns.SecDnsCreateExtension; +import google.registry.model.domain.secdns.SecDnsUpdateExtension; +import google.registry.model.domain.secdns.SecDnsUpdateExtension.Add; +import google.registry.model.domain.secdns.SecDnsUpdateExtension.Remove; +import google.registry.model.domain.superuser.DomainDeleteSuperuserExtension; +import google.registry.model.domain.superuser.DomainUpdateSuperuserExtension; +import google.registry.model.domain.token.AllocationTokenExtension; +import java.math.BigDecimal; +import javax.annotation.Nullable; +import org.joda.money.CurrencyUnit; +import org.joda.money.Money; + +/** Static helpers for creating common EPP extensions. */ +public class EppExtensions { + + /** + * Returns a metadata extension with the specified reason and flags. + * + * @param reason the reason for the change, recorded in history entries + * @param requestedByRegistrar whether the change was requested by a registrar + * @param isAnchorTenant whether the domain is an anchor tenant + */ + @Nullable + public static MetadataExtension metadata( + @Nullable String reason, + @Nullable Boolean requestedByRegistrar, + @Nullable Boolean isAnchorTenant) { + if (isNullOrEmpty(reason) && requestedByRegistrar == null && isAnchorTenant == null) { + return null; + } + return new MetadataExtension.Builder() + .setReason(reason) + .setRequestedByRegistrar(requestedByRegistrar) + .setAnchorTenant(isAnchorTenant) + .build(); + } + + /** Returns a metadata extension for standard tool commands. */ + @Nullable + public static MetadataExtension toolMetadata( + @Nullable String reason, @Nullable Boolean requestedByRegistrar) { + return metadata(reason, requestedByRegistrar, null); + } + + /** Returns an allocation token extension for the specified token string. */ + @Nullable + public static AllocationTokenExtension allocationToken(@Nullable String token) { + return isNullOrEmpty(token) ? null : AllocationTokenExtension.create(token); + } + + /** Returns a domain update superuser extension with the specified autorenew flag. */ + @Nullable + public static DomainUpdateSuperuserExtension updateSuperuser(@Nullable Boolean autorenews) { + return autorenews == null ? null : DomainUpdateSuperuserExtension.create(autorenews); + } + + /** Returns a domain delete superuser extension for immediate deletion if requested. */ + @Nullable + public static DomainDeleteSuperuserExtension deleteSuperuser(boolean immediately) { + return immediately ? DomainDeleteSuperuserExtension.create(0, 0) : null; + } + + /** Returns a fee create extension (V12) for a single fee. */ + @Nullable + public static FeeCreateCommandExtensionV12 feeCreate(@Nullable Money cost) { + return cost == null ? null : feeCreate(cost.getCurrencyUnit(), cost.getAmount()); + } + + /** Returns a fee create extension (V12) for a single fee with a simple currency and cost. */ + @Nullable + public static FeeCreateCommandExtensionV12 feeCreate( + @Nullable CurrencyUnit currency, @Nullable BigDecimal cost) { + if (currency == null || cost == null) { + return null; + } + return new FeeCreateCommandExtensionV12.Builder() + .setCurrency(currency) + .setFees(ImmutableList.of(new Fee.Builder().setCost(cost).build())) + .build(); + } + + /** Returns a fee create extension (V06) for a single fee. */ + @Nullable + public static FeeCreateCommandExtensionV06 feeCreateV06(@Nullable Money cost) { + if (cost == null) { + return null; + } + return new FeeCreateCommandExtensionV06.Builder() + .setCurrency(cost.getCurrencyUnit()) + .setFees(ImmutableList.of(new Fee.Builder().setCost(cost.getAmount()).build())) + .build(); + } + + /** Returns a secDNS create extension with the specified DS records. */ + @Nullable + public static SecDnsCreateExtension secDnsCreate(ImmutableSet dsData) { + if (dsData.isEmpty()) { + return null; + } + return new SecDnsCreateExtension.Builder().setDsData(dsData).build(); + } + + /** Returns a secDNS update extension to replace or modify DS records. */ + @Nullable + public static SecDnsUpdateExtension secDnsUpdate( + ImmutableSet add, ImmutableSet remove, boolean removeAll) { + if (add.isEmpty() && remove.isEmpty() && !removeAll) { + return null; + } + SecDnsUpdateExtension.Builder builder = new SecDnsUpdateExtension.Builder(); + if (removeAll) { + builder.setRemove(new Remove.Builder().setAll(true).build()); + } else if (!remove.isEmpty()) { + builder.setRemove(new Remove.Builder().setDsData(remove).build()); + } + if (!add.isEmpty()) { + builder.setAdd(new Add.Builder().setDsData(add).build()); + } + return builder.build(); + } + + /** Returns a fee check extension for domain creations (V06). */ + public static FeeCheckCommandExtensionV06 feeCheckCreateV06(ImmutableList domainNames) { + return feeCheckCreateV06(domainNames, 1); + } + + /** Returns a fee check extension for domain creations (V06) with a specific period. */ + public static FeeCheckCommandExtensionV06 feeCheckCreateV06( + ImmutableList domainNames, int years) { + FeeCheckCommandExtensionV06 feeCheck = new FeeCheckCommandExtensionV06(); + ImmutableList.Builder items = new ImmutableList.Builder<>(); + for (String domainName : domainNames) { + items.add( + FeeCheckCommandExtensionItemV06.create( + domainName, + null, + FeeExtensionCommandDescriptor.create(CREATE, null, null), + Period.create(years, Period.Unit.YEARS))); + } + feeCheck.setItems(items.build()); + return feeCheck; + } + + /** Returns a launch check extension for claims. */ + public static LaunchCheckExtension launchCheckClaims() { + return LaunchCheckExtension.create(CheckType.CLAIMS, LaunchPhase.CLAIMS); + } +} diff --git a/core/src/main/java/google/registry/model/eppinput/EppInput.java b/core/src/main/java/google/registry/model/eppinput/EppInput.java index 90425f95c10..a16b6b74c49 100644 --- a/core/src/main/java/google/registry/model/eppinput/EppInput.java +++ b/core/src/main/java/google/registry/model/eppinput/EppInput.java @@ -14,12 +14,14 @@ package google.registry.model.eppinput; +import static google.registry.util.CollectionUtils.isNullOrEmpty; import static google.registry.util.CollectionUtils.nullSafeImmutableCopy; import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import com.google.common.base.Ascii; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import google.registry.model.Buildable; import google.registry.model.ImmutableObject; import google.registry.model.domain.DomainCommand; import google.registry.model.domain.bulktoken.BulkTokenExtension; @@ -60,6 +62,8 @@ import google.registry.model.eppinput.ResourceCommand.ResourceCheck; import google.registry.model.eppinput.ResourceCommand.SingleResourceCommand; import google.registry.model.host.HostCommand; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAttribute; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlElementRef; @@ -69,21 +73,26 @@ import jakarta.xml.bind.annotation.XmlEnumValue; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlSchema; +import jakarta.xml.bind.annotation.XmlTransient; import jakarta.xml.bind.annotation.XmlType; import jakarta.xml.bind.annotation.adapters.XmlAdapter; import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.Set; import javax.annotation.Nullable; /** This class represents the root EPP XML element for input. */ @XmlRootElement(name = "epp") +@XmlAccessorType(XmlAccessType.FIELD) public class EppInput extends ImmutableObject { @XmlElements({ - @XmlElement(name = "command", type = CommandWrapper.class), - @XmlElement(name = "hello", type = Hello.class) }) + @XmlElement(name = "command", type = CommandWrapper.class), + @XmlElement(name = "hello", type = Hello.class) + }) CommandWrapper commandWrapper; public CommandWrapper getCommandWrapper() { @@ -107,11 +116,11 @@ public String getCommandType() { public Optional getResourceType() { ResourceCommand resourceCommand = getResourceCommand(); if (resourceCommand != null) { - XmlSchema xmlSchemaAnnotation = - resourceCommand.getClass().getPackage().getAnnotation(XmlSchema.class); - if (xmlSchemaAnnotation != null && xmlSchemaAnnotation.xmlns().length > 0) { - return Optional.of(xmlSchemaAnnotation.xmlns()[0].prefix()); - } + XmlSchema xmlSchemaAnnotation = + resourceCommand.getClass().getPackage().getAnnotation(XmlSchema.class); + if (xmlSchemaAnnotation != null && xmlSchemaAnnotation.xmlns().length > 0) { + return Optional.of(xmlSchemaAnnotation.xmlns()[0].prefix()); + } } return Optional.empty(); } @@ -123,6 +132,9 @@ public boolean isDomainType() { @Nullable private ResourceCommand getResourceCommand() { + if (commandWrapper == null) { + return null; + } InnerCommand innerCommand = commandWrapper.getCommand(); return innerCommand instanceof ResourceCommandWrapper resourceCommandWrapper ? resourceCommandWrapper.getResourceCommand() @@ -136,7 +148,7 @@ private ResourceCommand getResourceCommand() { public Optional getSingleTargetId() { ResourceCommand resourceCommand = getResourceCommand(); return resourceCommand instanceof SingleResourceCommand singleResourceCommand - ? Optional.of(singleResourceCommand.getTargetId()) + ? Optional.ofNullable(singleResourceCommand.getTargetId()) : Optional.empty(); } @@ -147,7 +159,8 @@ public Optional getSingleTargetId() { public ImmutableList getTargetIds() { ResourceCommand resourceCommand = getResourceCommand(); if (resourceCommand instanceof SingleResourceCommand singleResourceCommand) { - return ImmutableList.of(singleResourceCommand.getTargetId()); + String targetId = singleResourceCommand.getTargetId(); + return targetId == null ? ImmutableList.of() : ImmutableList.of(targetId); } else if (resourceCommand instanceof ResourceCheck resourceCheck) { return resourceCheck.getTargetIds(); } else { @@ -157,17 +170,53 @@ public ImmutableList getTargetIds() { /** Get the extension based on type, or null. If there are multiple, it chooses the first. */ public Optional getSingleExtension(Class clazz) { - return getCommandWrapper().getExtensions().stream() + if (commandWrapper == null) { + return Optional.empty(); + } + return commandWrapper.getExtensions().stream() .filter(clazz::isInstance) .map(clazz::cast) .findFirst(); } + /** + * Static factory method to create an {@link EppInput} from an {@link InnerCommand} and + * extensions. + */ + public static EppInput create(InnerCommand command, CommandExtension... extensions) { + EppInput instance = new EppInput(); + instance.commandWrapper = new CommandWrapper(); + instance.commandWrapper.command = command; + ImmutableList validExtensions = + Arrays.stream(extensions).filter(Objects::nonNull).collect(ImmutableList.toImmutableList()); + if (!validExtensions.isEmpty()) { + instance.commandWrapper.extension = validExtensions; + } + return instance; + } + + public EppInput withClTrid(String clTrid) { + this.commandWrapper.clTrid = clTrid; + return this; + } + + /** Builder for {@link EppInput}. */ + public static class Builder extends Buildable.Builder { + public Builder setCommandWrapper(CommandWrapper commandWrapper) { + getInstance().commandWrapper = commandWrapper; + return this; + } + } + /** A tag that goes inside an EPP {@literal }. */ - public static class InnerCommand extends ImmutableObject {} + @XmlTransient + @XmlAccessorType(XmlAccessType.FIELD) + public abstract static class InnerCommand extends ImmutableObject {} /** A command that has an extension inside of it. */ - public static class ResourceCommandWrapper extends InnerCommand { + @XmlTransient + @XmlAccessorType(XmlAccessType.FIELD) + public abstract static class ResourceCommandWrapper extends InnerCommand { @XmlElementRefs({ @XmlElementRef(type = DomainCommand.Check.class), @XmlElementRef(type = DomainCommand.Create.class), @@ -189,21 +238,65 @@ public ResourceCommand getResourceCommand() { } /** Epp envelope wrapper for check on some objects. */ - public static class Check extends ResourceCommandWrapper {} + @XmlAccessorType(XmlAccessType.FIELD) + public static class Check extends ResourceCommandWrapper { + public static Check create(ResourceCommand resourceCommand) { + Check instance = new Check(); + instance.resourceCommand = resourceCommand; + return instance; + } + } /** Epp envelope wrapper for create of some object. */ - public static class Create extends ResourceCommandWrapper {} + @XmlAccessorType(XmlAccessType.FIELD) + public static class Create extends ResourceCommandWrapper { + public static Create create(ResourceCommand resourceCommand) { + Create instance = new Create(); + instance.resourceCommand = resourceCommand; + return instance; + } + + /** Builder for {@link Create}. */ + public static class Builder extends Buildable.Builder { + public Builder setResourceCommand(ResourceCommand resourceCommand) { + getInstance().resourceCommand = resourceCommand; + return this; + } + } + } /** Epp envelope wrapper for delete of some object. */ - public static class Delete extends ResourceCommandWrapper {} + @XmlAccessorType(XmlAccessType.FIELD) + public static class Delete extends ResourceCommandWrapper { + public static Delete create(ResourceCommand resourceCommand) { + Delete instance = new Delete(); + instance.resourceCommand = resourceCommand; + return instance; + } + } /** Epp envelope wrapper for info on some object. */ - public static class Info extends ResourceCommandWrapper {} + @XmlAccessorType(XmlAccessType.FIELD) + public static class Info extends ResourceCommandWrapper { + public static Info create(ResourceCommand resourceCommand) { + Info instance = new Info(); + instance.resourceCommand = resourceCommand; + return instance; + } + } /** Epp envelope wrapper for renewing some object. */ - public static class Renew extends ResourceCommandWrapper {} + @XmlAccessorType(XmlAccessType.FIELD) + public static class Renew extends ResourceCommandWrapper { + public static Renew create(ResourceCommand resourceCommand) { + Renew instance = new Renew(); + instance.resourceCommand = resourceCommand; + return instance; + } + } /** Epp envelope wrapper for transferring some object. */ + @XmlAccessorType(XmlAccessType.FIELD) public static class Transfer extends ResourceCommandWrapper { /** Enum of the possible values for the "op" attribute in transfer flows. */ @@ -230,12 +323,35 @@ public enum TransferOp { public TransferOp getTransferOp() { return transferOp; } + + public static Transfer create(TransferOp transferOp, ResourceCommand resourceCommand) { + Transfer instance = new Transfer(); + instance.transferOp = transferOp; + instance.resourceCommand = resourceCommand; + return instance; + } } /** Epp envelope wrapper for update of some object. */ - public static class Update extends ResourceCommandWrapper {} + @XmlAccessorType(XmlAccessType.FIELD) + public static class Update extends ResourceCommandWrapper { + public static Update create(ResourceCommand resourceCommand) { + Update instance = new Update(); + instance.resourceCommand = resourceCommand; + return instance; + } + + /** Builder for {@link Update}. */ + public static class Builder extends Buildable.Builder { + public Builder setResourceCommand(ResourceCommand resourceCommand) { + getInstance().resourceCommand = resourceCommand; + return this; + } + } + } /** Poll command. */ + @XmlAccessorType(XmlAccessType.FIELD) public static class Poll extends InnerCommand { /** Enum of the possible values for the "op" attribute in poll commands. */ @@ -253,19 +369,28 @@ public enum PollOp { @XmlAttribute PollOp op; - @XmlAttribute - String msgID; + @XmlAttribute(name = "msgID") + String msgId; public PollOp getPollOp() { return op; } public String getMessageId() { - return msgID; + return msgId; + } + + public static Poll create(PollOp op, @Nullable String msgId) { + Poll instance = new Poll(); + instance.op = op; + instance.msgId = msgId; + return instance; } } /** Login command. */ + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(propOrder = {"clientId", "password", "newPassword", "options", "services"}) public static class Login extends InnerCommand { @XmlElement(name = "clID") String clientId; @@ -303,10 +428,12 @@ public Services getServices() { } /** Logout command. */ + @XmlAccessorType(XmlAccessType.FIELD) public static class Logout extends InnerCommand {} /** The "command" element that holds an actual command inside of it. */ - @XmlType(propOrder = {"command", "extension", "clTRID"}) + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(propOrder = {"command", "extension", "clTrid"}) public static class CommandWrapper extends ImmutableObject { @XmlElements({ @XmlElement(name = "check", type = Check.class), @@ -376,7 +503,9 @@ public static class CommandWrapper extends ImmutableObject { @XmlElementWrapper List extension; - @Nullable String clTRID; + @XmlElement(name = "clTRID") + @Nullable + String clTrid; /** * Returns the client transaction ID. @@ -384,7 +513,7 @@ public static class CommandWrapper extends ImmutableObject { *

This is optional (i.e. it may not be specified) per RFC 5730. */ public Optional getClTrid() { - return Optional.ofNullable(clTRID); + return Optional.ofNullable(clTrid); } public InnerCommand getCommand() { @@ -394,12 +523,34 @@ public InnerCommand getCommand() { public ImmutableList getExtensions() { return nullToEmptyImmutableCopy(extension); } + + /** Builder for {@link CommandWrapper}. */ + public static class Builder extends Buildable.Builder { + + public Builder setCommand(InnerCommand command) { + getInstance().command = command; + return this; + } + + public Builder setExtensions(ImmutableList extension) { + getInstance().extension = isNullOrEmpty(extension) ? null : extension; + return this; + } + + public Builder setClTrid(String clTrid) { + getInstance().clTrid = clTrid; + return this; + } + } } /** Empty type to represent the empty "hello" command. */ + @XmlAccessorType(XmlAccessType.FIELD) public static class Hello extends CommandWrapper {} /** An options object inside of {@link Login}. */ + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(propOrder = {"version", "language"}) public static class Options extends ImmutableObject { @XmlJavaTypeAdapter(VersionAdapter.class) String version; @@ -413,6 +564,8 @@ public String getLanguage() { } /** A services object inside of {@link Login}. */ + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(propOrder = {"objectServices", "serviceExtensions"}) public static class Services extends ImmutableObject { @XmlElement(name = "objURI") Set objectServices; @@ -431,15 +584,15 @@ public ImmutableSet getServiceExtensions() { } /** - * RFC 5730 says we should check the version and return special error code 2100 if it isn't - * what we support, but it also specifies a schema that only allows 1.0 in the version field, so - * any other version doesn't validate. As a result, if we didn't do this here it would throw a - * {@code SyntaxErrorException} when it failed to validate. + * RFC 5730 says we should check the version and return special error code 2100 if it isn't what + * we support, but it also specifies a schema that only allows 1.0 in the version field, so any + * other version doesn't validate. As a result, if we didn't do this here it would throw a {@code + * SyntaxErrorException} when it failed to validate. * - * @see - * RFC 5730 - EPP - Command error responses + * @see RFC 5730 - EPP - Command error + * responses */ - public static class VersionAdapter extends XmlAdapter { + public static class VersionAdapter extends XmlAdapter { @Override public String unmarshal(String version) throws Exception { if (!"1.0".equals(version)) { @@ -449,8 +602,8 @@ public String unmarshal(String version) throws Exception { } @Override - public String marshal(String ignored) { - throw new UnsupportedOperationException(); + public String marshal(String version) { + return version; } } diff --git a/core/src/main/java/google/registry/model/eppinput/ResourceCommand.java b/core/src/main/java/google/registry/model/eppinput/ResourceCommand.java index de81fce7d32..369204ee43d 100644 --- a/core/src/main/java/google/registry/model/eppinput/ResourceCommand.java +++ b/core/src/main/java/google/registry/model/eppinput/ResourceCommand.java @@ -15,7 +15,6 @@ package google.registry.model.eppinput; import static google.registry.util.CollectionUtils.nullSafeImmutableCopy; -import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -24,41 +23,50 @@ import google.registry.model.ImmutableObject; import google.registry.model.eppcommon.AuthInfo; import google.registry.model.eppcommon.StatusValue; -import google.registry.model.eppinput.ResourceCommand.ResourceUpdate.AddRemove; import google.registry.util.TypeUtils.TypeInstantiator; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlElements; import jakarta.xml.bind.annotation.XmlTransient; import java.util.List; -import java.util.Set; /** Commands for EPP resources. */ public interface ResourceCommand { - /** - * A command for a single {@link EppResource}. - * - *

In general commands should extend {@link AbstractSingleResourceCommand} instead of - * implementing this directly, but "Create" commands can't do that since they need to inherit from - * a base class that gives them all of the resource's fields. The domain "Info" command also can't - * do that since it's "name" field is overloaded with a "hosts" attribute. - */ + /** Interface for EPP commands that operate on a single resource. */ interface SingleResourceCommand extends ResourceCommand { + @Override String getTargetId(); + @Override AuthInfo getAuthInfo(); } + /** Returns the target ID for single-resource commands, or null otherwise. */ + default String getTargetId() { + return null; + } + + /** Returns the auth info for single-resource commands, or null otherwise. */ + default AuthInfo getAuthInfo() { + return null; + } + /** Abstract implementation of {@link ResourceCommand}. */ @XmlTransient - abstract class AbstractSingleResourceCommand extends ImmutableObject + @XmlAccessorType(XmlAccessType.FIELD) + public abstract class AbstractSingleResourceCommand extends ImmutableObject implements SingleResourceCommand { - @XmlElements({ - @XmlElement(name = "id"), - @XmlElement(name = "name") }) - String targetId; + + @XmlTransient public String targetId; + + public void setTargetId(String targetId) { + this.targetId = targetId; + } @Override + @XmlTransient public String getTargetId() { return targetId; } @@ -71,11 +79,14 @@ public AuthInfo getAuthInfo() { /** A check command for an {@link EppResource}. */ @XmlTransient - class ResourceCheck extends ImmutableObject implements ResourceCommand { - @XmlElements({ - @XmlElement(name = "id"), - @XmlElement(name = "name") }) - List targetUniqueIds; + @XmlAccessorType(XmlAccessType.FIELD) + public class ResourceCheck extends ImmutableObject implements ResourceCommand { + @XmlElements({@XmlElement(name = "id"), @XmlElement(name = "name")}) + public List targetUniqueIds; + + public void setTargetIds(ImmutableList targetUniqueIds) { + this.targetUniqueIds = targetUniqueIds; + } public ImmutableList getTargetIds() { return nullSafeImmutableCopy(targetUniqueIds); @@ -83,7 +94,7 @@ public ImmutableList getTargetIds() { } /** A create command, or the inner change (as opposed to add or remove) part of an update. */ - interface ResourceCreateOrChange> {} + public interface ResourceCreateOrChange> {} /** * An update command for an {@link EppResource}. @@ -92,21 +103,19 @@ interface ResourceCreateOrChange> {} * @param the change type */ @XmlTransient - abstract class ResourceUpdate< - A extends AddRemove, + public abstract class ResourceUpdate< + A extends ResourceUpdate.AddRemove, B extends EppResource.Builder, C extends ResourceCreateOrChange> extends AbstractSingleResourceCommand { /** Part of an update command that specifies set values to add or remove. */ @XmlTransient + @XmlAccessorType(XmlAccessType.FIELD) public abstract static class AddRemove extends ImmutableObject { - @XmlElement(name = "status") - Set statusValues; + public abstract void setStatusValues(ImmutableSet statusValues); - public ImmutableSet getStatusValues() { - return nullToEmptyImmutableCopy(statusValues); - } + public abstract ImmutableSet getStatusValues(); } protected abstract C getNullableInnerChange(); diff --git a/core/src/main/java/google/registry/model/host/HostCommand.java b/core/src/main/java/google/registry/model/host/HostCommand.java index b1baa0f681a..2fadaa5e0ea 100644 --- a/core/src/main/java/google/registry/model/host/HostCommand.java +++ b/core/src/main/java/google/registry/model/host/HostCommand.java @@ -14,14 +14,18 @@ package google.registry.model.host; -import static google.registry.util.CollectionUtils.nullSafeImmutableCopy; +import static google.registry.util.CollectionUtils.isNullOrEmpty; import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import com.google.common.collect.ImmutableSet; +import google.registry.model.Buildable; +import google.registry.model.eppcommon.StatusValue; import google.registry.model.eppinput.ResourceCommand.AbstractSingleResourceCommand; import google.registry.model.eppinput.ResourceCommand.ResourceCheck; import google.registry.model.eppinput.ResourceCommand.ResourceCreateOrChange; import google.registry.model.eppinput.ResourceCommand.ResourceUpdate; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlTransient; @@ -32,39 +36,95 @@ /** A collection of {@link Host} commands. */ public class HostCommand { - /** The fields on "chgType" from RFC5732. */ + /** The fields on "chgType" from RFC5732. */ @XmlTransient - abstract static class HostCreateOrChange extends AbstractSingleResourceCommand + @XmlAccessorType(XmlAccessType.FIELD) + public abstract static class HostCreateOrChange extends AbstractSingleResourceCommand implements ResourceCreateOrChange { + + @XmlElement(name = "name") + String name; + + @Override + public String getTargetId() { + return name; + } + + @Override + public void setTargetId(String targetId) { + this.name = targetId; + } + public String getHostName() { - return getTargetId(); + return name; } } /** * A create command for a {@link Host}, mapping "createType" from RFC5732. + * href="https://tools.ietf.org/html/rfc5732">RFC5732. */ - @XmlType(propOrder = {"targetId", "inetAddresses"}) + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(propOrder = {"name", "inetAddresses"}) @XmlRootElement - public static class Create extends HostCreateOrChange - implements ResourceCreateOrChange { + public static class Create extends HostCreateOrChange { /** IP Addresses for this host. Can be null if this is an external host. */ @XmlElement(name = "addr") Set inetAddresses; public ImmutableSet getInetAddresses() { - return nullSafeImmutableCopy(inetAddresses); + return nullToEmptyImmutableCopy(inetAddresses); + } + + /** Builder for {@link Create}. */ + public static class Builder extends Buildable.Builder { + public Builder setTargetId(String targetId) { + getInstance().setTargetId(targetId); + return this; + } + + public Builder setInetAddresses(ImmutableSet inetAddresses) { + getInstance().inetAddresses = inetAddresses; + return this; + } } } /** A delete command for a {@link Host}. */ @XmlRootElement - public static class Delete extends AbstractSingleResourceCommand {} + @XmlAccessorType(XmlAccessType.FIELD) + public static class Delete extends AbstractSingleResourceCommand { + @XmlElement(name = "name") + String name; + + @Override + public String getTargetId() { + return name; + } + + @Override + public void setTargetId(String targetId) { + this.name = targetId; + } + } /** An info request for a {@link Host}. */ @XmlRootElement - public static class Info extends AbstractSingleResourceCommand {} + @XmlAccessorType(XmlAccessType.FIELD) + public static class Info extends AbstractSingleResourceCommand { + @XmlElement(name = "name") + String name; + + @Override + public String getTargetId() { + return name; + } + + @Override + public void setTargetId(String targetId) { + this.name = targetId; + } + } /** A check request for {@link Host}. */ @XmlRootElement @@ -72,17 +132,32 @@ public static class Check extends ResourceCheck {} /** An update to a {@link Host}. */ @XmlRootElement - @XmlType(propOrder = {"targetId", "innerAdd", "innerRemove", "innerChange"}) - public static class Update extends ResourceUpdate { + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(propOrder = {"name", "innerAdd", "innerRemove", "innerChange"}) + public static class Update + extends ResourceUpdate { + + @XmlElement(name = "name") + String name; + + @Override + public String getTargetId() { + return name; + } + + @Override + public void setTargetId(String targetId) { + this.name = targetId; + } @XmlElement(name = "chg") protected Change innerChange; @XmlElement(name = "add") - protected AddRemove innerAdd; + protected HostAddRemove innerAdd; @XmlElement(name = "rem") - protected AddRemove innerRemove; + protected HostAddRemove innerRemove; @Override protected Change getNullableInnerChange() { @@ -90,28 +165,55 @@ protected Change getNullableInnerChange() { } @Override - protected AddRemove getNullableInnerAdd() { + protected HostAddRemove getNullableInnerAdd() { return innerAdd; } @Override - protected AddRemove getNullableInnerRemove() { + protected HostAddRemove getNullableInnerRemove() { return innerRemove; } /** The add/remove type on a host update command. */ - @XmlType(propOrder = { "inetAddresses", "statusValues" }) - public static class AddRemove extends ResourceUpdate.AddRemove { + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(propOrder = {"inetAddresses", "statusValues"}) + public static class HostAddRemove extends ResourceUpdate.AddRemove { /** IP Addresses for this host. Can be null if this is an external host. */ @XmlElement(name = "addr") Set inetAddresses; + @XmlElement(name = "status") + Set statusValues; + + @Override + public void setStatusValues(ImmutableSet statusValues) { + this.statusValues = statusValues; + } + + @Override + public ImmutableSet getStatusValues() { + return nullToEmptyImmutableCopy(statusValues); + } + public ImmutableSet getInetAddresses() { return nullToEmptyImmutableCopy(inetAddresses); } + + /** Builder for {@link HostAddRemove}. */ + public static class Builder extends Buildable.Builder { + public Builder setInetAddresses(ImmutableSet inetAddresses) { + getInstance().inetAddresses = isNullOrEmpty(inetAddresses) ? null : inetAddresses; + return this; + } + + public Builder setStatusValues(ImmutableSet statusValues) { + getInstance().statusValues = isNullOrEmpty(statusValues) ? null : statusValues; + return this; + } + } } - /** The inner change type on a host update command. */ + @XmlAccessorType(XmlAccessType.FIELD) public static class Change extends HostCreateOrChange {} } } diff --git a/core/src/main/java/google/registry/reporting/spec11/PublishSpec11ReportAction.java b/core/src/main/java/google/registry/reporting/spec11/PublishSpec11ReportAction.java index fe38f03f9f1..0cf62abaa27 100644 --- a/core/src/main/java/google/registry/reporting/spec11/PublishSpec11ReportAction.java +++ b/core/src/main/java/google/registry/reporting/spec11/PublishSpec11ReportAction.java @@ -24,7 +24,6 @@ import com.google.api.services.dataflow.Dataflow; import com.google.api.services.dataflow.model.Job; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; @@ -32,11 +31,10 @@ import com.google.common.collect.Sets; import com.google.common.flogger.FluentLogger; import com.google.common.net.MediaType; -import com.google.template.soy.parseinfo.SoyTemplateInfo; import google.registry.beam.spec11.ThreatMatch; import google.registry.config.RegistryConfig.Config; import google.registry.reporting.ReportingModule; -import google.registry.reporting.spec11.soy.Spec11EmailSoyInfo; +import google.registry.reporting.spec11.Spec11EmailUtils.Spec11EmailTemplate; import google.registry.request.Action; import google.registry.request.Parameter; import google.registry.request.Response; @@ -45,14 +43,13 @@ import java.io.IOException; import java.time.LocalDate; import java.util.Optional; -import java.util.Set; import org.json.JSONException; /** * Retries until a {@code Dataflow} job with a given {@code jobId} completes, continuing the Spec11 * pipeline accordingly. * - *

This calls {@link Spec11EmailUtils#emailSpec11Reports(LocalDate, SoyTemplateInfo, String, + *

This calls {@link Spec11EmailUtils#emailSpec11Reports(LocalDate, Spec11EmailTemplate, String, * ImmutableSet)} on success or {@link Spec11EmailUtils#sendAlertEmail(String, String)} on failure. */ @Action( @@ -134,7 +131,7 @@ public void run() { String.format("Spec11 %s job %s ended in status failure.", date, jobId)); } default -> { - logger.atInfo().log("Job in non-terminal state %s, retrying:", state); + logger.atInfo().log("Job in non-terminal state %s, retrying.", state); response.setStatus(SC_SERVICE_UNAVAILABLE); } } @@ -153,8 +150,7 @@ private void sendMonthlyEmail() throws IOException, JSONException { ImmutableSet monthlyMatchesSet = spec11RegistrarThreatMatchesParser.getRegistrarThreatMatches(date); String subject = String.format("%s Monthly Threat Detector [%s]", registryName, date); - emailUtils.emailSpec11Reports( - date, Spec11EmailSoyInfo.MONTHLY_SPEC_11_EMAIL, subject, monthlyMatchesSet); + emailUtils.emailSpec11Reports(date, Spec11EmailTemplate.MONTHLY, subject, monthlyMatchesSet); } private void processDailyDiff(LocalDate previousDate) throws IOException, JSONException { @@ -165,7 +161,7 @@ private void processDailyDiff(LocalDate previousDate) throws IOException, JSONEx String dailySubject = String.format("%s Daily Threat Detector [%s]", registryName, date); emailUtils.emailSpec11Reports( date, - Spec11EmailSoyInfo.DAILY_SPEC_11_EMAIL, + Spec11EmailTemplate.DAILY, dailySubject, getNewMatches(previousMatches, currentMatches)); } @@ -173,19 +169,20 @@ private void processDailyDiff(LocalDate previousDate) throws IOException, JSONEx private ImmutableSet getNewMatches( ImmutableSet previousMatchesSet, ImmutableSet currentMatchesSet) { - ImmutableMap> previousMatchesByEmail = + ImmutableMap> previousMatchesByRegistrarId = groupByKeyAndFlatMap(previousMatchesSet); - ImmutableMap> currentMatchesByEmail = + ImmutableMap> currentMatchesByRegistrarId = groupByKeyAndFlatMap(currentMatchesSet); ImmutableSet.Builder resultsBuilder = ImmutableSet.builder(); - for (String email : currentMatchesByEmail.keySet()) { + for (String registrarId : currentMatchesByRegistrarId.keySet()) { // Only include matches in the result if they're non-empty - Set difference = - Sets.difference( - currentMatchesByEmail.get(email), - previousMatchesByEmail.getOrDefault(email, ImmutableSet.of())); + ImmutableSet difference = + ImmutableSet.copyOf( + Sets.difference( + currentMatchesByRegistrarId.get(registrarId), + previousMatchesByRegistrarId.getOrDefault(registrarId, ImmutableSet.of()))); if (!difference.isEmpty()) { - resultsBuilder.add(RegistrarThreatMatches.create(email, ImmutableList.copyOf(difference))); + resultsBuilder.add(RegistrarThreatMatches.create(registrarId, difference.asList())); } } return resultsBuilder.build(); @@ -193,13 +190,13 @@ private ImmutableSet getNewMatches( private ImmutableMap> groupByKeyAndFlatMap( ImmutableSet registrarThreatMatches) { - // Group by email address then flat-map all of the ThreatMatch objects together + // Group by registrarId then flat-map all of the ThreatMatch objects together return ImmutableMap.copyOf( Maps.transformValues( - Multimaps.index(registrarThreatMatches, RegistrarThreatMatches::clientId).asMap(), + Multimaps.index(registrarThreatMatches, RegistrarThreatMatches::registrarId).asMap(), registrarThreatMatchesCollection -> registrarThreatMatchesCollection.stream() - .flatMap(matches -> matches.threatMatches().stream()) + .flatMap(rtm -> rtm.threatMatches().stream()) .collect(toImmutableSet()))); } diff --git a/core/src/main/java/google/registry/reporting/spec11/RegistrarThreatMatches.java b/core/src/main/java/google/registry/reporting/spec11/RegistrarThreatMatches.java index 6ad15633976..257cb6affd5 100644 --- a/core/src/main/java/google/registry/reporting/spec11/RegistrarThreatMatches.java +++ b/core/src/main/java/google/registry/reporting/spec11/RegistrarThreatMatches.java @@ -16,12 +16,17 @@ import com.google.common.collect.ImmutableList; import google.registry.beam.spec11.ThreatMatch; -import java.util.List; -/** Value record representing the registrar and list-of-threat-matches pair stored in GCS. */ -public record RegistrarThreatMatches(String clientId, ImmutableList threatMatches) { - - static RegistrarThreatMatches create(String clientId, List threatMatches) { - return new RegistrarThreatMatches(clientId, ImmutableList.copyOf(threatMatches)); +/** + * A value record representing a registrar and its associated list of threat matches. + * + * @param registrarId the ID of the registrar + * @param threatMatches the list of {@link ThreatMatch} objects associated with the registrar + */ +public record RegistrarThreatMatches(String registrarId, ImmutableList threatMatches) { + /** Creates a new {@link RegistrarThreatMatches} instance. */ + static RegistrarThreatMatches create( + String registrarId, ImmutableList threatMatches) { + return new RegistrarThreatMatches(registrarId, threatMatches); } } diff --git a/core/src/main/java/google/registry/reporting/spec11/Spec11EmailUtils.java b/core/src/main/java/google/registry/reporting/spec11/Spec11EmailUtils.java index 2d105a7302e..61edcdd5a9d 100644 --- a/core/src/main/java/google/registry/reporting/spec11/Spec11EmailUtils.java +++ b/core/src/main/java/google/registry/reporting/spec11/Spec11EmailUtils.java @@ -16,7 +16,6 @@ import static com.google.common.base.Throwables.getRootCause; import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.io.Resources.getResource; import static google.registry.persistence.transaction.QueryComposer.Comparator; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; @@ -25,41 +24,46 @@ import com.google.common.collect.ImmutableSet; import com.google.common.flogger.FluentLogger; import com.google.common.net.MediaType; -import com.google.template.soy.SoyFileSet; -import com.google.template.soy.parseinfo.SoyTemplateInfo; -import com.google.template.soy.tofu.SoyTofu; -import com.google.template.soy.tofu.SoyTofu.Renderer; import google.registry.beam.spec11.ThreatMatch; import google.registry.config.RegistryConfig.Config; import google.registry.groups.GmailClient; import google.registry.model.domain.Domain; import google.registry.model.registrar.Registrar; import google.registry.model.registrar.RegistrarPoc; -import google.registry.reporting.spec11.soy.Spec11EmailSoyInfo; import google.registry.util.EmailMessage; import google.registry.util.Sleeper; +import google.registry.util.TemplateRenderer; import jakarta.inject.Inject; import jakarta.mail.MessagingException; import jakarta.mail.internet.InternetAddress; import java.time.Duration; import java.time.LocalDate; -import java.util.List; import java.util.Map; /** Provides e-mail functionality for Spec11 tasks, such as sending Spec11 reports to registrars. */ public class Spec11EmailUtils { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); - private static final SoyTofu SOY_SAUCE = - SoyFileSet.builder() - .add( - getResource( - Spec11EmailSoyInfo.getInstance().getClass(), - Spec11EmailSoyInfo.getInstance().getFileName())) - .build() - .compileToTofu(); + + /** Enum of Spec11 email templates. */ + public enum Spec11EmailTemplate { + DAILY("daily_spec11_email.ftl"), + MONTHLY("monthly_spec11_email.ftl"); + + private final String ftlPath; + + Spec11EmailTemplate(String ftlPath) { + this.ftlPath = "google/registry/reporting/spec11/ftl/" + ftlPath; + } + + public String getFtlPath() { + return ftlPath; + } + } + private final GmailClient gmailClient; private final Sleeper sleeper; + private final TemplateRenderer templateRenderer; private final Duration emailThrottleDuration; private final InternetAddress outgoingEmailAddress; private final ImmutableList spec11BccEmailAddresses; @@ -71,6 +75,7 @@ public class Spec11EmailUtils { Spec11EmailUtils( GmailClient gmailClient, Sleeper sleeper, + TemplateRenderer templateRenderer, @Config("emailThrottleDuration") Duration emailThrottleDuration, @Config("newAlertRecipientEmailAddress") InternetAddress alertRecipientAddress, @Config("spec11OutgoingEmailAddress") InternetAddress spec11OutgoingEmailAddress, @@ -79,6 +84,7 @@ public class Spec11EmailUtils { @Config("registryName") String registryName) { this.gmailClient = gmailClient; this.sleeper = sleeper; + this.templateRenderer = templateRenderer; this.emailThrottleDuration = emailThrottleDuration; this.outgoingEmailAddress = spec11OutgoingEmailAddress; this.spec11BccEmailAddresses = spec11BccEmailAddresses; @@ -88,12 +94,18 @@ public class Spec11EmailUtils { } /** - * Processes a list of registrar/list-of-threat pairings and sends a notification email to the - * appropriate address. + * Processes a list of registrar/list-of-threat pairings and sends notification emails to the + * appropriate addresses. + * + * @param date the date the report was generated + * @param template the email template to use + * @param subject the subject line for the emails + * @param registrarThreatMatchesSet a set of {@link RegistrarThreatMatches} to be emailed + * @throws RuntimeException if emailing fails for one or more registrars */ void emailSpec11Reports( LocalDate date, - SoyTemplateInfo soyTemplateInfo, + Spec11EmailTemplate template, String subject, ImmutableSet registrarThreatMatchesSet) { ImmutableMap.Builder failedMatchesBuilder = @@ -108,14 +120,15 @@ void emailSpec11Reports( try { // Handle exceptions individually per registrar so that one failed email doesn't prevent // the rest from being sent. - emailRegistrar(date, soyTemplateInfo, subject, filteredMatches); + emailRegistrar(date, template, subject, filteredMatches); numRegistrarsEmailed++; } catch (Throwable e) { failedMatchesBuilder.put(registrarThreatMatches, getRootCause(e)); } } } - logger.atInfo().log("Emailed daily diffs to %s registrars.", numRegistrarsEmailed); + logger.atInfo().log("Emailed Spec11 reports to %s registrars.", numRegistrarsEmailed); + ImmutableMap failedMatches = failedMatchesBuilder.build(); if (!failedMatches.isEmpty()) { ImmutableList> failedMatchesList = @@ -130,7 +143,7 @@ void emailSpec11Reports( logger.atSevere().withCause(failedMatchesList.get(i).getValue()).log( "Additional exception thrown when sending email to registrar %s, in addition to the" + " re-thrown exception.", - failedMatchesList.get(i).getKey().clientId()); + failedMatchesList.get(i).getKey().registrarId()); } throw new RuntimeException( "Emailing Spec11 reports failed, first exception:", firstThrowable); @@ -144,43 +157,49 @@ private RegistrarThreatMatches filterOutNonPublishedMatches( RegistrarThreatMatches registrarThreatMatches) { ImmutableList filteredMatches = tm().transact( - () -> { - return registrarThreatMatches.threatMatches().stream() - .filter( - threatMatch -> - tm() - .createQueryComposer(Domain.class) - .where("domainName", Comparator.EQ, threatMatch.domainName()) - .stream() - .anyMatch(Domain::shouldPublishToDns)) - .collect(toImmutableList()); - }); - return RegistrarThreatMatches.create(registrarThreatMatches.clientId(), filteredMatches); + () -> + registrarThreatMatches.threatMatches().stream() + .filter( + threatMatch -> + tm() + .createQueryComposer(Domain.class) + .where("domainName", Comparator.EQ, threatMatch.domainName()) + .stream() + .anyMatch(Domain::shouldPublishToDns)) + .collect(toImmutableList())); + return RegistrarThreatMatches.create(registrarThreatMatches.registrarId(), filteredMatches); } private void emailRegistrar( LocalDate date, - SoyTemplateInfo soyTemplateInfo, + Spec11EmailTemplate template, String subject, RegistrarThreatMatches registrarThreatMatches) throws MessagingException { gmailClient.sendEmail( EmailMessage.newBuilder() .setSubject(subject) - .setBody(getEmailBody(date, soyTemplateInfo, registrarThreatMatches)) + .setBody(getEmailBody(date, template, registrarThreatMatches)) .setContentType(MediaType.HTML_UTF_8) - .addRecipient(getEmailAddressForRegistrar(registrarThreatMatches.clientId())) + .addRecipient(getEmailAddressForRegistrar(registrarThreatMatches.registrarId())) .setBccs(spec11BccEmailAddresses) .build()); } + /** + * Renders the email body using the specified template and registrar threat matches. + * + * @param date the date the report was generated + * @param template the email template to use + * @param registrarThreatMatches the matches for a specific registrar + * @return the rendered email body as an HTML string + */ private String getEmailBody( - LocalDate date, - SoyTemplateInfo soyTemplateInfo, - RegistrarThreatMatches registrarThreatMatches) { - Renderer renderer = SOY_SAUCE.newRenderer(soyTemplateInfo); - // Soy templates require that data be in raw map/list form. - List> threatMatchMap = + LocalDate date, Spec11EmailTemplate template, RegistrarThreatMatches registrarThreatMatches) { + // FreeMarker templates require that data be in raw map/list form or bean-style POJOs. + // We convert the ThreatMatch records to maps here to ensure compatibility and to + // apply email-safe domain name transformations. + ImmutableList> threatMatchMap = registrarThreatMatches.threatMatches().stream() .map( threatMatch -> @@ -189,15 +208,14 @@ private String getEmailBody( "threatType", threatMatch.threatType())) .collect(toImmutableList()); - Map data = + ImmutableMap data = ImmutableMap.of( "date", date.toString(), "registry", registryName, "replyToEmail", outgoingEmailAddress.getAddress(), "threats", threatMatchMap, "resources", spec11WebResources); - renderer.setData(data); - return renderer.render(); + return templateRenderer.render(template.getFtlPath(), data); } // Mutates a known bad domain to pass spam checks by Email sender and clients, as suggested by diff --git a/core/src/main/java/google/registry/tools/CheckDomainClaimsCommand.java b/core/src/main/java/google/registry/tools/CheckDomainClaimsCommand.java index c1b08d97470..27b78325276 100644 --- a/core/src/main/java/google/registry/tools/CheckDomainClaimsCommand.java +++ b/core/src/main/java/google/registry/tools/CheckDomainClaimsCommand.java @@ -16,10 +16,12 @@ import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; -import com.google.template.soy.data.SoyMapData; import google.registry.config.RegistryConfig.Config; -import google.registry.tools.soy.DomainCheckClaimsSoyInfo; +import google.registry.model.domain.DomainCommand; +import google.registry.model.eppinput.EppExtensions; +import google.registry.model.eppinput.EppInput; import jakarta.inject.Inject; import java.util.Collection; import java.util.List; @@ -50,11 +52,15 @@ void initEppToolCommand() { clientId = registryAdminClientId; } - Multimap domainNameMap = validateAndGroupDomainNamesByTld(mainParameters); + Multimap domainNameMap = + validateAndGroupDomainNamesByTld(ImmutableList.copyOf(mainParameters)); for (Collection values : domainNameMap.asMap().values()) { - setSoyTemplate( - DomainCheckClaimsSoyInfo.getInstance(), DomainCheckClaimsSoyInfo.DOMAINCHECKCLAIMS); - addSoyRecord(clientId, new SoyMapData("domainNames", values)); + DomainCommand.Check checkCommand = new DomainCommand.Check(); + checkCommand.setTargetIds(ImmutableList.copyOf(values)); + addEppInput( + clientId, + EppInput.create(EppInput.Check.create(checkCommand), EppExtensions.launchCheckClaims()) + .withClTrid("RegistryTool")); } } } diff --git a/core/src/main/java/google/registry/tools/CheckDomainCommand.java b/core/src/main/java/google/registry/tools/CheckDomainCommand.java index 76d5f060602..2db44e1a858 100644 --- a/core/src/main/java/google/registry/tools/CheckDomainCommand.java +++ b/core/src/main/java/google/registry/tools/CheckDomainCommand.java @@ -14,14 +14,14 @@ package google.registry.tools; -import static com.google.common.base.Strings.isNullOrEmpty; - import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; -import com.google.template.soy.data.SoyMapData; import google.registry.config.RegistryConfig.Config; -import google.registry.tools.soy.DomainCheckSoyInfo; +import google.registry.model.domain.DomainCommand; +import google.registry.model.eppinput.EppExtensions; +import google.registry.model.eppinput.EppInput; import jakarta.inject.Inject; import java.util.Collection; import java.util.List; @@ -57,14 +57,20 @@ void initEppToolCommand() { clientId = registryAdminClientId; } - Multimap domainNameMap = validateAndGroupDomainNamesByTld(mainParameters); + Multimap domainNameMap = + validateAndGroupDomainNamesByTld(ImmutableList.copyOf(mainParameters)); for (Collection values : domainNameMap.asMap().values()) { - setSoyTemplate(DomainCheckSoyInfo.getInstance(), DomainCheckSoyInfo.DOMAINCHECK); - SoyMapData soyMapData = new SoyMapData("domainNames", values); - if (!isNullOrEmpty(allocationToken)) { - soyMapData.put("allocationToken", allocationToken); - } - addSoyRecord(clientId, soyMapData); + ImmutableList domainNames = ImmutableList.copyOf(values); + DomainCommand.Check checkCommand = new DomainCommand.Check(); + checkCommand.setTargetIds(domainNames); + + addEppInput( + clientId, + EppInput.create( + EppInput.Check.create(checkCommand), + EppExtensions.feeCheckCreateV06(domainNames), + EppExtensions.allocationToken(allocationToken)) + .withClTrid("RegistryTool")); } } } diff --git a/core/src/main/java/google/registry/tools/CreateAnchorTenantCommand.java b/core/src/main/java/google/registry/tools/CreateAnchorTenantCommand.java index 626e08f7a2c..8bde7887e1d 100644 --- a/core/src/main/java/google/registry/tools/CreateAnchorTenantCommand.java +++ b/core/src/main/java/google/registry/tools/CreateAnchorTenantCommand.java @@ -23,8 +23,12 @@ import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import com.google.common.net.InternetDomainName; -import com.google.template.soy.data.SoyMapData; -import google.registry.tools.soy.CreateAnchorTenantSoyInfo; +import google.registry.model.domain.DomainAuthInfo; +import google.registry.model.domain.DomainCommand; +import google.registry.model.domain.Period; +import google.registry.model.eppcommon.AuthInfo.PasswordAuth; +import google.registry.model.eppinput.EppExtensions; +import google.registry.model.eppinput.EppInput; import google.registry.util.StringGenerator; import jakarta.inject.Inject; import jakarta.inject.Named; @@ -48,10 +52,12 @@ final class CreateAnchorTenantCommand extends MutatingEppToolCommand { required = true) private String domainName; + @SuppressWarnings("UnusedVariable") @Parameter( names = {"--contact"}, - description = "Contact ID for the request. This will be used for registrant, admin contact, " - + "and tech contact.", + description = + "Contact ID for the request. This will be used for registrant, admin contact, " + + "and tech contact.", required = true) private String contact; @@ -87,15 +93,18 @@ protected void initMutatingEppToolCommand() { cost = getDomainCreateCost(domainName, clock.now(), DEFAULT_ANCHOR_TENANT_PERIOD_YEARS); } - setSoyTemplate(CreateAnchorTenantSoyInfo.getInstance(), - CreateAnchorTenantSoyInfo.CREATEANCHORTENANT); - addSoyRecord(clientId, new SoyMapData( - "domainName", domainName, - "contactId", contact, - "reason", reason, - "password", password, - "period", DEFAULT_ANCHOR_TENANT_PERIOD_YEARS, - "feeCurrency", cost != null ? cost.getCurrencyUnit().toString() : null, - "fee", cost != null ? cost.getAmount().toString() : null)); + DomainCommand.Create.Builder createBuilder = + new DomainCommand.Create.Builder() + .setDomainName(domainName) + .setAuthInfo(DomainAuthInfo.create(PasswordAuth.create(password))) + .setPeriod(Period.create(DEFAULT_ANCHOR_TENANT_PERIOD_YEARS, Period.Unit.YEARS)); + + addEppInput( + clientId, + EppInput.create( + EppInput.Create.create(createBuilder.build()), + EppExtensions.metadata(reason, false, true), + EppExtensions.feeCreateV06(cost)) + .withClTrid("RegistryTool")); } } diff --git a/core/src/main/java/google/registry/tools/CreateDomainCommand.java b/core/src/main/java/google/registry/tools/CreateDomainCommand.java index af4af51219d..1c2f59e5c4a 100644 --- a/core/src/main/java/google/registry/tools/CreateDomainCommand.java +++ b/core/src/main/java/google/registry/tools/CreateDomainCommand.java @@ -20,12 +20,20 @@ import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; -import com.google.template.soy.data.SoyMapData; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSortedSet; +import google.registry.model.domain.DomainAuthInfo; +import google.registry.model.domain.DomainCommand; +import google.registry.model.domain.Period; +import google.registry.model.eppcommon.AuthInfo.PasswordAuth; +import google.registry.model.eppinput.EppExtensions; +import google.registry.model.eppinput.EppInput; import google.registry.model.pricing.PremiumPricingEngine.DomainPrices; -import google.registry.tools.soy.DomainCreateSoyInfo; import google.registry.util.StringGenerator; import jakarta.inject.Inject; import jakarta.inject.Named; +import java.math.BigDecimal; +import org.joda.money.CurrencyUnit; import org.joda.money.Money; /** A command to create a new domain via EPP. */ @@ -60,8 +68,8 @@ protected void initMutatingEppToolCommand() { } for (String domain : domains) { - String currency = null; - String cost = null; + CurrencyUnit currency = null; + BigDecimal cost = null; DomainPrices prices = getPricesForDomainName(domain, clock.now()); // Check if the domain is premium and set the fee on the create command if so. @@ -70,29 +78,32 @@ protected void initMutatingEppToolCommand() { !force || forcePremiums, "Forced creates on premium domain(s) require --force_premiums"); Money createCost = prices.getCreateCost(); - currency = createCost.getCurrencyUnit().getCode(); - cost = createCost.multipliedBy(period).getAmount().toString(); + currency = createCost.getCurrencyUnit(); + cost = createCost.multipliedBy(period).getAmount(); printStream.printf( "NOTE: %s is premium at %s per year; sending total cost for %d year(s) of %s %s.\n", domain, createCost, period, currency, cost); } - setSoyTemplate(DomainCreateSoyInfo.getInstance(), DomainCreateSoyInfo.DOMAINCREATE); - SoyMapData soyMapData = - new SoyMapData( - "domain", domain, - "period", period, - "nameservers", nameservers, - "password", password, - "currency", currency, - "price", cost, - "dsRecords", DsRecord.convertToSoy(dsRecords), - "reason", reason, - "allocationToken", allocationToken); - if (requestedByRegistrar != null) { - soyMapData.put("requestedByRegistrar", requestedByRegistrar.toString()); - } - addSoyRecord(clientId, soyMapData); + DomainCommand.Create.Builder createBuilder = + new DomainCommand.Create.Builder() + .setDomainName(domain) + .setAuthInfo(DomainAuthInfo.create(PasswordAuth.create(password))) + .setPeriod(Period.create(period, Period.Unit.YEARS)) + .setNameserverHostNames(ImmutableSortedSet.copyOf(nameservers)); + + addEppInput( + clientId, + EppInput.create( + EppInput.Create.create(createBuilder.build()), + EppExtensions.feeCreate(currency, cost), + EppExtensions.secDnsCreate( + dsRecords.stream() + .map(DsRecord::toDsData) + .collect(ImmutableSet.toImmutableSet())), + EppExtensions.toolMetadata(reason, requestedByRegistrar), + EppExtensions.allocationToken(allocationToken)) + .withClTrid("RegistryTool")); } } } diff --git a/core/src/main/java/google/registry/tools/CreateHostCommand.java b/core/src/main/java/google/registry/tools/CreateHostCommand.java index 55b129b8fdb..a328902be71 100644 --- a/core/src/main/java/google/registry/tools/CreateHostCommand.java +++ b/core/src/main/java/google/registry/tools/CreateHostCommand.java @@ -18,14 +18,14 @@ import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; -import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSortedSet; import com.google.common.net.InetAddresses; -import com.google.template.soy.data.SoyMapData; -import google.registry.tools.soy.HostCreateSoyInfo; +import google.registry.model.eppinput.EppInput; +import google.registry.model.host.HostCommand; import google.registry.util.DomainNameUtils; -import java.net.Inet4Address; -import java.net.Inet6Address; import java.net.InetAddress; +import java.util.Comparator; import java.util.List; /** A command to create a new host via EPP. */ @@ -52,25 +52,19 @@ final class CreateHostCommand extends MutatingEppToolCommand { @Override protected void initMutatingEppToolCommand() { - setSoyTemplate(HostCreateSoyInfo.getInstance(), HostCreateSoyInfo.HOSTCREATE); - ImmutableList.Builder ipv4Addresses = new ImmutableList.Builder<>(); - ImmutableList.Builder ipv6Addresses = new ImmutableList.Builder<>(); + ImmutableSet.Builder inetAddresses = new ImmutableSet.Builder<>(); for (String address : nullToEmpty(addresses)) { - InetAddress inetAddress = InetAddresses.forString(address); - if (inetAddress instanceof Inet4Address) { - ipv4Addresses.add(inetAddress.getHostAddress()); - } else if (inetAddress instanceof Inet6Address) { - ipv6Addresses.add(inetAddress.getHostAddress()); - } else { - throw new IllegalArgumentException( - String.format("IP address in unknown format: %s", address)); - } + inetAddresses.add(InetAddresses.forString(address)); } - addSoyRecord( + + HostCommand.Create.Builder createBuilder = new HostCommand.Create.Builder(); + createBuilder.setTargetId(DomainNameUtils.canonicalizeHostname(hostName)); + createBuilder.setInetAddresses( + ImmutableSortedSet.copyOf( + Comparator.comparing(InetAddresses::toAddrString), inetAddresses.build())); + + addEppInput( clientId, - new SoyMapData( - "hostname", DomainNameUtils.canonicalizeHostname(hostName), - "ipv4addresses", ipv4Addresses.build(), - "ipv6addresses", ipv6Addresses.build())); + EppInput.create(EppInput.Create.create(createBuilder.build())).withClTrid("RegistryTool")); } } diff --git a/core/src/main/java/google/registry/tools/DeleteDomainCommand.java b/core/src/main/java/google/registry/tools/DeleteDomainCommand.java index da042024158..4be16bd1e09 100644 --- a/core/src/main/java/google/registry/tools/DeleteDomainCommand.java +++ b/core/src/main/java/google/registry/tools/DeleteDomainCommand.java @@ -16,8 +16,9 @@ import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; -import com.google.template.soy.data.SoyMapData; -import google.registry.tools.soy.DomainDeleteSoyInfo; +import google.registry.model.domain.DomainCommand; +import google.registry.model.eppinput.EppExtensions; +import google.registry.model.eppinput.EppInput; /** A command to delete a domain via EPP. */ @Parameters(separators = " =", commandDescription = "Delete domain") @@ -60,11 +61,17 @@ protected void initMutatingEppToolCommand() { // Immediate deletion is accomplished using the superuser extension. superuser = true; } - setSoyTemplate(DomainDeleteSoyInfo.getInstance(), DomainDeleteSoyInfo.DELETEDOMAIN); - addSoyRecord(clientId, new SoyMapData( - "domainName", domainName, - "immediately", immediately, - "reason", reason, - "requestedByRegistrar", requestedByRegistrar)); + + DomainCommand.Delete deleteCommand = new DomainCommand.Delete(); + deleteCommand.setTargetId(domainName); + + addEppInput( + clientId, + EppInput.create( + EppInput.Delete.create(deleteCommand), + EppExtensions.toolMetadata( + "Deleted by registry administrator: " + reason, requestedByRegistrar), + EppExtensions.deleteSuperuser(immediately)) + .withClTrid("RegistryTool")); } } diff --git a/core/src/main/java/google/registry/tools/DeleteHostCommand.java b/core/src/main/java/google/registry/tools/DeleteHostCommand.java index 129e68318ab..ea7231f4468 100644 --- a/core/src/main/java/google/registry/tools/DeleteHostCommand.java +++ b/core/src/main/java/google/registry/tools/DeleteHostCommand.java @@ -16,8 +16,9 @@ import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; -import com.google.template.soy.data.SoyMapData; -import google.registry.tools.soy.HostDeleteSoyInfo; +import google.registry.model.eppinput.EppExtensions; +import google.registry.model.eppinput.EppInput; +import google.registry.model.host.HostCommand; import google.registry.util.DomainNameUtils; /** A command to delete a host via EPP. */ @@ -50,12 +51,15 @@ final class DeleteHostCommand extends MutatingEppToolCommand { @Override protected void initMutatingEppToolCommand() { - setSoyTemplate(HostDeleteSoyInfo.getInstance(), HostDeleteSoyInfo.DELETEHOST); - addSoyRecord( + HostCommand.Delete deleteCommand = new HostCommand.Delete(); + deleteCommand.setTargetId(DomainNameUtils.canonicalizeHostname(hostName)); + + addEppInput( clientId, - new SoyMapData( - "hostName", DomainNameUtils.canonicalizeHostname(hostName), - "reason", reason, - "requestedByRegistrar", requestedByRegistrar)); + EppInput.create( + EppInput.Delete.create(deleteCommand), + EppExtensions.toolMetadata( + "Deleted by registry administrator: " + reason, requestedByRegistrar)) + .withClTrid("RegistryTool")); } } diff --git a/core/src/main/java/google/registry/tools/DsRecord.java b/core/src/main/java/google/registry/tools/DsRecord.java index c5582f1c746..caa4c9ead20 100644 --- a/core/src/main/java/google/registry/tools/DsRecord.java +++ b/core/src/main/java/google/registry/tools/DsRecord.java @@ -15,7 +15,6 @@ package google.registry.tools; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableList.toImmutableList; import static google.registry.util.PreconditionsUtils.checkArgumentPresent; import com.beust.jcommander.IStringConverter; @@ -23,9 +22,8 @@ import com.google.common.base.CharMatcher; import com.google.common.base.Splitter; import com.google.common.io.BaseEncoding; -import com.google.template.soy.data.SoyListData; -import com.google.template.soy.data.SoyMapData; import google.registry.flows.domain.DomainFlowUtils; +import google.registry.model.domain.secdns.DomainDsData; import java.util.List; record DsRecord(int keyTag, int alg, int digestType, String digest) { @@ -76,16 +74,8 @@ public static DsRecord parse(String dsRecord) { elements.get(3)); } - public SoyMapData toSoyData() { - return new SoyMapData( - "keyTag", keyTag(), - "alg", alg(), - "digestType", digestType(), - "digest", digest()); - } - - public static SoyListData convertToSoy(List dsRecords) { - return new SoyListData(dsRecords.stream().map(DsRecord::toSoyData).collect(toImmutableList())); + public DomainDsData toDsData() { + return DomainDsData.create(keyTag(), alg(), digestType(), digest()); } public static class Converter implements IStringConverter { diff --git a/core/src/main/java/google/registry/tools/EppToolCommand.java b/core/src/main/java/google/registry/tools/EppToolCommand.java index 09d6665d1aa..08b2a9ce2cf 100644 --- a/core/src/main/java/google/registry/tools/EppToolCommand.java +++ b/core/src/main/java/google/registry/tools/EppToolCommand.java @@ -14,10 +14,8 @@ package google.registry.tools; -import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Strings.nullToEmpty; import static com.google.common.collect.Maps.filterValues; -import static com.google.common.io.Resources.getResource; import static google.registry.model.tld.Tlds.findTldForNameOrThrow; import static google.registry.tools.CommandUtilities.addHeader; import static google.registry.util.DomainNameUtils.canonicalizeHostname; @@ -33,19 +31,16 @@ import com.google.common.collect.Multimap; import com.google.common.net.InternetDomainName; import com.google.common.net.MediaType; -import com.google.template.soy.SoyFileSet; -import com.google.template.soy.data.SoyRecord; -import com.google.template.soy.parseinfo.SoyFileInfo; -import com.google.template.soy.parseinfo.SoyTemplateInfo; +import google.registry.model.eppcommon.EppXmlTransformer; +import google.registry.model.eppinput.EppInput; import google.registry.model.registrar.Registrar; import google.registry.util.Clock; +import google.registry.xml.ValidationMode; import jakarta.inject.Inject; import java.io.IOException; import java.net.URLEncoder; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Objects; /** A command to execute an epp command. */ @@ -58,9 +53,6 @@ abstract class EppToolCommand extends ConfirmingCommand implements CommandWithCo description = "Run in superuser mode") boolean superuser = false; - private SoyFileInfo soyFileInfo; - private SoyTemplateInfo soyRenderer; - private List commands = new ArrayList<>(); private ServiceConnection connection; @@ -74,10 +66,11 @@ public String toString() { } /** - * Helper function for grouping sets of domain names into respective TLDs. Useful for batched - * EPP calls when invoking commands (i.e. domain check) with sets of domains across multiple TLDs. + * Helper function for grouping sets of domain names into respective TLDs. Useful for batched EPP + * calls when invoking commands (i.e. domain check) with sets of domains across multiple TLDs. */ - protected static Multimap validateAndGroupDomainNamesByTld(List names) { + protected static Multimap validateAndGroupDomainNamesByTld( + ImmutableList names) { ImmutableMultimap.Builder builder = new ImmutableMultimap.Builder<>(); for (String name : names) { String canonicalDomain = canonicalizeHostname(name); @@ -87,11 +80,6 @@ protected static Multimap validateAndGroupDomainNamesByTld(List< return builder.build(); } - protected void setSoyTemplate(SoyFileInfo soyFileInfo, SoyTemplateInfo soyRenderer) { - this.soyFileInfo = soyFileInfo; - this.soyRenderer = soyRenderer; - } - @Override public void setConnection(ServiceConnection connection) { this.connection = connection; @@ -103,16 +91,20 @@ protected void addXmlCommand(String clientId, String xml) { commands.add(new XmlEppParameters(clientId, xml)); } - protected void addSoyRecord(String clientId, SoyRecord record) { - checkNotNull(soyFileInfo, "SoyFileInfo is missing, cannot add record."); - checkNotNull(soyRenderer, "SoyRenderer is missing, cannot add record."); - addXmlCommand(clientId, SoyFileSet.builder() - .add(getResource(soyFileInfo.getClass(), soyFileInfo.getFileName())) - .build() - .compileToTofu() - .newRenderer(soyRenderer) - .setData(record) - .render()); + /** + * Adds an EPP command to the list of commands to be executed. + * + * @param clientId the registrar client ID to execute the command as + * @param eppInput the EPP input object to marshal and send + */ + protected void addEppInput(String clientId, EppInput eppInput) { + try { + String xml = + new String(EppXmlTransformer.marshalInput(eppInput, ValidationMode.STRICT), UTF_8); + addXmlCommand(clientId, xml); + } catch (Exception e) { + throw new RuntimeException("Failed to marshal EppInput", e); + } } /** Subclasses can override to implement a dry run flag. False by default. */ @@ -133,21 +125,23 @@ public String prompt() throws IOException { return prompt; } - private List processCommands(boolean dryRun) throws IOException { + private ImmutableList processCommands(boolean dryRun) throws IOException { ImmutableList.Builder responses = new ImmutableList.Builder<>(); for (XmlEppParameters command : commands) { - Map params = new HashMap<>(); - params.put("dryRun", dryRun); - params.put("clientId", command.clientId); - params.put("superuser", superuser); - params.put("xml", URLEncoder.encode(command.xml, UTF_8)); + ImmutableMap params = + ImmutableMap.builder() + .put("dryRun", dryRun) + .put("clientId", command.clientId) + .put("superuser", superuser) + .put("xml", URLEncoder.encode(command.xml, UTF_8)) + .build(); String requestBody = Joiner.on('&').withKeyValueSeparator("=").join(filterValues(params, Objects::nonNull)); responses.add( nullToEmpty( connection.sendPostRequest( "/_dr/epptool", - ImmutableMap.of(), + ImmutableMap.of(), MediaType.FORM_DATA, requestBody.getBytes(UTF_8)))); } diff --git a/core/src/main/java/google/registry/tools/RenewDomainCommand.java b/core/src/main/java/google/registry/tools/RenewDomainCommand.java index da7b8b70751..e46ef00070e 100644 --- a/core/src/main/java/google/registry/tools/RenewDomainCommand.java +++ b/core/src/main/java/google/registry/tools/RenewDomainCommand.java @@ -18,18 +18,20 @@ import static com.google.common.base.Strings.isNullOrEmpty; import static google.registry.util.CollectionUtils.findDuplicates; import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; +import static java.time.ZoneOffset.UTC; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import com.google.common.base.Joiner; -import com.google.template.soy.data.SoyMapData; import google.registry.flows.ResourceFlowUtils; import google.registry.model.domain.Domain; -import google.registry.tools.soy.DomainRenewSoyInfo; +import google.registry.model.domain.DomainCommand; +import google.registry.model.domain.Period; +import google.registry.model.eppinput.EppExtensions; +import google.registry.model.eppinput.EppInput; import java.time.Instant; -import java.time.ZoneOffset; -import java.time.format.DateTimeFormatter; import java.util.List; +import java.util.Set; /** A command to renew domain(s) via EPP. */ @Parameters(separators = " =", commandDescription = "Renew domain(s) via EPP.") @@ -61,35 +63,36 @@ final class RenewDomainCommand extends MutatingEppToolCommand { arity = 1) Boolean requestedByRegistrar; - private static final DateTimeFormatter DATE_FORMATTER = - DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneOffset.UTC); - @Override - protected void initMutatingEppToolCommand() - throws ResourceFlowUtils.ResourceDoesNotExistException { - String duplicates = Joiner.on(", ").join(findDuplicates(mainParameters)); - checkArgument(duplicates.isEmpty(), "Duplicate domain arguments found: '%s'", duplicates); + protected void initMutatingEppToolCommand() throws Exception { + Set duplicates = findDuplicates(mainParameters); + checkArgument( + duplicates.isEmpty(), + "Duplicate domain arguments found: '%s'", + Joiner.on(", ").join(duplicates)); checkArgument(period < 10, "Cannot renew domains for 10 or more years"); Instant now = clock.now(); for (String domainName : mainParameters) { Domain domain = ResourceFlowUtils.loadAndVerifyExistence(Domain.class, domainName, now); - setSoyTemplate(DomainRenewSoyInfo.getInstance(), DomainRenewSoyInfo.RENEWDOMAIN); - SoyMapData soyMapData = - new SoyMapData( - "domainName", domain.getDomainName(), - "expirationDate", DATE_FORMATTER.format(domain.getRegistrationExpirationTime()), - "period", String.valueOf(period)); - if (requestedByRegistrar != null) { - soyMapData.put("requestedByRegistrar", requestedByRegistrar.toString()); - } if (reason != null) { checkArgumentNotNull( requestedByRegistrar, "--registrar_request is required when --reason is specified"); - soyMapData.put("reason", reason); } - addSoyRecord( - isNullOrEmpty(clientId) ? domain.getCurrentSponsorRegistrarId() : clientId, soyMapData); + + DomainCommand.Renew.Builder renewBuilder = + new DomainCommand.Renew.Builder() + .setTargetId(domain.getDomainName()) + .setPeriod(Period.create(period, Period.Unit.YEARS)) + .setCurrentExpirationDate( + domain.getRegistrationExpirationTime().atZone(UTC).toLocalDate()); + + addEppInput( + isNullOrEmpty(clientId) ? domain.getCurrentSponsorRegistrarId() : clientId, + EppInput.create( + EppInput.Renew.create(renewBuilder.build()), + EppExtensions.toolMetadata(reason, requestedByRegistrar)) + .withClTrid("RegistryTool")); } } } diff --git a/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java b/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java index df176a7e2bf..145b3e73c42 100644 --- a/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java +++ b/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java @@ -15,7 +15,7 @@ package google.registry.tools; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.common.collect.Sets.difference; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static java.time.ZoneOffset.UTC; @@ -27,36 +27,38 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; -import com.google.template.soy.data.SoyListData; -import com.google.template.soy.data.SoyMapData; import google.registry.flows.ResourceFlowUtils; import google.registry.model.ForeignKeyUtils; import google.registry.model.domain.Domain; +import google.registry.model.domain.DomainCommand; +import google.registry.model.domain.Period; import google.registry.model.domain.secdns.DomainDsData; import google.registry.model.eppcommon.StatusValue; +import google.registry.model.eppinput.EppExtensions; +import google.registry.model.eppinput.EppInput; import google.registry.model.host.Host; import google.registry.tools.params.NameserversParameter; -import google.registry.tools.soy.DomainRenewSoyInfo; -import google.registry.tools.soy.UniformRapidSuspensionSoyInfo; import jakarta.xml.bind.annotation.adapters.HexBinaryAdapter; import java.time.Instant; -import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; /** A command to suspend a domain for the Uniform Rapid Suspension process. */ -@Parameters(separators = " =", +@Parameters( + separators = " =", commandDescription = "Suspend a domain for Uniform Rapid Suspension.") final class UniformRapidSuspensionCommand extends MutatingEppToolCommand { - private static final ImmutableSet URS_LOCKS = ImmutableSet.of( - StatusValue.SERVER_DELETE_PROHIBITED.getXmlName(), - StatusValue.SERVER_TRANSFER_PROHIBITED.getXmlName(), - StatusValue.SERVER_UPDATE_PROHIBITED.getXmlName()); + private static final ImmutableSet URS_LOCKS = + ImmutableSet.of( + StatusValue.SERVER_DELETE_PROHIBITED.getXmlName(), + StatusValue.SERVER_TRANSFER_PROHIBITED.getXmlName(), + StatusValue.SERVER_UPDATE_PROHIBITED.getXmlName()); - /** Client id that made this change. Only recorded in the history entry. **/ + /** Client id that made this change. Only recorded in the history entry. * */ private static final String CLIENT_ID = "CharlestonRoad"; @Parameter( @@ -127,12 +129,12 @@ protected void initMutatingEppToolCommand() superuser = true; Instant now = clock.now(); Domain domain = ResourceFlowUtils.loadAndVerifyExistence(Domain.class, domainName, now); - Set missingHosts = - difference(newHosts, ForeignKeyUtils.loadKeys(Host.class, newHosts, now).keySet()); + ImmutableSet missingHosts = + ImmutableSet.copyOf( + difference(newHosts, ForeignKeyUtils.loadKeys(Host.class, newHosts, now).keySet())); checkArgument(missingHosts.isEmpty(), "Hosts do not exist: %s", missingHosts); checkArgument( - locksToPreserve.isEmpty() || undo, - "Locks can only be preserved when running with --undo"); + locksToPreserve.isEmpty() || undo, "Locks can only be preserved when running with --undo"); existingNameservers = getExistingNameservers(domain); existingLocks = getExistingLocks(domain); existingDsData = getExistingDsData(domain); @@ -152,54 +154,92 @@ protected void initMutatingEppToolCommand() // trigger renew flow if (renewOneYear) { - setSoyTemplate(DomainRenewSoyInfo.getInstance(), DomainRenewSoyInfo.RENEWDOMAIN); - addSoyRecord( + DomainCommand.Renew.Builder renewBuilder = + new DomainCommand.Renew.Builder() + .setTargetId(domain.getDomainName()) + .setPeriod(Period.create(1, Period.Unit.YEARS)) + .setCurrentExpirationDate( + domain.getRegistrationExpirationTime().atZone(UTC).toLocalDate()); + + addEppInput( CLIENT_ID, - new SoyMapData( - "domainName", - domain.getDomainName(), - "expirationDate", - DateTimeFormatter.ofPattern("yyyy-MM-dd") - .withZone(UTC) - .format(domain.getRegistrationExpirationTime()), - // period is the number of years to renew the registration for - "period", - String.valueOf(1), - // use the same values for reason and requestedByRegistrar from update flow - "reason", - (undo ? "Undo " : "") + "Uniform Rapid Suspension", - "requestedByRegistrar", - Boolean.toString(false))); + EppInput.create( + EppInput.Renew.create(renewBuilder.build()), + EppExtensions.toolMetadata( + (undo ? "Undo " : "") + "Uniform Rapid Suspension", false)) + .withClTrid("RegistryTool")); } // trigger update flow - setSoyTemplate( - UniformRapidSuspensionSoyInfo.getInstance(), - UniformRapidSuspensionSoyInfo.UNIFORMRAPIDSUSPENSION); - addSoyRecord( + DomainCommand.Update.Builder updateBuilder = + new DomainCommand.Update.Builder().setTargetId(domainName); + + DomainCommand.Update.DomainAddRemove.Builder addBuilder = + new DomainCommand.Update.DomainAddRemove.Builder(); + DomainCommand.Update.DomainAddRemove.Builder removeBuilder = + new DomainCommand.Update.DomainAddRemove.Builder(); + boolean hasAdd = false; + boolean hasRemove = false; + + if (!statusesToApply.isEmpty()) { + addBuilder.setStatusValues( + statusesToApply.stream() + .map(StatusValue::fromXmlName) + .collect(ImmutableSortedSet.toImmutableSortedSet(Comparator.naturalOrder()))); + hasAdd = true; + } + + ImmutableSet statusesToRemove = + undo + ? ImmutableSet.copyOf(difference(URS_LOCKS, ImmutableSet.copyOf(locksToPreserve))) + : removeStatuses; + + if (!statusesToRemove.isEmpty()) { + removeBuilder.setStatusValues( + statusesToRemove.stream() + .map(StatusValue::fromXmlName) + .collect(ImmutableSortedSet.toImmutableSortedSet(Comparator.naturalOrder()))); + hasRemove = true; + } + + ImmutableSet addNameservers = + ImmutableSet.copyOf(difference(newHosts, existingNameservers)); + if (!addNameservers.isEmpty()) { + addBuilder.setNameserverHostNames(ImmutableSortedSet.copyOf(addNameservers)); + hasAdd = true; + } + + ImmutableSet removeNameservers = + ImmutableSet.copyOf(difference(existingNameservers, newHosts)); + if (!removeNameservers.isEmpty()) { + removeBuilder.setNameserverHostNames(ImmutableSortedSet.copyOf(removeNameservers)); + hasRemove = true; + } + + if (hasAdd) { + updateBuilder.setInnerAdd(addBuilder.build()); + } + if (hasRemove) { + updateBuilder.setInnerRemove(removeBuilder.build()); + } + + addEppInput( CLIENT_ID, - new SoyMapData( - "domainName", - domainName, - "hostsToAdd", - difference(newHosts, existingNameservers), - "hostsToRemove", - difference(existingNameservers, newHosts), - "statusesToApply", - statusesToApply, - "statusesToRemove", - undo ? difference(URS_LOCKS, ImmutableSet.copyOf(locksToPreserve)) : removeStatuses, - "newDsData", - newDsData != null ? DsRecord.convertToSoy(newDsData) : new SoyListData(), - "reason", - (undo ? "Undo " : "") + "Uniform Rapid Suspension", - // Domain auto-renewal is disabled as part of URS, and it's re-enabled if URS is undone. - // Therefore, autorenews is set to false by default and it's set to true only if the - // command is run in --undo mode. - "autorenews", - Boolean.toString(undo))); + EppInput.create( + EppInput.Update.create(updateBuilder.build()), + EppExtensions.secDnsUpdate( + newDsData == null + ? ImmutableSet.of() + : newDsData.stream().map(DsRecord::toDsData).collect(toImmutableSet()), + ImmutableSet.of(), + true), + EppExtensions.updateSuperuser(undo), + EppExtensions.toolMetadata( + (undo ? "Undo " : "") + "Uniform Rapid Suspension", false)) + .withClTrid("RegistryTool")); } + /** Returns the set of existing nameservers for the specified domain. */ private ImmutableSortedSet getExistingNameservers(Domain domain) { ImmutableSortedSet.Builder nameservers = ImmutableSortedSet.naturalOrder(); for (Host host : tm().transact(() -> tm().loadByKeys(domain.getNameservers()).values())) { @@ -208,6 +248,7 @@ private ImmutableSortedSet getExistingNameservers(Domain domain) { return nameservers.build(); } + /** Returns the set of existing URS-related locks for the specified domain. */ private ImmutableSortedSet getExistingLocks(Domain domain) { ImmutableSortedSet.Builder locks = ImmutableSortedSet.naturalOrder(); for (StatusValue lock : domain.getStatusValues()) { @@ -218,6 +259,7 @@ private ImmutableSortedSet getExistingLocks(Domain domain) { return locks.build(); } + /** Returns whether the specified domain has a CLIENT_HOLD status. */ private boolean hasClientHold(Domain domain) { for (StatusValue status : domain.getStatusValues()) { if (status == StatusValue.CLIENT_HOLD) { @@ -227,6 +269,7 @@ private boolean hasClientHold(Domain domain) { return false; } + /** Returns a list of the existing DS records for the specified domain as JSON-like maps. */ private ImmutableList> getExistingDsData(Domain domain) { ImmutableList.Builder> dsDataJsons = new ImmutableList.Builder(); HexBinaryAdapter hexBinaryAdapter = new HexBinaryAdapter(); @@ -273,7 +316,7 @@ protected String postExecute() { rec.get("digestType"), rec.get("digest"))) .sorted() - .collect(toImmutableList()); + .collect(ImmutableList.toImmutableList()); undoBuilder.append(" --dsdata ").append(Joiner.on(',').join(formattedDsRecords)); } return undoBuilder.toString(); diff --git a/core/src/main/java/google/registry/tools/UpdateDomainCommand.java b/core/src/main/java/google/registry/tools/UpdateDomainCommand.java index 16be3b90c40..b882982957e 100644 --- a/core/src/main/java/google/registry/tools/UpdateDomainCommand.java +++ b/core/src/main/java/google/registry/tools/UpdateDomainCommand.java @@ -18,6 +18,7 @@ import static google.registry.model.domain.rgp.GracePeriodStatus.AUTO_RENEW; import static google.registry.model.eppcommon.StatusValue.PENDING_DELETE; import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED; +import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; import static java.util.function.Predicate.isEqual; import com.beust.jcommander.Parameter; @@ -26,19 +27,23 @@ import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Sets; import com.google.common.flogger.FluentLogger; -import com.google.template.soy.data.SoyMapData; import google.registry.flows.ResourceFlowUtils; import google.registry.model.domain.Domain; +import google.registry.model.domain.DomainAuthInfo; +import google.registry.model.domain.DomainCommand; import google.registry.model.domain.GracePeriodBase; +import google.registry.model.domain.secdns.SecDnsUpdateExtension; +import google.registry.model.eppcommon.AuthInfo.PasswordAuth; import google.registry.model.eppcommon.StatusValue; +import google.registry.model.eppinput.EppExtensions; +import google.registry.model.eppinput.EppInput; import google.registry.tools.params.NameserversParameter; -import google.registry.tools.soy.DomainUpdateSoyInfo; import java.time.Instant; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.TreeSet; import javax.annotation.Nullable; /** A command to update a new domain via EPP. */ @@ -60,9 +65,8 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand { private Set addNameservers = new HashSet<>(); @Parameter( - names = "--add_statuses", - description = "Statuses to add. Cannot be set if --statuses is set." - ) + names = "--add_statuses", + description = "Statuses to add. Cannot be set if --statuses is set.") private List addStatuses = new ArrayList<>(); @Parameter( @@ -82,9 +86,8 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand { private Set removeNameservers = new HashSet<>(); @Parameter( - names = "--remove_statuses", - description = "Statuses to remove. Cannot be set if --statuses is set." - ) + names = "--remove_statuses", + description = "Statuses to remove. Cannot be set if --statuses is set.") private List removeStatuses = new ArrayList<>(); @Parameter( @@ -95,10 +98,8 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand { private List removeDsRecords = new ArrayList<>(); @Parameter( - names = "--clear_ds_records", - description = - "removes all DS records. Is implied true if --ds_records is set." - ) + names = "--clear_ds_records", + description = "removes all DS records. Is implied true if --ds_records is set.") boolean clearDsRecords = false; @Nullable @@ -133,7 +134,7 @@ protected void initMutatingEppToolCommand() + "you cannot use the add_statuses and remove_statuses flags."); } - if (!dsRecords.isEmpty() || clearDsRecords){ + if (!dsRecords.isEmpty() || clearDsRecords) { checkArgument( addDsRecords.isEmpty() && removeDsRecords.isEmpty(), "If you provide the ds_records or clear_ds_records flags, " @@ -146,6 +147,12 @@ protected void initMutatingEppToolCommand() Instant now = clock.now(); for (String domainName : domains) { Domain domain = ResourceFlowUtils.loadAndVerifyExistence(Domain.class, domainName, now); + + if (reason != null) { + checkArgumentNotNull( + requestedByRegistrar, "--registrar_request is required when --reason is specified"); + } + checkArgument( !domain.getStatusValues().contains(SERVER_UPDATE_PROHIBITED), "The domain '%s' has status SERVER_UPDATE_PROHIBITED. Verify that you are allowed " @@ -158,61 +165,18 @@ protected void initMutatingEppToolCommand() + "--force_in_pending_delete parameter to allow this update.", domainName); - // Use TreeSets so that the results are always in the same order (this makes testing easier). - Set addNameserversThisDomain = new TreeSet<>(addNameservers); - Set removeNameserversThisDomain = new TreeSet<>(removeNameservers); - Set addStatusesThisDomain = new TreeSet<>(addStatuses); - Set removeStatusesThisDomain = new TreeSet<>(removeStatuses); - - if (!nameservers.isEmpty() || !statuses.isEmpty()) { - if (!nameservers.isEmpty()) { - ImmutableSortedSet existingNameservers = domain.loadNameserverHostNames(); - populateAddRemoveLists( - ImmutableSet.copyOf(nameservers), - existingNameservers, - addNameserversThisDomain, - removeNameserversThisDomain); - int numNameservers = - existingNameservers.size() - + addNameserversThisDomain.size() - - removeNameserversThisDomain.size(); - checkArgument( - numNameservers <= 13, - "The resulting nameservers count for domain %s would be more than 13", - domainName); - } - - if (!statuses.isEmpty()) { - Set currentStatusValues = new HashSet<>(); - for (StatusValue statusValue : domain.getStatusValues()) { - currentStatusValues.add(statusValue.getXmlName()); - } - populateAddRemoveLists( - ImmutableSet.copyOf(statuses), - currentStatusValues, - addStatusesThisDomain, - removeStatusesThisDomain); - } - } - - boolean add = - (!addNameserversThisDomain.isEmpty() - || !addStatusesThisDomain.isEmpty()); + if (!nameservers.isEmpty()) { + ImmutableSortedSet existingNameservers = domain.loadNameserverHostNames(); + ImmutableSet targetNameservers = ImmutableSet.copyOf(nameservers); - boolean remove = - (!removeNameserversThisDomain.isEmpty() - || !removeStatusesThisDomain.isEmpty()); - - boolean change = password != null; - boolean secDns = - (!addDsRecords.isEmpty() - || !removeDsRecords.isEmpty() - || !dsRecords.isEmpty() - || clearDsRecords); - - if (!add && !remove && !change && !secDns && autorenews == null) { - logger.atInfo().log("No changes need to be made to domain '%s'.", domainName); - continue; + int numNameservers = + existingNameservers.size() + + Sets.difference(targetNameservers, existingNameservers).size() + - Sets.difference(existingNameservers, targetNameservers).size(); + checkArgument( + numNameservers <= 13, + "The resulting nameservers count for domain %s would be more than 13", + domainName); } // If autorenew is being turned off and this domain is already in the autorenew grace period, @@ -225,30 +189,121 @@ protected void initMutatingEppToolCommand() } } - setSoyTemplate(DomainUpdateSoyInfo.getInstance(), DomainUpdateSoyInfo.DOMAINUPDATE); - SoyMapData soyMapData = - new SoyMapData( - "domain", domainName, - "add", add, - "addNameservers", addNameserversThisDomain, - "addStatuses", addStatusesThisDomain, - "remove", remove, - "removeNameservers", removeNameserversThisDomain, - "removeStatuses", removeStatusesThisDomain, - "change", change, - "password", password, - "secdns", secDns, - "addDsRecords", DsRecord.convertToSoy(addDsRecords), - "removeDsRecords", DsRecord.convertToSoy(removeDsRecords), - "removeAllDsRecords", clearDsRecords, - "reason", reason); - if (autorenews != null) { - soyMapData.put("autorenews", autorenews.toString()); + DomainCommand.Update.Builder updateBuilder = + new DomainCommand.Update.Builder().setTargetId(domainName); + DomainCommand.Update.DomainAddRemove.Builder addBuilder = + new DomainCommand.Update.DomainAddRemove.Builder(); + DomainCommand.Update.DomainAddRemove.Builder removeBuilder = + new DomainCommand.Update.DomainAddRemove.Builder(); + + boolean hasAdd = false; + boolean hasRemove = false; + boolean hasChange = false; + + if (!nameservers.isEmpty()) { + ImmutableSortedSet current = domain.loadNameserverHostNames(); + ImmutableSet target = ImmutableSet.copyOf(nameservers); + ImmutableSortedSet toAdd = + ImmutableSortedSet.copyOf(Sets.difference(target, current)); + ImmutableSortedSet toRemove = + ImmutableSortedSet.copyOf(Sets.difference(current, target)); + if (!toAdd.isEmpty()) { + addBuilder.setNameserverHostNames(toAdd); + hasAdd = true; + } + if (!toRemove.isEmpty()) { + removeBuilder.setNameserverHostNames(toRemove); + hasRemove = true; + } + } else { + if (!addNameservers.isEmpty()) { + addBuilder.setNameserverHostNames(ImmutableSortedSet.copyOf(addNameservers)); + hasAdd = true; + } + if (!removeNameservers.isEmpty()) { + removeBuilder.setNameserverHostNames(ImmutableSortedSet.copyOf(removeNameservers)); + hasRemove = true; + } + } + + if (!statuses.isEmpty()) { + ImmutableSortedSet current = + ImmutableSortedSet.copyOf(domain.getStatusValues()); + ImmutableSet target = + statuses.stream().map(StatusValue::fromXmlName).collect(ImmutableSet.toImmutableSet()); + ImmutableSortedSet toAdd = + ImmutableSortedSet.copyOf(Sets.difference(target, current)); + ImmutableSortedSet toRemove = + ImmutableSortedSet.copyOf(Sets.difference(current, target)); + if (!toAdd.isEmpty()) { + addBuilder.setStatusValues(toAdd); + hasAdd = true; + } + if (!toRemove.isEmpty()) { + removeBuilder.setStatusValues(toRemove); + hasRemove = true; + } + } else { + if (!addStatuses.isEmpty()) { + addBuilder.setStatusValues( + addStatuses.stream() + .map(StatusValue::fromXmlName) + .collect(ImmutableSortedSet.toImmutableSortedSet(Comparator.naturalOrder()))); + hasAdd = true; + } + if (!removeStatuses.isEmpty()) { + removeBuilder.setStatusValues( + removeStatuses.stream() + .map(StatusValue::fromXmlName) + .collect(ImmutableSortedSet.toImmutableSortedSet(Comparator.naturalOrder()))); + hasRemove = true; + } + } + + if (hasAdd) { + updateBuilder.setInnerAdd(addBuilder.build()); } - if (requestedByRegistrar != null) { - soyMapData.put("requestedByRegistrar", requestedByRegistrar.toString()); + if (hasRemove) { + updateBuilder.setInnerRemove(removeBuilder.build()); + } + + if (password != null) { + updateBuilder.setInnerChange( + new DomainCommand.Update.Change.Builder() + .setAuthInfo(DomainAuthInfo.create(PasswordAuth.create(password))) + .build()); + hasChange = true; + } + + SecDnsUpdateExtension secDnsUpdate = null; + if (!addDsRecords.isEmpty() + || !removeDsRecords.isEmpty() + || !dsRecords.isEmpty() + || clearDsRecords) { + secDnsUpdate = + EppExtensions.secDnsUpdate( + addDsRecords.stream() + .map(DsRecord::toDsData) + .collect(ImmutableSet.toImmutableSet()), + removeDsRecords.stream() + .map(DsRecord::toDsData) + .collect(ImmutableSet.toImmutableSet()), + clearDsRecords); + } + + if (hasAdd || hasRemove || hasChange || secDnsUpdate != null || autorenews != null) { + addEppInput( + clientId, + EppInput.create( + EppInput.Update.create(updateBuilder.build()), + EppExtensions.updateSuperuser(autorenews), + EppExtensions.toolMetadata(reason, requestedByRegistrar), + secDnsUpdate) + .withClTrid("RegistryTool")); + } else { + logger.atInfo().log( + "Skipping domain '%s' because there are no changes to make.", domainName); } - addSoyRecord(clientId, soyMapData); } ImmutableSet domainsToWarn = autorenewGracePeriodWarningDomains.build(); @@ -260,10 +315,4 @@ protected void initMutatingEppToolCommand() String.join(", ", domainsToWarn)); } } - - private void populateAddRemoveLists( - Set targetSet, Set oldSet, Set addSet, Set removeSet) { - addSet.addAll(Sets.difference(targetSet, oldSet)); - removeSet.addAll(Sets.difference(oldSet, targetSet)); - } } diff --git a/core/src/main/java/google/registry/tools/UpdateServerLocksCommand.java b/core/src/main/java/google/registry/tools/UpdateServerLocksCommand.java index 6c00ad2973a..a5706a55864 100644 --- a/core/src/main/java/google/registry/tools/UpdateServerLocksCommand.java +++ b/core/src/main/java/google/registry/tools/UpdateServerLocksCommand.java @@ -21,14 +21,17 @@ import static com.google.common.collect.Sets.union; import com.beust.jcommander.Parameter; +import com.beust.jcommander.ParameterException; import com.beust.jcommander.Parameters; import com.google.common.collect.ImmutableSet; -import com.google.template.soy.data.SoyMapData; +import com.google.common.collect.ImmutableSortedSet; +import google.registry.model.domain.DomainCommand; import google.registry.model.eppcommon.StatusValue; -import google.registry.tools.soy.UpdateServerLocksSoyInfo; +import google.registry.model.eppinput.EppExtensions; +import google.registry.model.eppinput.EppInput; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; -import java.util.Set; /** A command to execute a domain check claims epp command. */ @Parameters(separators = " =", @@ -36,79 +39,101 @@ final class UpdateServerLocksCommand extends MutatingEppToolCommand { @Parameter( - names = {"-c", "--client"}, - description = "Client identifier of the registrar to execute the command as", + names = {"-n", "--domain_name"}, + description = "Domain to lock/unlock.", required = true) - String clientId; + private String domainName; @Parameter( - names = {"-n", "--domain_name"}, - description = "Domain to modify.", + names = {"--client"}, + description = "Client ID to use for the EPP command.", required = true) - private String domainName; + private String clientId; @Parameter( - names = {"-a", "--apply"}, - description = "Comma-delimited set of locks to apply (or 'all'). " - + "Valid locks: serverDeleteProhibited, serverHold, serverRenewProhibited, " - + "serverTransferProhibited, serverUpdateProhibited") - private List locksToApply = new ArrayList<>(); + names = {"--reason"}, + description = "Reason for the change.") + private String reason; @Parameter( - names = {"-r", "--remove"}, - description = "Comma-delimited set of locks to remove (or 'all'). " - + "Valid locks: same as for 'apply'.") - private List locksToRemove = new ArrayList<>(); + names = {"--apply"}, + description = "Statuses to apply. Use \"all\" to apply all server locks.") + private List locksToApply = new ArrayList<>(); @Parameter( - names = {"--reason"}, - description = "Reason for the change. Required if registrar_request = false.") - private String reason; + names = {"--remove"}, + description = "Statuses to remove. Use \"all\" to remove all server locks.") + private List locksToRemove = new ArrayList<>(); @Parameter( names = {"--registrar_request"}, description = "Whether the change was requested by a registrar.", - required = true, arity = 1) - private boolean requestedByRegistrar; + private Boolean requestedByRegistrar; - private static final ImmutableSet ALLOWED_VALUES = ImmutableSet.of( - StatusValue.SERVER_DELETE_PROHIBITED.getXmlName(), - StatusValue.SERVER_HOLD.getXmlName(), - StatusValue.SERVER_RENEW_PROHIBITED.getXmlName(), - StatusValue.SERVER_TRANSFER_PROHIBITED.getXmlName(), - StatusValue.SERVER_UPDATE_PROHIBITED.getXmlName()); + private static final ImmutableSet ALLOWED_VALUES = + ImmutableSet.of( + StatusValue.SERVER_DELETE_PROHIBITED.getXmlName(), + StatusValue.SERVER_HOLD.getXmlName(), + StatusValue.SERVER_RENEW_PROHIBITED.getXmlName(), + StatusValue.SERVER_TRANSFER_PROHIBITED.getXmlName(), + StatusValue.SERVER_UPDATE_PROHIBITED.getXmlName()); - private static Set getStatusValuesSet(List statusValues) { - Set statusValuesSet = ImmutableSet.copyOf(statusValues); + private static ImmutableSet getStatusValuesSet(List statusValues) { + ImmutableSet statusValuesSet = ImmutableSet.copyOf(statusValues); if (statusValuesSet.contains("all")) { return ALLOWED_VALUES; } - Set badValues = difference(statusValuesSet, ALLOWED_VALUES); + ImmutableSet badValues = + ImmutableSet.copyOf(difference(statusValuesSet, ALLOWED_VALUES)); checkArgument(badValues.isEmpty(), "Invalid status values: %s", badValues); return statusValuesSet; } @Override protected void initMutatingEppToolCommand() { + if (requestedByRegistrar == null) { + throw new ParameterException("--registrar_request must be specified"); + } checkArgument( requestedByRegistrar || !isNullOrEmpty(reason), "A reason must be provided when a change is not requested by a registrar."); - Set valuesToApply = getStatusValuesSet(locksToApply); - Set valuesToRemove = getStatusValuesSet(locksToRemove); + ImmutableSet valuesToApply = getStatusValuesSet(locksToApply); + ImmutableSet valuesToRemove = getStatusValuesSet(locksToRemove); checkArgument( intersection(valuesToApply, valuesToRemove).isEmpty(), "Add and remove actions overlap"); checkArgument( !union(valuesToApply, valuesToRemove).isEmpty(), "Add and remove actions are both empty"); - setSoyTemplate( - UpdateServerLocksSoyInfo.getInstance(), UpdateServerLocksSoyInfo.UPDATESERVERLOCKS); - addSoyRecord(clientId, new SoyMapData( - "domainName", domainName, - "locksToApply", valuesToApply, - "locksToRemove", valuesToRemove, - "reason", reason, - "requestedByRegistrar", requestedByRegistrar)); + + DomainCommand.Update.Builder updateBuilder = + new DomainCommand.Update.Builder().setTargetId(domainName); + + if (!valuesToApply.isEmpty()) { + updateBuilder.setInnerAdd( + new DomainCommand.Update.DomainAddRemove.Builder() + .setStatusValues( + valuesToApply.stream() + .map(StatusValue::fromXmlName) + .collect(ImmutableSortedSet.toImmutableSortedSet(Comparator.naturalOrder()))) + .build()); + } + if (!valuesToRemove.isEmpty()) { + updateBuilder.setInnerRemove( + new DomainCommand.Update.DomainAddRemove.Builder() + .setStatusValues( + valuesToRemove.stream() + .map(StatusValue::fromXmlName) + .collect(ImmutableSortedSet.toImmutableSortedSet(Comparator.naturalOrder()))) + .build()); + } + + addEppInput( + clientId, + EppInput.create( + EppInput.Update.create(updateBuilder.build()), + EppExtensions.toolMetadata(reason, requestedByRegistrar)) + .withClTrid("RegistryTool")); } } diff --git a/core/src/main/resources/google/registry/reporting/spec11/ftl/daily_spec11_email.ftl b/core/src/main/resources/google/registry/reporting/spec11/ftl/daily_spec11_email.ftl new file mode 100644 index 00000000000..6a1b7155b16 --- /dev/null +++ b/core/src/main/resources/google/registry/reporting/spec11/ftl/daily_spec11_email.ftl @@ -0,0 +1,47 @@ +<#ftl output_format="HTML"> +<#-- Copyright 2026 The Nomulus Authors. All Rights Reserved. --> + +

Dear registrar partner,

+ +

${registry} conducts a daily analysis of all domains registered in its TLDs to + identify potential security concerns. On ${date}, the following domains that your + registrar manages were flagged for potential security concerns:

+ + + + + + + <#list threats as threat> + + + + + +
Domain NameThreat Type
${threat.domainName}${threat.threatType}
+ +

Please communicate these findings to the registrant and work with the + registrant to mitigate any security issues and have the domains delisted.

+ +<#if (resources?size > 0)> +

Some helpful resources for getting off a blocked list include:

+
    + <#list resources as resource> +
  • ${resource}
  • + +
+ + +

If you believe that any of the domains were reported in error, or are still receiving + reports for issues that have been remediated, + please submit + a request to have the site reviewed.

+ +

You will continue to receive daily notices when new domains managed by your registrar + are flagged for abuse, as well as a monthly summary of all of your domains under management + that remain flagged for abuse.

+ +

If you would like to change the email to which these notices are sent, please update your + abuse contact using your registrar portal account.

+ +

If you have any questions regarding this notice, please contact ${replyToEmail}.

diff --git a/core/src/main/resources/google/registry/reporting/spec11/ftl/monthly_spec11_email.ftl b/core/src/main/resources/google/registry/reporting/spec11/ftl/monthly_spec11_email.ftl new file mode 100644 index 00000000000..60ce40045e6 --- /dev/null +++ b/core/src/main/resources/google/registry/reporting/spec11/ftl/monthly_spec11_email.ftl @@ -0,0 +1,46 @@ +<#ftl output_format="HTML"> +<#-- Copyright 2026 The Nomulus Authors. All Rights Reserved. --> + +

Dear registrar partner,

+ +

${registry} previously notified you when the following domains managed by your + registrar were flagged for potential security concerns.

+ +

The following domains that you manage continue to be flagged by our analysis for + potential security concerns. This may be because the registrants have not completed the + requisite steps to mitigate the potential security abuse and/or have it reviewed and + delisted.

+ + + + + + + <#list threats as threat> + + + + + +
Domain NameThreat Type
${threat.domainName}${threat.threatType}
+ +

Please work with the registrant to mitigate any security issues and have the + domains delisted. If you believe that any of the domains were reported in error, or are + still receiving reports for issues that have been remediated, + please submit a + request to have the site reviewed.

+ +<#if (resources?size > 0)> +

Some helpful resources for getting off a blocked list include:

+
    + <#list resources as resource> +
  • ${resource}
  • + +
+ + +

You will continue to receive a monthly summary of all domains managed by your registrar + that remain on our lists of potential security threats. You will also receive a daily + notice when any new domains are added to these lists.

+ +

If you have any questions regarding this notice, please contact ${replyToEmail}.

diff --git a/core/src/main/resources/google/registry/reporting/spec11/soy/Spec11Email.soy b/core/src/main/resources/google/registry/reporting/spec11/soy/Spec11Email.soy deleted file mode 100644 index 0ea8a0c5027..00000000000 --- a/core/src/main/resources/google/registry/reporting/spec11/soy/Spec11Email.soy +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2019 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. - -{namespace registry.soy.reporting.spec11} - -/** - * Template for the content of the monthly spec11 email - */ -{template monthlySpec11Email} - {@param threats: list>} - {@param resources: list} - {@param registry: string} - {@param replyToEmail: string} - - Dear registrar partner, - -

{$registry} previously notified you when the following domains managed by your - registrar were flagged for potential security concerns.

- -

The following domains that you manage continue to be flagged by our analysis for potential - security concerns. This may be because the registrants have not completed the requisite steps - to mitigate the potential security abuse and/or have it reviewed and delisted.

- - {call threatMatchTable} - {param threats: $threats /} - {/call} - -

Please work with the registrant to mitigate any security issues and have the - domains delisted. If you believe that any of the domains were reported in error, or are still - receiving reports for issues that have been remediated, - please submit a - request to have the site reviewed.

- - {call resourceList} - {param resources: $resources /} - {/call} - -

You will continue to receive a monthly summary of all domains managed by your registrar - that remain on our lists of potential security threats. You will also receive a daily - notice when any new domains are added to these lists.

- -

If you have any questions regarding this notice, please contact {$replyToEmail}.

-{/template} - -/** - * Template for the content of the daily spec11 email - */ -{template dailySpec11Email} - {@param threats: list>} - {@param resources: list} - {@param date: string} - {@param registry: string} - {@param replyToEmail: string} - - Dear registrar partner, - -

{$registry} conducts a daily analysis of all domains registered in its TLDs to - identify potential security concerns. On {$date}, the following domains that your - registrar manages were flagged for potential security concerns:

- - {call threatMatchTable} - {param threats: $threats /} - {/call} - -

Please communicate these findings to the registrant and work with the - registrant to mitigate any security issues and have the domains delisted.

- - {call resourceList} - {param resources: $resources /} - {/call} - -

If you believe that any of the domains were reported in error, or are still receiving - reports for issues that have been remediated, - please submit - a request to have the site reviewed.

- -

You will continue to receive daily notices when new domains managed by your registrar - are flagged for abuse, as well as a monthly summary of all of your domains under management - that remain flagged for abuse.

- -

If you would like to change the email to which these notices are sent please update your - abuse contact using your registrar portal account.

- -

If you have any questions regarding this notice, please contact {$replyToEmail}.

-{/template} - -/** - * Template for the list of potentially-useful resources - */ -{template resourceList} - {@param resources: list} - {if length($resources) > 0} - Some helpful resources for getting off a blocked list include: -
    - {for $resource in $resources} -
  • {$resource}
  • - {/for} -
- {/if} -{/template} - -/** - * Template for the table containing the threats themselves - */ -{template threatMatchTable} - {@param threats: list>} - - - - - - {for $threat in $threats} - - - - - {/for} -
Domain NameThreat Type
{$threat.get('domainName')}{$threat.get('threatType')}
-{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/CreateAnchorTenant.soy b/core/src/main/resources/google/registry/tools/soy/CreateAnchorTenant.soy deleted file mode 100644 index 7078ad5a56b..00000000000 --- a/core/src/main/resources/google/registry/tools/soy/CreateAnchorTenant.soy +++ /dev/null @@ -1,62 +0,0 @@ -// 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. - -{namespace domain.registry.tools.create_anchor_tenant} - -/** - * Create anchor tenant domain - */ -{template createanchortenant stricthtml="false"} -{@param domainName: string} -{@param contactId: string} -{@param password: string} -{@param period: int} -{@param? reason: string|null} -{@param? feeCurrency: string|null} -{@param? fee: string|null} - - - - - - {$domainName} - {$period} - {$contactId} - {$contactId} - {$contactId} - - {$password} - - - - - - {if $reason} - {$reason} - {/if} - false - true - - {if $fee} - - {$feeCurrency} - {$fee} - - {/if} - - RegistryTool - - -{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/DomainCheck.soy b/core/src/main/resources/google/registry/tools/soy/DomainCheck.soy deleted file mode 100644 index bd869a243df..00000000000 --- a/core/src/main/resources/google/registry/tools/soy/DomainCheck.soy +++ /dev/null @@ -1,53 +0,0 @@ -// 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. - -{namespace domain.registry.tools.domain_check} - -/** - * Domain check request - */ -{template domaincheck stricthtml="false"} -{@param domainNames: list} -{@param? allocationToken: string|null} - - - - - - {for $d in $domainNames} - {$d} - {/for} - - - - - {for $d in $domainNames} - - {$d} - create - 1 - - {/for} - - {if $allocationToken} - - {$allocationToken} - - {/if} - - RegistryTool - - -{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/DomainCheckClaims.soy b/core/src/main/resources/google/registry/tools/soy/DomainCheckClaims.soy deleted file mode 100644 index cec71abed00..00000000000 --- a/core/src/main/resources/google/registry/tools/soy/DomainCheckClaims.soy +++ /dev/null @@ -1,42 +0,0 @@ -// 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. - -{namespace domain.registry.tools.domain_check_claims} - -/** - * Domain check claims request - */ -{template domaincheckclaims stricthtml="false"} -{@param domainNames: list} - - - - - - {for $d in $domainNames} - {$d} - {/for} - - - - - claims - - - RegistryTool - - -{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/DomainCreate.soy b/core/src/main/resources/google/registry/tools/soy/DomainCreate.soy deleted file mode 100644 index 9b20b85d61e..00000000000 --- a/core/src/main/resources/google/registry/tools/soy/DomainCreate.soy +++ /dev/null @@ -1,108 +0,0 @@ -// 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. - -{namespace domain.registry.tools.domain_create} -/** - * Create domain - */ -{template domaincreate stricthtml="false"} - {@param domain: string} - {@param period: int} - {@param nameservers: list} - {@param? registrant: string|null} - {@param? admins: list|null} - {@param? techs: list|null} - {@param password: string} - {@param? currency: string|null} - {@param? price: string|null} - {@param dsRecords: list<[keyTag:int, alg:int, digestType:int, digest:string]>} - {@param? reason: string|null} - {@param? requestedByRegistrar: string|null} - {@param? allocationToken: string|null} - - - - - - - {$domain} - {$period} - {if length($nameservers) > 0} - - {for $s in $nameservers} - {$s} - {/for} - - {/if} - {if $registrant != null} - {$registrant} - {/if} - {if $admins != null} - {for $admin in $admins} - {$admin} - {/for} - {/if} - {if $techs != null} - {for $tech in $techs} - {$tech} - {/for} - {/if} - - {$password} - - - - {if length($dsRecords) > 0 || $price != null || $reason || $requestedByRegistrar || $allocationToken} - - {if $price != null} - - {$currency} - {$price} - - {/if} - {if length($dsRecords) > 0} - - {for $dsRecord in $dsRecords} - - {$dsRecord.keyTag} - {$dsRecord.alg} - {$dsRecord.digestType} - {$dsRecord.digest} - - {/for} - - {/if} - {if $reason || $requestedByRegistrar} - - {if $reason} - {$reason} - {/if} - {if $requestedByRegistrar} - {$requestedByRegistrar} - {/if} - - {/if} - {if $allocationToken} - - {$allocationToken} - - {/if} - - {/if} - RegistryTool - - -{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/DomainDelete.soy b/core/src/main/resources/google/registry/tools/soy/DomainDelete.soy deleted file mode 100644 index 89d91474b75..00000000000 --- a/core/src/main/resources/google/registry/tools/soy/DomainDelete.soy +++ /dev/null @@ -1,49 +0,0 @@ -// 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. - -{namespace domain.registry.tools.domain_delete} - -/** - * Delete domain request - */ -{template deletedomain stricthtml="false"} -{@param domainName: string} -{@param immediately: bool} -{@param reason: string} -{@param requestedByRegistrar: any} - - - - - - {$domainName} - - - - - Deleted by registry administrator: {$reason} - {$requestedByRegistrar} - - {if $immediately} - - 0 - 0 - - {/if} - - RegistryTool - - -{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/DomainRenew.soy b/core/src/main/resources/google/registry/tools/soy/DomainRenew.soy deleted file mode 100644 index bf5fef9dc41..00000000000 --- a/core/src/main/resources/google/registry/tools/soy/DomainRenew.soy +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2018 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. - -{namespace domain.registry.tools.domain_renew} - -/** - * Renew domain request - */ -{template renewdomain stricthtml="false"} -{@param domainName: string} -{@param expirationDate: string} -{@param period: string} -{@param? reason: string|null} -{@param? requestedByRegistrar: string|null} - - - - - - {$domainName} - {$expirationDate} - {$period} - - - {if $reason || $requestedByRegistrar} - - - {if $reason} - {$reason} - {/if} - {if $requestedByRegistrar} - {$requestedByRegistrar} - {/if} - - - {/if} - RegistryTool - - -{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/DomainUpdate.soy b/core/src/main/resources/google/registry/tools/soy/DomainUpdate.soy deleted file mode 100644 index 7436c95d91a..00000000000 --- a/core/src/main/resources/google/registry/tools/soy/DomainUpdate.soy +++ /dev/null @@ -1,137 +0,0 @@ -// 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. - -{namespace domain.registry.tools.domain_update} -/** - * Update domain - */ -{template domainupdate stricthtml="false"} - {@param domain: string} - {@param add: bool} - {@param addNameservers: list} - {@param addStatuses: list} - {@param remove: bool} - {@param removeNameservers: list} - {@param removeStatuses: list} - {@param change: bool} - {@param? password: string|null} - {@param secdns: bool} - {@param addDsRecords: list<[keyTag:int, alg:int, digestType:int, digest:string]>} - {@param removeDsRecords: list<[keyTag:int, alg:int, digestType:int, digest:string]>} - {@param removeAllDsRecords: bool} - {@param? autorenews: string|null} - {@param? reason: string|null} - {@param? requestedByRegistrar: string|null} - - - - - - - {$domain} - {if $add} - - {if length($addNameservers) > 0} - - {for $s in $addNameservers} - {$s} - {/for} - - {/if} - {for $status in $addStatuses} - - {/for} - - {/if} - {if $remove} - - {if length($removeNameservers) > 0} - - {for $s in $removeNameservers} - {$s} - {/for} - - {/if} - {for $status in $removeStatuses} - - {/for} - - {/if} - {if $change} - - {if $password} - - {$password} - - {/if} - - {/if} - - - {if $secdns || $autorenews || $reason || $requestedByRegistrar} - - {if $secdns} - - {if $removeAllDsRecords} - - true - - {/if} - {if length($removeDsRecords) > 0} - - {for $dsRecord in $removeDsRecords} - - {$dsRecord.keyTag} - {$dsRecord.alg} - {$dsRecord.digestType} - {$dsRecord.digest} - - {/for} - - {/if} - {if length($addDsRecords) > 0} - - {for $dsRecord in $addDsRecords} - - {$dsRecord.keyTag} - {$dsRecord.alg} - {$dsRecord.digestType} - {$dsRecord.digest} - - {/for} - - {/if} - - {/if} - {if $autorenews} - - {$autorenews} - - {/if} - {if $reason || $requestedByRegistrar} - - {if $reason} - {$reason} - {/if} - {if $requestedByRegistrar} - {$requestedByRegistrar} - {/if} - - {/if} - - {/if} - RegistryTool - - -{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/HostCreate.soy b/core/src/main/resources/google/registry/tools/soy/HostCreate.soy deleted file mode 100644 index 0e583f916c7..00000000000 --- a/core/src/main/resources/google/registry/tools/soy/HostCreate.soy +++ /dev/null @@ -1,45 +0,0 @@ -// 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. - -{namespace domain.registry.tools.host_create} -/** - * Create host - */ -{template hostcreate stricthtml="false"} - {@param hostname: string} - {@param? ipv4addresses: list|null} - {@param? ipv6addresses: list|null} - - - - - - - {$hostname} - {if $ipv4addresses} - {for $ipv4 in $ipv4addresses} - {$ipv4} - {/for} - {/if} - {if $ipv6addresses} - {for $ipv6 in $ipv6addresses} - {$ipv6} - {/for} - {/if} - - - RegistryTool - - -{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/HostDelete.soy b/core/src/main/resources/google/registry/tools/soy/HostDelete.soy deleted file mode 100644 index 63a07c88f72..00000000000 --- a/core/src/main/resources/google/registry/tools/soy/HostDelete.soy +++ /dev/null @@ -1,42 +0,0 @@ -// 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. - -{namespace domain.registry.tools.host_delete} - -/** - * Delete host request - */ -{template deletehost stricthtml="false"} -{@param hostName: string} -{@param reason: string} -{@param requestedByRegistrar: any} - - - - - - {$hostName} - - - - - Deleted by registry administrator: {$reason} - {$requestedByRegistrar} - - - RegistryTool - - -{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/RemoveIpAddress.soy b/core/src/main/resources/google/registry/tools/soy/RemoveIpAddress.soy deleted file mode 100644 index 281e0abbb03..00000000000 --- a/core/src/main/resources/google/registry/tools/soy/RemoveIpAddress.soy +++ /dev/null @@ -1,47 +0,0 @@ -// 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. - -{namespace domain.registry.tools.remove_ip_address} - -/** - * Request to remove IP addresses. - */ -{template remove_ip_address stricthtml="false"} -{@param name: string} -{@param ipAddresses: list>} -{@param requestedByRegistrar: string} - - - - - - {$name} - {for $ip in $ipAddresses} - - {$ip['address']} - - {/for} - - - - - External IP address removed by registry administrator. - {$requestedByRegistrar} - - - ABC-12345 - - -{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/UniformRapidSuspension.soy b/core/src/main/resources/google/registry/tools/soy/UniformRapidSuspension.soy deleted file mode 100644 index 8df6e19054c..00000000000 --- a/core/src/main/resources/google/registry/tools/soy/UniformRapidSuspension.soy +++ /dev/null @@ -1,90 +0,0 @@ -// 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. - -{namespace domain.registry.tools.uniform_rapid_suspension} - -/** - * Uniform Rapid Suspension - */ -{template uniformrapidsuspension stricthtml="false"} -{@param domainName: string} -{@param hostsToAdd: list} -{@param hostsToRemove: list} -{@param statusesToApply: list} -{@param statusesToRemove: list} -{@param newDsData: list<[keyTag:int, alg:int, digestType:int, digest:string]>} -{@param reason: string} -{@param autorenews: string} - - - - - - {$domainName} - - {if length($hostsToAdd) > 0} - - {for $ha in $hostsToAdd} - {$ha} - {/for} - - {/if} - {for $la in $statusesToApply} - - {/for} - - - {if length($hostsToRemove) > 0} - - {for $hr in $hostsToRemove} - {$hr} - {/for} - - {/if} - {for $lr in $statusesToRemove} - - {/for} - - - - - - - true - - {if length($newDsData) > 0} - - {for $ds in $newDsData} - - {$ds.keyTag} - {$ds.alg} - {$ds.digestType} - {$ds.digest} - - {/for} - - {/if} - - - {$autorenews} - - - {$reason} - false - - - RegistryTool - - -{/template} diff --git a/core/src/main/resources/google/registry/tools/soy/UpdateServerLocks.soy b/core/src/main/resources/google/registry/tools/soy/UpdateServerLocks.soy deleted file mode 100644 index 48706c97d16..00000000000 --- a/core/src/main/resources/google/registry/tools/soy/UpdateServerLocks.soy +++ /dev/null @@ -1,56 +0,0 @@ -// 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. - -{namespace domain.registry.tools.update_server_locks} - -/** - * Update server locks - */ -{template updateserverlocks stricthtml="false"} -{@param domainName: string} -{@param locksToApply: list} -{@param locksToRemove: list} -{@param requestedByRegistrar: any} -{@param? reason: string|null} - - - - - - {$domainName} - - {for $a in $locksToApply} - - {/for} - - - {for $r in $locksToRemove} - - {/for} - - - - - - {if $reason} - {$reason} - {/if} - {$requestedByRegistrar} - - - RegistryTool - - -{/template} diff --git a/core/src/test/java/google/registry/flows/ResourceFlowTestCase.java b/core/src/test/java/google/registry/flows/ResourceFlowTestCase.java index 8b9f04a1ba0..ceddfcc5055 100644 --- a/core/src/test/java/google/registry/flows/ResourceFlowTestCase.java +++ b/core/src/test/java/google/registry/flows/ResourceFlowTestCase.java @@ -85,10 +85,9 @@ protected T reloadResourceAndCloneAtTime(T resource, Ins return refreshedResource; } - private ResourceCommand.SingleResourceCommand getResourceCommand() throws Exception { - return (ResourceCommand.SingleResourceCommand) - ((ResourceCommandWrapper) eppLoader.getEpp().getCommandWrapper().getCommand()) - .getResourceCommand(); + private ResourceCommand getResourceCommand() throws Exception { + return ((ResourceCommandWrapper) eppLoader.getEpp().getCommandWrapper().getCommand()) + .getResourceCommand(); } protected String getUniqueIdFromCommand() throws Exception { diff --git a/core/src/test/java/google/registry/model/domain/DomainCommandTest.java b/core/src/test/java/google/registry/model/domain/DomainCommandTest.java index cdbd927ddb5..6444efcab96 100644 --- a/core/src/test/java/google/registry/model/domain/DomainCommandTest.java +++ b/core/src/test/java/google/registry/model/domain/DomainCommandTest.java @@ -15,7 +15,7 @@ package google.registry.model.domain; import static google.registry.testing.DatabaseHelper.persistActiveHost; -import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertThrows; import google.registry.flows.FlowUtils; import google.registry.flows.domain.DomainFlowUtils.RegistrantProhibitedException; diff --git a/core/src/test/java/google/registry/model/eppinput/EppInputTest.java b/core/src/test/java/google/registry/model/eppinput/EppInputTest.java index ac7e14bfef4..36a4caf0572 100644 --- a/core/src/test/java/google/registry/model/eppinput/EppInputTest.java +++ b/core/src/test/java/google/registry/model/eppinput/EppInputTest.java @@ -14,57 +14,177 @@ package google.registry.model.eppinput; -import static com.google.common.truth.Truth.assertThat; -import static google.registry.model.eppcommon.EppXmlTransformer.unmarshal; -import static google.registry.testing.TestDataHelper.loadBytes; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static google.registry.xml.XmlTestUtils.assertXmlEquals; +import static java.nio.charset.StandardCharsets.UTF_8; -import google.registry.model.domain.DomainTest; -import google.registry.model.eppinput.EppInput.InnerCommand; -import google.registry.model.eppinput.EppInput.Login; -import google.registry.xml.XmlException; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import google.registry.model.domain.DomainCommand; +import google.registry.model.eppcommon.EppXmlTransformer; +import google.registry.model.eppcommon.StatusValue; +import google.registry.xml.ValidationMode; import org.junit.jupiter.api.Test; -/** Unit tests for {@link EppInput}. */ +/** Unit tests for {@link EppInput} builders and marshaling. */ class EppInputTest { @Test - void testUnmarshalling_domainCheck() throws Exception { - EppInput input = - unmarshal(EppInput.class, loadBytes(DomainTest.class, "domain_check.xml").read()); - assertThat(input.getCommandWrapper().getClTrid()).hasValue("ABC-12345"); - assertThat(input.getCommandType()).isEqualTo("check"); - assertThat(input.getResourceType()).hasValue("domain"); - assertThat(input.getSingleTargetId()).isEmpty(); - assertThat(input.getTargetIds()).containsExactly("example.com", "example.net", "example.org"); + void testBuilder_emptyExtensions_omitsExtensionTag() throws Exception { + EppInput eppInput = + new EppInput.Builder() + .setCommandWrapper( + new EppInput.CommandWrapper.Builder() + .setCommand( + new EppInput.Create.Builder() + .setResourceCommand( + new DomainCommand.Create.Builder() + .setDomainName("example.tld") + .build()) + .build()) + .setExtensions(ImmutableList.of()) + .setClTrid("RegistryTool") + .build()) + .build(); + + String xml = + new String(EppXmlTransformer.marshalInput(eppInput, ValidationMode.LENIENT), UTF_8); + assertXmlEquals( + """ + + + + + + example.tld + + + RegistryTool + + + """, + xml); } @Test - void testUnmarshalling_login() throws Exception { - EppInput input = unmarshal(EppInput.class, loadBytes(getClass(), "login_valid.xml").read()); - assertThat(input.getCommandWrapper().getClTrid()).hasValue("ABC-12345"); - assertThat(input.getCommandType()).isEqualTo("login"); - assertThat(input.getResourceType()).isEmpty(); - assertThat(input.getSingleTargetId()).isEmpty(); - assertThat(input.getTargetIds()).isEmpty(); - InnerCommand command = input.getCommandWrapper().getCommand(); - assertThat(command).isInstanceOf(Login.class); - Login loginCommand = (Login) command; - assertThat(loginCommand.clientId).isEqualTo("NewRegistrar"); - assertThat(loginCommand.password).isEqualTo("foo-BAR2"); - assertThat(loginCommand.newPassword).isNull(); - assertThat(loginCommand.options.version).isEqualTo("1.0"); - assertThat(loginCommand.options.language).isEqualTo("en"); - assertThat(loginCommand.services.objectServices) - .containsExactly("urn:ietf:params:xml:ns:host-1.0", "urn:ietf:params:xml:ns:domain-1.0"); - assertThat(loginCommand.services.serviceExtensions) - .containsExactly("urn:ietf:params:xml:ns:launch-1.0", "urn:ietf:params:xml:ns:rgp-1.0"); + void testBuilder_nullExtensions_omitsExtensionTag() throws Exception { + EppInput eppInput = + new EppInput.Builder() + .setCommandWrapper( + new EppInput.CommandWrapper.Builder() + .setCommand( + new EppInput.Create.Builder() + .setResourceCommand( + new DomainCommand.Create.Builder() + .setDomainName("example.tld") + .build()) + .build()) + .setExtensions(null) + .setClTrid("RegistryTool") + .build()) + .build(); + + String xml = + new String(EppXmlTransformer.marshalInput(eppInput, ValidationMode.LENIENT), UTF_8); + assertXmlEquals( + """ + + + + + + example.tld + + + RegistryTool + + + """, + xml); } @Test - void testUnmarshalling_loginTagInWrongCase_throws() { - assertThrows( - XmlException.class, - () -> unmarshal(EppInput.class, loadBytes(getClass(), "login_wrong_case.xml").read())); + void testBuilder_domainUpdate_emptyAddRemove_omitsInnerTags() throws Exception { + EppInput eppInput = + new EppInput.Builder() + .setCommandWrapper( + new EppInput.CommandWrapper.Builder() + .setCommand( + new EppInput.Update.Builder() + .setResourceCommand( + new DomainCommand.Update.Builder() + .setTargetId("example.tld") + .setInnerAdd( + new DomainCommand.Update.DomainAddRemove.Builder().build()) + .setInnerRemove( + new DomainCommand.Update.DomainAddRemove.Builder().build()) + .build()) + .build()) + .setClTrid("RegistryTool") + .build()) + .build(); + + String xml = + new String(EppXmlTransformer.marshalInput(eppInput, ValidationMode.LENIENT), UTF_8); + assertXmlEquals( + """ + + + + + + example.tld + + + + + RegistryTool + + + """, + xml); + } + + @Test + void testBuilder_domainUpdate_withStatuses() throws Exception { + EppInput eppInput = + new EppInput.Builder() + .setCommandWrapper( + new EppInput.CommandWrapper.Builder() + .setCommand( + new EppInput.Update.Builder() + .setResourceCommand( + new DomainCommand.Update.Builder() + .setTargetId("example.tld") + .setInnerAdd( + new DomainCommand.Update.DomainAddRemove.Builder() + .setStatusValues( + ImmutableSet.of(StatusValue.CLIENT_HOLD)) + .build()) + .build()) + .build()) + .setClTrid("RegistryTool") + .build()) + .build(); + + String xml = + new String(EppXmlTransformer.marshalInput(eppInput, ValidationMode.LENIENT), UTF_8); + assertXmlEquals( + """ + + + + + + example.tld + + + + + + RegistryTool + + + """, + xml); } } diff --git a/core/src/test/java/google/registry/reporting/spec11/PublishSpec11ReportActionTest.java b/core/src/test/java/google/registry/reporting/spec11/PublishSpec11ReportActionTest.java index 25079f2d357..4cf4aba7259 100644 --- a/core/src/test/java/google/registry/reporting/spec11/PublishSpec11ReportActionTest.java +++ b/core/src/test/java/google/registry/reporting/spec11/PublishSpec11ReportActionTest.java @@ -37,7 +37,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.net.MediaType; import google.registry.beam.spec11.ThreatMatch; -import google.registry.reporting.spec11.soy.Spec11EmailSoyInfo; +import google.registry.reporting.spec11.Spec11EmailUtils.Spec11EmailTemplate; import google.registry.testing.FakeResponse; import java.io.IOException; import java.time.LocalDate; @@ -76,7 +76,6 @@ void beforeEach() throws Exception { expectedJob = new Job(); when(get.execute()).thenReturn(expectedJob); emailUtils = mock(Spec11EmailUtils.class); - parser = mock(Spec11RegistrarThreatMatchesParser.class); response = new FakeResponse(); parser = mock(Spec11RegistrarThreatMatchesParser.class); publishAction = @@ -113,7 +112,7 @@ void testJobDone_emailsOnlyMonthlyResultsOnSecondOfMonth() throws Exception { verify(emailUtils) .emailSpec11Reports( secondOfMonth, - Spec11EmailSoyInfo.MONTHLY_SPEC_11_EMAIL, + Spec11EmailTemplate.MONTHLY, "Super Cool Registry Monthly Threat Detector [2018-06-02]", sampleThreatMatches()); verifyNoMoreInteractions(emailUtils); @@ -163,7 +162,7 @@ void testJobDone_onlyDailyResults() throws Exception { verify(emailUtils) .emailSpec11Reports( date, - Spec11EmailSoyInfo.DAILY_SPEC_11_EMAIL, + Spec11EmailTemplate.DAILY, "Super Cool Registry Daily Threat Detector [2018-06-05]", sampleThreatMatches()); verifyNoMoreInteractions(emailUtils); @@ -196,7 +195,7 @@ void testJobDone_multipleEntriesWithSameEmail() throws Exception { verify(emailUtils) .emailSpec11Reports( date, - Spec11EmailSoyInfo.DAILY_SPEC_11_EMAIL, + Spec11EmailTemplate.DAILY, "Super Cool Registry Daily Threat Detector [2018-06-05]", expectedMatchSet); verifyNoMoreInteractions(emailUtils); @@ -214,7 +213,7 @@ void testJobDone_noDifferentResults() throws Exception { verify(emailUtils) .emailSpec11Reports( date, - Spec11EmailSoyInfo.DAILY_SPEC_11_EMAIL, + Spec11EmailTemplate.DAILY, "Super Cool Registry Daily Threat Detector [2018-06-05]", ImmutableSet.of()); verifyNoMoreInteractions(emailUtils); diff --git a/core/src/test/java/google/registry/reporting/spec11/Spec11EmailUtilsTest.java b/core/src/test/java/google/registry/reporting/spec11/Spec11EmailUtilsTest.java index 2085bdfb3b2..e2ec7279472 100644 --- a/core/src/test/java/google/registry/reporting/spec11/Spec11EmailUtilsTest.java +++ b/core/src/test/java/google/registry/reporting/spec11/Spec11EmailUtilsTest.java @@ -23,6 +23,7 @@ import static google.registry.reporting.spec11.Spec11RegistrarThreatMatchesParserTest.sampleThreatMatches; import static google.registry.testing.DatabaseHelper.createTld; import static google.registry.testing.DatabaseHelper.loadByEntity; +import static google.registry.testing.DatabaseHelper.newDomain; import static google.registry.testing.DatabaseHelper.persistActiveHost; import static google.registry.testing.DatabaseHelper.persistResource; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -39,10 +40,10 @@ import google.registry.model.host.Host; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension; -import google.registry.reporting.spec11.soy.Spec11EmailSoyInfo; -import google.registry.testing.DatabaseHelper; +import google.registry.reporting.spec11.Spec11EmailUtils.Spec11EmailTemplate; import google.registry.util.EmailMessage; import google.registry.util.Sleeper; +import google.registry.util.TemplateRenderer; import jakarta.mail.MessagingException; import jakarta.mail.internet.InternetAddress; import java.time.Duration; @@ -64,40 +65,80 @@ class Spec11EmailUtilsTest { private static final ImmutableList FAKE_RESOURCES = ImmutableList.of("foo"); private static final String DAILY_EMAIL_FORMAT = - "Dear registrar partner,

Super Cool Registry conducts a daily analysis of all domains" - + " registered in its TLDs to identify potential security concerns. On 2018-07-15, the" - + " following domains that your registrar manages were flagged for potential security" - + " concerns:

%s" - + "
Domain NameThreat Type

Please communicate these findings to the registrant and work with the" - + " registrant to mitigate any security issues and have the domains delisted.

" - + "Some helpful resources for getting off a blocked list include:" - + "
  • foo

If you believe that any of the domains were reported in" - + " error, or are still receiving reports for issues that have been remediated, please" - + " submit" - + " a request to have the site reviewed.

You will continue to receive daily" - + " notices when new domains managed by your registrar are flagged for abuse, as well as" - + " a monthly summary of all of your domains under management that remain flagged for" - + " abuse.

If you would like to change the email to which these notices are sent" - + " please update your abuse contact using your registrar portal account.

If you" - + " have any questions regarding this notice, please contact abuse@test.com.

"; + """ + +

Dear registrar partner,

+ +

Super Cool Registry conducts a daily analysis of all domains registered in its TLDs to + identify potential security concerns. On 2018-07-15, the following domains that your + registrar manages were flagged for potential security concerns:

+ + + + + + + %s
Domain NameThreat Type
+ +

Please communicate these findings to the registrant and work with the + registrant to mitigate any security issues and have the domains delisted.

+ +

Some helpful resources for getting off a blocked list include:

+
    +
  • foo
  • +
+ +

If you believe that any of the domains were reported in error, or are still receiving + reports for issues that have been remediated, + please submit + a request to have the site reviewed.

+ +

You will continue to receive daily notices when new domains managed by your registrar + are flagged for abuse, as well as a monthly summary of all of your domains under management + that remain flagged for abuse.

+ +

If you would like to change the email to which these notices are sent, please update your + abuse contact using your registrar portal account.

+ +

If you have any questions regarding this notice, please contact abuse@test.com.

+ """; private static final String MONTHLY_EMAIL_FORMAT = - "Dear registrar partner,

Super Cool Registry previously notified you when the following" - + " domains managed by your registrar were flagged for potential security" - + " concerns.

The following domains that you manage continue to be flagged by our" - + " analysis for potential security concerns. This may be because the registrants have" - + " not completed the requisite steps to mitigate the potential security abuse and/or" - + " have it reviewed and delisted.

%s
Domain NameThreat" - + " Type

Please work with the registrant to mitigate any security" - + " issues and have the domains delisted. If you believe that any of the domains were" - + " reported in error, or are still receiving reports for issues that have been" - + " remediated, please submit a" - + " request to have the site reviewed.

Some helpful resources for getting off a" - + " blocked list include:
  • foo

You will continue to receive a monthly" - + " summary of all domains managed by your registrar that remain on our lists of" - + " potential security threats. You will also receive a daily notice when any new" - + " domains are added to these lists.

If you have any questions regarding this" - + " notice, please contact abuse@test.com.

"; + """ + +

Dear registrar partner,

+ +

Super Cool Registry previously notified you when the following domains managed by your + registrar were flagged for potential security concerns.

+ +

The following domains that you manage continue to be flagged by our analysis for + potential security concerns. This may be because the registrants have not completed the + requisite steps to mitigate the potential security abuse and/or have it reviewed and + delisted.

+ + + + + + + %s
Domain NameThreat Type
+ +

Please work with the registrant to mitigate any security issues and have the + domains delisted. If you believe that any of the domains were reported in error, or are + still receiving reports for issues that have been remediated, + please submit a + request to have the site reviewed.

+ +

Some helpful resources for getting off a blocked list include:

+
    +
  • foo
  • +
+ +

You will continue to receive a monthly summary of all domains managed by your registrar + that remain on our lists of potential security threats. You will also receive a daily + notice when any new domains are added to these lists.

+ +

If you have any questions regarding this notice, please contact abuse@test.com.

+ """; @RegisterExtension final JpaIntegrationTestExtension jpa = @@ -120,6 +161,7 @@ void beforeEach() throws Exception { new Spec11EmailUtils( gmailClient, sleeper, + new TemplateRenderer(), emailThrottleDuration, new InternetAddress("my-receiver@test.com"), new InternetAddress("abuse@test.com"), @@ -139,7 +181,7 @@ void beforeEach() throws Exception { void testSuccess_sleepsBetweenSending() throws Exception { emailUtils.emailSpec11Reports( date, - Spec11EmailSoyInfo.MONTHLY_SPEC_11_EMAIL, + Spec11EmailTemplate.MONTHLY, "Super Cool Registry Monthly Threat Detector [2018-07-15]", sampleThreatMatches()); // We inspect individual parameters because Message doesn't implement equals(). @@ -152,7 +194,7 @@ void testSuccess_sleepsBetweenSending() throws Exception { void testSuccess_emailMonthlySpec11Reports() throws Exception { emailUtils.emailSpec11Reports( date, - Spec11EmailSoyInfo.MONTHLY_SPEC_11_EMAIL, + Spec11EmailTemplate.MONTHLY, "Super Cool Registry Monthly Threat Detector [2018-07-15]", sampleThreatMatches()); // We inspect individual parameters because Message doesn't implement equals(). @@ -163,7 +205,14 @@ void testSuccess_emailMonthlySpec11Reports() throws Exception { "the.registrar@example.com", ImmutableList.of("abuse@test.com", "bcc@test.com"), "Super Cool Registry Monthly Threat Detector [2018-07-15]", - String.format(MONTHLY_EMAIL_FORMAT, "a[.]comMALWARE"), + String.format( + MONTHLY_EMAIL_FORMAT, + """ + + a[.]com + MALWARE + + """), Optional.of(MediaType.HTML_UTF_8)); validateMessage( capturedContents.get(1), @@ -172,7 +221,16 @@ void testSuccess_emailMonthlySpec11Reports() throws Exception { "Super Cool Registry Monthly Threat Detector [2018-07-15]", String.format( MONTHLY_EMAIL_FORMAT, - "b[.]comMALWAREc[.]comMALWARE"), + """ + + b[.]com + MALWARE + + + c[.]com + MALWARE + + """), Optional.of(MediaType.HTML_UTF_8)); validateMessage( capturedContents.get(2), @@ -187,7 +245,7 @@ void testSuccess_emailMonthlySpec11Reports() throws Exception { void testSuccess_emailDailySpec11Reports() throws Exception { emailUtils.emailSpec11Reports( date, - Spec11EmailSoyInfo.DAILY_SPEC_11_EMAIL, + Spec11EmailTemplate.DAILY, "Super Cool Registry Daily Threat Detector [2018-07-15]", sampleThreatMatches()); // We inspect individual parameters because Message doesn't implement equals(). @@ -198,7 +256,14 @@ void testSuccess_emailDailySpec11Reports() throws Exception { "the.registrar@example.com", ImmutableList.of("abuse@test.com", "bcc@test.com"), "Super Cool Registry Daily Threat Detector [2018-07-15]", - String.format(DAILY_EMAIL_FORMAT, "a[.]comMALWARE"), + String.format( + DAILY_EMAIL_FORMAT, + """ + + a[.]com + MALWARE + + """), Optional.of(MediaType.HTML_UTF_8)); validateMessage( capturedMessages.get(1), @@ -207,7 +272,16 @@ void testSuccess_emailDailySpec11Reports() throws Exception { "Super Cool Registry Daily Threat Detector [2018-07-15]", String.format( DAILY_EMAIL_FORMAT, - "b[.]comMALWAREc[.]comMALWARE"), + """ + + b[.]com + MALWARE + + + c[.]com + MALWARE + + """), Optional.of(MediaType.HTML_UTF_8)); validateMessage( capturedMessages.get(2), @@ -225,7 +299,7 @@ void testSuccess_skipsInactiveDomain() throws Exception { persistResource(loadByEntity(bDomain).asBuilder().addStatusValue(CLIENT_HOLD).build()); emailUtils.emailSpec11Reports( date, - Spec11EmailSoyInfo.MONTHLY_SPEC_11_EMAIL, + Spec11EmailTemplate.MONTHLY, "Super Cool Registry Monthly Threat Detector [2018-07-15]", sampleThreatMatches()); // We inspect individual parameters because Message doesn't implement equals(). @@ -236,7 +310,14 @@ void testSuccess_skipsInactiveDomain() throws Exception { "new.registrar@example.com", ImmutableList.of("abuse@test.com", "bcc@test.com"), "Super Cool Registry Monthly Threat Detector [2018-07-15]", - String.format(MONTHLY_EMAIL_FORMAT, "c[.]comMALWARE"), + String.format( + MONTHLY_EMAIL_FORMAT, + """ + + c[.]com + MALWARE + + """), Optional.of(MediaType.HTML_UTF_8)); validateMessage( capturedContents.get(1), @@ -252,11 +333,13 @@ void testSuccess_dealsWithDeletedDomains() throws Exception { // Create an inactive domain and an active domain with the same name. persistResource(loadByEntity(aDomain).asBuilder().addStatusValue(SERVER_HOLD).build()); Host host = persistActiveHost("ns1.example.com"); - aDomain = persistResource(aDomain.asBuilder().setNameservers(host.createVKey()).build()); + aDomain = + persistResource( + aDomain.asBuilder().setNameservers(ImmutableSet.of(host.createVKey())).build()); emailUtils.emailSpec11Reports( date, - Spec11EmailSoyInfo.MONTHLY_SPEC_11_EMAIL, + Spec11EmailTemplate.MONTHLY, "Super Cool Registry Monthly Threat Detector [2018-07-15]", sampleThreatMatches()); // We inspect individual parameters because Message doesn't implement equals(). @@ -267,7 +350,14 @@ void testSuccess_dealsWithDeletedDomains() throws Exception { "the.registrar@example.com", ImmutableList.of("abuse@test.com", "bcc@test.com"), "Super Cool Registry Monthly Threat Detector [2018-07-15]", - String.format(MONTHLY_EMAIL_FORMAT, "a[.]comMALWARE"), + String.format( + MONTHLY_EMAIL_FORMAT, + """ + + a[.]com + MALWARE + + """), Optional.of(MediaType.HTML_UTF_8)); validateMessage( capturedContents.get(1), @@ -276,7 +366,16 @@ void testSuccess_dealsWithDeletedDomains() throws Exception { "Super Cool Registry Monthly Threat Detector [2018-07-15]", String.format( MONTHLY_EMAIL_FORMAT, - "b[.]comMALWAREc[.]comMALWARE"), + """ + + b[.]com + MALWARE + + + c[.]com + MALWARE + + """), Optional.of(MediaType.HTML_UTF_8)); validateMessage( capturedContents.get(2), @@ -304,7 +403,7 @@ void testOneFailure_sendsAlert() throws Exception { () -> emailUtils.emailSpec11Reports( date, - Spec11EmailSoyInfo.MONTHLY_SPEC_11_EMAIL, + Spec11EmailTemplate.MONTHLY, "Super Cool Registry Monthly Threat Detector [2018-07-15]", ImmutableSet.copyOf(matches))); assertThat(thrown) @@ -319,7 +418,14 @@ void testOneFailure_sendsAlert() throws Exception { "the.registrar@example.com", ImmutableList.of("abuse@test.com", "bcc@test.com"), "Super Cool Registry Monthly Threat Detector [2018-07-15]", - String.format(MONTHLY_EMAIL_FORMAT, "a[.]comMALWARE"), + String.format( + MONTHLY_EMAIL_FORMAT, + """ + + a[.]com + MALWARE + + """), Optional.of(MediaType.HTML_UTF_8)); validateMessage( capturedMessages.get(1), @@ -328,7 +434,16 @@ void testOneFailure_sendsAlert() throws Exception { "Super Cool Registry Monthly Threat Detector [2018-07-15]", String.format( MONTHLY_EMAIL_FORMAT, - "b[.]comMALWAREc[.]comMALWARE"), + """ + + b[.]com + MALWARE + + + c[.]com + MALWARE + + """), Optional.of(MediaType.HTML_UTF_8)); validateMessage( capturedMessages.get(2), @@ -363,7 +478,7 @@ void testSuccess_useRdapAbuseEmailIfAvailable() throws Exception { .build()); emailUtils.emailSpec11Reports( date, - Spec11EmailSoyInfo.MONTHLY_SPEC_11_EMAIL, + Spec11EmailTemplate.MONTHLY, "Super Cool Registry Monthly Threat Detector [2018-07-15]", sampleThreatMatches()); verify(gmailClient, times(3)).sendEmail(contentCaptor.capture()); @@ -379,7 +494,7 @@ void testFailure_badClientId() { () -> emailUtils.emailSpec11Reports( date, - Spec11EmailSoyInfo.MONTHLY_SPEC_11_EMAIL, + Spec11EmailTemplate.MONTHLY, "Super Cool Registry Monthly Threat Detector [2018-07-15]", ImmutableSet.of( RegistrarThreatMatches.create( @@ -409,12 +524,13 @@ private void validateMessage( expectedContentBuilder.addBcc(new InternetAddress(bcc)); } contentType.ifPresent(expectedContentBuilder::setContentType); + assertThat(message.body()).isEqualTo(body); assertThat(message).isEqualTo(expectedContentBuilder.build()); } private static Domain persistDomainWithHost(String domainName, Host host) { return persistResource( - DatabaseHelper.newDomain(domainName) + newDomain(domainName) .asBuilder() .setNameservers(ImmutableSet.of(host.createVKey())) .build()); diff --git a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant.xml b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant.xml index 4435e4601ab..6a7aecd05ff 100644 --- a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant.xml +++ b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant.xml @@ -1,13 +1,10 @@ + - + example.tld 2 - jd1234 - jd1234 - jd1234 abcdefghijklmnop diff --git a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_fee_premium.xml b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_fee_premium.xml index a8baf0c384d..10944ac8c22 100644 --- a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_fee_premium.xml +++ b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_fee_premium.xml @@ -1,13 +1,10 @@ + - + premium.tld 2 - jd1234 - jd1234 - jd1234 abcdefghijklmnop diff --git a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_fee_standard.xml b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_fee_standard.xml index 08030ce7f6d..ab703522bc9 100644 --- a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_fee_standard.xml +++ b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_fee_standard.xml @@ -1,13 +1,10 @@ + - + example.tld 2 - jd1234 - jd1234 - jd1234 abcdefghijklmnop diff --git a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_multiple_word_reason.xml b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_multiple_word_reason.xml index 3479f42a4cd..59a285b1f20 100644 --- a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_multiple_word_reason.xml +++ b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_multiple_word_reason.xml @@ -1,13 +1,10 @@ + - + example.tld 2 - jd1234 - jd1234 - jd1234 abcdefghijklmnop diff --git a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_no_reason.xml b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_no_reason.xml index 8e460d390b7..5a9967d0b3c 100644 --- a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_no_reason.xml +++ b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_no_reason.xml @@ -1,13 +1,10 @@ + - + example.tld 2 - jd1234 - jd1234 - jd1234 abcdefghijklmnop diff --git a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_password.xml b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_password.xml index 1a1472f7a61..8df4654b25e 100644 --- a/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_password.xml +++ b/core/src/test/resources/google/registry/tools/server/domain_create_anchor_tenant_password.xml @@ -1,13 +1,10 @@ + - + example.tld 2 - jd1234 - jd1234 - jd1234 foo diff --git a/core/src/test/resources/google/registry/tools/server/domain_create_palladium.xml b/core/src/test/resources/google/registry/tools/server/domain_create_palladium.xml index a2363d29f52..5848e9edbca 100644 --- a/core/src/test/resources/google/registry/tools/server/domain_create_palladium.xml +++ b/core/src/test/resources/google/registry/tools/server/domain_create_palladium.xml @@ -1,9 +1,8 @@ - + - + palladium.tld 1 diff --git a/core/src/test/resources/google/registry/tools/server/domain_create_parajiumu_3yrs.xml b/core/src/test/resources/google/registry/tools/server/domain_create_parajiumu_3yrs.xml index fc89c75bb74..18f2a8f1e62 100644 --- a/core/src/test/resources/google/registry/tools/server/domain_create_parajiumu_3yrs.xml +++ b/core/src/test/resources/google/registry/tools/server/domain_create_parajiumu_3yrs.xml @@ -1,9 +1,8 @@ - + - + parajiumu.baar 3 diff --git a/core/src/test/resources/google/registry/tools/server/update_server_locks_apply_all.xml b/core/src/test/resources/google/registry/tools/server/update_server_locks_apply_all.xml index da247f34bab..15399b65330 100644 --- a/core/src/test/resources/google/registry/tools/server/update_server_locks_apply_all.xml +++ b/core/src/test/resources/google/registry/tools/server/update_server_locks_apply_all.xml @@ -5,13 +5,12 @@ example.tld - - - - - + + + + + -
diff --git a/core/src/test/resources/google/registry/tools/server/update_server_locks_apply_one.xml b/core/src/test/resources/google/registry/tools/server/update_server_locks_apply_one.xml index 4f16abdf500..32cb7b8f36d 100644 --- a/core/src/test/resources/google/registry/tools/server/update_server_locks_apply_one.xml +++ b/core/src/test/resources/google/registry/tools/server/update_server_locks_apply_one.xml @@ -5,9 +5,8 @@ example.tld - + -
diff --git a/core/src/test/resources/google/registry/tools/server/update_server_locks_multiple_word_reason.xml b/core/src/test/resources/google/registry/tools/server/update_server_locks_multiple_word_reason.xml index 7517dfd5dfe..8599d9c2c86 100644 --- a/core/src/test/resources/google/registry/tools/server/update_server_locks_multiple_word_reason.xml +++ b/core/src/test/resources/google/registry/tools/server/update_server_locks_multiple_word_reason.xml @@ -5,9 +5,8 @@ example.tld - + - diff --git a/core/src/test/resources/google/registry/tools/server/update_server_locks_remove_all.xml b/core/src/test/resources/google/registry/tools/server/update_server_locks_remove_all.xml index 438460b1eb4..2266889ae58 100644 --- a/core/src/test/resources/google/registry/tools/server/update_server_locks_remove_all.xml +++ b/core/src/test/resources/google/registry/tools/server/update_server_locks_remove_all.xml @@ -4,13 +4,12 @@ example.tld - - - - - - + + + + + diff --git a/core/src/test/resources/google/registry/tools/server/update_server_locks_remove_one.xml b/core/src/test/resources/google/registry/tools/server/update_server_locks_remove_one.xml index 08a80cf10d3..188d4a4f7a2 100644 --- a/core/src/test/resources/google/registry/tools/server/update_server_locks_remove_one.xml +++ b/core/src/test/resources/google/registry/tools/server/update_server_locks_remove_one.xml @@ -4,9 +4,8 @@ example.tld - - + diff --git a/core/src/test/resources/google/registry/tools/server/xn--q9jyb4c_2010-10-17_full_S1_R0.xml b/core/src/test/resources/google/registry/tools/server/xn--q9jyb4c_2010-10-17_full_S1_R0.xml index ae19d0cfbae..0f9f7f82f9c 100644 --- a/core/src/test/resources/google/registry/tools/server/xn--q9jyb4c_2010-10-17_full_S1_R0.xml +++ b/core/src/test/resources/google/registry/tools/server/xn--q9jyb4c_2010-10-17_full_S1_R0.xml @@ -131,7 +131,7 @@ 0 1 2 - 4 + 3 diff --git a/db/buildscript-gradle.lockfile b/db/buildscript-gradle.lockfile index d109decc7ed..929906e7097 100644 --- a/db/buildscript-gradle.lockfile +++ b/db/buildscript-gradle.lockfile @@ -4,8 +4,8 @@ com.fasterxml.jackson.core:jackson-annotations:2.21=classpath gradle.plugin.org.flywaydb:gradle-plugin-publishing:12.2.0=classpath org.flywaydb.flyway:org.flywaydb.flyway.gradle.plugin:12.2.0=classpath -org.flywaydb:flyway-core:12.6.1=classpath -org.flywaydb:flyway-database-postgresql:12.6.1=classpath +org.flywaydb:flyway-core:12.6.2=classpath +org.flywaydb:flyway-database-postgresql:12.6.2=classpath tools.jackson.core:jackson-core:3.1.1=classpath tools.jackson.core:jackson-databind:3.1.1=classpath tools.jackson:jackson-bom:3.1.1=classpath diff --git a/db/gradle.lockfile b/db/gradle.lockfile index ec6eb6cd0fe..d498b059e9a 100644 --- a/db/gradle.lockfile +++ b/db/gradle.lockfile @@ -71,7 +71,7 @@ dnsjava:dnsjava:3.6.4=deploy_jar,runtimeClasspath,testRuntimeClasspath info.picocli:picocli:4.7.7=checkstyle io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,testAnnotationProcessor io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,testAnnotationProcessor -io.github.java-diff-utils:java-diff-utils:4.16=testCompileClasspath,testRuntimeClasspath +io.github.java-diff-utils:java-diff-utils:4.17=testCompileClasspath,testRuntimeClasspath io.grpc:grpc-api:1.70.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-context:1.70.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.opencensus:opencensus-api:0.31.1=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -109,8 +109,9 @@ org.codehaus.plexus:plexus-classworlds:2.6.0=checkstyle org.codehaus.plexus:plexus-component-annotations:2.1.0=checkstyle org.codehaus.plexus:plexus-container-default:2.1.0=checkstyle org.codehaus.plexus:plexus-utils:3.3.0=checkstyle -org.flywaydb:flyway-core:12.6.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.flywaydb:flyway-database-postgresql:12.6.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.flywaydb:flyway-core:12.6.2=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.flywaydb:flyway-database-postgresql:12.6.2=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.freemarker:freemarker:2.3.34=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest-core:1.3=testCompileClasspath,testRuntimeClasspath org.jacoco:org.jacoco.agent:0.8.14=jacocoAgent,jacocoAnt org.jacoco:org.jacoco.ant:0.8.14=jacocoAnt diff --git a/dependencies.gradle b/dependencies.gradle index 4395e93d02c..8fe8bd0e6fc 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -84,7 +84,7 @@ ext { 'com.google.apis:google-api-services-drive:[v3-rev20240521-2.0.0,)', 'com.google.apis:google-api-services-gmail:[v1-rev20220404-2.0.0,)', 'com.google.apis:google-api-services-groupssettings:[v1-rev82-1.25.0,)', - 'com.google.apis:google-api-services-iam:[v1-rev20240118-2.0.0,0)', + 'com.google.apis:google-api-services-iam:[v1-rev20240118-2.0.0,)', 'com.google.apis:google-api-services-monitoring:[v3-rev20240519-2.0.0,)', 'com.google.apis:google-api-services-sheets:[v4-rev20240514-2.0.0,)', 'com.google.apis:google-api-services-storage:[v1-rev20210127-1.31.0,)', @@ -123,9 +123,9 @@ ext { 'com.google.oauth-client:google-oauth-client-servlet:[1.31.4,)', 'com.google.oauth-client:google-oauth-client:[1.31.4,)', 'com.google.re2j:re2j:[1.6,)', - 'com.google.template:soy:[2024-02-26,)', 'com.google.truth:truth:[1.1.2,)', 'com.googlecode.json-simple:json-simple:[1.1.1,)', + 'org.freemarker:freemarker:[2.3.32,)', 'com.ibm.icu:icu4j:[68.2,)', 'com.jcraft:jsch:[0.1.55,)', 'com.squareup:javapoet:[1.13.0,)', @@ -141,7 +141,7 @@ ext { 'io.netty:netty-tcnative-boringssl-static:[2.0.36.Final,)', 'jakarta.inject:jakarta.inject-api:[2.0.0,)', 'jakarta.mail:jakarta.mail-api:[2.1.3,)', - 'jakarta.persistence:jakarta.persistence-api:[3.2.0,4.+)', + 'jakarta.persistence:jakarta.persistence-api:[3.2.0,4.0.0)', 'jakarta.servlet:jakarta.servlet-api:[6.0,6.1)', 'jakarta.xml.bind:jakarta.xml.bind-api:[4.0.2,)', // Antlr is not a direct dependency, but we need to ensure that the diff --git a/jetty/gradle.lockfile b/jetty/gradle.lockfile index f152ea2161e..5b3f4574cf5 100644 --- a/jetty/gradle.lockfile +++ b/jetty/gradle.lockfile @@ -1,7 +1,6 @@ # This is a Gradle generated file for dependency locking. # Manual edits can break the build and are not advised. # This file is expected to be part of source control. -aopalliance:aopalliance:1.0=deploy_jar,runtimeClasspath,testRuntimeClasspath args4j:args4j:2.33=deploy_jar,runtimeClasspath,testRuntimeClasspath com.charleskorn.kaml:kaml:0.20.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.fasterxml.jackson.core:jackson-annotations:2.21=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -70,13 +69,13 @@ com.google.api:api-common:2.57.1=deploy_jar,runtimeClasspath,testRuntimeClasspat com.google.api:gax-grpc:2.74.1=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.api:gax-httpjson:2.74.1=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.api:gax:2.74.1=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.apis:google-api-services-admin-directory:directory_v1-rev20260227-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.apis:google-api-services-admin-directory:directory_v1-rev20260421-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.apis:google-api-services-bigquery:v2-rev20251012-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.apis:google-api-services-cloudresourcemanager:v1-rev20250606-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.apis:google-api-services-dataflow:v1b3-rev20260405-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.apis:google-api-services-dataflow:v1b3-rev20260503-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.apis:google-api-services-dns:v1-rev20260421-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.apis:google-api-services-drive:v3-rev20260428-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.apis:google-api-services-gmail:v1-rev20260427-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.apis:google-api-services-gmail:v1-rev20260511-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.apis:google-api-services-groupssettings:v1-rev20220614-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.apis:google-api-services-healthcare:v1-rev20240130-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.apis:google-api-services-iam:v2-rev20250502-2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -122,7 +121,6 @@ com.google.cloud:libraries-bom:26.48.0=deploy_jar,runtimeClasspath,testRuntimeCl com.google.cloud:proto-google-cloud-firestore-bundle-v1:3.37.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.code.findbugs:jsr305:3.0.2=checkstyle,deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.code.gson:gson:2.13.2=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.common.html.types:types:1.0.8=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.dagger:dagger:2.59.2=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.errorprone:error_prone_annotation:2.48.0=annotationProcessor,testAnnotationProcessor com.google.errorprone:error_prone_annotations:2.36.0=checkstyle @@ -130,11 +128,10 @@ com.google.errorprone:error_prone_annotations:2.48.0=annotationProcessor,testAnn com.google.errorprone:error_prone_annotations:2.49.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.errorprone:error_prone_check_api:2.48.0=annotationProcessor,testAnnotationProcessor com.google.errorprone:error_prone_core:2.48.0=annotationProcessor,testAnnotationProcessor -com.google.escapevelocity:escapevelocity:1.1=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.flatbuffers:flatbuffers-java:24.3.25=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.flogger:flogger-system-backend:0.7.4=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.flogger:flogger:0.7.4=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.flogger:google-extensions:0.7.4=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.flogger:google-extensions:0.7.1=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.googlejavaformat:google-java-format:1.34.1=annotationProcessor,testAnnotationProcessor com.google.guava:failureaccess:1.0.3=annotationProcessor,checkstyle,deploy_jar,runtimeClasspath,testAnnotationProcessor,testRuntimeClasspath com.google.guava:guava:33.4.8-jre=checkstyle @@ -148,7 +145,6 @@ com.google.http-client:google-http-client-gson:2.1.0=deploy_jar,runtimeClasspath com.google.http-client:google-http-client-jackson2:1.46.3=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.http-client:google-http-client-protobuf:2.1.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.http-client:google-http-client:2.1.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.inject:guice:7.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.j2objc:j2objc-annotations:3.0.0=checkstyle com.google.j2objc:j2objc-annotations:3.1=annotationProcessor,deploy_jar,runtimeClasspath,testAnnotationProcessor,testRuntimeClasspath com.google.jsinterop:jsinterop-annotations:2.0.0=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -160,9 +156,8 @@ com.google.oauth-client:google-oauth-client-servlet:1.36.0=deploy_jar,runtimeCla com.google.oauth-client:google-oauth-client:1.39.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.protobuf:protobuf-java-util:4.33.2=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.protobuf:protobuf-java:4.33.2=annotationProcessor,testAnnotationProcessor -com.google.protobuf:protobuf-java:4.35.0-RC2=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.protobuf:protobuf-java:4.35.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.re2j:re2j:1.8=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.template:soy:2024-02-26=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.truth:truth:1.4.5=deploy_jar,runtimeClasspath,testRuntimeClasspath com.googlecode.json-simple:json-simple:1.1.1=deploy_jar,runtimeClasspath,testRuntimeClasspath com.ibm.icu:icu4j:73.2=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -202,7 +197,7 @@ io.apicurio:apicurio-registry-protobuf-schema-utilities:3.0.0.M2=deploy_jar,runt io.github.classgraph:classgraph:4.8.162=deploy_jar,runtimeClasspath,testRuntimeClasspath io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,testAnnotationProcessor io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,testAnnotationProcessor -io.github.java-diff-utils:java-diff-utils:4.16=deploy_jar,runtimeClasspath,testRuntimeClasspath +io.github.java-diff-utils:java-diff-utils:4.17=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-alts:1.76.2=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-api:1.76.2=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-auth:1.76.2=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -275,7 +270,6 @@ jakarta.servlet:jakarta.servlet-api:6.0.0=deploy_jar,runtimeClasspath,testRuntim jakarta.transaction:jakarta.transaction-api:2.0.1=deploy_jar,runtimeClasspath,testRuntimeClasspath jakarta.xml.bind:jakarta.xml.bind-api:4.0.4=deploy_jar,runtimeClasspath,testRuntimeClasspath javax.annotation:javax.annotation-api:1.3.2=deploy_jar,runtimeClasspath,testRuntimeClasspath -javax.annotation:jsr250-api:1.0=deploy_jar,runtimeClasspath,testRuntimeClasspath javax.inject:javax.inject:1=annotationProcessor,deploy_jar,runtimeClasspath,testAnnotationProcessor,testRuntimeClasspath javax.jdo:jdo2-api:2.3-20090302111651=deploy_jar,runtimeClasspath,testRuntimeClasspath javax.validation:validation-api:1.0.0.GA=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -344,8 +338,9 @@ org.codehaus.plexus:plexus-utils:3.3.0=checkstyle org.conscrypt:conscrypt-openjdk-uber:2.5.2=deploy_jar,runtimeClasspath,testRuntimeClasspath org.eclipse.angus:angus-activation:2.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath org.eclipse.angus:jakarta.mail:2.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath -org.flywaydb:flyway-core:12.6.1=deploy_jar,runtimeClasspath,testRuntimeClasspath -org.flywaydb:flyway-database-postgresql:12.6.1=deploy_jar,runtimeClasspath,testRuntimeClasspath +org.flywaydb:flyway-core:12.6.2=deploy_jar,runtimeClasspath,testRuntimeClasspath +org.flywaydb:flyway-database-postgresql:12.6.2=deploy_jar,runtimeClasspath,testRuntimeClasspath +org.freemarker:freemarker:2.3.34=deploy_jar,runtimeClasspath,testRuntimeClasspath org.glassfish.jaxb:jaxb-core:4.0.6=deploy_jar,runtimeClasspath,testRuntimeClasspath org.glassfish.jaxb:jaxb-runtime:4.0.6=deploy_jar,runtimeClasspath,testRuntimeClasspath org.glassfish.jaxb:txw2:4.0.6=deploy_jar,runtimeClasspath,testRuntimeClasspath diff --git a/load-testing/gradle.lockfile b/load-testing/gradle.lockfile index ebae8ed8e8f..d08a84670b7 100644 --- a/load-testing/gradle.lockfile +++ b/load-testing/gradle.lockfile @@ -31,14 +31,14 @@ commons-collections:commons-collections:3.2.2=checkstyle info.picocli:picocli:4.7.7=checkstyle io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,testAnnotationProcessor io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,testAnnotationProcessor -io.netty:netty-buffer:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec-http:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-common:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-handler:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-resolver:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-transport-native-unix-common:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-transport:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-buffer:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec-http:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-common:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-handler:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-resolver:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-transport-native-unix-common:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-transport:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath javax.inject:javax.inject:1=annotationProcessor,testAnnotationProcessor net.sf.saxon:Saxon-HE:12.5=checkstyle org.antlr:antlr4-runtime:4.13.2=checkstyle diff --git a/networking/gradle.lockfile b/networking/gradle.lockfile index 11e3304de3c..57089825b4c 100644 --- a/networking/gradle.lockfile +++ b/networking/gradle.lockfile @@ -57,9 +57,9 @@ com.google.http-client:google-http-client:2.1.0=deploy_jar,runtimeClasspath,test com.google.j2objc:j2objc-annotations:3.0.0=checkstyle,testCompileClasspath com.google.j2objc:j2objc-annotations:3.1=annotationProcessor,compileClasspath,deploy_jar,runtimeClasspath,testAnnotationProcessor,testRuntimeClasspath com.google.oauth-client:google-oauth-client:1.36.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.protobuf:protobuf-java-util:4.35.0-RC2=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.protobuf:protobuf-java-util:4.35.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.protobuf:protobuf-java:4.33.2=annotationProcessor,testAnnotationProcessor -com.google.protobuf:protobuf-java:4.35.0-RC2=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.protobuf:protobuf-java:4.35.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.re2j:re2j:1.7=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.truth:truth:1.4.5=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.ibm.icu:icu4j:78.3=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -74,7 +74,7 @@ commons-logging:commons-logging:1.2=deploy_jar,runtimeClasspath,testRuntimeClass info.picocli:picocli:4.7.7=checkstyle io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,testAnnotationProcessor io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,testAnnotationProcessor -io.github.java-diff-utils:java-diff-utils:4.16=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.github.java-diff-utils:java-diff-utils:4.17=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.grpc:grpc-alts:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-api:1.70.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-auth:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -90,16 +90,16 @@ io.grpc:grpc-services:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-stub:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-util:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-xds:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.netty:netty-buffer:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec-http:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-common:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-handler:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-resolver:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-buffer:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec-http:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-common:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-handler:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-resolver:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.netty:netty-tcnative-boringssl-static:2.0.77.Final=deploy_jar,runtimeClasspath,testRuntimeClasspath io.netty:netty-tcnative-classes:2.0.77.Final=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.netty:netty-transport-native-unix-common:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-transport:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-transport-native-unix-common:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-transport:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.opencensus:opencensus-api:0.31.1=deploy_jar,runtimeClasspath,testRuntimeClasspath io.opencensus:opencensus-contrib-http-util:0.31.1=deploy_jar,runtimeClasspath,testRuntimeClasspath io.perfmark:perfmark-api:0.27.0=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -147,6 +147,7 @@ org.codehaus.plexus:plexus-utils:3.3.0=checkstyle org.conscrypt:conscrypt-openjdk-uber:2.5.2=deploy_jar,runtimeClasspath,testRuntimeClasspath org.eclipse.angus:angus-activation:2.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath org.eclipse.angus:jakarta.mail:2.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath +org.freemarker:freemarker:2.3.34=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest-core:1.3=testCompileClasspath,testRuntimeClasspath org.jacoco:org.jacoco.agent:0.8.14=jacocoAgent,jacocoAnt org.jacoco:org.jacoco.ant:0.8.14=jacocoAnt diff --git a/prober/gradle.lockfile b/prober/gradle.lockfile index 9da46133598..014c5bd6fe0 100644 --- a/prober/gradle.lockfile +++ b/prober/gradle.lockfile @@ -60,9 +60,9 @@ com.google.j2objc:j2objc-annotations:3.1=annotationProcessor,compileClasspath,de com.google.monitoring-client:contrib:1.0.7=testCompileClasspath,testRuntimeClasspath com.google.monitoring-client:metrics:1.0.7=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.oauth-client:google-oauth-client:1.36.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -com.google.protobuf:protobuf-java-util:4.35.0-RC2=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.protobuf:protobuf-java-util:4.35.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.protobuf:protobuf-java:4.33.2=annotationProcessor,testAnnotationProcessor -com.google.protobuf:protobuf-java:4.35.0-RC2=deploy_jar,runtimeClasspath,testRuntimeClasspath +com.google.protobuf:protobuf-java:4.35.0=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.re2j:re2j:1.1=compileClasspath,testCompileClasspath com.google.re2j:re2j:1.7=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.truth:truth:1.4.5=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -78,7 +78,7 @@ commons-logging:commons-logging:1.2=deploy_jar,runtimeClasspath,testRuntimeClass info.picocli:picocli:4.7.7=checkstyle io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,testAnnotationProcessor io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,testAnnotationProcessor -io.github.java-diff-utils:java-diff-utils:4.16=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.github.java-diff-utils:java-diff-utils:4.17=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.grpc:grpc-alts:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-api:1.70.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-auth:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -94,16 +94,16 @@ io.grpc:grpc-services:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-stub:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-util:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-xds:1.68.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.netty:netty-buffer:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec-http:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-common:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-handler:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-resolver:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-buffer:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec-http:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-common:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-handler:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-resolver:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.netty:netty-tcnative-boringssl-static:2.0.77.Final=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.netty:netty-tcnative-classes:2.0.77.Final=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-transport-native-unix-common:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-transport:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-transport-native-unix-common:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-transport:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.opencensus:opencensus-api:0.31.1=deploy_jar,runtimeClasspath,testRuntimeClasspath io.opencensus:opencensus-contrib-http-util:0.31.1=deploy_jar,runtimeClasspath,testRuntimeClasspath io.perfmark:perfmark-api:0.27.0=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -153,6 +153,7 @@ org.codehaus.plexus:plexus-utils:3.3.0=checkstyle org.conscrypt:conscrypt-openjdk-uber:2.5.2=deploy_jar,runtimeClasspath,testRuntimeClasspath org.eclipse.angus:angus-activation:2.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath org.eclipse.angus:jakarta.mail:2.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath +org.freemarker:freemarker:2.3.34=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest-core:1.3=testCompileClasspath,testRuntimeClasspath org.jacoco:org.jacoco.agent:0.8.14=jacocoAgent,jacocoAnt org.jacoco:org.jacoco.ant:0.8.14=jacocoAnt diff --git a/proxy/gradle.lockfile b/proxy/gradle.lockfile index ade45080cd3..89f11f5a06a 100644 --- a/proxy/gradle.lockfile +++ b/proxy/gradle.lockfile @@ -25,7 +25,7 @@ com.google.api:api-common:2.47.1=compileClasspath,deploy_jar,runtimeClasspath,te com.google.api:gax-grpc:2.64.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.api:gax-httpjson:2.64.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.api:gax:2.64.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.apis:google-api-services-cloudkms:v1-rev20260319-2.0.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.apis:google-api-services-cloudkms:v1-rev20260506-2.0.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-monitoring:v3-rev20260129-2.0.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.apis:google-api-services-storage:v1-rev20250312-2.0.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.auth:google-auth-library-credentials:1.33.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -96,7 +96,7 @@ commons-logging:commons-logging:1.2=compileClasspath,deploy_jar,runtimeClasspath info.picocli:picocli:4.7.7=checkstyle io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,testAnnotationProcessor io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,testAnnotationProcessor -io.github.java-diff-utils:java-diff-utils:4.16=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.github.java-diff-utils:java-diff-utils:4.17=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.grpc:grpc-alts:1.70.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.grpc:grpc-api:1.70.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.grpc:grpc-auth:1.70.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -114,16 +114,16 @@ io.grpc:grpc-services:1.70.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-stub:1.70.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.grpc:grpc-util:1.70.0=deploy_jar,runtimeClasspath,testRuntimeClasspath io.grpc:grpc-xds:1.70.0=deploy_jar,runtimeClasspath,testRuntimeClasspath -io.netty:netty-buffer:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec-http:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-codec:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-common:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-handler:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-resolver:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-buffer:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec-http:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-codec:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-common:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-handler:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-resolver:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.netty:netty-tcnative-boringssl-static:2.0.77.Final=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.netty:netty-tcnative-classes:2.0.77.Final=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-transport-native-unix-common:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -io.netty:netty-transport:4.1.133.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-transport-native-unix-common:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.netty:netty-transport:4.1.134.Final=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.opencensus:opencensus-api:0.31.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.opencensus:opencensus-contrib-http-util:0.31.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.opentelemetry.contrib:opentelemetry-gcp-resources:1.37.0-alpha=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -183,6 +183,7 @@ org.codehaus.plexus:plexus-utils:3.3.0=checkstyle org.conscrypt:conscrypt-openjdk-uber:2.5.2=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.eclipse.angus:angus-activation:2.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath org.eclipse.angus:jakarta.mail:2.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath +org.freemarker:freemarker:2.3.34=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest-core:1.3=testCompileClasspath,testRuntimeClasspath org.jacoco:org.jacoco.agent:0.8.14=jacocoAgent,jacocoAnt org.jacoco:org.jacoco.ant:0.8.14=jacocoAnt diff --git a/util/gradle.lockfile b/util/gradle.lockfile index b5e0142c9a3..f8d9a9e9138 100644 --- a/util/gradle.lockfile +++ b/util/gradle.lockfile @@ -56,9 +56,9 @@ com.google.http-client:google-http-client:2.1.0=compileClasspath,deploy_jar,runt com.google.j2objc:j2objc-annotations:3.0.0=checkstyle com.google.j2objc:j2objc-annotations:3.1=annotationProcessor,compileClasspath,deploy_jar,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath com.google.oauth-client:google-oauth-client:1.36.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.protobuf:protobuf-java-util:4.35.0-RC2=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.protobuf:protobuf-java-util:4.35.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.protobuf:protobuf-java:4.33.2=annotationProcessor,testAnnotationProcessor -com.google.protobuf:protobuf-java:4.35.0-RC2=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.protobuf:protobuf-java:4.35.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.re2j:re2j:1.7=deploy_jar,runtimeClasspath,testRuntimeClasspath com.google.re2j:re2j:1.8=compileClasspath,testCompileClasspath com.google.truth:truth:1.4.5=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath @@ -74,7 +74,7 @@ commons-logging:commons-logging:1.2=compileClasspath,deploy_jar,runtimeClasspath info.picocli:picocli:4.7.7=checkstyle io.github.eisop:dataflow-errorprone:3.41.0-eisop1=annotationProcessor,testAnnotationProcessor io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,testAnnotationProcessor -io.github.java-diff-utils:java-diff-utils:4.16=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.github.java-diff-utils:java-diff-utils:4.17=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.grpc:grpc-alts:1.68.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath io.grpc:grpc-api:1.68.0=compileClasspath,testCompileClasspath io.grpc:grpc-api:1.70.0=deploy_jar,runtimeClasspath,testRuntimeClasspath @@ -139,6 +139,7 @@ org.codehaus.plexus:plexus-utils:3.3.0=checkstyle org.conscrypt:conscrypt-openjdk-uber:2.5.2=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.eclipse.angus:angus-activation:2.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath org.eclipse.angus:jakarta.mail:2.1.0-M1=deploy_jar,runtimeClasspath,testRuntimeClasspath +org.freemarker:freemarker:2.3.34=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest-core:3.0=testCompileClasspath,testRuntimeClasspath org.hamcrest:hamcrest:3.0=testCompileClasspath,testRuntimeClasspath org.jacoco:org.jacoco.agent:0.8.14=jacocoAgent,jacocoAnt