mirror of
https://github.com/google/nomulus
synced 2026-06-09 16:33:02 +00:00
Compare commits
5 Commits
nomulus-20
...
nomulus-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53b92d602e | ||
|
|
b3fc57c7f7 | ||
|
|
bd70cd91a5 | ||
|
|
73725e94fe | ||
|
|
c3f8ec8c85 |
@@ -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']
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* <p>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<String, Object> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<String, Object> 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<String, Object> 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<String, Object> data =
|
||||
ImmutableMap.of("name", "<b>World</b>", "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<String, Object> 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");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<#-- Copyright 2026 The Nomulus Authors. All Rights Reserved. -->
|
||||
Hello ${name}!
|
||||
Your score is ${score}.
|
||||
<#if showMessage>
|
||||
Message: ${message}
|
||||
</#if>
|
||||
@@ -90,15 +90,15 @@ PRESUBMITS = {
|
||||
# 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 @@ PRESUBMITS = {
|
||||
"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",
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -68,6 +68,8 @@ public class BsaCredential {
|
||||
|
||||
private final Keyring keyring;
|
||||
|
||||
private final Gson gson;
|
||||
|
||||
private final Clock clock;
|
||||
|
||||
@Nullable private String authToken;
|
||||
@@ -79,11 +81,13 @@ public class BsaCredential {
|
||||
@Config("bsaAuthUrl") String authUrl,
|
||||
@Config("bsaAuthTokenExpiry") Duration authTokenExpiry,
|
||||
Keyring keyring,
|
||||
Gson gson,
|
||||
Clock clock) {
|
||||
this.urlConnectionService = urlConnectionService;
|
||||
this.authUrl = authUrl;
|
||||
this.authTokenExpiry = authTokenExpiry;
|
||||
this.keyring = keyring;
|
||||
this.gson = gson;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@@ -143,8 +147,7 @@ public class BsaCredential {
|
||||
// TODO: catch json syntax exception
|
||||
@SuppressWarnings("unchecked")
|
||||
String idToken =
|
||||
new Gson()
|
||||
.fromJson(new String(getResponseBytes(connection), UTF_8), Map.class)
|
||||
gson.fromJson(new String(getResponseBytes(connection), UTF_8), Map.class)
|
||||
.getOrDefault(ID_TOKEN, "")
|
||||
.toString();
|
||||
if (idToken.isEmpty()) {
|
||||
|
||||
@@ -60,12 +60,15 @@ public final class CacheModule {
|
||||
public static Optional<UnifiedJedis> provideJedis(
|
||||
@ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle,
|
||||
@Config("valkeyHostsAndPorts") Optional<ImmutableList<String>> valkeyHostsAndPorts,
|
||||
@Config("valkeySslSocketFactory") SSLSocketFactory valkeySslSocketFactory) {
|
||||
if (valkeyHostsAndPorts.map(ImmutableList::isEmpty).orElse(true)) {
|
||||
@Config("valkeyCertificateAuthority") Optional<String> valkeyCertificateAuthority) {
|
||||
if (valkeyHostsAndPorts.map(ImmutableList::isEmpty).orElse(true)
|
||||
|| valkeyCertificateAuthority.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
ImmutableSet<HostAndPort> hostsAndPorts =
|
||||
valkeyHostsAndPorts.get().stream().map(HostAndPort::from).collect(toImmutableSet());
|
||||
SSLSocketFactory valkeySslSocketFactory =
|
||||
createValkeySslSocketFactory(valkeyCertificateAuthority.get());
|
||||
JedisClientConfig clientConfig =
|
||||
DefaultJedisClientConfig.builder()
|
||||
.ssl(true)
|
||||
@@ -111,11 +114,7 @@ public final class CacheModule {
|
||||
return new MultilayerHostCache(jedisClient.get(), cacheMetrics);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Config("valkeySslSocketFactory")
|
||||
static SSLSocketFactory provideValkeySslSocketFactory(
|
||||
@Config("valkeyCertificateAuthority") String valkeyCertificateAuthority) {
|
||||
private static SSLSocketFactory createValkeySslSocketFactory(String valkeyCertificateAuthority) {
|
||||
try {
|
||||
ImmutableList<X509Certificate> trustedCerts =
|
||||
CertificateFactory.getInstance("X.509")
|
||||
|
||||
@@ -43,8 +43,8 @@ import static google.registry.util.CollectionUtils.nullToEmpty;
|
||||
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 @@ public class DomainFlowUtils {
|
||||
InternetDomainName domainName,
|
||||
Optional<Domain> domain,
|
||||
@Nullable CurrencyUnit topLevelCurrency,
|
||||
Instant currentDate,
|
||||
Instant now,
|
||||
DomainPricingLogic pricingLogic,
|
||||
Optional<AllocationToken> 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 @@ public class DomainFlowUtils {
|
||||
return fee.getType();
|
||||
}
|
||||
ImmutableList<FeeType> 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 @@ public class DomainFlowUtils {
|
||||
*/
|
||||
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 @@ public class DomainFlowUtils {
|
||||
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 @@ public class DomainFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/** 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 @@ public class DomainFlowUtils {
|
||||
*
|
||||
* <p>{@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 @@ public class DomainFlowUtils {
|
||||
/**
|
||||
* 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 @@ public class DomainFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/** 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 @@ public class DomainFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/** 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 @@ public class DomainFlowUtils {
|
||||
* 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.
|
||||
*
|
||||
* <p>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.
|
||||
* <p>The steps are as follows:
|
||||
*
|
||||
* <ol>
|
||||
* <li>Find all HistoryEntries under the domain modified in the past, up to the maxSearchPeriod.
|
||||
* <li>Only keep HistoryEntries with a DomainTransactionRecord that a) hasn't been reported yet
|
||||
* and b) matches the predicate
|
||||
* <li>Return the transactionRecords under the most recent HistoryEntry that fits the above
|
||||
* criteria, with negated reportAmounts.
|
||||
* </ol>
|
||||
*
|
||||
* @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<DomainTransactionRecord> createCancelingRecords(
|
||||
Domain domain,
|
||||
@@ -1225,13 +1261,6 @@ public class DomainFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/** 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 class DomainFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/** 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) {
|
||||
|
||||
@@ -34,8 +34,8 @@ import static google.registry.flows.domain.token.AllocationTokenFlowUtils.maybeA
|
||||
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 final class DomainRenewFlow implements MutatingFlow {
|
||||
existingDomain = maybeApplyBulkPricingRemovalToken(existingDomain, allocationToken);
|
||||
|
||||
Instant newExpirationTime =
|
||||
existingDomain
|
||||
.getRegistrationExpirationTime()
|
||||
.atZone(UTC)
|
||||
.plusYears(years)
|
||||
.toInstant(); // Uncapped
|
||||
plusYears(existingDomain.getRegistrationExpirationTime(), years); // Uncapped
|
||||
validateRegistrationPeriod(now, newExpirationTime);
|
||||
Optional<FeeRenewCommandExtension> feeRenew =
|
||||
eppInput.getSingleExtension(FeeRenewCommandExtension.class);
|
||||
|
||||
@@ -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.DomainUpdateFlowCustomLogic.BeforeSaveParame
|
||||
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 @@ import java.util.Optional;
|
||||
* @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 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
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 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
}
|
||||
|
||||
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<SecDnsUpdateExtension> secDnsUpdate =
|
||||
eppInput.getSingleExtension(SecDnsUpdateExtension.class);
|
||||
verifyAddsAndRemoves(domain.getNameservers(), add.getNameservers(), remove.getNameservers());
|
||||
@@ -251,28 +251,29 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
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<DomainUpdateSuperuserExtension> superuserExt =
|
||||
|
||||
@@ -36,7 +36,6 @@ import static google.registry.util.CollectionUtils.isNullOrEmpty;
|
||||
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.eppinput.ResourceCommand;
|
||||
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 final class HostUpdateFlow implements MutatingFlow {
|
||||
? tm().loadByKey(existingHost.getSuperordinateDomain()).cloneProjectedAtTime(now)
|
||||
: null;
|
||||
// Note that lookupSuperordinateDomain calls cloneProjectedAtTime on the domain for us.
|
||||
|
||||
Optional<Domain> newSuperordinateDomain =
|
||||
lookupSuperordinateDomain(validateHostName(newHostName), now);
|
||||
verifySuperordinateDomainNotInPendingDelete(newSuperordinateDomain.orElse(null));
|
||||
@@ -157,8 +156,8 @@ public final class HostUpdateFlow implements MutatingFlow {
|
||||
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(
|
||||
|
||||
@@ -22,6 +22,7 @@ import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.keyring.api.Keyring;
|
||||
import google.registry.keyring.secretmanager.SecretManagerKeyring;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.util.Optional;
|
||||
|
||||
/** Dagger module for {@link Keyring} */
|
||||
@Module
|
||||
@@ -55,7 +56,7 @@ public abstract class KeyringModule {
|
||||
|
||||
@Provides
|
||||
@Config("valkeyCertificateAuthority")
|
||||
public static String provideValkeyCertificateAuthority(Keyring keyring) {
|
||||
return keyring.getValkeyCertificateAuthority();
|
||||
public static Optional<String> provideValkeyCertificateAuthority(Keyring keyring) {
|
||||
return Optional.ofNullable(keyring.getValkeyCertificateAuthority());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,19 +106,24 @@ public abstract class ImmutableObject implements Cloneable {
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
/** Returns a clone of the given object. */
|
||||
@SuppressWarnings("unchecked")
|
||||
protected static <T extends ImmutableObject> 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 extends ImmutableObject> T clone(T t) {
|
||||
return (T) t.clone();
|
||||
}
|
||||
|
||||
/** Returns a clone of the given object with empty fields set to null. */
|
||||
protected static <T extends ImmutableObject> T cloneEmptyToNull(T t) {
|
||||
return ModelUtils.cloneEmptyToNull(t);
|
||||
@@ -233,7 +238,7 @@ public abstract class ImmutableObject implements Cloneable {
|
||||
}
|
||||
}
|
||||
|
||||
/** 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)
|
||||
|
||||
@@ -17,7 +17,6 @@ package google.registry.model.domain;
|
||||
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 com.google.common.collect.ImmutableSet;
|
||||
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.ResourceUpdate;
|
||||
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 @@ public class DomainCommand {
|
||||
throws InvalidReferencesException, ParameterValuePolicyErrorException;
|
||||
}
|
||||
|
||||
/** The fields on "chgType" from <a href="http://tools.ietf.org/html/rfc5731">RFC5731</a>. */
|
||||
/** The fields on "chgType" from <a href="https://tools.ietf.org/html/rfc5731">RFC5731</a>. */
|
||||
@XmlTransient
|
||||
public static class DomainCreateOrChange<B extends Domain.Builder> extends ImmutableObject
|
||||
implements ResourceCreateOrChange<B> {
|
||||
public abstract static class DomainCreateOrChange<B extends Domain.Builder>
|
||||
extends ImmutableObject implements ResourceCreateOrChange<B> {
|
||||
|
||||
/** The contactId of the registrant who registered this domain. */
|
||||
@XmlElement(name = "registrant")
|
||||
@@ -92,9 +96,10 @@ public class DomainCommand {
|
||||
|
||||
/**
|
||||
* A create command for a {@link Domain}, mapping "createType" from <a
|
||||
* href="http://tools.ietf.org/html/rfc5731">RFC5731</a>.
|
||||
* href="https://tools.ietf.org/html/rfc5731">RFC5731</a>.
|
||||
*/
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlType(
|
||||
propOrder = {
|
||||
"domainName",
|
||||
@@ -147,17 +152,12 @@ public class DomainCommand {
|
||||
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 class DomainCommand {
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
/** Builder for {@link Create}. */
|
||||
public static class Builder extends Buildable.Builder<Create> {
|
||||
public Builder setDomainName(String domainName) {
|
||||
getInstance().domainName = domainName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setPeriod(Period period) {
|
||||
getInstance().period = period;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setNameserverHostNames(ImmutableSet<String> nameserverHostNames) {
|
||||
getInstance().nameserverHostNames =
|
||||
isNullOrEmpty(nameserverHostNames) ? null : nameserverHostNames;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setForeignKeyedDesignatedContacts(
|
||||
ImmutableSet<ForeignKeyedDesignatedContact> 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 class DomainCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainAuthInfo getAuthInfo() {
|
||||
public AuthInfo getAuthInfo() {
|
||||
return authInfo;
|
||||
}
|
||||
}
|
||||
@@ -237,12 +288,27 @@ public class DomainCommand {
|
||||
|
||||
/** 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 class DomainCommand {
|
||||
public Period getPeriod() {
|
||||
return firstNonNull(period, DEFAULT_PERIOD);
|
||||
}
|
||||
|
||||
/** Builder for {@link Renew}. */
|
||||
public static class Builder extends Buildable.Builder<Renew> {
|
||||
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 class DomainCommand {
|
||||
}
|
||||
|
||||
@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<Update.AddRemove, Domain.Builder, Update.Change>
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlType(propOrder = {"name", "innerAdd", "innerRemove", "innerChange"})
|
||||
public static class Update
|
||||
extends ResourceUpdate<Update.DomainAddRemove, Domain.Builder, Update.Change>
|
||||
implements CreateOrUpdate<Update> {
|
||||
|
||||
@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 @@ public class DomainCommand {
|
||||
}
|
||||
|
||||
@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<Update> {
|
||||
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 class DomainCommand {
|
||||
@XmlElement(name = "contact")
|
||||
Set<ForeignKeyedDesignatedContact> foreignKeyedDesignatedContacts;
|
||||
|
||||
@XmlElement(name = "status")
|
||||
Set<StatusValue> statusValues;
|
||||
|
||||
public boolean isEmpty() {
|
||||
return isNullOrEmpty(nameserverHostNames)
|
||||
&& isNullOrEmpty(foreignKeyedDesignatedContacts)
|
||||
&& isNullOrEmpty(statusValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatusValues(ImmutableSet<StatusValue> statusValues) {
|
||||
this.statusValues = statusValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableSet<StatusValue> getStatusValues() {
|
||||
return nullToEmptyImmutableCopy(statusValues);
|
||||
}
|
||||
|
||||
public ImmutableSet<String> getNameserverHostNames() {
|
||||
return nullSafeImmutableCopy(nameserverHostNames);
|
||||
}
|
||||
@@ -332,11 +489,25 @@ public class DomainCommand {
|
||||
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<DomainAddRemove> {
|
||||
public Builder setNameserverHostNames(ImmutableSet<String> nameserverHostNames) {
|
||||
getInstance().nameserverHostNames =
|
||||
isNullOrEmpty(nameserverHostNames) ? null : nameserverHostNames;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setStatusValues(ImmutableSet<StatusValue> 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 @@ public class DomainCommand {
|
||||
}
|
||||
|
||||
/** The inner change type on a domain update command. */
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlType(propOrder = {"registrantContactId", "authInfo"})
|
||||
public static class Change extends DomainCreateOrChange<Domain.Builder> {
|
||||
/** Builder for {@link Change}. */
|
||||
public static class Builder extends Buildable.Builder<Change> {
|
||||
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 class DomainCommand {
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<VKey<Host>> linkHosts(Set<String> hostNames, Instant now)
|
||||
private static ImmutableSet<VKey<Host>> linkHosts(ImmutableSet<String> hostNames, Instant now)
|
||||
throws InvalidReferencesException {
|
||||
if (hostNames == null) {
|
||||
return null;
|
||||
@@ -383,7 +563,7 @@ public class DomainCommand {
|
||||
|
||||
/** Loads host keys to cached EPP resources by their foreign keys. */
|
||||
private static ImmutableMap<String, VKey<Host>> loadByForeignKeysCached(
|
||||
Set<String> foreignKeys, Instant now) throws InvalidReferencesException {
|
||||
ImmutableSet<String> foreignKeys, Instant now) throws InvalidReferencesException {
|
||||
ImmutableMap<String, VKey<Host>> fks =
|
||||
ForeignKeyUtils.loadKeysByCacheIfEnabled(Host.class, foreignKeys, now);
|
||||
if (!fks.keySet().equals(foreignKeys)) {
|
||||
@@ -394,14 +574,14 @@ public class DomainCommand {
|
||||
}
|
||||
|
||||
/** Exception to throw when referenced objects don't exist. */
|
||||
public static class InvalidReferencesException extends Exception {
|
||||
public static class InvalidReferencesException extends ParameterValuePolicyErrorException {
|
||||
private final ImmutableSet<String> foreignKeys;
|
||||
private final Class<?> type;
|
||||
|
||||
InvalidReferencesException(Class<?> type, ImmutableSet<String> foreignKeys) {
|
||||
public InvalidReferencesException(Class<?> type, Set<String> 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<String> getForeignKeys() {
|
||||
|
||||
@@ -20,16 +20,22 @@ import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
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 @@ public class Fee extends BaseFee {
|
||||
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 @@ public class Fee extends BaseFee {
|
||||
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<Fee> {
|
||||
|
||||
/** 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,10 @@ public abstract class FeeQueryCommandExtensionItem extends ImmutableObject {
|
||||
/** The period for the command being checked. */
|
||||
Period period;
|
||||
|
||||
public void setPeriod(Period period) {
|
||||
this.period = period;
|
||||
}
|
||||
|
||||
/**
|
||||
* Three-character ISO4217 currency code.
|
||||
*
|
||||
|
||||
@@ -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
|
||||
* <p>This is a list because a single operation can involve multiple fees.
|
||||
*/
|
||||
@XmlElement(name = "fee")
|
||||
List<Fee> fees;
|
||||
public List<Fee> fees;
|
||||
|
||||
public CurrencyUnit getCurrency() {
|
||||
return currency;
|
||||
|
||||
@@ -31,7 +31,7 @@ import org.joda.money.CurrencyUnit;
|
||||
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.
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -25,16 +25,22 @@ import jakarta.xml.bind.annotation.XmlRootElement;
|
||||
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<FeeCheckCommandExtensionItemV06> items;
|
||||
|
||||
public void setItems(ImmutableList<FeeCheckCommandExtensionItemV06> items) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CurrencyUnit getCurrency() {
|
||||
return null; // This version of the fee extension doesn't specify a top-level currency.
|
||||
|
||||
@@ -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 class FeeCreateCommandExtensionV06 extends FeeCreateCommandExtension {
|
||||
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<Credit> getCredits() {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
/** Builder for {@link FeeCreateCommandExtensionV06}. */
|
||||
public static class Builder extends Buildable.Builder<FeeCreateCommandExtensionV06> {
|
||||
public Builder setCurrency(CurrencyUnit currency) {
|
||||
getInstance().currency = currency;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setFees(ImmutableList<Fee> fees) {
|
||||
getInstance().fees = fees;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 @@ import jakarta.xml.bind.annotation.XmlType;
|
||||
* 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<Credit> getCredits() {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
}
|
||||
@XmlType(propOrder = {"currency", "fees", "credits"})
|
||||
public class FeeCreateResponseExtensionV06 extends FeeTransformResponseExtension {}
|
||||
|
||||
@@ -20,7 +20,7 @@ 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 create commands.
|
||||
* domain delete commands.
|
||||
*/
|
||||
@XmlRootElement(name = "delData")
|
||||
@XmlType(propOrder = {"currency", "fees", "credits"})
|
||||
|
||||
@@ -19,9 +19,13 @@ import google.registry.model.domain.fee.Credit;
|
||||
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 class FeeRenewCommandExtensionV06 extends FeeRenewCommandExtension {
|
||||
|
||||
/** This version of the extension doesn't support the "credit" field. */
|
||||
@Override
|
||||
@XmlTransient
|
||||
public ImmutableList<Credit> getCredits() {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@@ -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 @@ import jakarta.xml.bind.annotation.XmlType;
|
||||
* 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<Credit> getCredits() {
|
||||
return super.getCredits();
|
||||
}
|
||||
}
|
||||
@XmlType(propOrder = {"currency", "fees", "credits"})
|
||||
public class FeeRenewResponseExtensionV06 extends FeeTransformResponseExtension {}
|
||||
|
||||
@@ -19,9 +19,13 @@ import google.registry.model.domain.fee.Credit;
|
||||
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 class FeeTransferCommandExtensionV06 extends FeeTransferCommandExtension
|
||||
|
||||
/** This version of the extension doesn't support the "credit" field. */
|
||||
@Override
|
||||
@XmlTransient
|
||||
public ImmutableList<Credit> getCredits() {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@@ -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<Credit> getCredits() {
|
||||
return super.getCredits();
|
||||
}
|
||||
}
|
||||
@XmlType(propOrder = {"currency", "fees", "credits"})
|
||||
public class FeeTransferResponseExtensionV06 extends FeeTransformResponseExtension {}
|
||||
|
||||
@@ -19,9 +19,13 @@ import google.registry.model.domain.fee.Credit;
|
||||
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 class FeeUpdateCommandExtensionV06 extends FeeUpdateCommandExtension {
|
||||
|
||||
/** This version of the extension doesn't support the "credit" field. */
|
||||
@Override
|
||||
@XmlTransient
|
||||
public ImmutableList<Credit> getCredits() {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@@ -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 @@ import jakarta.xml.bind.annotation.XmlType;
|
||||
* 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<Credit> getCredits() {
|
||||
return super.getCredits();
|
||||
}
|
||||
}
|
||||
@XmlType(propOrder = {"currency", "fees", "credits"})
|
||||
public class FeeUpdateResponseExtensionV06 extends FeeTransformResponseExtension {}
|
||||
|
||||
@@ -17,13 +17,16 @@ package google.registry.model.domain.fee12;
|
||||
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 class FeeCreateCommandExtensionV12 extends FeeCreateCommandExtension {
|
||||
public FeeTransformResponseExtension.Builder createResponseBuilder() {
|
||||
return new FeeTransformResponseExtension.Builder(new FeeCreateResponseExtensionV12());
|
||||
}
|
||||
|
||||
/** Builder for {@link FeeCreateCommandExtensionV12}. */
|
||||
public static class Builder extends Buildable.Builder<FeeCreateCommandExtensionV12> {
|
||||
public Builder setCurrency(CurrencyUnit currency) {
|
||||
getInstance().currency = currency;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setFees(ImmutableList<Fee> fees) {
|
||||
getInstance().fees = fees;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCredits(ImmutableList<Credit> credits) {
|
||||
getInstance().credits = credits;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,18 +17,21 @@ package google.registry.model.domain.fee12;
|
||||
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<Credit> credits;
|
||||
@@ -42,4 +45,22 @@ public class FeeRenewCommandExtensionV12 extends FeeRenewCommandExtension {
|
||||
public FeeTransformResponseExtension.Builder createResponseBuilder() {
|
||||
return new FeeTransformResponseExtension.Builder(new FeeRenewResponseExtensionV12());
|
||||
}
|
||||
|
||||
/** Builder for {@link FeeRenewCommandExtensionV12}. */
|
||||
public static class Builder extends Buildable.Builder<FeeRenewCommandExtensionV12> {
|
||||
public Builder setCurrency(CurrencyUnit currency) {
|
||||
getInstance().currency = currency;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setFees(ImmutableList<Fee> fees) {
|
||||
getInstance().fees = fees;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCredits(ImmutableList<Credit> credits) {
|
||||
getInstance().credits = credits;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,16 @@ package google.registry.model.domain.fee12;
|
||||
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 class FeeTransferCommandExtensionV12 extends FeeTransferCommandExtension
|
||||
public FeeTransformResponseExtension.Builder createResponseBuilder() {
|
||||
return new FeeTransformResponseExtension.Builder(new FeeTransferResponseExtensionV12());
|
||||
}
|
||||
|
||||
/** Builder for {@link FeeTransferCommandExtensionV12}. */
|
||||
public static class Builder extends Buildable.Builder<FeeTransferCommandExtensionV12> {
|
||||
public Builder setCurrency(CurrencyUnit currency) {
|
||||
getInstance().currency = currency;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setFees(ImmutableList<Fee> fees) {
|
||||
getInstance().fees = fees;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCredits(ImmutableList<Credit> credits) {
|
||||
getInstance().credits = credits;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,16 @@ package google.registry.model.domain.fee12;
|
||||
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 class FeeUpdateCommandExtensionV12 extends FeeUpdateCommandExtension {
|
||||
public FeeTransformResponseExtension.Builder createResponseBuilder() {
|
||||
return new FeeTransformResponseExtension.Builder(new FeeUpdateResponseExtensionV12());
|
||||
}
|
||||
|
||||
/** Builder for {@link FeeUpdateCommandExtensionV12}. */
|
||||
public static class Builder extends Buildable.Builder<FeeUpdateCommandExtensionV12> {
|
||||
public Builder setCurrency(CurrencyUnit currency) {
|
||||
getInstance().currency = currency;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setFees(ImmutableList<Fee> fees) {
|
||||
getInstance().fees = fees;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCredits(ImmutableList<Credit> credits) {
|
||||
getInstance().credits = credits;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,34 +19,37 @@ import static com.google.common.base.MoreObjects.firstNonNull;
|
||||
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.
|
||||
*
|
||||
* <p>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:
|
||||
*
|
||||
* <pre> {@code
|
||||
* <epp>
|
||||
* <command>
|
||||
* <create>
|
||||
* <!-- domain check XML data -->
|
||||
* </create>
|
||||
* <extension>
|
||||
* <launch:check>
|
||||
* <!-- launch check XML payload data -->
|
||||
* </launch:check>
|
||||
* </extension>
|
||||
* </command>
|
||||
* </epp>
|
||||
* } </pre>
|
||||
* <pre>{@code
|
||||
* <epp>
|
||||
* <command>
|
||||
* <create>
|
||||
* <!-- domain check XML data -->
|
||||
* </create>
|
||||
* <extension>
|
||||
* <launch:check>
|
||||
* <!-- launch check XML payload data -->
|
||||
* </launch:check>
|
||||
* </extension>
|
||||
* </command>
|
||||
* </epp>
|
||||
* }</pre>
|
||||
*
|
||||
* @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 class LaunchCheckExtension extends ImmutableObject implements CommandExte
|
||||
* 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);
|
||||
}
|
||||
|
||||
@@ -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 <a href="https://www.google.com/search?q=EPP+metadata+extension">EPP Metadata Extension</a>
|
||||
*/
|
||||
@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<MetadataExtension> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,12 +23,14 @@ import jakarta.persistence.Transient;
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
* <p>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<DomainDsData> dsData;
|
||||
|
||||
public Long getMaxSigLife() {
|
||||
@@ -43,6 +46,19 @@ public class SecDnsCreateExtension extends ImmutableObject implements CommandExt
|
||||
}
|
||||
|
||||
public ImmutableSet<DomainDsData> getDsData() {
|
||||
return nullSafeImmutableCopy(dsData);
|
||||
return nullToEmptyImmutableCopy(dsData);
|
||||
}
|
||||
|
||||
/** Builder for {@link SecDnsCreateExtension}. */
|
||||
public static class Builder extends Buildable.Builder<SecDnsCreateExtension> {
|
||||
public Builder setDsData(ImmutableSet<DomainDsData> dsData) {
|
||||
getInstance().dsData = dsData;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMaxSigLife(Long maxSigLife) {
|
||||
getInstance().maxSigLife = maxSigLife;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package google.registry.model.domain.secdns;
|
||||
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 class SecDnsUpdateExtension extends ImmutableObject implements CommandExt
|
||||
return Optional.ofNullable(change);
|
||||
}
|
||||
|
||||
/** Builder for {@link SecDnsUpdateExtension}. */
|
||||
public static class Builder extends Buildable.Builder<SecDnsUpdateExtension> {
|
||||
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<T extends AddRemoveBase, B extends Builder<T, B>>
|
||||
extends Buildable.Builder<T> {
|
||||
public abstract B setDsData(ImmutableSet<DomainDsData> 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<DomainDsData> dsData;
|
||||
|
||||
public ImmutableSet<DomainDsData> 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<Add, Builder> {
|
||||
@Override
|
||||
public Builder setDsData(ImmutableSet<DomainDsData> 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<DomainDsData> dsData;
|
||||
|
||||
public Boolean getAll() {
|
||||
return all;
|
||||
}
|
||||
|
||||
public ImmutableSet<DomainDsData> getDsData() {
|
||||
return nullToEmptyImmutableCopy(dsData);
|
||||
}
|
||||
|
||||
/** Builder for {@link Remove}. */
|
||||
public static class Builder extends AddRemoveBase.Builder<Remove, Builder> {
|
||||
public Builder setAll(Boolean all) {
|
||||
getInstance().all = all;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder setDsData(ImmutableSet<DomainDsData> 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 class SecDnsUpdateExtension extends ImmutableObject implements CommandExt
|
||||
* <p>We do not support expirations, but we need this field to be able to return appropriate
|
||||
* errors.
|
||||
*/
|
||||
@XmlElement(name = "maxSigLife")
|
||||
Long maxSigLife;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,11 @@ package google.registry.model.domain.superuser;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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<Boolean> getAutorenews() {
|
||||
return Optional.ofNullable(isNullOrEmpty(autorenews) ? null : Boolean.valueOf(autorenews));
|
||||
return Optional.ofNullable(autorenews);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<DomainDsData> 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<DomainDsData> add, ImmutableSet<DomainDsData> 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<String> domainNames) {
|
||||
return feeCheckCreateV06(domainNames, 1);
|
||||
}
|
||||
|
||||
/** Returns a fee check extension for domain creations (V06) with a specific period. */
|
||||
public static FeeCheckCommandExtensionV06 feeCheckCreateV06(
|
||||
ImmutableList<String> domainNames, int years) {
|
||||
FeeCheckCommandExtensionV06 feeCheck = new FeeCheckCommandExtensionV06();
|
||||
ImmutableList.Builder<FeeCheckCommandExtensionItemV06> 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);
|
||||
}
|
||||
}
|
||||
@@ -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.domain.token.AllocationTokenExtension;
|
||||
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.XmlElements;
|
||||
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 class EppInput extends ImmutableObject {
|
||||
public Optional<String> 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 class EppInput extends ImmutableObject {
|
||||
|
||||
@Nullable
|
||||
private ResourceCommand getResourceCommand() {
|
||||
if (commandWrapper == null) {
|
||||
return null;
|
||||
}
|
||||
InnerCommand innerCommand = commandWrapper.getCommand();
|
||||
return innerCommand instanceof ResourceCommandWrapper resourceCommandWrapper
|
||||
? resourceCommandWrapper.getResourceCommand()
|
||||
@@ -136,7 +148,7 @@ public class EppInput extends ImmutableObject {
|
||||
public Optional<String> getSingleTargetId() {
|
||||
ResourceCommand resourceCommand = getResourceCommand();
|
||||
return resourceCommand instanceof SingleResourceCommand singleResourceCommand
|
||||
? Optional.of(singleResourceCommand.getTargetId())
|
||||
? Optional.ofNullable(singleResourceCommand.getTargetId())
|
||||
: Optional.empty();
|
||||
}
|
||||
|
||||
@@ -147,7 +159,8 @@ public class EppInput extends ImmutableObject {
|
||||
public ImmutableList<String> 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 class EppInput extends ImmutableObject {
|
||||
|
||||
/** Get the extension based on type, or null. If there are multiple, it chooses the first. */
|
||||
public <E extends CommandExtension> Optional<E> getSingleExtension(Class<E> 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<CommandExtension> 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<EppInput> {
|
||||
public Builder setCommandWrapper(CommandWrapper commandWrapper) {
|
||||
getInstance().commandWrapper = commandWrapper;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/** A tag that goes inside an EPP {@literal <command>}. */
|
||||
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 class EppInput extends ImmutableObject {
|
||||
}
|
||||
|
||||
/** 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<Create> {
|
||||
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 class EppInput extends ImmutableObject {
|
||||
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<Update> {
|
||||
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 class EppInput extends ImmutableObject {
|
||||
@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 class EppInput extends ImmutableObject {
|
||||
}
|
||||
|
||||
/** 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 class EppInput extends ImmutableObject {
|
||||
@XmlElementWrapper
|
||||
List<CommandExtension> extension;
|
||||
|
||||
@Nullable String clTRID;
|
||||
@XmlElement(name = "clTRID")
|
||||
@Nullable
|
||||
String clTrid;
|
||||
|
||||
/**
|
||||
* Returns the client transaction ID.
|
||||
@@ -384,7 +513,7 @@ public class EppInput extends ImmutableObject {
|
||||
* <p>This is optional (i.e. it may not be specified) per RFC 5730.
|
||||
*/
|
||||
public Optional<String> getClTrid() {
|
||||
return Optional.ofNullable(clTRID);
|
||||
return Optional.ofNullable(clTrid);
|
||||
}
|
||||
|
||||
public InnerCommand getCommand() {
|
||||
@@ -394,12 +523,34 @@ public class EppInput extends ImmutableObject {
|
||||
public ImmutableList<CommandExtension> getExtensions() {
|
||||
return nullToEmptyImmutableCopy(extension);
|
||||
}
|
||||
|
||||
/** Builder for {@link CommandWrapper}. */
|
||||
public static class Builder extends Buildable.Builder<CommandWrapper> {
|
||||
|
||||
public Builder setCommand(InnerCommand command) {
|
||||
getInstance().command = command;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setExtensions(ImmutableList<CommandExtension> 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 class EppInput extends ImmutableObject {
|
||||
}
|
||||
|
||||
/** A services object inside of {@link Login}. */
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlType(propOrder = {"objectServices", "serviceExtensions"})
|
||||
public static class Services extends ImmutableObject {
|
||||
@XmlElement(name = "objURI")
|
||||
Set<String> objectServices;
|
||||
@@ -431,15 +584,15 @@ public class EppInput extends ImmutableObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 <a href="http://tools.ietf.org/html/rfc5730#page-41">
|
||||
* RFC 5730 - EPP - Command error responses</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5730#page-41">RFC 5730 - EPP - Command error
|
||||
* responses</a>
|
||||
*/
|
||||
public static class VersionAdapter extends XmlAdapter<String, String> {
|
||||
public static class VersionAdapter extends XmlAdapter<String, String> {
|
||||
@Override
|
||||
public String unmarshal(String version) throws Exception {
|
||||
if (!"1.0".equals(version)) {
|
||||
@@ -449,8 +602,8 @@ public class EppInput extends ImmutableObject {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String marshal(String ignored) {
|
||||
throw new UnsupportedOperationException();
|
||||
public String marshal(String version) {
|
||||
return version;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.EppResource;
|
||||
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}.
|
||||
*
|
||||
* <p>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 interface ResourceCommand {
|
||||
|
||||
/** A check command for an {@link EppResource}. */
|
||||
@XmlTransient
|
||||
class ResourceCheck extends ImmutableObject implements ResourceCommand {
|
||||
@XmlElements({
|
||||
@XmlElement(name = "id"),
|
||||
@XmlElement(name = "name") })
|
||||
List<String> targetUniqueIds;
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class ResourceCheck extends ImmutableObject implements ResourceCommand {
|
||||
@XmlElements({@XmlElement(name = "id"), @XmlElement(name = "name")})
|
||||
public List<String> targetUniqueIds;
|
||||
|
||||
public void setTargetIds(ImmutableList<String> targetUniqueIds) {
|
||||
this.targetUniqueIds = targetUniqueIds;
|
||||
}
|
||||
|
||||
public ImmutableList<String> getTargetIds() {
|
||||
return nullSafeImmutableCopy(targetUniqueIds);
|
||||
@@ -83,7 +94,7 @@ public interface ResourceCommand {
|
||||
}
|
||||
|
||||
/** A create command, or the inner change (as opposed to add or remove) part of an update. */
|
||||
interface ResourceCreateOrChange<B extends Buildable.Builder<?>> {}
|
||||
public interface ResourceCreateOrChange<B extends Buildable.Builder<?>> {}
|
||||
|
||||
/**
|
||||
* An update command for an {@link EppResource}.
|
||||
@@ -92,21 +103,19 @@ public interface ResourceCommand {
|
||||
* @param <C> 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<B>>
|
||||
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<StatusValue> statusValues;
|
||||
public abstract void setStatusValues(ImmutableSet<StatusValue> statusValues);
|
||||
|
||||
public ImmutableSet<StatusValue> getStatusValues() {
|
||||
return nullToEmptyImmutableCopy(statusValues);
|
||||
}
|
||||
public abstract ImmutableSet<StatusValue> getStatusValues();
|
||||
}
|
||||
|
||||
protected abstract C getNullableInnerChange();
|
||||
|
||||
@@ -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 @@ import java.util.Set;
|
||||
/** A collection of {@link Host} commands. */
|
||||
public class HostCommand {
|
||||
|
||||
/** The fields on "chgType" from <a href="http://tools.ietf.org/html/rfc5732">RFC5732</a>. */
|
||||
/** The fields on "chgType" from <a href="https://tools.ietf.org/html/rfc5732">RFC5732</a>. */
|
||||
@XmlTransient
|
||||
abstract static class HostCreateOrChange extends AbstractSingleResourceCommand
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public abstract static class HostCreateOrChange extends AbstractSingleResourceCommand
|
||||
implements ResourceCreateOrChange<Host.Builder> {
|
||||
|
||||
@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 <a
|
||||
* href="http://tools.ietf.org/html/rfc5732">RFC5732</a>.
|
||||
* href="https://tools.ietf.org/html/rfc5732">RFC5732</a>.
|
||||
*/
|
||||
@XmlType(propOrder = {"targetId", "inetAddresses"})
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlType(propOrder = {"name", "inetAddresses"})
|
||||
@XmlRootElement
|
||||
public static class Create extends HostCreateOrChange
|
||||
implements ResourceCreateOrChange<Host.Builder> {
|
||||
public static class Create extends HostCreateOrChange {
|
||||
/** IP Addresses for this host. Can be null if this is an external host. */
|
||||
@XmlElement(name = "addr")
|
||||
Set<InetAddress> inetAddresses;
|
||||
|
||||
public ImmutableSet<InetAddress> getInetAddresses() {
|
||||
return nullSafeImmutableCopy(inetAddresses);
|
||||
return nullToEmptyImmutableCopy(inetAddresses);
|
||||
}
|
||||
|
||||
/** Builder for {@link Create}. */
|
||||
public static class Builder extends Buildable.Builder<Create> {
|
||||
public Builder setTargetId(String targetId) {
|
||||
getInstance().setTargetId(targetId);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setInetAddresses(ImmutableSet<InetAddress> 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 class HostCommand {
|
||||
|
||||
/** An update to a {@link Host}. */
|
||||
@XmlRootElement
|
||||
@XmlType(propOrder = {"targetId", "innerAdd", "innerRemove", "innerChange"})
|
||||
public static class Update extends ResourceUpdate<Update.AddRemove, Host.Builder, Update.Change> {
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlType(propOrder = {"name", "innerAdd", "innerRemove", "innerChange"})
|
||||
public static class Update
|
||||
extends ResourceUpdate<Update.HostAddRemove, Host.Builder, Update.Change> {
|
||||
|
||||
@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 @@ public class HostCommand {
|
||||
}
|
||||
|
||||
@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<InetAddress> inetAddresses;
|
||||
|
||||
@XmlElement(name = "status")
|
||||
Set<StatusValue> statusValues;
|
||||
|
||||
@Override
|
||||
public void setStatusValues(ImmutableSet<StatusValue> statusValues) {
|
||||
this.statusValues = statusValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableSet<StatusValue> getStatusValues() {
|
||||
return nullToEmptyImmutableCopy(statusValues);
|
||||
}
|
||||
|
||||
public ImmutableSet<InetAddress> getInetAddresses() {
|
||||
return nullToEmptyImmutableCopy(inetAddresses);
|
||||
}
|
||||
|
||||
/** Builder for {@link HostAddRemove}. */
|
||||
public static class Builder extends Buildable.Builder<HostAddRemove> {
|
||||
public Builder setInetAddresses(ImmutableSet<InetAddress> inetAddresses) {
|
||||
getInstance().inetAddresses = isNullOrEmpty(inetAddresses) ? null : inetAddresses;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setStatusValues(ImmutableSet<StatusValue> 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 {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import static jakarta.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE;
|
||||
|
||||
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.Multimaps;
|
||||
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 jakarta.inject.Inject;
|
||||
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.
|
||||
*
|
||||
* <p>This calls {@link Spec11EmailUtils#emailSpec11Reports(LocalDate, SoyTemplateInfo, String,
|
||||
* <p>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 class PublishSpec11ReportAction implements Runnable {
|
||||
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 @@ public class PublishSpec11ReportAction implements Runnable {
|
||||
ImmutableSet<RegistrarThreatMatches> 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 @@ public class PublishSpec11ReportAction implements Runnable {
|
||||
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 @@ public class PublishSpec11ReportAction implements Runnable {
|
||||
private ImmutableSet<RegistrarThreatMatches> getNewMatches(
|
||||
ImmutableSet<RegistrarThreatMatches> previousMatchesSet,
|
||||
ImmutableSet<RegistrarThreatMatches> currentMatchesSet) {
|
||||
ImmutableMap<String, ImmutableSet<ThreatMatch>> previousMatchesByEmail =
|
||||
ImmutableMap<String, ImmutableSet<ThreatMatch>> previousMatchesByRegistrarId =
|
||||
groupByKeyAndFlatMap(previousMatchesSet);
|
||||
ImmutableMap<String, ImmutableSet<ThreatMatch>> currentMatchesByEmail =
|
||||
ImmutableMap<String, ImmutableSet<ThreatMatch>> currentMatchesByRegistrarId =
|
||||
groupByKeyAndFlatMap(currentMatchesSet);
|
||||
ImmutableSet.Builder<RegistrarThreatMatches> 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<ThreatMatch> difference =
|
||||
Sets.difference(
|
||||
currentMatchesByEmail.get(email),
|
||||
previousMatchesByEmail.getOrDefault(email, ImmutableSet.of()));
|
||||
ImmutableSet<ThreatMatch> 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 @@ public class PublishSpec11ReportAction implements Runnable {
|
||||
|
||||
private ImmutableMap<String, ImmutableSet<ThreatMatch>> groupByKeyAndFlatMap(
|
||||
ImmutableSet<RegistrarThreatMatches> 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())));
|
||||
}
|
||||
|
||||
|
||||
@@ -16,12 +16,17 @@ package google.registry.reporting.spec11;
|
||||
|
||||
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<ThreatMatch> threatMatches) {
|
||||
|
||||
static RegistrarThreatMatches create(String clientId, List<ThreatMatch> 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<ThreatMatch> threatMatches) {
|
||||
/** Creates a new {@link RegistrarThreatMatches} instance. */
|
||||
static RegistrarThreatMatches create(
|
||||
String registrarId, ImmutableList<ThreatMatch> threatMatches) {
|
||||
return new RegistrarThreatMatches(registrarId, threatMatches);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ package google.registry.reporting.spec11;
|
||||
|
||||
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.ImmutableMap;
|
||||
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<InternetAddress> 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<RegistrarThreatMatches> registrarThreatMatchesSet) {
|
||||
ImmutableMap.Builder<RegistrarThreatMatches, Throwable> failedMatchesBuilder =
|
||||
@@ -108,14 +120,15 @@ public class Spec11EmailUtils {
|
||||
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<RegistrarThreatMatches, Throwable> failedMatches = failedMatchesBuilder.build();
|
||||
if (!failedMatches.isEmpty()) {
|
||||
ImmutableList<Map.Entry<RegistrarThreatMatches, Throwable>> failedMatchesList =
|
||||
@@ -130,7 +143,7 @@ public class Spec11EmailUtils {
|
||||
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 @@ public class Spec11EmailUtils {
|
||||
RegistrarThreatMatches registrarThreatMatches) {
|
||||
ImmutableList<ThreatMatch> 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<Map<String, String>> 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<ImmutableMap<String, String>> threatMatchMap =
|
||||
registrarThreatMatches.threatMatches().stream()
|
||||
.map(
|
||||
threatMatch ->
|
||||
@@ -189,15 +208,14 @@ public class Spec11EmailUtils {
|
||||
"threatType", threatMatch.threatType()))
|
||||
.collect(toImmutableList());
|
||||
|
||||
Map<String, Object> data =
|
||||
ImmutableMap<String, Object> 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
|
||||
|
||||
@@ -18,8 +18,10 @@ import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
|
||||
import com.google.api.client.http.javanet.NetHttpTransport;
|
||||
import com.google.api.client.json.JsonFactory;
|
||||
import com.google.api.client.json.gson.GsonFactory;
|
||||
import com.google.gson.Gson;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.tools.GsonUtils;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.net.HttpURLConnection;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
@@ -49,9 +51,16 @@ public final class Modules {
|
||||
@Module
|
||||
public static final class GsonModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
static JsonFactory provideJsonFactory() {
|
||||
return GsonFactory.getDefaultInstance();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public static Gson provideGson() {
|
||||
return GsonUtils.provideGson();
|
||||
}
|
||||
}
|
||||
|
||||
/** Dagger module that provides standard {@link NetHttpTransport}. */
|
||||
|
||||
@@ -44,7 +44,6 @@ import google.registry.request.HttpException.UnsupportedMediaTypeException;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.lock.LockHandler;
|
||||
import google.registry.request.lock.LockHandlerImpl;
|
||||
import google.registry.tools.GsonUtils;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
@@ -74,13 +73,6 @@ public final class RequestModule {
|
||||
this.authResult = authResult;
|
||||
}
|
||||
|
||||
@RequestScope
|
||||
@VisibleForTesting
|
||||
@Provides
|
||||
public static Gson provideGson() {
|
||||
return GsonUtils.provideGson();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter(RequestParameters.PARAM_TLD)
|
||||
static String provideTld(HttpServletRequest req) {
|
||||
|
||||
@@ -146,20 +146,20 @@ public class AuthModule {
|
||||
@LocalCredentialJson
|
||||
public static String provideLocalCredentialJson(
|
||||
Lazy<GoogleClientSecrets> clientSecrets,
|
||||
Gson gson,
|
||||
@StoredCredential Lazy<Credential> credential,
|
||||
@Nullable @Config("credentialFilePath") String credentialFilePath) {
|
||||
try {
|
||||
if (credentialFilePath != null) {
|
||||
return Files.readString(Paths.get(credentialFilePath));
|
||||
} else {
|
||||
return new Gson()
|
||||
.toJson(
|
||||
ImmutableMap.<String, String>builder()
|
||||
.put("type", "authorized_user")
|
||||
.put("client_id", clientSecrets.get().getDetails().getClientId())
|
||||
.put("client_secret", clientSecrets.get().getDetails().getClientSecret())
|
||||
.put("refresh_token", credential.get().getRefreshToken())
|
||||
.build());
|
||||
return gson.toJson(
|
||||
ImmutableMap.<String, String>builder()
|
||||
.put("type", "authorized_user")
|
||||
.put("client_id", clientSecrets.get().getDetails().getClientId())
|
||||
.put("client_secret", clientSecrets.get().getDetails().getClientSecret())
|
||||
.put("refresh_token", credential.get().getRefreshToken())
|
||||
.build());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
@@ -16,10 +16,12 @@ package google.registry.tools;
|
||||
|
||||
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 @@ final class CheckDomainClaimsCommand extends NonMutatingEppToolCommand {
|
||||
clientId = registryAdminClientId;
|
||||
}
|
||||
|
||||
Multimap<String, String> domainNameMap = validateAndGroupDomainNamesByTld(mainParameters);
|
||||
Multimap<String, String> domainNameMap =
|
||||
validateAndGroupDomainNamesByTld(ImmutableList.copyOf(mainParameters));
|
||||
for (Collection<String> 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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 @@ final class CheckDomainCommand extends NonMutatingEppToolCommand {
|
||||
clientId = registryAdminClientId;
|
||||
}
|
||||
|
||||
Multimap<String, String> domainNameMap = validateAndGroupDomainNamesByTld(mainParameters);
|
||||
Multimap<String, String> domainNameMap =
|
||||
validateAndGroupDomainNamesByTld(ImmutableList.copyOf(mainParameters));
|
||||
for (Collection<String> 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<String> 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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,12 @@ import static google.registry.util.StringGenerator.DEFAULT_PASSWORD_LENGTH;
|
||||
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 @@ final class CreateAnchorTenantCommand extends MutatingEppToolCommand {
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,20 @@ import static google.registry.pricing.PricingEngineProxy.getPricesForDomainName;
|
||||
|
||||
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 @@ final class CreateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
}
|
||||
|
||||
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 @@ final class CreateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
!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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,14 @@ import static google.registry.util.CollectionUtils.nullToEmpty;
|
||||
|
||||
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<String> ipv4Addresses = new ImmutableList.Builder<>();
|
||||
ImmutableList.Builder<String> ipv6Addresses = new ImmutableList.Builder<>();
|
||||
ImmutableSet.Builder<InetAddress> 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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,9 @@ package google.registry.tools;
|
||||
|
||||
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 @@ final class DeleteDomainCommand extends MutatingEppToolCommand {
|
||||
// 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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,9 @@ package google.registry.tools;
|
||||
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.Ascii;
|
||||
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 @@ record DsRecord(int keyTag, int alg, int digestType, String digest) {
|
||||
elements.get(3));
|
||||
}
|
||||
|
||||
public SoyMapData toSoyData() {
|
||||
return new SoyMapData(
|
||||
"keyTag", keyTag(),
|
||||
"alg", alg(),
|
||||
"digestType", digestType(),
|
||||
"digest", digest());
|
||||
}
|
||||
|
||||
public static SoyListData convertToSoy(List<DsRecord> 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<DsRecord> {
|
||||
|
||||
@@ -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.ImmutableMultimap;
|
||||
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<XmlEppParameters> commands = new ArrayList<>();
|
||||
|
||||
private ServiceConnection connection;
|
||||
@@ -74,10 +66,11 @@ abstract class EppToolCommand extends ConfirmingCommand implements CommandWithCo
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<String, String> validateAndGroupDomainNamesByTld(List<String> names) {
|
||||
protected static Multimap<String, String> validateAndGroupDomainNamesByTld(
|
||||
ImmutableList<String> names) {
|
||||
ImmutableMultimap.Builder<String, String> builder = new ImmutableMultimap.Builder<>();
|
||||
for (String name : names) {
|
||||
String canonicalDomain = canonicalizeHostname(name);
|
||||
@@ -87,11 +80,6 @@ abstract class EppToolCommand extends ConfirmingCommand implements CommandWithCo
|
||||
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 @@ abstract class EppToolCommand extends ConfirmingCommand implements CommandWithCo
|
||||
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 @@ abstract class EppToolCommand extends ConfirmingCommand implements CommandWithCo
|
||||
return prompt;
|
||||
}
|
||||
|
||||
private List<String> processCommands(boolean dryRun) throws IOException {
|
||||
private ImmutableList<String> processCommands(boolean dryRun) throws IOException {
|
||||
ImmutableList.Builder<String> responses = new ImmutableList.Builder<>();
|
||||
for (XmlEppParameters command : commands) {
|
||||
Map<String, Object> 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<String, Object> params =
|
||||
ImmutableMap.<String, Object>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.<String, String>of(),
|
||||
ImmutableMap.of(),
|
||||
MediaType.FORM_DATA,
|
||||
requestBody.getBytes(UTF_8))));
|
||||
}
|
||||
|
||||
@@ -18,18 +18,20 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
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<String> 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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.ImmutableList;
|
||||
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<String> URS_LOCKS = ImmutableSet.of(
|
||||
StatusValue.SERVER_DELETE_PROHIBITED.getXmlName(),
|
||||
StatusValue.SERVER_TRANSFER_PROHIBITED.getXmlName(),
|
||||
StatusValue.SERVER_UPDATE_PROHIBITED.getXmlName());
|
||||
private static final ImmutableSet<String> 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 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand {
|
||||
superuser = true;
|
||||
Instant now = clock.now();
|
||||
Domain domain = ResourceFlowUtils.loadAndVerifyExistence(Domain.class, domainName, now);
|
||||
Set<String> missingHosts =
|
||||
difference(newHosts, ForeignKeyUtils.loadKeys(Host.class, newHosts, now).keySet());
|
||||
ImmutableSet<String> 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 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand {
|
||||
|
||||
// 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<String> 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<String> addNameservers =
|
||||
ImmutableSet.copyOf(difference(newHosts, existingNameservers));
|
||||
if (!addNameservers.isEmpty()) {
|
||||
addBuilder.setNameserverHostNames(ImmutableSortedSet.copyOf(addNameservers));
|
||||
hasAdd = true;
|
||||
}
|
||||
|
||||
ImmutableSet<String> 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<String> getExistingNameservers(Domain domain) {
|
||||
ImmutableSortedSet.Builder<String> nameservers = ImmutableSortedSet.naturalOrder();
|
||||
for (Host host : tm().transact(() -> tm().loadByKeys(domain.getNameservers()).values())) {
|
||||
@@ -208,6 +248,7 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand {
|
||||
return nameservers.build();
|
||||
}
|
||||
|
||||
/** Returns the set of existing URS-related locks for the specified domain. */
|
||||
private ImmutableSortedSet<String> getExistingLocks(Domain domain) {
|
||||
ImmutableSortedSet.Builder<String> locks = ImmutableSortedSet.naturalOrder();
|
||||
for (StatusValue lock : domain.getStatusValues()) {
|
||||
@@ -218,6 +259,7 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand {
|
||||
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 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns a list of the existing DS records for the specified domain as JSON-like maps. */
|
||||
private ImmutableList<ImmutableMap<String, Object>> getExistingDsData(Domain domain) {
|
||||
ImmutableList.Builder<ImmutableMap<String, Object>> dsDataJsons = new ImmutableList.Builder();
|
||||
HexBinaryAdapter hexBinaryAdapter = new HexBinaryAdapter();
|
||||
@@ -273,7 +316,7 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand {
|
||||
rec.get("digestType"),
|
||||
rec.get("digest")))
|
||||
.sorted()
|
||||
.collect(toImmutableList());
|
||||
.collect(ImmutableList.toImmutableList());
|
||||
undoBuilder.append(" --dsdata ").append(Joiner.on(',').join(formattedDsRecords));
|
||||
}
|
||||
return undoBuilder.toString();
|
||||
|
||||
@@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
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.ImmutableSet;
|
||||
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<String> 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<String> addStatuses = new ArrayList<>();
|
||||
|
||||
@Parameter(
|
||||
@@ -82,9 +86,8 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
private Set<String> 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<String> removeStatuses = new ArrayList<>();
|
||||
|
||||
@Parameter(
|
||||
@@ -95,10 +98,8 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
private List<DsRecord> 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 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
+ "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 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
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 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
+ "--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<String> addNameserversThisDomain = new TreeSet<>(addNameservers);
|
||||
Set<String> removeNameserversThisDomain = new TreeSet<>(removeNameservers);
|
||||
Set<String> addStatusesThisDomain = new TreeSet<>(addStatuses);
|
||||
Set<String> removeStatusesThisDomain = new TreeSet<>(removeStatuses);
|
||||
if (!nameservers.isEmpty()) {
|
||||
ImmutableSortedSet<String> existingNameservers = domain.loadNameserverHostNames();
|
||||
ImmutableSet<String> targetNameservers = ImmutableSet.copyOf(nameservers);
|
||||
|
||||
if (!nameservers.isEmpty() || !statuses.isEmpty()) {
|
||||
if (!nameservers.isEmpty()) {
|
||||
ImmutableSortedSet<String> 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<String> 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());
|
||||
|
||||
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 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
}
|
||||
}
|
||||
|
||||
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<String> current = domain.loadNameserverHostNames();
|
||||
ImmutableSet<String> target = ImmutableSet.copyOf(nameservers);
|
||||
ImmutableSortedSet<String> toAdd =
|
||||
ImmutableSortedSet.copyOf(Sets.difference(target, current));
|
||||
ImmutableSortedSet<String> 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 (requestedByRegistrar != null) {
|
||||
soyMapData.put("requestedByRegistrar", requestedByRegistrar.toString());
|
||||
|
||||
if (!statuses.isEmpty()) {
|
||||
ImmutableSortedSet<StatusValue> current =
|
||||
ImmutableSortedSet.copyOf(domain.getStatusValues());
|
||||
ImmutableSet<StatusValue> target =
|
||||
statuses.stream().map(StatusValue::fromXmlName).collect(ImmutableSet.toImmutableSet());
|
||||
ImmutableSortedSet<StatusValue> toAdd =
|
||||
ImmutableSortedSet.copyOf(Sets.difference(target, current));
|
||||
ImmutableSortedSet<StatusValue> 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 (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<String> domainsToWarn = autorenewGracePeriodWarningDomains.build();
|
||||
@@ -260,10 +315,4 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
String.join(", ", domainsToWarn));
|
||||
}
|
||||
}
|
||||
|
||||
private void populateAddRemoveLists(
|
||||
Set<String> targetSet, Set<String> oldSet, Set<String> addSet, Set<String> removeSet) {
|
||||
addSet.addAll(Sets.difference(targetSet, oldSet));
|
||||
removeSet.addAll(Sets.difference(oldSet, targetSet));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,94 +21,119 @@ import static com.google.common.collect.Sets.intersection;
|
||||
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 = " =",
|
||||
commandDescription = "Toggle server locks on a domain.")
|
||||
final class UpdateServerLocksCommand extends MutatingEppToolCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-c", "--client"},
|
||||
description = "Client identifier of the registrar to execute the command as",
|
||||
required = true)
|
||||
String clientId;
|
||||
|
||||
@Parameter(
|
||||
names = {"-n", "--domain_name"},
|
||||
description = "Domain to modify.",
|
||||
description = "Domain to lock/unlock.",
|
||||
required = true)
|
||||
private String domainName;
|
||||
|
||||
@Parameter(
|
||||
names = {"-a", "--apply"},
|
||||
description = "Comma-delimited set of locks to apply (or 'all'). "
|
||||
+ "Valid locks: serverDeleteProhibited, serverHold, serverRenewProhibited, "
|
||||
+ "serverTransferProhibited, serverUpdateProhibited")
|
||||
private List<String> locksToApply = new ArrayList<>();
|
||||
|
||||
@Parameter(
|
||||
names = {"-r", "--remove"},
|
||||
description = "Comma-delimited set of locks to remove (or 'all'). "
|
||||
+ "Valid locks: same as for 'apply'.")
|
||||
private List<String> locksToRemove = new ArrayList<>();
|
||||
names = {"--client"},
|
||||
description = "Client ID to use for the EPP command.",
|
||||
required = true)
|
||||
private String clientId;
|
||||
|
||||
@Parameter(
|
||||
names = {"--reason"},
|
||||
description = "Reason for the change. Required if registrar_request = false.")
|
||||
description = "Reason for the change.")
|
||||
private String reason;
|
||||
|
||||
@Parameter(
|
||||
names = {"--apply"},
|
||||
description = "Statuses to apply. Use \"all\" to apply all server locks.")
|
||||
private List<String> locksToApply = new ArrayList<>();
|
||||
|
||||
@Parameter(
|
||||
names = {"--remove"},
|
||||
description = "Statuses to remove. Use \"all\" to remove all server locks.")
|
||||
private List<String> 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<String> 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<String> 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<String> getStatusValuesSet(List<String> statusValues) {
|
||||
Set<String> statusValuesSet = ImmutableSet.copyOf(statusValues);
|
||||
private static ImmutableSet<String> getStatusValuesSet(List<String> statusValues) {
|
||||
ImmutableSet<String> statusValuesSet = ImmutableSet.copyOf(statusValues);
|
||||
if (statusValuesSet.contains("all")) {
|
||||
return ALLOWED_VALUES;
|
||||
}
|
||||
Set<String> badValues = difference(statusValuesSet, ALLOWED_VALUES);
|
||||
ImmutableSet<String> 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<String> valuesToApply = getStatusValuesSet(locksToApply);
|
||||
Set<String> valuesToRemove = getStatusValuesSet(locksToRemove);
|
||||
ImmutableSet<String> valuesToApply = getStatusValuesSet(locksToApply);
|
||||
ImmutableSet<String> 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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<#ftl output_format="HTML">
|
||||
<#-- Copyright 2026 The Nomulus Authors. All Rights Reserved. -->
|
||||
|
||||
<p>Dear registrar partner,</p>
|
||||
|
||||
<p>${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:</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Domain Name</th>
|
||||
<th>Threat Type</th>
|
||||
</tr>
|
||||
<#list threats as threat>
|
||||
<tr>
|
||||
<td>${threat.domainName}</td>
|
||||
<td>${threat.threatType}</td>
|
||||
</tr>
|
||||
</#list>
|
||||
</table>
|
||||
|
||||
<p><b>Please communicate these findings to the registrant and work with the
|
||||
registrant to mitigate any security issues and have the domains delisted.</b></p>
|
||||
|
||||
<#if (resources?size > 0)>
|
||||
<p>Some helpful resources for getting off a blocked list include:</p>
|
||||
<ul>
|
||||
<#list resources as resource>
|
||||
<li>${resource}</li>
|
||||
</#list>
|
||||
</ul>
|
||||
</#if>
|
||||
|
||||
<p>If you believe that any of the domains were reported in error, or are still receiving
|
||||
reports for issues that have been remediated,
|
||||
please <a href="https://safebrowsing.google.com/safebrowsing/report_error/?hl=en">submit
|
||||
a request</a> to have the site reviewed.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>If you would like to change the email to which these notices are sent, please update your
|
||||
abuse contact using your registrar portal account.</p>
|
||||
|
||||
<p>If you have any questions regarding this notice, please contact ${replyToEmail}.</p>
|
||||
@@ -0,0 +1,46 @@
|
||||
<#ftl output_format="HTML">
|
||||
<#-- Copyright 2026 The Nomulus Authors. All Rights Reserved. -->
|
||||
|
||||
<p>Dear registrar partner,</p>
|
||||
|
||||
<p>${registry} previously notified you when the following domains managed by your
|
||||
registrar were flagged for potential security concerns.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Domain Name</th>
|
||||
<th>Threat Type</th>
|
||||
</tr>
|
||||
<#list threats as threat>
|
||||
<tr>
|
||||
<td>${threat.domainName}</td>
|
||||
<td>${threat.threatType}</td>
|
||||
</tr>
|
||||
</#list>
|
||||
</table>
|
||||
|
||||
<p>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 <a href="https://safebrowsing.google.com/safebrowsing/report_error/?hl=en">submit a
|
||||
request</a> to have the site reviewed.</p>
|
||||
|
||||
<#if (resources?size > 0)>
|
||||
<p>Some helpful resources for getting off a blocked list include:</p>
|
||||
<ul>
|
||||
<#list resources as resource>
|
||||
<li>${resource}</li>
|
||||
</#list>
|
||||
</ul>
|
||||
</#if>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>If you have any questions regarding this notice, please contact ${replyToEmail}.</p>
|
||||
@@ -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<map<string, string>>}
|
||||
{@param resources: list<string>}
|
||||
{@param registry: string}
|
||||
{@param replyToEmail: string}
|
||||
|
||||
Dear registrar partner,
|
||||
|
||||
<p>{$registry} previously notified you when the following domains managed by your
|
||||
registrar were flagged for potential security concerns.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
{call threatMatchTable}
|
||||
{param threats: $threats /}
|
||||
{/call}
|
||||
|
||||
<p>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 <a href="https://safebrowsing.google.com/safebrowsing/report_error/?hl=en">submit a
|
||||
request</a> to have the site reviewed.</p>
|
||||
|
||||
{call resourceList}
|
||||
{param resources: $resources /}
|
||||
{/call}
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>If you have any questions regarding this notice, please contact {$replyToEmail}.</p>
|
||||
{/template}
|
||||
|
||||
/**
|
||||
* Template for the content of the daily spec11 email
|
||||
*/
|
||||
{template dailySpec11Email}
|
||||
{@param threats: list<map<string, string>>}
|
||||
{@param resources: list<string>}
|
||||
{@param date: string}
|
||||
{@param registry: string}
|
||||
{@param replyToEmail: string}
|
||||
|
||||
Dear registrar partner,
|
||||
|
||||
<p>{$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:</p>
|
||||
|
||||
{call threatMatchTable}
|
||||
{param threats: $threats /}
|
||||
{/call}
|
||||
|
||||
<p><b>Please communicate these findings to the registrant and work with the
|
||||
registrant to mitigate any security issues and have the domains delisted.</b></p>
|
||||
|
||||
{call resourceList}
|
||||
{param resources: $resources /}
|
||||
{/call}
|
||||
|
||||
<p>If you believe that any of the domains were reported in error, or are still receiving
|
||||
reports for issues that have been remediated,
|
||||
please <a href="https://safebrowsing.google.com/safebrowsing/report_error/?hl=en">submit
|
||||
a request</a> to have the site reviewed.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>If you would like to change the email to which these notices are sent please update your
|
||||
abuse contact using your registrar portal account.</p>
|
||||
|
||||
<p>If you have any questions regarding this notice, please contact {$replyToEmail}.</p>
|
||||
{/template}
|
||||
|
||||
/**
|
||||
* Template for the list of potentially-useful resources
|
||||
*/
|
||||
{template resourceList}
|
||||
{@param resources: list<string>}
|
||||
{if length($resources) > 0}
|
||||
Some helpful resources for getting off a blocked list include:
|
||||
<ul>
|
||||
{for $resource in $resources}
|
||||
<li>{$resource}</li>
|
||||
{/for}
|
||||
</ul>
|
||||
{/if}
|
||||
{/template}
|
||||
|
||||
/**
|
||||
* Template for the table containing the threats themselves
|
||||
*/
|
||||
{template threatMatchTable}
|
||||
{@param threats: list<map<string, string>>}
|
||||
<table>
|
||||
<tr>
|
||||
<th>Domain Name</th>
|
||||
<th>Threat Type</th>
|
||||
</tr>
|
||||
{for $threat in $threats}
|
||||
<tr>
|
||||
<td>{$threat.get('domainName')}</td>
|
||||
<td>{$threat.get('threatType')}</td>
|
||||
</tr>
|
||||
{/for}
|
||||
</table>
|
||||
{/template}
|
||||
@@ -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}
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<domain:create
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>{$domainName}</domain:name>
|
||||
<domain:period unit="y">{$period}</domain:period>
|
||||
<domain:registrant>{$contactId}</domain:registrant>
|
||||
<domain:contact type="admin">{$contactId}</domain:contact>
|
||||
<domain:contact type="tech">{$contactId}</domain:contact>
|
||||
<domain:authInfo>
|
||||
<domain:pw>{$password}</domain:pw>
|
||||
</domain:authInfo>
|
||||
</domain:create>
|
||||
</create>
|
||||
<extension>
|
||||
<metadata:metadata xmlns:metadata="urn:google:params:xml:ns:metadata-1.0">
|
||||
{if $reason}
|
||||
<metadata:reason>{$reason}</metadata:reason>
|
||||
{/if}
|
||||
<metadata:requestedByRegistrar>false</metadata:requestedByRegistrar>
|
||||
<metadata:anchorTenant>true</metadata:anchorTenant>
|
||||
</metadata:metadata>
|
||||
{if $fee}
|
||||
<fee:create xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
|
||||
<fee:currency>{$feeCurrency}</fee:currency>
|
||||
<fee:fee>{$fee}</fee:fee>
|
||||
</fee:create>
|
||||
{/if}
|
||||
</extension>
|
||||
<clTRID>RegistryTool</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
{/template}
|
||||
@@ -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<string>}
|
||||
{@param? allocationToken: string|null}
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<check>
|
||||
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
{for $d in $domainNames}
|
||||
<domain:name>{$d}</domain:name>
|
||||
{/for}
|
||||
</domain:check>
|
||||
</check>
|
||||
<extension>
|
||||
<fee:check xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
|
||||
{for $d in $domainNames}
|
||||
<fee:domain>
|
||||
<fee:name>{$d}</fee:name>
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
</fee:domain>
|
||||
{/for}
|
||||
</fee:check>
|
||||
{if $allocationToken}
|
||||
<allocationToken:allocationToken
|
||||
xmlns:allocationToken="urn:ietf:params:xml:ns:allocationToken-1.0">
|
||||
{$allocationToken}
|
||||
</allocationToken:allocationToken>
|
||||
{/if}
|
||||
</extension>
|
||||
<clTRID>RegistryTool</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
{/template}
|
||||
@@ -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<string>}
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<check>
|
||||
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
{for $d in $domainNames}
|
||||
<domain:name>{$d}</domain:name>
|
||||
{/for}
|
||||
</domain:check>
|
||||
</check>
|
||||
<extension>
|
||||
<launch:check
|
||||
xmlns:launch="urn:ietf:params:xml:ns:launch-1.0"
|
||||
type="claims">
|
||||
<launch:phase>claims</launch:phase>
|
||||
</launch:check>
|
||||
</extension>
|
||||
<clTRID>RegistryTool</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
{/template}
|
||||
@@ -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<string>}
|
||||
{@param? registrant: string|null}
|
||||
{@param? admins: list<string>|null}
|
||||
{@param? techs: list<string>|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}
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>{$domain}</domain:name>
|
||||
<domain:period unit="y">{$period}</domain:period>
|
||||
{if length($nameservers) > 0}
|
||||
<domain:ns>
|
||||
{for $s in $nameservers}
|
||||
<domain:hostObj>{$s}</domain:hostObj>
|
||||
{/for}
|
||||
</domain:ns>
|
||||
{/if}
|
||||
{if $registrant != null}
|
||||
<domain:registrant>{$registrant}</domain:registrant>
|
||||
{/if}
|
||||
{if $admins != null}
|
||||
{for $admin in $admins}
|
||||
<domain:contact type="admin">{$admin}</domain:contact>
|
||||
{/for}
|
||||
{/if}
|
||||
{if $techs != null}
|
||||
{for $tech in $techs}
|
||||
<domain:contact type="tech">{$tech}</domain:contact>
|
||||
{/for}
|
||||
{/if}
|
||||
<domain:authInfo>
|
||||
<domain:pw>{$password}</domain:pw>
|
||||
</domain:authInfo>
|
||||
</domain:create>
|
||||
</create>
|
||||
{if length($dsRecords) > 0 || $price != null || $reason || $requestedByRegistrar || $allocationToken}
|
||||
<extension>
|
||||
{if $price != null}
|
||||
<fee:create xmlns:fee="urn:ietf:params:xml:ns:fee-0.12">
|
||||
<fee:currency>{$currency}</fee:currency>
|
||||
<fee:fee>{$price}</fee:fee>
|
||||
</fee:create>
|
||||
{/if}
|
||||
{if length($dsRecords) > 0}
|
||||
<secDNS:create xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1">
|
||||
{for $dsRecord in $dsRecords}
|
||||
<secDNS:dsData>
|
||||
<secDNS:keyTag>{$dsRecord.keyTag}</secDNS:keyTag>
|
||||
<secDNS:alg>{$dsRecord.alg}</secDNS:alg>
|
||||
<secDNS:digestType>{$dsRecord.digestType}</secDNS:digestType>
|
||||
<secDNS:digest>{$dsRecord.digest}</secDNS:digest>
|
||||
</secDNS:dsData>
|
||||
{/for}
|
||||
</secDNS:create>
|
||||
{/if}
|
||||
{if $reason || $requestedByRegistrar}
|
||||
<metadata:metadata xmlns:metadata="urn:google:params:xml:ns:metadata-1.0">
|
||||
{if $reason}
|
||||
<metadata:reason>{$reason}</metadata:reason>
|
||||
{/if}
|
||||
{if $requestedByRegistrar}
|
||||
<metadata:requestedByRegistrar>{$requestedByRegistrar}</metadata:requestedByRegistrar>
|
||||
{/if}
|
||||
</metadata:metadata>
|
||||
{/if}
|
||||
{if $allocationToken}
|
||||
<allocationToken:allocationToken
|
||||
xmlns:allocationToken=
|
||||
"urn:ietf:params:xml:ns:allocationToken-1.0">
|
||||
{$allocationToken}
|
||||
</allocationToken:allocationToken>
|
||||
{/if}
|
||||
</extension>
|
||||
{/if}
|
||||
<clTRID>RegistryTool</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
{/template}
|
||||
@@ -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}
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<delete>
|
||||
<domain:delete
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>{$domainName}</domain:name>
|
||||
</domain:delete>
|
||||
</delete>
|
||||
<extension>
|
||||
<metadata:metadata xmlns:metadata="urn:google:params:xml:ns:metadata-1.0">
|
||||
<metadata:reason>Deleted by registry administrator: {$reason}</metadata:reason>
|
||||
<metadata:requestedByRegistrar>{$requestedByRegistrar}</metadata:requestedByRegistrar>
|
||||
</metadata:metadata>
|
||||
{if $immediately}
|
||||
<superuser:domainDelete xmlns:superuser="urn:google:params:xml:ns:superuser-1.0">
|
||||
<superuser:redemptionGracePeriodDays>0</superuser:redemptionGracePeriodDays>
|
||||
<superuser:pendingDeleteDays>0</superuser:pendingDeleteDays>
|
||||
</superuser:domainDelete>
|
||||
{/if}
|
||||
</extension>
|
||||
<clTRID>RegistryTool</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
{/template}
|
||||
@@ -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}
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<renew>
|
||||
<domain:renew
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>{$domainName}</domain:name>
|
||||
<domain:curExpDate>{$expirationDate}</domain:curExpDate>
|
||||
<domain:period unit="y">{$period}</domain:period>
|
||||
</domain:renew>
|
||||
</renew>
|
||||
{if $reason || $requestedByRegistrar}
|
||||
<extension>
|
||||
<metadata:metadata xmlns:metadata="urn:google:params:xml:ns:metadata-1.0">
|
||||
{if $reason}
|
||||
<metadata:reason>{$reason}</metadata:reason>
|
||||
{/if}
|
||||
{if $requestedByRegistrar}
|
||||
<metadata:requestedByRegistrar>{$requestedByRegistrar}</metadata:requestedByRegistrar>
|
||||
{/if}
|
||||
</metadata:metadata>
|
||||
</extension>
|
||||
{/if}
|
||||
<clTRID>RegistryTool</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
{/template}
|
||||
@@ -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<string>}
|
||||
{@param addStatuses: list<string>}
|
||||
{@param remove: bool}
|
||||
{@param removeNameservers: list<string>}
|
||||
{@param removeStatuses: list<string>}
|
||||
{@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}
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>{$domain}</domain:name>
|
||||
{if $add}
|
||||
<domain:add>
|
||||
{if length($addNameservers) > 0}
|
||||
<domain:ns>
|
||||
{for $s in $addNameservers}
|
||||
<domain:hostObj>{$s}</domain:hostObj>
|
||||
{/for}
|
||||
</domain:ns>
|
||||
{/if}
|
||||
{for $status in $addStatuses}
|
||||
<domain:status s="{$status}"/>
|
||||
{/for}
|
||||
</domain:add>
|
||||
{/if}
|
||||
{if $remove}
|
||||
<domain:rem>
|
||||
{if length($removeNameservers) > 0}
|
||||
<domain:ns>
|
||||
{for $s in $removeNameservers}
|
||||
<domain:hostObj>{$s}</domain:hostObj>
|
||||
{/for}
|
||||
</domain:ns>
|
||||
{/if}
|
||||
{for $status in $removeStatuses}
|
||||
<domain:status s="{$status}"/>
|
||||
{/for}
|
||||
</domain:rem>
|
||||
{/if}
|
||||
{if $change}
|
||||
<domain:chg>
|
||||
{if $password}
|
||||
<domain:authInfo>
|
||||
<domain:pw>{$password}</domain:pw>
|
||||
</domain:authInfo>
|
||||
{/if}
|
||||
</domain:chg>
|
||||
{/if}
|
||||
</domain:update>
|
||||
</update>
|
||||
{if $secdns || $autorenews || $reason || $requestedByRegistrar}
|
||||
<extension>
|
||||
{if $secdns}
|
||||
<secDNS:update xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1">
|
||||
{if $removeAllDsRecords}
|
||||
<secDNS:rem>
|
||||
<secDNS:all>true</secDNS:all>
|
||||
</secDNS:rem>
|
||||
{/if}
|
||||
{if length($removeDsRecords) > 0}
|
||||
<secDNS:rem>
|
||||
{for $dsRecord in $removeDsRecords}
|
||||
<secDNS:dsData>
|
||||
<secDNS:keyTag>{$dsRecord.keyTag}</secDNS:keyTag>
|
||||
<secDNS:alg>{$dsRecord.alg}</secDNS:alg>
|
||||
<secDNS:digestType>{$dsRecord.digestType}</secDNS:digestType>
|
||||
<secDNS:digest>{$dsRecord.digest}</secDNS:digest>
|
||||
</secDNS:dsData>
|
||||
{/for}
|
||||
</secDNS:rem>
|
||||
{/if}
|
||||
{if length($addDsRecords) > 0}
|
||||
<secDNS:add>
|
||||
{for $dsRecord in $addDsRecords}
|
||||
<secDNS:dsData>
|
||||
<secDNS:keyTag>{$dsRecord.keyTag}</secDNS:keyTag>
|
||||
<secDNS:alg>{$dsRecord.alg}</secDNS:alg>
|
||||
<secDNS:digestType>{$dsRecord.digestType}</secDNS:digestType>
|
||||
<secDNS:digest>{$dsRecord.digest}</secDNS:digest>
|
||||
</secDNS:dsData>
|
||||
{/for}
|
||||
</secDNS:add>
|
||||
{/if}
|
||||
</secDNS:update>
|
||||
{/if}
|
||||
{if $autorenews}
|
||||
<superuser:domainUpdate xmlns:superuser="urn:google:params:xml:ns:superuser-1.0">
|
||||
<superuser:autorenews>{$autorenews}</superuser:autorenews>
|
||||
</superuser:domainUpdate>
|
||||
{/if}
|
||||
{if $reason || $requestedByRegistrar}
|
||||
<metadata:metadata xmlns:metadata="urn:google:params:xml:ns:metadata-1.0">
|
||||
{if $reason}
|
||||
<metadata:reason>{$reason}</metadata:reason>
|
||||
{/if}
|
||||
{if $requestedByRegistrar}
|
||||
<metadata:requestedByRegistrar>{$requestedByRegistrar}</metadata:requestedByRegistrar>
|
||||
{/if}
|
||||
</metadata:metadata>
|
||||
{/if}
|
||||
</extension>
|
||||
{/if}
|
||||
<clTRID>RegistryTool</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
{/template}
|
||||
@@ -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<string>|null}
|
||||
{@param? ipv6addresses: list<string>|null}
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<host:create xmlns:host="urn:ietf:params:xml:ns:host-1.0">
|
||||
<host:name>{$hostname}</host:name>
|
||||
{if $ipv4addresses}
|
||||
{for $ipv4 in $ipv4addresses}
|
||||
<host:addr ip="v4">{$ipv4}</host:addr>
|
||||
{/for}
|
||||
{/if}
|
||||
{if $ipv6addresses}
|
||||
{for $ipv6 in $ipv6addresses}
|
||||
<host:addr ip="v6">{$ipv6}</host:addr>
|
||||
{/for}
|
||||
{/if}
|
||||
</host:create>
|
||||
</create>
|
||||
<clTRID>RegistryTool</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
{/template}
|
||||
@@ -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}
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<delete>
|
||||
<host:delete
|
||||
xmlns:host="urn:ietf:params:xml:ns:host-1.0">
|
||||
<host:name>{$hostName}</host:name>
|
||||
</host:delete>
|
||||
</delete>
|
||||
<extension>
|
||||
<metadata:metadata xmlns:metadata="urn:google:params:xml:ns:metadata-1.0">
|
||||
<metadata:reason>Deleted by registry administrator: {$reason}</metadata:reason>
|
||||
<metadata:requestedByRegistrar>{$requestedByRegistrar}</metadata:requestedByRegistrar>
|
||||
</metadata:metadata>
|
||||
</extension>
|
||||
<clTRID>RegistryTool</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
{/template}
|
||||
@@ -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<legacy_object_map<string, string>>}
|
||||
{@param requestedByRegistrar: string}
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<host:update
|
||||
xmlns:host="urn:ietf:params:xml:ns:host-1.0">
|
||||
<host:name>{$name}</host:name>
|
||||
{for $ip in $ipAddresses}
|
||||
<host:rem>
|
||||
<host:addr ip="{$ip['type']}">{$ip['address']}</host:addr>
|
||||
</host:rem>
|
||||
{/for}
|
||||
</host:update>
|
||||
</update>
|
||||
<extension>
|
||||
<metadata:metadata xmlns:metadata="urn:google:params:xml:ns:metadata-1.0">
|
||||
<metadata:reason>External IP address removed by registry administrator.</metadata:reason>
|
||||
<metadata:requestedByRegistrar>{$requestedByRegistrar}</metadata:requestedByRegistrar>
|
||||
</metadata:metadata>
|
||||
</extension>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
{/template}
|
||||
@@ -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<string>}
|
||||
{@param hostsToRemove: list<string>}
|
||||
{@param statusesToApply: list<string>}
|
||||
{@param statusesToRemove: list<string>}
|
||||
{@param newDsData: list<[keyTag:int, alg:int, digestType:int, digest:string]>}
|
||||
{@param reason: string}
|
||||
{@param autorenews: string}
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>{$domainName}</domain:name>
|
||||
<domain:add>
|
||||
{if length($hostsToAdd) > 0}
|
||||
<domain:ns>
|
||||
{for $ha in $hostsToAdd}
|
||||
<domain:hostObj>{$ha}</domain:hostObj>
|
||||
{/for}
|
||||
</domain:ns>
|
||||
{/if}
|
||||
{for $la in $statusesToApply}
|
||||
<domain:status s="{$la}" />
|
||||
{/for}
|
||||
</domain:add>
|
||||
<domain:rem>
|
||||
{if length($hostsToRemove) > 0}
|
||||
<domain:ns>
|
||||
{for $hr in $hostsToRemove}
|
||||
<domain:hostObj>{$hr}</domain:hostObj>
|
||||
{/for}
|
||||
</domain:ns>
|
||||
{/if}
|
||||
{for $lr in $statusesToRemove}
|
||||
<domain:status s="{$lr}" />
|
||||
{/for}
|
||||
</domain:rem>
|
||||
</domain:update>
|
||||
</update>
|
||||
<extension>
|
||||
<secDNS:update xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1">
|
||||
<secDNS:rem>
|
||||
<secDNS:all>true</secDNS:all>
|
||||
</secDNS:rem>
|
||||
{if length($newDsData) > 0}
|
||||
<secDNS:add>
|
||||
{for $ds in $newDsData}
|
||||
<secDNS:dsData>
|
||||
<secDNS:keyTag>{$ds.keyTag}</secDNS:keyTag>
|
||||
<secDNS:alg>{$ds.alg}</secDNS:alg>
|
||||
<secDNS:digestType>{$ds.digestType}</secDNS:digestType>
|
||||
<secDNS:digest>{$ds.digest}</secDNS:digest>
|
||||
</secDNS:dsData>
|
||||
{/for}
|
||||
</secDNS:add>
|
||||
{/if}
|
||||
</secDNS:update>
|
||||
<superuser:domainUpdate xmlns:superuser="urn:google:params:xml:ns:superuser-1.0">
|
||||
<superuser:autorenews>{$autorenews}</superuser:autorenews>
|
||||
</superuser:domainUpdate>
|
||||
<metadata:metadata xmlns:metadata="urn:google:params:xml:ns:metadata-1.0">
|
||||
<metadata:reason>{$reason}</metadata:reason>
|
||||
<metadata:requestedByRegistrar>false</metadata:requestedByRegistrar>
|
||||
</metadata:metadata>
|
||||
</extension>
|
||||
<clTRID>RegistryTool</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
{/template}
|
||||
@@ -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<string>}
|
||||
{@param locksToRemove: list<string>}
|
||||
{@param requestedByRegistrar: any}
|
||||
{@param? reason: string|null}
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<domain:update
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>{$domainName}</domain:name>
|
||||
<domain:add>
|
||||
{for $a in $locksToApply}
|
||||
<domain:status s="{$a}" lang="en"></domain:status>
|
||||
{/for}
|
||||
</domain:add>
|
||||
<domain:rem>
|
||||
{for $r in $locksToRemove}
|
||||
<domain:status s="{$r}" lang="en"></domain:status>
|
||||
{/for}
|
||||
</domain:rem>
|
||||
</domain:update>
|
||||
</update>
|
||||
<extension>
|
||||
<metadata:metadata xmlns:metadata="urn:google:params:xml:ns:metadata-1.0">
|
||||
{if $reason}
|
||||
<metadata:reason>{$reason}</metadata:reason>
|
||||
{/if}
|
||||
<metadata:requestedByRegistrar>{$requestedByRegistrar}</metadata:requestedByRegistrar>
|
||||
</metadata:metadata>
|
||||
</extension>
|
||||
<clTRID>RegistryTool</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
{/template}
|
||||
@@ -28,9 +28,11 @@ import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.keyring.api.Keyring;
|
||||
import google.registry.request.UrlConnectionService;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.tools.GsonUtils;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
@@ -49,6 +51,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||
class BsaCredentialTest {
|
||||
|
||||
private static final Duration AUTH_TOKEN_EXPIRY = Duration.ofMinutes(30);
|
||||
private static final Gson GSON = GsonUtils.provideGson();
|
||||
|
||||
@Mock OutputStream connectionOutputStream;
|
||||
@Mock HttpsURLConnection connection;
|
||||
@@ -60,7 +63,8 @@ class BsaCredentialTest {
|
||||
@BeforeEach
|
||||
void setup() throws Exception {
|
||||
credential =
|
||||
new BsaCredential(connectionService, "https://authUrl", AUTH_TOKEN_EXPIRY, keyring, clock);
|
||||
new BsaCredential(
|
||||
connectionService, "https://authUrl", AUTH_TOKEN_EXPIRY, keyring, GSON, clock);
|
||||
}
|
||||
|
||||
void setupHttp() throws Exception {
|
||||
|
||||
@@ -85,10 +85,9 @@ public abstract class ResourceFlowTestCase<F extends Flow, R extends EppResource
|
||||
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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
</domain:create>
|
||||
</create>
|
||||
<clTRID>RegistryTool</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
""",
|
||||
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(
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
</domain:create>
|
||||
</create>
|
||||
<clTRID>RegistryTool</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
""",
|
||||
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(
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:add/>
|
||||
<domain:rem/>
|
||||
</domain:update>
|
||||
</update>
|
||||
<clTRID>RegistryTool</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
""",
|
||||
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(
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:add>
|
||||
<domain:status s="clientHold"/>
|
||||
</domain:add>
|
||||
</domain:update>
|
||||
</update>
|
||||
<clTRID>RegistryTool</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
""",
|
||||
xml);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ import com.google.common.collect.ImmutableList;
|
||||
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 @@ class PublishSpec11ReportActionTest {
|
||||
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 @@ class PublishSpec11ReportActionTest {
|
||||
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 @@ class PublishSpec11ReportActionTest {
|
||||
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 @@ class PublishSpec11ReportActionTest {
|
||||
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 @@ class PublishSpec11ReportActionTest {
|
||||
verify(emailUtils)
|
||||
.emailSpec11Reports(
|
||||
date,
|
||||
Spec11EmailSoyInfo.DAILY_SPEC_11_EMAIL,
|
||||
Spec11EmailTemplate.DAILY,
|
||||
"Super Cool Registry Daily Threat Detector [2018-06-05]",
|
||||
ImmutableSet.of());
|
||||
verifyNoMoreInteractions(emailUtils);
|
||||
|
||||
@@ -23,6 +23,7 @@ import static google.registry.reporting.spec11.Spec11RegistrarThreatMatchesParse
|
||||
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.domain.Domain;
|
||||
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<String> FAKE_RESOURCES = ImmutableList.of("foo");
|
||||
private static final String DAILY_EMAIL_FORMAT =
|
||||
"Dear registrar partner,<p>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:</p><table><tr><th>Domain Name</th><th>Threat Type</th></tr>%s"
|
||||
+ "</table><p><b>Please communicate these findings to the registrant and work with the"
|
||||
+ " registrant to mitigate any security issues and have the domains delisted.</b></p>"
|
||||
+ "Some helpful resources for getting off a blocked list include:"
|
||||
+ "<ul><li>foo</li></ul><p>If you believe that any of the domains were reported in"
|
||||
+ " error, or are still receiving reports for issues that have been remediated, please"
|
||||
+ " <a href=\"https://safebrowsing.google.com/safebrowsing/report_error/?hl=en\">submit"
|
||||
+ " a request</a> to have the site reviewed.</p><p>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.</p><p>If you would like to change the email to which these notices are sent"
|
||||
+ " please update your abuse contact using your registrar portal account.</p><p>If you"
|
||||
+ " have any questions regarding this notice, please contact abuse@test.com.</p>";
|
||||
"""
|
||||
|
||||
<p>Dear registrar partner,</p>
|
||||
|
||||
<p>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:</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Domain Name</th>
|
||||
<th>Threat Type</th>
|
||||
</tr>
|
||||
%s</table>
|
||||
|
||||
<p><b>Please communicate these findings to the registrant and work with the
|
||||
registrant to mitigate any security issues and have the domains delisted.</b></p>
|
||||
|
||||
<p>Some helpful resources for getting off a blocked list include:</p>
|
||||
<ul>
|
||||
<li>foo</li>
|
||||
</ul>
|
||||
|
||||
<p>If you believe that any of the domains were reported in error, or are still receiving
|
||||
reports for issues that have been remediated,
|
||||
please <a href="https://safebrowsing.google.com/safebrowsing/report_error/?hl=en">submit
|
||||
a request</a> to have the site reviewed.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>If you would like to change the email to which these notices are sent, please update your
|
||||
abuse contact using your registrar portal account.</p>
|
||||
|
||||
<p>If you have any questions regarding this notice, please contact abuse@test.com.</p>
|
||||
""";
|
||||
private static final String MONTHLY_EMAIL_FORMAT =
|
||||
"Dear registrar partner,<p>Super Cool Registry previously notified you when the following"
|
||||
+ " domains managed by your registrar were flagged for potential security"
|
||||
+ " concerns.</p><p>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.</p><table><tr><th>Domain Name</th><th>Threat"
|
||||
+ " Type</th></tr>%s</table><p>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 <a"
|
||||
+ " href=\"https://safebrowsing.google.com/safebrowsing/report_error/?hl=en\">submit a"
|
||||
+ " request</a> to have the site reviewed.</p>Some helpful resources for getting off a"
|
||||
+ " blocked list include:<ul><li>foo</li></ul><p>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.</p><p>If you have any questions regarding this"
|
||||
+ " notice, please contact abuse@test.com.</p>";
|
||||
"""
|
||||
|
||||
<p>Dear registrar partner,</p>
|
||||
|
||||
<p>Super Cool Registry previously notified you when the following domains managed by your
|
||||
registrar were flagged for potential security concerns.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Domain Name</th>
|
||||
<th>Threat Type</th>
|
||||
</tr>
|
||||
%s</table>
|
||||
|
||||
<p>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 <a href="https://safebrowsing.google.com/safebrowsing/report_error/?hl=en">submit a
|
||||
request</a> to have the site reviewed.</p>
|
||||
|
||||
<p>Some helpful resources for getting off a blocked list include:</p>
|
||||
<ul>
|
||||
<li>foo</li>
|
||||
</ul>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>If you have any questions regarding this notice, please contact abuse@test.com.</p>
|
||||
""";
|
||||
|
||||
@RegisterExtension
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
@@ -120,6 +161,7 @@ class Spec11EmailUtilsTest {
|
||||
new Spec11EmailUtils(
|
||||
gmailClient,
|
||||
sleeper,
|
||||
new TemplateRenderer(),
|
||||
emailThrottleDuration,
|
||||
new InternetAddress("my-receiver@test.com"),
|
||||
new InternetAddress("abuse@test.com"),
|
||||
@@ -139,7 +181,7 @@ class Spec11EmailUtilsTest {
|
||||
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 @@ class Spec11EmailUtilsTest {
|
||||
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 @@ class Spec11EmailUtilsTest {
|
||||
"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, "<tr><td>a[.]com</td><td>MALWARE</td></tr>"),
|
||||
String.format(
|
||||
MONTHLY_EMAIL_FORMAT,
|
||||
"""
|
||||
<tr>
|
||||
<td>a[.]com</td>
|
||||
<td>MALWARE</td>
|
||||
</tr>
|
||||
"""),
|
||||
Optional.of(MediaType.HTML_UTF_8));
|
||||
validateMessage(
|
||||
capturedContents.get(1),
|
||||
@@ -172,7 +221,16 @@ class Spec11EmailUtilsTest {
|
||||
"Super Cool Registry Monthly Threat Detector [2018-07-15]",
|
||||
String.format(
|
||||
MONTHLY_EMAIL_FORMAT,
|
||||
"<tr><td>b[.]com</td><td>MALWARE</td></tr><tr><td>c[.]com</td><td>MALWARE</td></tr>"),
|
||||
"""
|
||||
<tr>
|
||||
<td>b[.]com</td>
|
||||
<td>MALWARE</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>c[.]com</td>
|
||||
<td>MALWARE</td>
|
||||
</tr>
|
||||
"""),
|
||||
Optional.of(MediaType.HTML_UTF_8));
|
||||
validateMessage(
|
||||
capturedContents.get(2),
|
||||
@@ -187,7 +245,7 @@ class Spec11EmailUtilsTest {
|
||||
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 @@ class Spec11EmailUtilsTest {
|
||||
"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, "<tr><td>a[.]com</td><td>MALWARE</td></tr>"),
|
||||
String.format(
|
||||
DAILY_EMAIL_FORMAT,
|
||||
"""
|
||||
<tr>
|
||||
<td>a[.]com</td>
|
||||
<td>MALWARE</td>
|
||||
</tr>
|
||||
"""),
|
||||
Optional.of(MediaType.HTML_UTF_8));
|
||||
validateMessage(
|
||||
capturedMessages.get(1),
|
||||
@@ -207,7 +272,16 @@ class Spec11EmailUtilsTest {
|
||||
"Super Cool Registry Daily Threat Detector [2018-07-15]",
|
||||
String.format(
|
||||
DAILY_EMAIL_FORMAT,
|
||||
"<tr><td>b[.]com</td><td>MALWARE</td></tr><tr><td>c[.]com</td><td>MALWARE</td></tr>"),
|
||||
"""
|
||||
<tr>
|
||||
<td>b[.]com</td>
|
||||
<td>MALWARE</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>c[.]com</td>
|
||||
<td>MALWARE</td>
|
||||
</tr>
|
||||
"""),
|
||||
Optional.of(MediaType.HTML_UTF_8));
|
||||
validateMessage(
|
||||
capturedMessages.get(2),
|
||||
@@ -225,7 +299,7 @@ class Spec11EmailUtilsTest {
|
||||
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 @@ class Spec11EmailUtilsTest {
|
||||
"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, "<tr><td>c[.]com</td><td>MALWARE</td></tr>"),
|
||||
String.format(
|
||||
MONTHLY_EMAIL_FORMAT,
|
||||
"""
|
||||
<tr>
|
||||
<td>c[.]com</td>
|
||||
<td>MALWARE</td>
|
||||
</tr>
|
||||
"""),
|
||||
Optional.of(MediaType.HTML_UTF_8));
|
||||
validateMessage(
|
||||
capturedContents.get(1),
|
||||
@@ -252,11 +333,13 @@ class Spec11EmailUtilsTest {
|
||||
// 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 @@ class Spec11EmailUtilsTest {
|
||||
"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, "<tr><td>a[.]com</td><td>MALWARE</td></tr>"),
|
||||
String.format(
|
||||
MONTHLY_EMAIL_FORMAT,
|
||||
"""
|
||||
<tr>
|
||||
<td>a[.]com</td>
|
||||
<td>MALWARE</td>
|
||||
</tr>
|
||||
"""),
|
||||
Optional.of(MediaType.HTML_UTF_8));
|
||||
validateMessage(
|
||||
capturedContents.get(1),
|
||||
@@ -276,7 +366,16 @@ class Spec11EmailUtilsTest {
|
||||
"Super Cool Registry Monthly Threat Detector [2018-07-15]",
|
||||
String.format(
|
||||
MONTHLY_EMAIL_FORMAT,
|
||||
"<tr><td>b[.]com</td><td>MALWARE</td></tr><tr><td>c[.]com</td><td>MALWARE</td></tr>"),
|
||||
"""
|
||||
<tr>
|
||||
<td>b[.]com</td>
|
||||
<td>MALWARE</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>c[.]com</td>
|
||||
<td>MALWARE</td>
|
||||
</tr>
|
||||
"""),
|
||||
Optional.of(MediaType.HTML_UTF_8));
|
||||
validateMessage(
|
||||
capturedContents.get(2),
|
||||
@@ -304,7 +403,7 @@ class Spec11EmailUtilsTest {
|
||||
() ->
|
||||
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 @@ class Spec11EmailUtilsTest {
|
||||
"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, "<tr><td>a[.]com</td><td>MALWARE</td></tr>"),
|
||||
String.format(
|
||||
MONTHLY_EMAIL_FORMAT,
|
||||
"""
|
||||
<tr>
|
||||
<td>a[.]com</td>
|
||||
<td>MALWARE</td>
|
||||
</tr>
|
||||
"""),
|
||||
Optional.of(MediaType.HTML_UTF_8));
|
||||
validateMessage(
|
||||
capturedMessages.get(1),
|
||||
@@ -328,7 +434,16 @@ class Spec11EmailUtilsTest {
|
||||
"Super Cool Registry Monthly Threat Detector [2018-07-15]",
|
||||
String.format(
|
||||
MONTHLY_EMAIL_FORMAT,
|
||||
"<tr><td>b[.]com</td><td>MALWARE</td></tr><tr><td>c[.]com</td><td>MALWARE</td></tr>"),
|
||||
"""
|
||||
<tr>
|
||||
<td>b[.]com</td>
|
||||
<td>MALWARE</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>c[.]com</td>
|
||||
<td>MALWARE</td>
|
||||
</tr>
|
||||
"""),
|
||||
Optional.of(MediaType.HTML_UTF_8));
|
||||
validateMessage(
|
||||
capturedMessages.get(2),
|
||||
@@ -363,7 +478,7 @@ class Spec11EmailUtilsTest {
|
||||
.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 @@ class Spec11EmailUtilsTest {
|
||||
() ->
|
||||
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 @@ class Spec11EmailUtilsTest {
|
||||
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());
|
||||
|
||||
@@ -20,7 +20,7 @@ import static org.mockito.Mockito.when;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.groups.GmailClient;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.request.Modules;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
import google.registry.ui.server.SendEmailUtils;
|
||||
@@ -50,6 +50,6 @@ public final class ConsoleApiParamsUtils {
|
||||
authResult,
|
||||
sendEmailUtils,
|
||||
xsrfTokenManager,
|
||||
RequestModule.provideGson());
|
||||
Modules.GsonModule.provideGson());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,8 @@ class AuthModuleTest {
|
||||
private static final String ACCESS_TOKEN = "FakeAccessToken";
|
||||
private static final String REFRESH_TOKEN = "FakeReFreshToken";
|
||||
|
||||
private static final Gson GSON = GsonUtils.provideGson();
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@TempDir
|
||||
Path folder;
|
||||
@@ -166,7 +168,7 @@ class AuthModuleTest {
|
||||
void test_provideLocalCredentialJson() {
|
||||
String credentialJson =
|
||||
AuthModule.provideLocalCredentialJson(
|
||||
AuthModuleTest::getSecrets, this::getCredential, null);
|
||||
AuthModuleTest::getSecrets, GSON, this::getCredential, null);
|
||||
Map<String, String> jsonMap =
|
||||
new Gson().fromJson(credentialJson, new TypeToken<Map<String, String>>() {}.getType());
|
||||
assertThat(jsonMap.get("type")).isEqualTo("authorized_user");
|
||||
@@ -182,7 +184,10 @@ class AuthModuleTest {
|
||||
Files.writeString(credentialFile.toPath(), "{some_field: some_value}");
|
||||
String credentialJson =
|
||||
AuthModule.provideLocalCredentialJson(
|
||||
AuthModuleTest::getSecrets, this::getCredential, credentialFile.getCanonicalPath());
|
||||
AuthModuleTest::getSecrets,
|
||||
GSON,
|
||||
this::getCredential,
|
||||
credentialFile.getCanonicalPath());
|
||||
assertThat(credentialJson).isEqualTo("{some_field: some_value}");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<domain:create
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:period unit="y">2</domain:period>
|
||||
<domain:registrant>jd1234</domain:registrant>
|
||||
<domain:contact type="admin">jd1234</domain:contact>
|
||||
<domain:contact type="tech">jd1234</domain:contact>
|
||||
<domain:authInfo>
|
||||
<domain:pw>abcdefghijklmnop</domain:pw>
|
||||
</domain:authInfo>
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<domain:create
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>premium.tld</domain:name>
|
||||
<domain:period unit="y">2</domain:period>
|
||||
<domain:registrant>jd1234</domain:registrant>
|
||||
<domain:contact type="admin">jd1234</domain:contact>
|
||||
<domain:contact type="tech">jd1234</domain:contact>
|
||||
<domain:authInfo>
|
||||
<domain:pw>abcdefghijklmnop</domain:pw>
|
||||
</domain:authInfo>
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<domain:create
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:period unit="y">2</domain:period>
|
||||
<domain:registrant>jd1234</domain:registrant>
|
||||
<domain:contact type="admin">jd1234</domain:contact>
|
||||
<domain:contact type="tech">jd1234</domain:contact>
|
||||
<domain:authInfo>
|
||||
<domain:pw>abcdefghijklmnop</domain:pw>
|
||||
</domain:authInfo>
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<domain:create
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:period unit="y">2</domain:period>
|
||||
<domain:registrant>jd1234</domain:registrant>
|
||||
<domain:contact type="admin">jd1234</domain:contact>
|
||||
<domain:contact type="tech">jd1234</domain:contact>
|
||||
<domain:authInfo>
|
||||
<domain:pw>abcdefghijklmnop</domain:pw>
|
||||
</domain:authInfo>
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<domain:create
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:period unit="y">2</domain:period>
|
||||
<domain:registrant>jd1234</domain:registrant>
|
||||
<domain:contact type="admin">jd1234</domain:contact>
|
||||
<domain:contact type="tech">jd1234</domain:contact>
|
||||
<domain:authInfo>
|
||||
<domain:pw>abcdefghijklmnop</domain:pw>
|
||||
</domain:authInfo>
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<domain:create
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:period unit="y">2</domain:period>
|
||||
<domain:registrant>jd1234</domain:registrant>
|
||||
<domain:contact type="admin">jd1234</domain:contact>
|
||||
<domain:contact type="tech">jd1234</domain:contact>
|
||||
<domain:authInfo>
|
||||
<domain:pw>foo</domain:pw>
|
||||
</domain:authInfo>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<domain:create
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>palladium.tld</domain:name>
|
||||
<domain:period unit="y">1</domain:period>
|
||||
<domain:authInfo>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<domain:create
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>parajiumu.baar</domain:name>
|
||||
<domain:period unit="y">3</domain:period>
|
||||
<domain:authInfo>
|
||||
|
||||
@@ -5,13 +5,12 @@
|
||||
<domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:add>
|
||||
<domain:status s="serverHold" lang="en"></domain:status>
|
||||
<domain:status s="serverRenewProhibited" lang="en"></domain:status>
|
||||
<domain:status s="serverTransferProhibited" lang="en"></domain:status>
|
||||
<domain:status s="serverDeleteProhibited" lang="en"></domain:status>
|
||||
<domain:status s="serverUpdateProhibited" lang="en"></domain:status>
|
||||
<domain:status s="serverHold"></domain:status>
|
||||
<domain:status s="serverRenewProhibited"></domain:status>
|
||||
<domain:status s="serverTransferProhibited"></domain:status>
|
||||
<domain:status s="serverDeleteProhibited"></domain:status>
|
||||
<domain:status s="serverUpdateProhibited"></domain:status>
|
||||
</domain:add>
|
||||
<domain:rem></domain:rem>
|
||||
</domain:update>
|
||||
</update>
|
||||
<extension>
|
||||
|
||||
@@ -5,9 +5,8 @@
|
||||
<domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:add>
|
||||
<domain:status s="serverRenewProhibited" lang="en"></domain:status>
|
||||
<domain:status s="serverRenewProhibited"></domain:status>
|
||||
</domain:add>
|
||||
<domain:rem></domain:rem>
|
||||
</domain:update>
|
||||
</update>
|
||||
<extension>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user