1
0
mirror of https://github.com/google/nomulus synced 2026-06-09 16:33:02 +00:00

Compare commits

...

13 Commits

Author SHA1 Message Date
gbrodman 84e97aa2db Use time-constant comparison in password testing (#3090)
Normal comparison exits out once a difference is found so theoretically
a time variance can leak information. This isn't really a huge deal but
is probably still worth doing.
2026-06-17 21:00:13 +00:00
Ben McIlwain 4df4bf1489 Add FeatureFlag helper methods to DatabaseHelper (#3087)
Introduced overloaded helper methods `persistFeatureFlag` in `DatabaseHelper` to simplify the creation and persistence of `FeatureFlag` entities in test setups.

1. `persistFeatureFlag(FeatureName, FeatureStatus)`: Persists a feature flag with a single status starting from the Unix Epoch (`START_INSTANT`).
2. `persistFeatureFlag(FeatureName, FeatureStatus, Instant, FeatureStatus)`: Persists a feature flag with an initial status at `START_INSTANT` and a subsequent transition at a specified time.

Refactored 11 occurrences of manual 1-transition flag creation and 5 occurrences of 2-transition flag creation across the test suite to use these new helpers, significantly reducing boilerplate and improving test readability.

TAG=agy
CONV=583b8a23-9fe5-476d-ac35-aeba7b218eb0
2026-06-16 21:24:01 +00:00
gbrodman 9faabb0f1d Forbid deprecated algorithms in DNSSEC data (#3078)
This is similar to PR #3069 but for the algorithms themselves rather
than the digest data. This forbids algorithms, that, according to RFC
9904, should not be used.
2026-06-16 19:09:05 +00:00
gbrodman ea9d717378 Use custom Protostuff delegate for InetAddresses (#3079)
We need this to serialize/deserialize hosts, because we're not allowed
to reflectively access InetAddress.
2026-06-08 19:16:35 +00:00
Juan Celhay d1769b29ef fix relative path of manifests (#3084) 2026-06-08 15:37:17 +00:00
gbrodman 14376953e5 Skip EPP params for BRDA (#3083)
this is an extra field that shouldn't be included in this output
apparently
2026-06-05 20:52:54 +00:00
Juan Celhay c13c9b53e3 Add sandbox to the Cloud Deploy delivery pipeline (#3080)
* add sandbox target to the delivery pipeline

* add sandbox target
2026-06-05 19:32:28 +00:00
Ben McIlwain 1cd6026151 Fix npx build overriding Angular output paths (#3082)
This commit reverts changes from PR #3068 that swapped 'npm run build' for 'npx ng build' while attempting to dynamically set the '--output-path' via the CLI.

Passing '--output-path' on the command line overrides the entire 'outputPath' configuration object in angular.json. Because the new Angular 18 Application Builder (esbuild) nests outputs inside a 'browser/' directory by default, overriding the configuration bypassed the 'browser: ""' flattening property, causing all client assets to be nested deeper than expected.

This resulted in empty deployments because downstream tasks (like Jetty's copyConsole and the deployment tar scripts) expected the assets to be completely flat. By removing the '--output-path' override from the 'npx ng build' calls, the Angular CLI once again respects angular.json, flattens the output into 'staged/dist/', and the restored 'doLast' block successfully copies the artifacts where they belong.
2026-06-05 16:31:14 +00:00
Ben McIlwain 75524fd403 Restore default builds and fix Kokoro tests (#3081)
This commit reverts changes from 5599a0eb3d and most of 5286b1a0dc (PR #3068) that stripped essential dependencies (buildConsoleForAll, buildNomulusImage, buildToolImage, fragileTest) from the default './gradlew build' target, which broke downstream deployment pipelines. It restores the default build to correctly generate all necessary production artifacts and Docker images.

It introduces a new 'fastBuild' target designed explicitly for local developers and CI checks. This lightweight target disables the execution of heavy Docker image builds, Angular compilations, and fragile tests to provide rapid feedback. Sequential execution constraints for parallel Angular builds are maintained to prevent cache corruption.

It updates the ':core:generateSqlSchema' task to execute using the 'unittest' environment instead of 'alpha'. The 'alpha' configuration is a private, internal environment config that is not distributed in the open-source repository, which caused the task to fail for public contributors. By switching to 'unittest', the generator can successfully run using the public test configuration. With this fixed, it also includes the newly generated 'db-schema.sql.generated' file, which now correctly tracks the 'FORBID_INSECURE_ALGORITHMS_RFC_9904' feature flag that was recently added.

Finally, it implements a split-runner execution strategy for the 'sqlIntegrationTest' task to permanently resolve 'failed to discover tests' and 'NoSuchMethodError' exceptions on Kokoro. Because Kokoro tests cross-version compatibility against both legacy deployed artifacts (compiled with JUnit 4 @RunWith wrappers) and modern artifacts (compiled with JUnit 5 @Suite annotations), we cannot statically configure a single test runner. We now dynamically run both the legacy 'useJUnit()' and modern 'useJUnitPlatform()' runners sequentially with 'failOnNoDiscoveredTests' disabled, allowing the appropriate engine to discover and execute the suite without causing classpath collisions.
2026-06-04 15:38:03 +00:00
gbrodman a5b280838c Remove usages of json-simple (#3060)
We should use gson wherever possible. There's no point in having
unnecessary dependencies (we'll need to keep around jackson for YAML
parsing).
2026-06-01 20:16:32 +00:00
Ben McIlwain 5599a0eb3d Add buildAll task and fix fragile builds (#3077)
This commit adds the buildAll task to restore the existence of a target that builds everything, which was unintentionally removed when the default build was stripped down in PR #3068. It also introduces necessary sequential constraints to the console-webapp build tasks to prevent parallel execution from corrupting the Angular CLI cache. Finally, it addresses paths for the newer Angular esbuild output and hardens the style injection in ConsoleScreenshotTest to prevent fragile test failures.
2026-06-01 19:11:05 +00:00
gbrodman dde41078cd Forbid SHA-1 digests as part of RFC 9904 changes (#3069)
We can't change digest types that are already in the database but that's
fine (since we just store them as integers). But we forbid them as part
of domain creates/updates.
2026-06-01 17:59:19 +00:00
Ben McIlwain 0030645b1a Harden XML parsing, serialization, and randomness (#3075)
This commit introduces several security hardening improvements across the codebase:
1. XML Processing: Hardened `TransformerFactory` and `SchemaFactory` instantiations in `EppMessage.java` by explicitly enabling `XMLConstants.FEATURE_SECURE_PROCESSING` and disabling external schema access.
2. Randomness: Replaced instances of `java.util.Random` with `java.security.SecureRandom` in `SelfSignedCaCertificate.java` for stronger entropy. (Added documentation in `ProxyModule.java` explaining why `java.util.Random` is intentionally retained there for metrics sampling).
3. Deserialization: Hardened `SerializeUtils.java` by injecting an `ObjectInputFilter` into the `ObjectInputStream`, restricting deserialization strictly to expected `google.registry` classes and standard Java collections.
2026-06-01 14:25:42 +00:00
106 changed files with 1140 additions and 552 deletions
+20
View File
@@ -285,6 +285,25 @@ def check_diff_anti_patterns():
if 'src/test/' in current_file and clock_now_pattern.search(code_line):
log_warning(f"[{current_file}] Prefer using a fixed, static constant Instant over capturing clock.now() in tests to prevent flakiness.")
def check_missing_tests():
print("\n--- Checking for Missing Tests ---")
diff_files = run_cmd("git diff HEAD^ --name-only").split('\n')
main_files_modified = False
test_files_modified = False
for f in diff_files:
if f.startswith('core/src/main/') or f.startswith('util/src/main/'):
if f.endswith('.java'):
main_files_modified = True
if f.startswith('core/src/test/') or f.startswith('util/src/test/'):
if f.endswith('Test.java'):
test_files_modified = True
if main_files_modified and not test_files_modified:
log_warning("Production code (.java files in src/main/) was modified, but no corresponding tests (.java files in src/test/) were added or updated. You MUST proactively write tests for any new logic or bug fixes.")
else:
log_success("Test coverage check passed (tests were included or no production Java code was changed).")
def main():
print("========================================")
print(" NOMULUS PR POLISHER CHECKLIST ")
@@ -295,6 +314,7 @@ def main():
check_workspace_clean()
check_package_lock()
check_license_headers()
check_missing_tests()
check_formatting()
check_diff_anti_patterns()
+1
View File
@@ -46,6 +46,7 @@ This document outlines foundational mandates, architectural patterns, and projec
- **Transactional Time:** Ensure code that relies on `tm().getTransactionTime()` (or `tm().getTxTime()`) is executed within a transaction context.
### 5. Testing Best Practices
- **Mandatory Proactive Testing:** You MUST automatically write and update tests alongside your code changes WITHOUT waiting for the user to prompt you. If you add a new feature, fix a bug, or change core logic, you are explicitly required to identify the corresponding `*Test.java` file and implement comprehensive test coverage for your changes.
- **FakeClock and Sleeper:** Use `FakeClock` and `Sleeper` for any logic involving timeouts, delays, or expiration.
- **Empirical Reproduction:** Before fixing a bug, always create a test case that reproduces the failure.
- **Base Classes:** Leverage `CommandTestCase`, `EppToolCommandTestCase`, etc., to reduce boilerplate and ensure consistent setup (e.g., clock initialization).
+20
View File
@@ -609,3 +609,23 @@ gradle.taskGraph.whenReady { graph ->
}
}
}
task fastBuild {
group = 'build'
description = 'A lightweight build for local dev. Compiles Java, runs standard tests, and checks formatting, but skips Docker images, fragile tests, and the massive Angular console builds. (Do not use this target to verify console changes.)'
dependsOn build
// Remove the heavy default dependencies specifically for fastBuild
gradle.taskGraph.whenReady { graph ->
if (graph.hasTask(fastBuild)) {
project(':console-webapp').tasks.named('buildConsoleForAll').get().enabled = false
project(':jetty').tasks.named('buildNomulusImage').get().enabled = false
project(':core').tasks.named('buildToolImage').get().enabled = false
project(':core').tasks.named('fragileTest').get().enabled = false
project(':jetty').tasks.named('stage').get().enabled = false
if (project.tasks.findByName('stage') != null) {
project.tasks.named('stage').get().enabled = false
}
}
}
}
+17 -1
View File
@@ -53,10 +53,17 @@ def createConsoleTask = { env ->
project.tasks.register("buildConsoleFor${env.capitalize()}", Exec) {
workingDir "${consoleDir}/"
executable 'npx'
args 'ng', 'build', '--base-href=/console/', "--configuration=${env}", "--output-path=staged/console-${env}"
args 'ng', 'build', '--base-href=/console/', "--configuration=${env}"
doFirst {
println "Building console for environment: ${env}"
}
doLast {
copy {
from "${consoleDir}/staged/dist/"
into "${consoleDir}/staged/console-${env}"
}
delete "${consoleDir}/staged/dist"
}
dependsOn(tasks.npmInstallDeps)
}
project.tasks.register("deleteConsoleFor${env.capitalize()}", Delete) {
@@ -74,6 +81,14 @@ def createConsoleTask = { env ->
createConsoleTask(env)
}
// Force an order so we don't run these tasks in parallel.
tasks.buildConsoleForCrash.mustRunAfter(tasks.buildConsoleForAlpha)
tasks.buildConsoleForQa.mustRunAfter(tasks.buildConsoleForCrash)
tasks.buildConsoleForSandbox.mustRunAfter(tasks.buildConsoleForQa)
tasks.buildConsoleForProduction.mustRunAfter(tasks.buildConsoleForSandbox)
// This task must run last, otherwise the previous tasks will have deleted the "dist" folder.
tasks.buildConsoleWebapp.mustRunAfter(tasks.buildConsoleForProduction)
task applyFormatting(type: Exec) {
workingDir "${consoleDir}/"
executable 'npm'
@@ -92,3 +107,4 @@ tasks.applyFormatting.dependsOn(tasks.npmInstallDeps)
tasks.checkFormatting.dependsOn(tasks.npmInstallDeps)
tasks.build.dependsOn(tasks.checkFormatting)
tasks.build.dependsOn(tasks.runConsoleWebappUnitTests)
tasks.build.dependsOn(tasks.buildConsoleForAll)
+4 -5
View File
@@ -170,7 +170,6 @@ dependencies {
implementation deps['com.google.oauth-client:google-oauth-client-servlet']
implementation deps['com.google.re2j:re2j']
implementation deps['org.freemarker:freemarker']
implementation deps['com.googlecode.json-simple:json-simple']
implementation deps['com.github.mwiede:jsch']
implementation deps['com.zaxxer:HikariCP']
implementation deps['com.squareup.okhttp3:okhttp']
@@ -443,7 +442,7 @@ project.tasks.create('generateSqlSchema', JavaExec) {
mainClass = 'google.registry.tools.DevTool'
jvmArgs "--sun-misc-unsafe-memory-access=allow"
args = [
'-e', 'alpha',
'-e', 'unittest',
'generate_sql_schema', '--start_postgresql', '-o',
"${rootProject.projectRootDir}/db/src/main/resources/sql/schema/" +
"db-schema.sql.generated"
@@ -742,9 +741,9 @@ test {
// Don't run any tests from this task, all testing gets done in the
// FilteringTest tasks.
exclude "**"
}.dependsOn(standardTest, registryToolIntegrationTest, sqlIntegrationTest)
}.dependsOn(standardTest, registryToolIntegrationTest, sqlIntegrationTest, fragileTest)
// When we override tests, we also break the cleanTest command.
cleanTest.dependsOn(cleanStandardTest, cleanRegistryToolIntegrationTest, cleanSqlIntegrationTest)
cleanTest.dependsOn(cleanStandardTest, cleanRegistryToolIntegrationTest, cleanSqlIntegrationTest, cleanFragileTest)
project.build.dependsOn devtool
project.build.dependsOn devtool, buildToolImage
-1
View File
@@ -192,7 +192,6 @@ com.google.protobuf:protobuf-java:4.33.2=annotationProcessor,nonprodAnnotationPr
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.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,testCompileClasspath,testRuntimeClasspath
com.lmax:disruptor:3.4.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.puppycrawl.tools:checkstyle:10.24.0=checkstyle
@@ -212,17 +212,16 @@ public class RdeIO {
}
}
// Don't write the IDN elements for BRDA.
// Don't write the IDN elements or EPP params for BRDA.
if (mode == RdeMode.FULL) {
for (IdnTableEnum idn : IdnTableEnum.values()) {
output.write(marshaller.marshalIdn(idn.getTable()));
counter.increment(RdeResourceType.IDN);
}
output.write(marshaller.marshalRdeEppParams());
counter.increment(RdeResourceType.EPP_PARAMS);
}
output.write(marshaller.marshalRdeEppParams());
counter.increment(RdeResourceType.EPP_PARAMS);
// Output XML that says how many resources were emitted.
header = counter.makeHeader(tld, mode);
output.write(marshaller.marshalOrDie(new XjcRdeHeaderElement(header)));
@@ -372,7 +372,7 @@ public class RdePipeline implements Serializable {
* <p>The (repoId, pendingDeposit) pairs denote hosts that are referenced from a domain, that are
* to be included in the corresponding pending deposit.
*
* <p>The (repoId, revisionId) paris come from the most recent history entry query, which can be
* <p>The (repoId, revisionId) pairs come from the most recent history entry query, which can be
* used to load the embedded resources themselves.
*
* @return a pair of (repoId, ([pendingDeposit], [revisionId])) where neither the pendingDeposit
@@ -25,10 +25,20 @@ import com.google.common.flogger.FluentLogger;
import google.registry.model.EppResource;
import google.registry.model.domain.Domain;
import google.registry.model.host.Host;
import io.protostuff.Input;
import io.protostuff.LinkedBuffer;
import io.protostuff.Output;
import io.protostuff.Pipe;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.WireFormat;
import io.protostuff.runtime.DefaultIdStrategy;
import io.protostuff.runtime.Delegate;
import io.protostuff.runtime.RuntimeSchema;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import redis.clients.jedis.AbstractPipeline;
@@ -52,11 +62,20 @@ public class SimplifiedJedisClient {
Domain.class, "d_",
Host.class, "h_");
/** We need to inform Protostuff of the custom {@link InetAddress} delegates. */
private static DefaultIdStrategy createIdStrategy() {
DefaultIdStrategy strategy = new DefaultIdStrategy();
strategy.registerDelegate(new GenericInetAddressDelegate<>(InetAddress.class));
strategy.registerDelegate(new GenericInetAddressDelegate<>(Inet4Address.class));
strategy.registerDelegate(new GenericInetAddressDelegate<>(Inet6Address.class));
return strategy;
}
private static final ImmutableMap<Class<? extends EppResource>, Schema<? extends EppResource>>
VALUE_SCHEMAS =
ImmutableMap.of(
Domain.class, RuntimeSchema.getSchema(Domain.class),
Host.class, RuntimeSchema.getSchema(Host.class));
Host.class, RuntimeSchema.getSchema(Host.class, createIdStrategy()));
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -151,4 +170,46 @@ public class SimplifiedJedisClient {
checkArgument(VALUE_SCHEMAS.containsKey(clazz), "Unknown class type %s", clazz);
return (Schema<V>) VALUE_SCHEMAS.get(clazz);
}
/**
* A custom Protostuff {@link Delegate} for {@link InetAddress} and its subclasses.
*
* <p>This is required in Java 17+ because Protostuff's default runtime schema serialization
* relies on reflection. Since {@link InetAddress} is part of the encapsulated {@code java.base}
* module, reflective access is restricted and throws {@link
* java.lang.reflect.InaccessibleObjectException}.
*
* <p>This delegate serializes the IP address as a raw byte array using {@link
* InetAddress#getAddress()} and reconstructs it using {@link InetAddress#getByAddress(byte[])}
*/
private record GenericInetAddressDelegate<T extends InetAddress>(Class<T> clazz)
implements Delegate<T> {
@Override
public WireFormat.FieldType getFieldType() {
return WireFormat.FieldType.BYTES;
}
@Override
public Class<T> typeClass() {
return clazz;
}
@SuppressWarnings("unchecked")
@Override
public T readFrom(Input input) throws IOException {
return (T) InetAddress.getByAddress(input.readByteArray());
}
@Override
public void writeTo(Output output, int number, T value, boolean repeated) throws IOException {
output.writeByteArray(number, value.getAddress(), repeated);
}
@Override
public void transfer(Pipe pipe, Input input, Output output, int number, boolean repeated)
throws IOException {
output.writeByteArray(number, input.readByteArray(), repeated);
}
}
}
@@ -36,13 +36,13 @@ import static google.registry.persistence.PersistenceModule.TransactionIsolation
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
import static google.registry.pricing.PricingEngineProxy.isDomainPremium;
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import static org.json.simple.JSONValue.toJSONString;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import com.google.common.net.InternetDomainName;
import com.google.common.net.MediaType;
import com.google.gson.Gson;
import dagger.Module;
import dagger.Provides;
import google.registry.flows.domain.DomainFlowUtils.BadCommandForRegistryPhaseException;
@@ -83,6 +83,7 @@ public class CheckApiAction implements Runnable {
@Inject Response response;
@Inject CheckApiMetric.Builder metricBuilder;
@Inject CheckApiMetrics checkApiMetrics;
@Inject Gson gson;
@Inject
CheckApiAction() {}
@@ -94,7 +95,7 @@ public class CheckApiAction implements Runnable {
response.setHeader("X-Content-Type-Options", "nosniff");
response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
response.setContentType(MediaType.JSON_UTF_8);
response.setPayload(toJSONString(doCheck()));
response.setPayload(gson.toJson(doCheck()));
} finally {
CheckApiMetric metric = metricBuilder.build();
checkApiMetrics.incrementCheckApiRequest(metric);
@@ -24,6 +24,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import com.google.common.flogger.FluentLogger;
import com.google.gson.Gson;
import google.registry.flows.FlowModule.EppExceptionInProviderException;
import google.registry.model.eppcommon.Trid;
import google.registry.model.eppinput.EppInput;
@@ -34,7 +35,6 @@ import google.registry.model.eppoutput.Result.Code;
import google.registry.monitoring.whitebox.EppMetric;
import jakarta.inject.Inject;
import java.util.Optional;
import org.json.simple.JSONValue;
/**
* An implementation of the EPP command/response protocol.
@@ -50,6 +50,8 @@ public final class EppController {
@Inject EppMetric.Builder eppMetricBuilder;
@Inject EppMetrics eppMetrics;
@Inject ServerTridProvider serverTridProvider;
@Inject Gson gson;
@Inject EppController() {}
/** Reads EPP XML, executes the matching flow, and returns an {@link EppOutput}. */
@@ -72,7 +74,7 @@ public final class EppController {
e.getMessage(),
lazy(
() ->
JSONValue.toJSONString(
gson.toJson(
ImmutableMap.<String, Object>of(
"clientId",
nullToEmpty(sessionMetadata.getRegistrarId()),
@@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import com.google.gson.Gson;
import google.registry.flows.FlowModule.InputXml;
import google.registry.flows.FlowModule.RegistrarId;
import google.registry.flows.annotations.ReportingSpec;
@@ -30,7 +31,6 @@ import google.registry.model.eppcommon.Trid;
import google.registry.model.eppinput.EppInput;
import jakarta.inject.Inject;
import java.util.Optional;
import org.json.simple.JSONValue;
/** Reporter used by {@link FlowRunner} to record flow execution data for reporting. */
public class FlowReporter {
@@ -49,6 +49,8 @@ public class FlowReporter {
@Inject @InputXml byte[] inputXmlBytes;
@Inject EppInput eppInput;
@Inject Class<? extends Flow> flowClass;
@Inject Gson gson;
@Inject FlowReporter() {}
/** Records information about the current flow execution in the request logs. */
@@ -61,7 +63,7 @@ public class FlowReporter {
logger.atInfo().log(
"%s: %s",
METADATA_LOG_SIGNATURE,
JSONValue.toJSONString(
gson.toJson(
new ImmutableMap.Builder<String, Object>()
.put("serverTrid", trid.getServerTransactionId())
.put("clientId", registrarId)
@@ -44,6 +44,8 @@ import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.Host;
import google.registry.model.transfer.TransferStatus;
import google.registry.persistence.VKey;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.Instant;
import java.util.List;
import java.util.Objects;
@@ -134,7 +136,9 @@ public final class ResourceFlowUtils {
}
String authPassword = authInfo.getPw().getValue();
String domainPassword = domain.getAuthInfo().getPw().getValue();
if (!domainPassword.equals(authPassword)) {
if (!MessageDigest.isEqual(
authPassword.getBytes(StandardCharsets.UTF_8),
domainPassword.getBytes(StandardCharsets.UTF_8))) {
throw new BadAuthInfoForResourceException();
}
}
@@ -23,6 +23,7 @@ import static com.google.common.collect.Sets.difference;
import static com.google.common.collect.Sets.intersection;
import static com.google.common.collect.Sets.union;
import static google.registry.bsa.persistence.BsaLabelUtils.isLabelBlocked;
import static google.registry.model.common.FeatureFlag.FeatureName.FORBID_INSECURE_ALGORITHMS_RFC_9904;
import static google.registry.model.domain.Domain.MAX_REGISTRATION_YEARS;
import static google.registry.model.domain.token.AllocationToken.TokenType.REGISTER_BSA;
import static google.registry.model.tld.Tld.TldState.GENERAL_AVAILABILITY;
@@ -73,6 +74,7 @@ import google.registry.model.EppResource;
import google.registry.model.billing.BillingBase.Flag;
import google.registry.model.billing.BillingBase.Reason;
import google.registry.model.billing.BillingRecurrence;
import google.registry.model.common.FeatureFlag;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainCommand.Create;
import google.registry.model.domain.DomainCommand.CreateOrUpdate;
@@ -341,7 +343,7 @@ public class DomainFlowUtils {
}
ImmutableList<DomainDsData> invalidAlgorithms =
dsData.stream()
.filter(ds -> !validateAlgorithm(ds.getAlgorithm()))
.filter(ds -> algorithmIsInvalid(ds.getAlgorithm()))
.collect(toImmutableList());
if (!invalidAlgorithms.isEmpty()) {
throw new InvalidDsRecordException(
@@ -349,9 +351,16 @@ public class DomainFlowUtils {
"Domain contains DS record(s) with an invalid algorithm wire value: %s",
invalidAlgorithms));
}
boolean forbidInsecureTypes = FeatureFlag.isActiveNow(FORBID_INSECURE_ALGORITHMS_RFC_9904);
ImmutableList<DomainDsData> invalidDigestTypes =
dsData.stream()
.filter(ds -> DigestType.fromWireValue(ds.getDigestType()).isEmpty())
.filter(
ds -> {
Optional<DigestType> digestType = DigestType.fromWireValue(ds.getDigestType());
return digestType
.map(type -> forbidInsecureTypes && !type.isAllowedInRfc9904())
.orElse(true);
})
.collect(toImmutableList());
if (!invalidDigestTypes.isEmpty()) {
throw new InvalidDsRecordException(
@@ -376,14 +385,18 @@ public class DomainFlowUtils {
}
}
public static boolean validateAlgorithm(int alg) {
public static boolean algorithmIsInvalid(int alg) {
if (alg > 255 || alg < 0) {
return false;
return true;
}
if (DomainDsData.FORBIDDEN_ALGORITHMS.contains(alg)
&& FeatureFlag.isActiveNow(FORBID_INSECURE_ALGORITHMS_RFC_9904)) {
return true;
}
// Algorithms that are reserved or unassigned will just return a string representation of their
// integer wire value.
String algorithm = Algorithm.string(alg);
return !algorithm.equals(Integer.toString(alg));
return algorithm.equals(Integer.toString(alg));
}
/** We only allow specifying years in a period. */
@@ -84,7 +84,10 @@ public class FeatureFlag extends ImmutableObject implements Buildable {
INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS(FeatureStatus.INACTIVE),
/** If we're prohibiting the inclusion of the contact object URI on login. */
PROHIBIT_CONTACT_OBJECTS_ON_LOGIN(FeatureStatus.INACTIVE);
PROHIBIT_CONTACT_OBJECTS_ON_LOGIN(FeatureStatus.INACTIVE),
/** If we're prohibiting insecure algorithms as detailed by RFC 9904. */
FORBID_INSECURE_ALGORITHMS_RFC_9904(FeatureStatus.INACTIVE);
private final FeatureStatus defaultStatus;
@@ -14,6 +14,7 @@
package google.registry.model.domain.secdns;
import com.google.common.collect.ImmutableSet;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
import jakarta.persistence.Access;
@@ -26,6 +27,7 @@ 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;
import org.xbill.DNS.DNSSEC.Algorithm;
/** Base class for {@link DomainDsData} and {@link DomainDsDataHistory}. */
@MappedSuperclass
@@ -33,6 +35,16 @@ import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlType(propOrder = {"keyTag", "algorithm", "digestType", "digest"})
public abstract class DomainDsDataBase extends ImmutableObject implements UnsafeSerializable {
// A set of algorithms that we do not allow. See RFC 9904 for more details.
public static final ImmutableSet<Integer> FORBIDDEN_ALGORITHMS =
ImmutableSet.of(
Algorithm.RSAMD5,
Algorithm.RSASHA1,
Algorithm.RSA_NSEC3_SHA1,
Algorithm.DSA,
Algorithm.DSA_NSEC3_SHA1,
Algorithm.ECC_GOST);
@XmlTransient @Transient @Insignificant String domainRepoId;
/** The identifier for this particular key in the domain. */
@@ -30,7 +30,7 @@ public enum RdeResourceType {
REGISTRAR("urn:ietf:params:xml:ns:rdeRegistrar-1.0", EnumSet.of(FULL, THIN)),
IDN("urn:ietf:params:xml:ns:rdeIDN-1.0", EnumSet.of(FULL)),
HEADER("urn:ietf:params:xml:ns:rdeHeader-1.0", EnumSet.of(FULL, THIN)),
EPP_PARAMS("urn:ietf:params:xml:ns:rdeEppParams-1.0", EnumSet.of(FULL, THIN));
EPP_PARAMS("urn:ietf:params:xml:ns:rdeEppParams-1.0", EnumSet.of(FULL));
private final String uri;
private final ImmutableSet<RdeMode> modes;
@@ -18,8 +18,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.net.HttpHeaders.CONTENT_DISPOSITION;
import static com.google.common.net.HttpHeaders.X_CONTENT_TYPE_OPTIONS;
import static com.google.common.net.MediaType.JSON_UTF_8;
import static org.json.simple.JSONValue.toJSONString;
import com.google.gson.Gson;
import jakarta.inject.Inject;
import java.time.Instant;
import java.util.Map;
@@ -31,10 +31,12 @@ public class JsonResponse {
public static final String JSON_SAFETY_PREFIX = ")]}'\n";
protected final Response response;
protected final Gson gson;
@Inject
public JsonResponse(Response rsp) {
public JsonResponse(Response rsp, Gson gson) {
this.response = rsp;
this.gson = gson;
}
/**
@@ -55,7 +57,7 @@ public class JsonResponse {
// response, even if all else fails. It's basically another anti-sniffing mechanism in the sense
// that if you hit this url directly, it would try to download the file instead of showing it.
response.setHeader(CONTENT_DISPOSITION, "attachment");
response.setPayload(JSON_SAFETY_PREFIX + toJSONString(checkNotNull(responseMap)));
response.setPayload(JSON_SAFETY_PREFIX + gson.toJson(checkNotNull(responseMap)));
}
/**
@@ -14,6 +14,7 @@
package google.registry.request;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.net.MediaType.JSON_UTF_8;
import static google.registry.dns.PublishDnsUpdatesAction.CLOUD_TASKS_RETRY_HEADER;
import static google.registry.model.tld.Tlds.assertTldExists;
@@ -28,7 +29,6 @@ import static google.registry.request.RequestParameters.extractSetOfParameters;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.VerifyException;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.ByteStreams;
@@ -36,6 +36,8 @@ import com.google.common.io.CharStreams;
import com.google.common.net.MediaType;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import com.google.protobuf.ByteString;
import dagger.Module;
import dagger.Provides;
@@ -50,8 +52,6 @@ import jakarta.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Map;
import java.util.Optional;
import org.json.simple.JSONValue;
import org.json.simple.parser.ParseException;
/** Dagger module for servlets. */
@Module
@@ -202,18 +202,16 @@ public final class RequestModule {
@Provides
@JsonPayload
@SuppressWarnings("unchecked")
static Map<String, Object> provideJsonPayload(
@Header("Content-Type") MediaType contentType, @Payload String payload) {
@Header("Content-Type") MediaType contentType, @Payload String payload, Gson gson) {
if (!JSON_UTF_8.is(contentType.withCharset(UTF_8))) {
throw new UnsupportedMediaTypeException(
String.format("Expected %s Content-Type", JSON_UTF_8.withoutParameters()));
}
try {
return (Map<String, Object>) JSONValue.parseWithException(payload);
} catch (ParseException e) {
throw new BadRequestException(
"Malformed JSON", new VerifyException("Malformed JSON:\n" + payload, e));
return checkNotNull(gson.fromJson(payload, new TypeToken<>() {}));
} catch (JsonSyntaxException | NullPointerException e) {
throw new BadRequestException("Malformed JSON:\n" + payload);
}
}
@@ -18,10 +18,13 @@ import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.net.HttpHeaders.CONTENT_DISPOSITION;
import static com.google.common.net.HttpHeaders.X_CONTENT_TYPE_OPTIONS;
import static com.google.common.net.MediaType.JSON_UTF_8;
import static org.json.simple.JSONValue.writeJSONString;
import com.google.common.flogger.FluentLogger;
import com.google.common.net.MediaType;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import google.registry.tools.GsonUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@@ -29,8 +32,6 @@ import java.io.Reader;
import java.io.Writer;
import java.util.Map;
import javax.annotation.Nullable;
import org.json.simple.JSONValue;
import org.json.simple.parser.ParseException;
/**
* Helper class for servlets that read or write JSON.
@@ -41,6 +42,8 @@ public final class JsonHttp {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final Gson GSON = GsonUtils.provideGson();
/** String prefixed to all JSON-like responses. */
public static final String JSON_SAFETY_PREFIX = ")]}'\n";
@@ -51,7 +54,6 @@ public final class JsonHttp {
* @throws IOException if we failed to read from {@code req}.
*/
@Nullable
@SuppressWarnings("unchecked")
public static Map<String, ?> read(HttpServletRequest req) throws IOException {
if (!"POST".equals(req.getMethod())
&& !"PUT".equals(req.getMethod())) {
@@ -64,8 +66,8 @@ public final class JsonHttp {
}
try (Reader jsonReader = req.getReader()) {
try {
return checkNotNull((Map<String, ?>) JSONValue.parseWithException(jsonReader));
} catch (ParseException | NullPointerException | ClassCastException e) {
return checkNotNull(GSON.fromJson(jsonReader, new TypeToken<>() {}));
} catch (JsonSyntaxException | NullPointerException | ClassCastException e) {
logger.atWarning().withCause(e).log("Malformed JSON.");
return null;
}
@@ -88,7 +90,7 @@ public final class JsonHttp {
rsp.setHeader(CONTENT_DISPOSITION, "attachment");
try (Writer writer = rsp.getWriter()) {
writer.write(JSON_SAFETY_PREFIX);
writeJSONString(jsonObject, writer);
GSON.toJson(jsonObject, writer);
}
}
}
@@ -25,6 +25,7 @@ import com.google.common.hash.Hashing;
import google.registry.model.server.ServerSecret;
import google.registry.util.Clock;
import jakarta.inject.Inject;
import java.security.MessageDigest;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
@@ -102,7 +103,7 @@ public final class XsrfTokenManager {
}
// Reconstruct the token to verify validity.
String reconstructedToken = encodeToken(ServerSecret.get().asBytes(), email, timestampMillis);
if (!token.equals(reconstructedToken)) {
if (!MessageDigest.isEqual(token.getBytes(UTF_8), reconstructedToken.getBytes(UTF_8))) {
logger.atWarning().log(
"Reconstructed XSRF mismatch (got != expected): %s != %s", token, reconstructedToken);
return false;
@@ -29,21 +29,26 @@ import java.util.Optional;
* https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml
*/
public enum DigestType {
SHA1(1, 20),
SHA256(2, 32),
// Algorithm number 1 is SHA-1 and will be is deliberately NOT SUPPORTED.
// RFC 9904 specifies that this algorithm MUST NOT be used for DNSSEC delegations.
// This prohibition is gated behind a feature flag.
SHA1(1, 20, false),
SHA256(2, 32, true),
// Algorithm number 3 is GOST R 34.11-94 and is deliberately NOT SUPPORTED.
// This algorithm was reviewed by ise-crypto and deemed academically broken (b/207029800).
// In addition, RFC 8624 specifies that this algorithm MUST NOT be used for DNSSEC delegations.
// TODO(sarhabot@): Add note in Cloud DNS code to notify the Registry of any new changes to
// supported digest types.
SHA384(4, 48);
SHA384(4, 48, true);
private final int wireValue;
private final int bytes;
private final boolean allowedInRfc9904;
DigestType(int wireValue, int bytes) {
DigestType(int wireValue, int bytes, boolean allowedInRfc9904) {
this.wireValue = wireValue;
this.bytes = bytes;
this.allowedInRfc9904 = allowedInRfc9904;
}
private static final ImmutableMap<Integer, DigestType> WIRE_VALUE_TO_DIGEST_TYPE =
@@ -63,4 +68,9 @@ public enum DigestType {
public int getBytes() {
return bytes;
}
/** Whether this digest type is supported as of RFC 9904. */
public boolean isAllowedInRfc9904() {
return allowedInRfc9904;
}
}
@@ -15,6 +15,7 @@
package google.registry.tools;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
import com.beust.jcommander.IStringConverter;
@@ -46,7 +47,7 @@ record DsRecord(int keyTag, int alg, int digestType, String digest) {
String.format("DS record has an invalid digest length: %s", digest));
}
if (!DomainFlowUtils.validateAlgorithm(alg)) {
if (tm().reTransact(() -> DomainFlowUtils.algorithmIsInvalid(alg))) {
throw new IllegalArgumentException(
String.format("DS record uses an unrecognized algorithm: %d", alg));
}
@@ -25,6 +25,7 @@ import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import google.registry.model.domain.Domain;
import google.registry.model.host.Host;
import google.registry.persistence.transaction.QueryComposer.Comparator;
@@ -38,7 +39,6 @@ import java.nio.file.Paths;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import org.json.simple.JSONValue;
/** Command to generate a report of all DNS data. */
@Parameters(separators = " =", commandDescription = "Generate report of all DNS data in a TLD.")
@@ -57,6 +57,7 @@ final class GenerateDnsReportCommand implements Command {
private Path output = Paths.get("/dev/stdout");
@Inject Clock clock;
@Inject Gson gson;
@Override
public void run() throws Exception {
@@ -144,7 +145,7 @@ final class GenerateDnsReportCommand implements Command {
} else {
result.append(",\n");
}
result.append(JSONValue.toJSONString(map));
result.append(gson.toJson(map));
}
}
}
@@ -16,6 +16,7 @@ package google.registry.tools;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.ToNumberPolicy;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
@@ -82,6 +83,7 @@ public class GsonUtils {
.registerTypeAdapter(Serializable.class, new SerializableJsonTypeAdapter())
.registerTypeAdapterFactory(new ClassProcessingTypeAdapterFactory())
.registerTypeAdapterFactory(new GsonPostProcessableTypeAdapterFactory())
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.excludeFieldsWithoutExposeAnnotation()
.create();
}
@@ -23,10 +23,13 @@ import com.beust.jcommander.Parameter;
import com.google.common.base.VerifyException;
import com.google.common.collect.ImmutableMap;
import com.google.common.net.MediaType;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import org.json.simple.JSONValue;
/**
* Abstract base class for commands that list objects by calling a server task.
@@ -35,6 +38,8 @@ import org.json.simple.JSONValue;
*/
abstract class ListObjectsCommand implements CommandWithConnection {
private static final Gson GSON = GsonUtils.provideGson();
@Nullable
@Parameter(
names = {"-f", "--fields"},
@@ -87,14 +92,13 @@ abstract class ListObjectsCommand implements CommandWithConnection {
connection.sendPostRequest(
getCommandPath(), params.build(), MediaType.PLAIN_TEXT_UTF_8, new byte[0]);
// Parse the returned JSON and make sure it's a map.
Object obj = JSONValue.parse(response.substring(JSON_SAFETY_PREFIX.length()));
if (!(obj instanceof Map<?, ?>)) {
JsonElement element = JsonParser.parseString(response.substring(JSON_SAFETY_PREFIX.length()));
if (!element.isJsonObject()) {
throw new VerifyException("Server returned unexpected JSON: " + response);
}
@SuppressWarnings("unchecked")
Map<String, Object> responseMap = (Map<String, Object>) obj;
Map<String, Object> responseMap = GSON.fromJson(element, new TypeToken<>() {});
// Get the status.
obj = responseMap.get("status");
Object obj = responseMap.get("status");
if (obj == null) {
throw new VerifyException("Server returned no status");
}
@@ -34,6 +34,8 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.CharStreams;
import com.google.common.net.MediaType;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import google.registry.config.RegistryConfig.Config;
import google.registry.request.Action.Service;
import jakarta.inject.Inject;
@@ -42,7 +44,6 @@ import java.io.InputStreamReader;
import java.net.URL;
import java.util.Map;
import javax.annotation.Nullable;
import org.json.simple.JSONValue;
/**
* An HTTP connection to a service.
@@ -55,23 +56,25 @@ public class ServiceConnection {
private final Service service;
private final boolean useCanary;
private final HttpRequestFactory requestFactory;
private final Gson gson;
@Inject
ServiceConnection(
@Config("useCanary") boolean useCanary,
HttpRequestFactory requestFactory) {
this(Service.BACKEND, requestFactory, useCanary);
@Config("useCanary") boolean useCanary, HttpRequestFactory requestFactory, Gson gson) {
this(Service.BACKEND, requestFactory, useCanary, gson);
}
private ServiceConnection(Service service, HttpRequestFactory requestFactory, boolean useCanary) {
private ServiceConnection(
Service service, HttpRequestFactory requestFactory, boolean useCanary, Gson gson) {
this.service = service;
this.requestFactory = requestFactory;
this.useCanary = useCanary;
this.gson = gson;
}
/** Returns a copy of this connection that talks to a different service endpoint. */
public ServiceConnection withService(Service service, boolean useCanary) {
return new ServiceConnection(service, requestFactory, useCanary);
return new ServiceConnection(service, requestFactory, useCanary, gson);
}
/** Returns the HTML from the connection error stream, if any, otherwise the empty string. */
@@ -99,7 +102,7 @@ public class ServiceConnection {
request.setFollowRedirects(false);
request.setThrowExceptionOnExecuteError(false);
request.setUnsuccessfulResponseHandler(
(request1, response, supportsRetry) -> {
(request1, response, _) -> {
String error = getErrorHtmlAsString(response);
throw new IOException(
String.format(
@@ -137,14 +140,10 @@ public class ServiceConnection {
return internalSend(endpoint, params, MediaType.PLAIN_TEXT_UTF_8, null);
}
@SuppressWarnings("unchecked")
public Map<String, Object> sendJson(String endpoint, Map<String, ?> object) throws IOException {
String response =
sendPostRequest(
endpoint,
ImmutableMap.of(),
JSON_UTF_8,
JSONValue.toJSONString(object).getBytes(UTF_8));
return (Map<String, Object>) JSONValue.parse(response.substring(JSON_SAFETY_PREFIX.length()));
endpoint, ImmutableMap.of(), JSON_UTF_8, gson.toJson(object).getBytes(UTF_8));
return gson.fromJson(response.substring(JSON_SAFETY_PREFIX.length()), new TypeToken<>() {});
}
}
@@ -19,6 +19,7 @@ import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableO
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistActiveHost;
import static google.registry.testing.DatabaseHelper.persistActiveSubordinateHost;
import static google.registry.testing.DatabaseHelper.persistDeletedDomain;
import com.google.common.collect.ImmutableList;
@@ -67,7 +68,8 @@ public class SimplifiedJedisClientTest {
@Test
void testClient_roundTrip_host() {
Host host = persistActiveHost("ns1.example.tld");
Domain domain = persistActiveDomain("example.tld");
Host host = persistActiveSubordinateHost("ns1.example.tld", domain);
SimplifiedJedisClient client = createJedisClient();
client.set(new SimplifiedJedisClient.JedisResource<>("repoId1", host));
assertThat(client.get(Host.class, "repoId1")).hasValue(host);
@@ -21,8 +21,8 @@ import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistDeletedDomain;
import static google.registry.testing.DatabaseHelper.persistFeatureFlag;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.START_INSTANT;
import static google.registry.util.DateTimeUtils.plusDays;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -35,10 +35,8 @@ import com.google.cloud.storage.BlobId;
import com.google.cloud.storage.StorageException;
import com.google.cloud.storage.contrib.nio.testing.LocalStorageHelper;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.net.MediaType;
import google.registry.gcs.GcsUtils;
import google.registry.model.common.FeatureFlag;
import google.registry.model.domain.Domain;
import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.rgp.GracePeriodStatus;
@@ -80,7 +78,7 @@ class ExportDomainListsActionTest {
action.gcsUtils = gcsUtils;
action.clock = clock;
action.driveConnection = driveConnection;
persistFeatureFlag(INACTIVE);
persistFeatureFlag(INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS, INACTIVE);
}
private void verifyExportedToDrive(String folderId, String filename, String domains)
@@ -110,7 +108,7 @@ class ExportDomainListsActionTest {
@Test
void test_outputsOnlyActiveDomains_csv() throws Exception {
persistFeatureFlag(ACTIVE);
persistFeatureFlag(INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS, ACTIVE);
persistActiveDomain("onetwo.tld");
persistActiveDomain("rudnitzky.tld");
persistDeletedDomain("mortuary.tld", Instant.parse("2001-03-14T10:11:12Z"));
@@ -144,7 +142,7 @@ class ExportDomainListsActionTest {
@Test
void test_outputsOnlyDomainsOnRealTlds_csv() throws Exception {
persistFeatureFlag(ACTIVE);
persistFeatureFlag(INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS, ACTIVE);
persistActiveDomain("onetwo.tld");
persistActiveDomain("rudnitzky.tld");
persistActiveDomain("wontgo.testtld");
@@ -164,7 +162,7 @@ class ExportDomainListsActionTest {
@Test
void test_outputIncludesDeletionTimes_forPendingDeletes_notRdemption() throws Exception {
persistFeatureFlag(ACTIVE);
persistFeatureFlag(INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS, ACTIVE);
// Domains pending delete (meaning the 5 day period, not counting the 30 day redemption period)
// should include their pending deletion date
persistActiveDomain("active.tld");
@@ -227,7 +225,7 @@ class ExportDomainListsActionTest {
@Test
void test_outputsDomainsFromDifferentTldsToMultipleFiles_csv() throws Exception {
persistFeatureFlag(ACTIVE);
persistFeatureFlag(INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS, ACTIVE);
createTld("tldtwo");
persistResource(Tld.get("tldtwo").asBuilder().setDriveFolderId("hooray").build());
@@ -255,13 +253,4 @@ class ExportDomainListsActionTest {
// tldthree does not have a drive id, so no export to drive is performed.
verifyNoMoreInteractions(driveConnection);
}
private void persistFeatureFlag(FeatureFlag.FeatureStatus status) {
persistResource(
new FeatureFlag()
.asBuilder()
.setFeatureName(INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS)
.setStatusMap(ImmutableSortedMap.of(START_INSTANT, status))
.build());
}
}
@@ -29,6 +29,7 @@ import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.START_INSTANT;
import static org.mockito.Mockito.verify;
import com.google.gson.reflect.TypeToken;
import google.registry.bsa.persistence.BsaTestingUtils;
import google.registry.model.tld.Tld;
import google.registry.monitoring.whitebox.CheckApiMetric;
@@ -39,10 +40,10 @@ import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeResponse;
import google.registry.tools.GsonUtils;
import java.time.Instant;
import java.util.Map;
import java.util.Optional;
import org.json.simple.JSONValue;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -83,9 +84,9 @@ class CheckApiActionTest {
.build());
}
@SuppressWarnings("unchecked")
private Map<String, Object> getCheckResponse(String domain) {
CheckApiAction action = new CheckApiAction();
action.gson = GsonUtils.provideGson();
action.domain = domain;
action.response = new FakeResponse();
action.metricBuilder = CheckApiMetric.builder(fakeClock);
@@ -94,7 +95,8 @@ class CheckApiActionTest {
endTime = fakeClock.now();
action.run();
return (Map<String, Object>) JSONValue.parse(((FakeResponse) action.response).getPayload());
return action.gson.fromJson(
((FakeResponse) action.response).getPayload(), new TypeToken<>() {});
}
@Test
@@ -31,6 +31,8 @@ import static org.mockito.Mockito.when;
import com.google.common.base.Splitter;
import com.google.common.testing.TestLogHandler;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import google.registry.flows.EppException.UnimplementedExtensionException;
import google.registry.flows.EppTestComponent.FakeServerTridProvider;
import google.registry.flows.FlowModule.EppExceptionInProviderException;
@@ -43,6 +45,7 @@ import google.registry.monitoring.whitebox.EppMetric;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
import google.registry.testing.FakeClock;
import google.registry.tools.GsonUtils;
import google.registry.util.Clock;
import google.registry.xml.ValidationMode;
import java.time.Instant;
@@ -50,7 +53,6 @@ import java.util.List;
import java.util.Map;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import org.json.simple.JSONValue;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -82,6 +84,7 @@ class EppControllerTest {
@Mock Result result;
private static final Instant START_TIME = Instant.parse("2016-09-01T00:00:00Z");
private static final Gson GSON = GsonUtils.provideGson();
private final Clock clock = new FakeClock(START_TIME);
private final TestLogHandler logHandler = new TestLogHandler();
@@ -110,6 +113,7 @@ class EppControllerTest {
when(result.getCode()).thenReturn(Code.SUCCESS_WITH_NO_MESSAGES);
eppController = new EppController();
eppController.gson = GSON;
eppController.eppMetricBuilder = EppMetric.builderForRequest(clock);
when(flowRunner.run(eppController.eppMetricBuilder)).thenReturn(eppOutput);
eppController.flowComponentBuilder = flowComponentBuilder;
@@ -247,8 +251,7 @@ class EppControllerTest {
assertThat(logRecord.getThrown()).isInstanceOf(IllegalStateException.class);
}
@SuppressWarnings("unchecked")
private static Map<String, Object> parseJsonMap(String json) throws Exception {
return (Map<String, Object>) JSONValue.parseWithException(json);
return GSON.fromJson(json, new TypeToken<>() {});
}
}
@@ -28,6 +28,7 @@ import google.registry.flows.custom.TestCustomLogicFactory;
import google.registry.flows.domain.DomainDeletionTimeCache;
import google.registry.flows.domain.DomainFlowTmchUtils;
import google.registry.monitoring.whitebox.EppMetric;
import google.registry.request.Modules.GsonModule;
import google.registry.request.RequestScope;
import google.registry.request.lock.LockHandler;
import google.registry.testing.CloudTasksHelper;
@@ -42,7 +43,8 @@ import jakarta.inject.Singleton;
/** Dagger component for running EPP tests. */
@Singleton
@Component(modules = {ConfigModule.class, EppTestComponent.FakesAndMocksModule.class})
@Component(
modules = {ConfigModule.class, GsonModule.class, EppTestComponent.FakesAndMocksModule.class})
public interface EppTestComponent {
RequestComponent startRequest();
@@ -22,22 +22,26 @@ import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableList;
import com.google.common.testing.TestLogHandler;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import google.registry.flows.annotations.ReportingSpec;
import google.registry.model.eppcommon.Trid;
import google.registry.model.eppinput.EppInput;
import google.registry.model.eppoutput.EppOutput.ResponseOrGreeting;
import google.registry.model.eppoutput.EppResponse;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.tools.GsonUtils;
import google.registry.util.JdkLoggerConfig;
import java.util.Map;
import java.util.Optional;
import org.json.simple.JSONValue;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link FlowReporter}. */
class FlowReporterTest {
private static final Gson GSON = GsonUtils.provideGson();
static class TestCommandFlow implements Flow {
@Override
public ResponseOrGreeting run() {
@@ -60,6 +64,7 @@ class FlowReporterTest {
void beforeEach() {
JdkLoggerConfig.getConfig(FlowReporter.class).addHandler(handler);
flowReporter.trid = Trid.create("client-123", "server-456");
flowReporter.gson = GSON;
flowReporter.registrarId = "TheRegistrar";
flowReporter.inputXmlBytes = "<xml/>".getBytes(UTF_8);
flowReporter.flowClass = TestCommandFlow.class;
@@ -205,8 +210,7 @@ class FlowReporterTest {
assertThat(json).containsEntry("tlds", ImmutableList.of());
}
@SuppressWarnings("unchecked")
private static Map<String, Object> parseJsonMap(String json) throws Exception {
return (Map<String, Object>) JSONValue.parseWithException(json);
return GSON.fromJson(json, new TypeToken<>() {});
}
}
@@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.testing.TestLogHandler;
import com.google.gson.Gson;
import google.registry.model.EppResource;
import google.registry.model.ForeignKeyUtils;
import google.registry.model.domain.DomainBase;
@@ -34,13 +35,13 @@ import google.registry.model.tmch.ClaimsList;
import google.registry.model.tmch.ClaimsListDao;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.TestCacheExtension;
import google.registry.tools.GsonUtils;
import google.registry.util.JdkLoggerConfig;
import google.registry.util.TypeUtils.TypeInstantiator;
import java.time.Duration;
import java.time.Instant;
import java.util.logging.Level;
import javax.annotation.Nullable;
import org.json.simple.JSONValue;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -53,6 +54,8 @@ import org.junit.jupiter.api.extension.RegisterExtension;
public abstract class ResourceFlowTestCase<F extends Flow, R extends EppResource>
extends FlowTestCase<F> {
private static final Gson GSON = GsonUtils.provideGson();
protected final TestLogHandler logHandler = new TestLogHandler();
@RegisterExtension
@@ -108,21 +111,23 @@ public abstract class ResourceFlowTestCase<F extends Flow, R extends EppResource
.that(logHandler)
.hasLogAtLevelWithMessage(Level.INFO, "FLOW-LOG-SIGNATURE-METADATA")
.which()
.contains("\"clientId\":" + JSONValue.toJSONString(registrarId));
.contains("\"clientId\":" + GSON.toJson(registrarId));
}
protected void assertTldsFieldLogged(String... tlds) {
assertAboutLogs().that(logHandler)
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(Level.INFO, "FLOW-LOG-SIGNATURE-METADATA")
.which()
.contains("\"tlds\":" + JSONValue.toJSONString(ImmutableList.copyOf(tlds)));
.contains("\"tlds\":" + GSON.toJson(ImmutableList.copyOf(tlds)));
}
protected void assertIcannReportingActivityFieldLogged(String fieldName) {
assertAboutLogs().that(logHandler)
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(Level.INFO, "FLOW-LOG-SIGNATURE-METADATA")
.which()
.contains("\"icannActivityReportField\":" + JSONValue.toJSONString(fieldName));
.contains("\"icannActivityReportField\":" + GSON.toJson(fieldName));
}
protected void assertLastHistoryContainsResource(EppResource resource) {
@@ -24,6 +24,7 @@ import static google.registry.model.billing.BillingBase.Flag.RESERVED;
import static google.registry.model.billing.BillingBase.Flag.SUNRISE;
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.NONPREMIUM;
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.SPECIFIED;
import static google.registry.model.common.FeatureFlag.FeatureName.FORBID_INSECURE_ALGORITHMS_RFC_9904;
import static google.registry.model.domain.fee.Fee.FEE_EXTENSION_URIS;
import static google.registry.model.domain.token.AllocationToken.TokenType.BULK_PRICING;
import static google.registry.model.domain.token.AllocationToken.TokenType.DEFAULT_PROMO;
@@ -52,6 +53,7 @@ import static google.registry.testing.DatabaseHelper.loadRegistrar;
import static google.registry.testing.DatabaseHelper.newHost;
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistActiveHost;
import static google.registry.testing.DatabaseHelper.persistFeatureFlag;
import static google.registry.testing.DatabaseHelper.persistReservedList;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.DomainSubject.assertAboutDomains;
@@ -150,6 +152,7 @@ import google.registry.model.billing.BillingBase.Reason;
import google.registry.model.billing.BillingBase.RenewalPriceBehavior;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingRecurrence;
import google.registry.model.common.FeatureFlag.FeatureStatus;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.GracePeriod;
@@ -794,7 +797,11 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.that(domain)
.hasExactlyDsData(
DomainDsData.create(
12345, 3, 1, base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))
12345,
8,
2,
base16()
.decode("D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A"))
.cloneWithDomainRepoId(domain.getRepoId()));
}
@@ -957,6 +964,30 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_secDnsSha1DigestType() throws Exception {
setEppInput("domain_create_dsdata_sha1.xml");
persistHosts();
persistFeatureFlag(FORBID_INSECURE_ALGORITHMS_RFC_9904, FeatureStatus.ACTIVE);
EppException thrown = assertThrows(InvalidDsRecordException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testSuccess_secDnsSha1_flagInactive() throws Exception {
setEppInput("domain_create_dsdata_sha1.xml");
persistHosts();
persistFeatureFlag(FORBID_INSECURE_ALGORITHMS_RFC_9904, FeatureStatus.INACTIVE);
doSuccessfulTest("tld");
Domain domain = reloadResourceByForeignKey();
assertAboutDomains()
.that(domain)
.hasExactlyDsData(
DomainDsData.create(
12345, 8, 1, base16().decode("49FD46E6C4B45C55D4AC49FD46E6C4B45C55D4AC"))
.cloneWithDomainRepoId(domain.getRepoId()));
}
@Test
void testFailure_secDnsInvalidAlgorithm() throws Exception {
setEppInput("domain_create_dsdata_bad_algorithms.xml");
@@ -965,6 +996,34 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_secDnsForbiddenAlgorithm() throws Exception {
setEppInput("domain_create_dsdata_forbidden_algorithm.xml");
persistHosts();
persistFeatureFlag(FORBID_INSECURE_ALGORITHMS_RFC_9904, FeatureStatus.ACTIVE);
EppException thrown = assertThrows(InvalidDsRecordException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testSuccess_secDnsForbiddenAlgorithm_flagInactive() throws Exception {
setEppInput("domain_create_dsdata_forbidden_algorithm.xml");
persistHosts();
persistFeatureFlag(FORBID_INSECURE_ALGORITHMS_RFC_9904, FeatureStatus.INACTIVE);
doSuccessfulTest("tld");
Domain domain = reloadResourceByForeignKey();
assertAboutDomains()
.that(domain)
.hasExactlyDsData(
DomainDsData.create(
12345,
1,
2,
base16()
.decode("D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A"))
.cloneWithDomainRepoId(domain.getRepoId()));
}
@Test
void testFailure_wrongExtension() {
setEppInput("domain_create_wrong_extension.xml");
@@ -98,6 +98,8 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
"UNIT", "y");
private static final Pattern OK_PATTERN = Pattern.compile("\"ok\"");
private static final String SHA_256_DIGEST =
"D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A";
private Host host1;
private Host host2;
@@ -314,8 +316,7 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
domain
.asBuilder()
.setDsData(
ImmutableSet.of(
DomainDsData.create(12345, 3, 1, base16().decode("49FD46E6C4B45C55D4AC"))))
ImmutableSet.of(DomainDsData.create(12345, 8, 2, base16().decode(SHA_256_DIGEST))))
.setNameservers(ImmutableSet.of(host1.createVKey(), host3.createVKey()))
.build());
doSuccessfulTest("domain_info_response_dsdata.xml", false);
@@ -519,8 +520,7 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
"TheRegistrar",
null))
.setDsData(
ImmutableSet.of(
DomainDsData.create(12345, 3, 1, base16().decode("49FD46E6C4B45C55D4AC"))))
ImmutableSet.of(DomainDsData.create(12345, 8, 2, base16().decode(SHA_256_DIGEST))))
.build());
doSuccessfulTest("domain_info_response_dsdata_addperiod.xml", false);
}
@@ -19,6 +19,7 @@ import static com.google.common.collect.Sets.union;
import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ForeignKeyUtils.loadResource;
import static google.registry.model.common.FeatureFlag.FeatureName.FORBID_INSECURE_ALGORITHMS_RFC_9904;
import static google.registry.model.eppcommon.StatusValue.CLIENT_DELETE_PROHIBITED;
import static google.registry.model.eppcommon.StatusValue.CLIENT_HOLD;
import static google.registry.model.eppcommon.StatusValue.CLIENT_RENEW_PROHIBITED;
@@ -46,6 +47,7 @@ import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistActiveHost;
import static google.registry.testing.DatabaseHelper.persistActiveSubordinateHost;
import static google.registry.testing.DatabaseHelper.persistDeletedDomain;
import static google.registry.testing.DatabaseHelper.persistFeatureFlag;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.DomainSubject.assertAboutDomains;
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
@@ -94,6 +96,7 @@ import google.registry.flows.exceptions.ResourceStatusProhibitsOperationExceptio
import google.registry.model.ImmutableObject;
import google.registry.model.billing.BillingBase.Reason;
import google.registry.model.billing.BillingEvent;
import google.registry.model.common.FeatureFlag.FeatureStatus;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainAuthInfo;
import google.registry.model.domain.DomainHistory;
@@ -115,6 +118,9 @@ import org.junit.jupiter.api.Test;
/** Unit tests for {@link DomainUpdateFlow}. */
class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain> {
private static final String SHA_256_DIGEST =
"D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A";
private static final DomainDsData SOME_DSDATA =
DomainDsData.create(
1,
@@ -124,9 +130,9 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
private static final ImmutableMap<String, String> OTHER_DSDATA_TEMPLATE_MAP =
ImmutableMap.of(
"KEY_TAG", "12346",
"ALG", "3",
"DIGEST_TYPE", "1",
"DIGEST", "A94A8FE5CCB19BA61C4C0873D391E987982FBBD3");
"ALG", "8",
"DIGEST_TYPE", "2",
"DIGEST", SHA_256_DIGEST);
@BeforeEach
void beforeEach() {
@@ -453,18 +459,9 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
doSecDnsSuccessfulTest(
"domain_update_dsdata_add.xml",
null,
ImmutableSet.of(
DomainDsData.create(
12346, 3, 1, base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))),
ImmutableSet.of(DomainDsData.create(12346, 8, 2, base16().decode(SHA_256_DIGEST))),
ImmutableMap.of(
"KEY_TAG",
"12346",
"ALG",
"3",
"DIGEST_TYPE",
"1",
"DIGEST",
"A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"),
"KEY_TAG", "12346", "ALG", "8", "DIGEST_TYPE", "2", "DIGEST", SHA_256_DIGEST),
true);
}
@@ -474,18 +471,9 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
"domain_update_dsdata_add.xml",
ImmutableSet.of(SOME_DSDATA),
ImmutableSet.of(
SOME_DSDATA,
DomainDsData.create(
12346, 3, 1, base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))),
SOME_DSDATA, DomainDsData.create(12346, 8, 2, base16().decode(SHA_256_DIGEST))),
ImmutableMap.of(
"KEY_TAG",
"12346",
"ALG",
"3",
"DIGEST_TYPE",
"1",
"DIGEST",
"A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"),
"KEY_TAG", "12346", "ALG", "8", "DIGEST_TYPE", "2", "DIGEST", SHA_256_DIGEST),
true);
}
@@ -660,11 +648,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
union(
commonDsData,
ImmutableSet.of(
DomainDsData.create(
12346,
3,
1,
base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))))),
DomainDsData.create(12346, 8, 2, base16().decode(SHA_256_DIGEST))))),
true);
}
@@ -673,9 +657,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
doSecDnsSuccessfulTest(
"domain_update_dsdata_rem.xml",
ImmutableSet.of(
SOME_DSDATA,
DomainDsData.create(
12346, 3, 1, base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))),
SOME_DSDATA, DomainDsData.create(12346, 8, 2, base16().decode(SHA_256_DIGEST))),
ImmutableSet.of(SOME_DSDATA),
true);
}
@@ -686,9 +668,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
doSecDnsSuccessfulTest(
"domain_update_dsdata_rem_all.xml",
ImmutableSet.of(
SOME_DSDATA,
DomainDsData.create(
12346, 3, 1, base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))),
SOME_DSDATA, DomainDsData.create(12346, 8, 2, base16().decode(SHA_256_DIGEST))),
ImmutableSet.of(),
true);
}
@@ -698,13 +678,9 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
doSecDnsSuccessfulTest(
"domain_update_dsdata_add_rem.xml",
ImmutableSet.of(
SOME_DSDATA,
DomainDsData.create(
12345, 3, 1, base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))),
SOME_DSDATA, DomainDsData.create(12345, 8, 2, base16().decode(SHA_256_DIGEST))),
ImmutableSet.of(
SOME_DSDATA,
DomainDsData.create(
12346, 3, 1, base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))),
SOME_DSDATA, DomainDsData.create(12346, 8, 2, base16().decode(SHA_256_DIGEST))),
true);
}
@@ -727,20 +703,12 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
union(
commonDsData,
ImmutableSet.of(
DomainDsData.create(
12345,
3,
1,
base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))))),
DomainDsData.create(12345, 8, 2, base16().decode(SHA_256_DIGEST))))),
ImmutableSet.copyOf(
union(
commonDsData,
ImmutableSet.of(
DomainDsData.create(
12346,
3,
1,
base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"))))),
DomainDsData.create(12346, 8, 2, base16().decode(SHA_256_DIGEST))))),
true);
}
@@ -915,6 +883,41 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_secDnsSha1DigestType() throws Exception {
setEppInput(
"domain_update_dsdata_add.xml",
ImmutableMap.of(
"KEY_TAG", "12346",
"ALG", "3",
"DIGEST_TYPE", "1",
"DIGEST", "A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"));
persistResource(DatabaseHelper.newDomain(getUniqueIdFromCommand()));
persistFeatureFlag(FORBID_INSECURE_ALGORITHMS_RFC_9904, FeatureStatus.ACTIVE);
EppException thrown = assertThrows(InvalidDsRecordException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testSuccess_secDnsSha1_flagInactive() throws Exception {
setEppInput(
"domain_update_dsdata_add.xml",
ImmutableMap.of(
"KEY_TAG", "12346",
"ALG", "3",
"DIGEST_TYPE", "1",
"DIGEST", "A94A8FE5CCB19BA61C4C0873D391E987982FBBD3"));
persistResource(DatabaseHelper.newDomain(getUniqueIdFromCommand()));
persistFeatureFlag(FORBID_INSECURE_ALGORITHMS_RFC_9904, FeatureStatus.INACTIVE);
runFlow();
Domain domain = reloadResourceByForeignKey();
assertAboutDomains()
.that(domain)
.hasExactlyDsData(
DomainDsData.create(
12346, 3, 1, base16().decode("A94A8FE5CCB19BA61C4C0873D391E987982FBBD3")));
}
@Test
void testFailure_secDnsMultipleInvalidDigestTypes() throws Exception {
setEppInput("domain_update_dsdata_add.xml", OTHER_DSDATA_TEMPLATE_MAP);
@@ -938,7 +941,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
persistResource(
DatabaseHelper.newDomain(getUniqueIdFromCommand())
.asBuilder()
.setDsData(ImmutableSet.of(DomainDsData.create(1, 2, 1, new byte[] {0, 1, 2})))
.setDsData(ImmutableSet.of(DomainDsData.create(1, 2, 2, new byte[] {0, 1, 2})))
.build());
EppException thrown = assertThrows(InvalidDsRecordException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
@@ -955,7 +958,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
.asBuilder()
.setDsData(
ImmutableSet.of(
DomainDsData.create(1, 2, 1, new byte[] {0, 1, 2, 3, 4}),
DomainDsData.create(1, 2, 2, new byte[] {0, 1, 2, 3, 4}),
DomainDsData.create(2, 2, 2, new byte[] {5, 6, 7})))
.build());
EppException thrown = assertThrows(InvalidDsRecordException.class, this::runFlow);
@@ -979,6 +982,70 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testFailure_secDnsForbiddenAlgorithm() throws Exception {
setEppInput("domain_update_dsdata_add.xml", OTHER_DSDATA_TEMPLATE_MAP);
persistResource(
DatabaseHelper.newDomain(getUniqueIdFromCommand())
.asBuilder()
.setDsData(
ImmutableSet.of(DomainDsData.create(1, 1, 2, base16().decode(SHA_256_DIGEST))))
.build());
persistFeatureFlag(FORBID_INSECURE_ALGORITHMS_RFC_9904, FeatureStatus.ACTIVE);
EppException thrown = assertThrows(InvalidDsRecordException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testSuccess_secDnsForbiddenAlgorithm_flagInactive() throws Exception {
setEppInput("domain_update_dsdata_add.xml", OTHER_DSDATA_TEMPLATE_MAP);
persistResource(
DatabaseHelper.newDomain(getUniqueIdFromCommand())
.asBuilder()
.setDsData(
ImmutableSet.of(DomainDsData.create(1, 1, 2, base16().decode(SHA_256_DIGEST))))
.build());
persistFeatureFlag(FORBID_INSECURE_ALGORITHMS_RFC_9904, FeatureStatus.INACTIVE);
runFlow();
Domain domain = reloadResourceByForeignKey();
assertAboutDomains()
.that(domain)
.hasExactlyDsData(
DomainDsData.create(1, 1, 2, base16().decode(SHA_256_DIGEST)),
DomainDsData.create(12346, 8, 2, base16().decode(SHA_256_DIGEST)));
}
@Test
void testFailure_unrelatedUpdate_existingForbiddenAlgorithm() throws Exception {
persistReferencedEntities();
persistResource(
persistDomain()
.asBuilder()
.setDsData(
ImmutableSet.of(DomainDsData.create(1, 1, 2, base16().decode(SHA_256_DIGEST))))
.build());
persistFeatureFlag(FORBID_INSECURE_ALGORITHMS_RFC_9904, FeatureStatus.ACTIVE);
assertAboutEppExceptions()
.that(assertThrows(InvalidDsRecordException.class, this::runFlow))
.marshalsToXml();
}
@Test
void testSuccess_unrelatedUpdate_existingForbiddenAlgorithm_flagInactive() throws Exception {
persistReferencedEntities();
persistResource(
persistDomain()
.asBuilder()
.setDsData(
ImmutableSet.of(DomainDsData.create(1, 1, 2, base16().decode(SHA_256_DIGEST))))
.build());
runFlow();
Domain updatedDomain = reloadResourceByForeignKey();
assertAboutDomains()
.that(updatedDomain)
.hasExactlyDsData(DomainDsData.create(1, 1, 2, base16().decode(SHA_256_DIGEST)));
}
@Test
void testFailure_secDnsMultipleInvalidAlgorithms() throws Exception {
setEppInput("domain_update_dsdata_add.xml", OTHER_DSDATA_TEMPLATE_MAP);
@@ -20,13 +20,12 @@ import static google.registry.model.common.FeatureFlag.FeatureName.PROHIBIT_CONT
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatabaseHelper.deleteResource;
import static google.registry.testing.DatabaseHelper.loadRegistrar;
import static google.registry.testing.DatabaseHelper.persistFeatureFlag;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
import static google.registry.util.DateTimeUtils.START_INSTANT;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedMap;
import google.registry.flows.EppException;
import google.registry.flows.EppException.UnimplementedExtensionException;
import google.registry.flows.EppException.UnimplementedObjectServiceException;
@@ -39,7 +38,6 @@ import google.registry.flows.session.LoginFlow.BadRegistrarIdException;
import google.registry.flows.session.LoginFlow.RegistrarAccountNotActiveException;
import google.registry.flows.session.LoginFlow.TooManyFailedLoginsException;
import google.registry.flows.session.LoginFlow.UnsupportedLanguageException;
import google.registry.model.common.FeatureFlag;
import google.registry.model.common.FeatureFlag.FeatureStatus;
import google.registry.model.eppoutput.EppOutput;
import google.registry.model.registrar.Registrar;
@@ -61,11 +59,7 @@ public abstract class LoginFlowTestCase extends FlowTestCase<LoginFlow> {
sessionMetadata.setRegistrarId(null); // Don't implicitly log in (all other flows need to).
registrar = loadRegistrar("NewRegistrar");
registrarBuilder = registrar.asBuilder();
persistResource(
new FeatureFlag.Builder()
.setFeatureName(PROHIBIT_CONTACT_OBJECTS_ON_LOGIN)
.setStatusMap(ImmutableSortedMap.of(START_INSTANT, FeatureStatus.ACTIVE))
.build());
persistFeatureFlag(PROHIBIT_CONTACT_OBJECTS_ON_LOGIN, FeatureStatus.ACTIVE);
}
// Can't inline this since it may be overridden in subclasses.
@@ -18,17 +18,21 @@ import static com.google.common.truth.Truth.assertThat;
import static google.registry.request.JsonResponse.JSON_SAFETY_PREFIX;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import google.registry.testing.FakeResponse;
import google.registry.tools.GsonUtils;
import java.time.Instant;
import java.util.Map;
import org.json.simple.JSONValue;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link JsonResponse}. */
class JsonResponseTest {
private static final Gson GSON = GsonUtils.provideGson();
private FakeResponse fakeResponse = new FakeResponse();
private JsonResponse jsonResponse = new JsonResponse(fakeResponse);
private JsonResponse jsonResponse = new JsonResponse(fakeResponse, GSON);
@Test
void testSetStatus() {
@@ -44,9 +48,8 @@ class JsonResponseTest {
jsonResponse.setPayload(responseValues);
String payload = fakeResponse.getPayload();
assertThat(payload).startsWith(JSON_SAFETY_PREFIX);
@SuppressWarnings("unchecked")
Map<String, Object> responseMap = (Map<String, Object>)
JSONValue.parse(payload.substring(JSON_SAFETY_PREFIX.length()));
Map<String, Object> responseMap =
GSON.fromJson(payload.substring(JSON_SAFETY_PREFIX.length()), new TypeToken<>() {});
assertThat(responseMap).containsExactlyEntriesIn(responseValues);
}
@@ -19,21 +19,26 @@ import static google.registry.request.RequestModule.provideJsonPayload;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.net.MediaType;
import com.google.gson.Gson;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.HttpException.UnsupportedMediaTypeException;
import google.registry.tools.GsonUtils;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link RequestModule}. */
final class RequestModuleTest {
private static final Gson GSON = GsonUtils.provideGson();
@Test
void testProvideJsonPayload() {
assertThat(provideJsonPayload(MediaType.JSON_UTF_8, "{\"k\":\"v\"}")).containsExactly("k", "v");
assertThat(provideJsonPayload(MediaType.JSON_UTF_8, "{\"k\":\"v\"}", GSON))
.containsExactly("k", "v");
}
@Test
void testProvideJsonPayload_contentTypeWithoutCharsetAllowed() {
assertThat(provideJsonPayload(MediaType.JSON_UTF_8.withoutParameters(), "{\"k\":\"v\"}"))
assertThat(provideJsonPayload(MediaType.JSON_UTF_8.withoutParameters(), "{\"k\":\"v\"}", GSON))
.containsExactly("k", "v");
}
@@ -41,14 +46,16 @@ final class RequestModuleTest {
void testProvideJsonPayload_malformedInput_throws500() {
BadRequestException thrown =
assertThrows(
BadRequestException.class, () -> provideJsonPayload(MediaType.JSON_UTF_8, "{\"k\":"));
BadRequestException.class,
() -> provideJsonPayload(MediaType.JSON_UTF_8, "{\"k\":", GSON));
assertThat(thrown).hasMessageThat().contains("Malformed JSON");
}
@Test
void testProvideJsonPayload_emptyInput_throws500() {
BadRequestException thrown =
assertThrows(BadRequestException.class, () -> provideJsonPayload(MediaType.JSON_UTF_8, ""));
assertThrows(
BadRequestException.class, () -> provideJsonPayload(MediaType.JSON_UTF_8, "", GSON));
assertThat(thrown).hasMessageThat().contains("Malformed JSON");
}
@@ -56,13 +63,13 @@ final class RequestModuleTest {
void testProvideJsonPayload_nonJsonContentType_throws415() {
assertThrows(
UnsupportedMediaTypeException.class,
() -> provideJsonPayload(MediaType.PLAIN_TEXT_UTF_8, "{}"));
() -> provideJsonPayload(MediaType.PLAIN_TEXT_UTF_8, "{}", GSON));
}
@Test
void testProvideJsonPayload_contentTypeWithWeirdParam_throws415() {
assertThrows(
UnsupportedMediaTypeException.class,
() -> provideJsonPayload(MediaType.JSON_UTF_8.withParameter("omg", "handel"), "{}"));
() -> provideJsonPayload(MediaType.JSON_UTF_8.withParameter("omg", "handel"), "{}", GSON));
}
}
@@ -20,21 +20,25 @@ import static com.google.common.truth.Truth.assertWithMessage;
import static google.registry.security.JsonHttp.JSON_SAFETY_PREFIX;
import com.google.common.base.Supplier;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import google.registry.tools.GsonUtils;
import java.io.BufferedReader;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Map;
import org.json.simple.JSONValue;
import org.json.simple.parser.ParseException;
/**
* Helper class for testing JSON RPC servlets.
*/
public final class JsonHttpTestUtils {
private static final Gson GSON = GsonUtils.provideGson();
/** Returns JSON payload for mocked result of {@code rsp.getReader()}. */
public static BufferedReader createJsonPayload(Map<String, ?> object) {
return createJsonPayload(JSONValue.toJSONString(object));
return createJsonPayload(GSON.toJson(object));
}
/** @see #createJsonPayload(Map) */
@@ -58,10 +62,8 @@ public final class JsonHttpTestUtils {
assertThat(jsonText).startsWith(JSON_SAFETY_PREFIX);
jsonText = jsonText.substring(JSON_SAFETY_PREFIX.length());
try {
@SuppressWarnings("unchecked")
Map<String, Object> json = (Map<String, Object>) JSONValue.parseWithException(jsonText);
return json;
} catch (ClassCastException | ParseException e) {
return GSON.fromJson(jsonText, new TypeToken<>() {});
} catch (ClassCastException | JsonSyntaxException e) {
assertWithMessage("Bad JSON: %s\n%s", e.getMessage(), jsonText).fail();
throw new AssertionError();
}
@@ -33,7 +33,7 @@ public final class RegistryTestServer {
public static final ImmutableMap<String, Path> RUNFILES =
new ImmutableMap.Builder<String, Path>()
.put("/console/*", PROJECT_ROOT.resolve("console-webapp/staged/dist"))
.put("/console/*", PROJECT_ROOT.resolve("console-webapp/staged/dist/browser"))
.build();
public static final ImmutableList<Route> ROUTES =
@@ -69,6 +69,9 @@ import google.registry.model.billing.BillingCancellation;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingRecurrence;
import google.registry.model.common.DnsRefreshRequest;
import google.registry.model.common.FeatureFlag;
import google.registry.model.common.FeatureFlag.FeatureName;
import google.registry.model.common.FeatureFlag.FeatureStatus;
import google.registry.model.console.GlobalRole;
import google.registry.model.console.User;
import google.registry.model.console.UserRoles;
@@ -682,6 +685,32 @@ public final class DatabaseHelper {
return newRegistrars.build();
}
/** Persists and returns a {@link FeatureFlag} with a single status starting from the epoch. */
public static FeatureFlag persistFeatureFlag(FeatureName featureName, FeatureStatus status) {
return persistFeatureFlag(featureName, ImmutableSortedMap.of(START_INSTANT, status));
}
/** Persists and returns a {@link FeatureFlag} with an initial status and one transition. */
public static FeatureFlag persistFeatureFlag(
FeatureName featureName,
FeatureStatus initialStatus,
Instant transitionTime,
FeatureStatus status) {
return persistFeatureFlag(
featureName,
ImmutableSortedMap.<Instant, FeatureStatus>naturalOrder()
.put(START_INSTANT, initialStatus)
.put(transitionTime, status)
.build());
}
/** Persists and returns a {@link FeatureFlag} with a custom status map. */
public static FeatureFlag persistFeatureFlag(
FeatureName featureName, ImmutableSortedMap<Instant, FeatureStatus> statusMap) {
return persistResource(
new FeatureFlag.Builder().setFeatureName(featureName).setStatusMap(statusMap).build());
}
public static Iterable<BillingBase> getBillingEvents() {
return tm().transact(
() ->
@@ -15,6 +15,7 @@
package google.registry.testing;
import google.registry.request.JsonResponse;
import google.registry.tools.GsonUtils;
import java.util.Map;
/** Fake implementation of {@link JsonResponse} for testing. */
@@ -23,7 +24,7 @@ public final class FakeJsonResponse extends JsonResponse {
private Map<String, ?> responseMap;
public FakeJsonResponse() {
super(new FakeResponse());
super(new FakeResponse(), GsonUtils.provideGson());
}
@Override
@@ -19,7 +19,7 @@ import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATAS
import static google.registry.model.common.FeatureFlag.FeatureName.TEST_FEATURE;
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.DatabaseHelper.persistFeatureFlag;
import static google.registry.util.DateTimeUtils.START_INSTANT;
import static google.registry.util.DateTimeUtils.plusWeeks;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -27,7 +27,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import com.beust.jcommander.ParameterException;
import com.google.common.collect.ImmutableSortedMap;
import google.registry.model.common.FeatureFlag;
import google.registry.model.common.FeatureFlag.FeatureStatus;
import google.registry.model.common.TimedTransitionProperty;
import google.registry.testing.FakeClock;
import java.time.Instant;
@@ -81,14 +80,7 @@ public class ConfigureFeatureFlagCommandTest extends CommandTestCase<ConfigureFe
@Test
void testUpdate() throws Exception {
persistResource(
new FeatureFlag.Builder()
.setFeatureName(TEST_FEATURE)
.setStatusMap(
ImmutableSortedMap.<Instant, FeatureStatus>naturalOrder()
.put(START_INSTANT, INACTIVE)
.build())
.build());
persistFeatureFlag(TEST_FEATURE, INACTIVE);
Instant featureStart = plusWeeks(clock.now(), 6);
assertThat(FeatureFlag.get(TEST_FEATURE).getStatusMap())
@@ -110,14 +102,7 @@ public class ConfigureFeatureFlagCommandTest extends CommandTestCase<ConfigureFe
@Test
void testConfigure_multipleFlags() throws Exception {
persistResource(
new FeatureFlag.Builder()
.setFeatureName(TEST_FEATURE)
.setStatusMap(
ImmutableSortedMap.<Instant, FeatureStatus>naturalOrder()
.put(START_INSTANT, INACTIVE)
.build())
.build());
persistFeatureFlag(TEST_FEATURE, INACTIVE);
Instant featureStart = plusWeeks(clock.now(), 6);
assertThat(FeatureFlag.get(TEST_FEATURE).getStatusMap())
@@ -179,14 +164,7 @@ public class ConfigureFeatureFlagCommandTest extends CommandTestCase<ConfigureFe
@Test
void testUpdate_invalidStatusMap() throws Exception {
persistResource(
new FeatureFlag.Builder()
.setFeatureName(TEST_FEATURE)
.setStatusMap(
ImmutableSortedMap.<Instant, FeatureStatus>naturalOrder()
.put(START_INSTANT, INACTIVE)
.build())
.build());
persistFeatureFlag(TEST_FEATURE, INACTIVE);
Instant featureStart = plusWeeks(clock.now(), 6);
assertThat(FeatureFlag.get(TEST_FEATURE).getStatusMap())
@@ -15,8 +15,10 @@
package google.registry.tools;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.common.FeatureFlag.FeatureName.FORBID_INSECURE_ALGORITHMS_RFC_9904;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistFeatureFlag;
import static google.registry.testing.DatabaseHelper.persistPremiumList;
import static google.registry.testing.DatabaseHelper.persistResource;
import static org.joda.money.CurrencyUnit.JPY;
@@ -25,6 +27,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import com.beust.jcommander.ParameterException;
import com.google.common.collect.ImmutableSet;
import google.registry.dns.writer.VoidDnsWriter;
import google.registry.model.common.FeatureFlag.FeatureStatus;
import google.registry.model.pricing.StaticPremiumListPricingEngine;
import google.registry.model.tld.Tld;
import google.registry.model.tld.label.PremiumListDao;
@@ -49,9 +52,9 @@ class CreateDomainCommandTest extends EppToolCommandTestCase<CreateDomainCommand
"--period=1",
"--nameservers=ns1.zdns.google,ns2.zdns.google,ns3.zdns.google,ns4.zdns.google",
"--password=2fooBAR",
"--ds_records=1 2 2 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08,4 5 1"
+ " A94A8FE5CCB19BA61C4C0873D391E987982FBBD3",
"--ds_records=60485 5 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--ds_records=1 2 2 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08,4 8 2"
+ " D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--ds_records=60485 8 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"example.tld");
eppVerifier.verifySent("domain_create_complete.xml");
}
@@ -63,9 +66,9 @@ class CreateDomainCommandTest extends EppToolCommandTestCase<CreateDomainCommand
"--period=1",
"--nameservers=NS1.zdns.google,ns2.ZDNS.google,ns3.zdns.gOOglE,ns4.zdns.google",
"--password=2fooBAR",
"--ds_records=1 2 2 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08,4 5 1"
+ " A94A8FE5CCB19BA61C4C0873D391E987982FBBD3",
"--ds_records=60485 5 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--ds_records=1 2 2 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08,4 8 2"
+ " D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--ds_records=60485 8 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"example.tld");
eppVerifier.verifySent("domain_create_complete.xml");
}
@@ -77,9 +80,9 @@ class CreateDomainCommandTest extends EppToolCommandTestCase<CreateDomainCommand
"--period=1",
"--nameservers=ns[1-4].zdns.google",
"--password=2fooBAR",
"--ds_records=1 2 2 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08,4 5 1"
+ " A94A8FE5CCB19BA61C4C0873D391E987982FBBD3",
"--ds_records=60485 5 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--ds_records=1 2 2 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08,4 8 2"
+ " D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--ds_records=60485 8 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"example.tld");
eppVerifier.verifySent("domain_create_complete.xml");
}
@@ -91,9 +94,9 @@ class CreateDomainCommandTest extends EppToolCommandTestCase<CreateDomainCommand
"--period=1",
"--nameservers=NS[1-4].zdns.google",
"--password=2fooBAR",
"--ds_records=1 2 2 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08,4 5 1"
+ " A94A8FE5CCB19BA61C4C0873D391E987982FBBD3",
"--ds_records=60485 5 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--ds_records=1 2 2 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08,4 8 2"
+ " D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--ds_records=60485 8 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"example.tld");
eppVerifier.verifySent("domain_create_complete.xml");
}
@@ -280,15 +283,28 @@ class CreateDomainCommandTest extends EppToolCommandTestCase<CreateDomainCommand
}
@Test
void testFailure_invalidDigestLength() {
void testFailure_forbiddenAlgorithm() {
persistFeatureFlag(FORBID_INSECURE_ALGORITHMS_RFC_9904, FeatureStatus.ACTIVE);
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
runCommandForced(
"--client=NewRegistrar",
"--ds_records=1 2 1 abcd",
"--ds_records=1 1 2"
+ " D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"example.tld"));
assertThat(thrown).hasMessageThat().isEqualTo("DS record uses an unrecognized algorithm: 1");
}
@Test
void testFailure_invalidDigestLength() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
runCommandForced(
"--client=NewRegistrar", "--ds_records=1 2 2 abcd", "example.tld"));
assertThat(thrown).hasMessageThat().isEqualTo("DS record has an invalid digest length: ABCD");
}
@@ -18,10 +18,8 @@ import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.common.FeatureFlag.FeatureName.TEST_FEATURE;
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.START_INSTANT;
import static google.registry.testing.DatabaseHelper.persistFeatureFlag;
import com.google.common.collect.ImmutableSortedMap;
import google.registry.model.common.FeatureFlag;
import org.junit.jupiter.api.Test;
@@ -30,12 +28,7 @@ public class DeleteFeatureFlagCommandTest extends CommandTestCase<DeleteFeatureF
@Test
void testSimpleSuccess() throws Exception {
persistResource(
new FeatureFlag()
.asBuilder()
.setFeatureName(TEST_FEATURE)
.setStatusMap(ImmutableSortedMap.of(START_INSTANT, ACTIVE))
.build());
persistFeatureFlag(TEST_FEATURE, ACTIVE);
assertThat(tm().transact(() -> FeatureFlag.isActiveNow(TEST_FEATURE))).isTrue();
runCommandForced("TEST_FEATURE");
assertThat(FeatureFlag.getUncached(TEST_FEATURE)).isEmpty();
@@ -0,0 +1,48 @@
// 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.tools;
import static com.google.common.truth.Truth.assertThat;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link DigestType}. */
class DigestTypeTest {
@Test
void testFromWireValue_sha1_returnsSha1() {
assertThat(DigestType.fromWireValue(1)).hasValue(DigestType.SHA1);
}
@Test
void testFromWireValue_sha256_returnsSha256() {
assertThat(DigestType.fromWireValue(2)).hasValue(DigestType.SHA256);
}
@Test
void testFromWireValue_gost_returnsEmpty() {
assertThat(DigestType.fromWireValue(3)).isEmpty();
}
@Test
void testFromWireValue_sha384_returnsSha384() {
assertThat(DigestType.fromWireValue(4)).hasValue(DigestType.SHA384);
}
@Test
void testFromWireValue_invalid_returnsEmpty() {
assertThat(DigestType.fromWireValue(5)).isEmpty();
}
}
@@ -85,7 +85,8 @@ final class GcpProjectConnectionTest {
when(lowLevelHttpResponse.getStatusCode()).thenReturn(200);
httpTransport = new TestHttpTransport();
connection = new ServiceConnection(false, httpTransport.createRequestFactory());
connection =
new ServiceConnection(false, httpTransport.createRequestFactory(), GsonUtils.provideGson());
}
@Test
@@ -31,6 +31,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.net.InetAddresses;
import com.google.gson.Gson;
import google.registry.model.domain.Domain;
import google.registry.model.domain.secdns.DomainDsData;
import google.registry.model.eppcommon.StatusValue;
@@ -41,19 +42,19 @@ import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import org.json.simple.JSONValue;
import org.json.simple.parser.ParseException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link GenerateDnsReportCommand}. */
class GenerateDnsReportCommandTest extends CommandTestCase<GenerateDnsReportCommand> {
private static final Gson GSON = GsonUtils.provideGson();
private Path output;
private Object getOutputAsJson() throws IOException, ParseException {
private Object getOutputAsJson() throws IOException {
try (Reader reader = Files.newBufferedReader(output, UTF_8)) {
return JSONValue.parseWithException(reader);
return GSON.fromJson(reader, Object.class);
}
}
@@ -114,6 +115,7 @@ class GenerateDnsReportCommandTest extends CommandTestCase<GenerateDnsReportComm
void beforeEach() {
output = tmpDir.resolve("out.dat");
command.clock = fakeClock;
command.gson = GSON;
createTlds("xn--q9jyb4c", "example");
nameserver1 =
@@ -19,7 +19,7 @@ import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATAS
import static google.registry.model.common.FeatureFlag.FeatureName.TEST_FEATURE;
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.DatabaseHelper.persistFeatureFlag;
import static google.registry.util.DateTimeUtils.START_INSTANT;
import static google.registry.util.DateTimeUtils.plusWeeks;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -27,7 +27,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import com.beust.jcommander.ParameterException;
import com.google.common.collect.ImmutableSortedMap;
import google.registry.model.EntityYamlUtils;
import google.registry.model.common.FeatureFlag;
import google.registry.model.common.FeatureFlag.FeatureFlagNotFoundException;
import google.registry.model.common.FeatureFlag.FeatureStatus;
import google.registry.testing.FakeClock;
@@ -47,15 +46,7 @@ public class GetFeatureFlagCommandTest extends CommandTestCase<GetFeatureFlagCom
@Test
void testSuccess() throws Exception {
persistResource(
new FeatureFlag.Builder()
.setFeatureName(TEST_FEATURE)
.setStatusMap(
ImmutableSortedMap.<Instant, FeatureStatus>naturalOrder()
.put(START_INSTANT, INACTIVE)
.put(plusWeeks(clock.now(), 8), ACTIVE)
.build())
.build());
persistFeatureFlag(TEST_FEATURE, INACTIVE, plusWeeks(clock.now(), 8), ACTIVE);
runCommand("TEST_FEATURE");
assertInStdout(
"""
@@ -68,24 +59,13 @@ public class GetFeatureFlagCommandTest extends CommandTestCase<GetFeatureFlagCom
@Test
void testSuccess_multipleArguments() throws Exception {
persistResource(
new FeatureFlag.Builder()
.setFeatureName(TEST_FEATURE)
.setStatusMap(
ImmutableSortedMap.<Instant, FeatureStatus>naturalOrder()
.put(START_INSTANT, INACTIVE)
.put(plusWeeks(clock.now(), 8), ACTIVE)
.build())
.build());
persistResource(
new FeatureFlag.Builder()
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
.setStatusMap(
ImmutableSortedMap.<Instant, FeatureStatus>naturalOrder()
.put(START_INSTANT, INACTIVE)
.put(plusWeeks(clock.now(), 3), ACTIVE)
.put(plusWeeks(clock.now(), 6), INACTIVE)
.build())
persistFeatureFlag(TEST_FEATURE, INACTIVE, plusWeeks(clock.now(), 8), ACTIVE);
persistFeatureFlag(
MINIMUM_DATASET_CONTACTS_OPTIONAL,
ImmutableSortedMap.<Instant, FeatureStatus>naturalOrder()
.put(START_INSTANT, INACTIVE)
.put(plusWeeks(clock.now(), 3), ACTIVE)
.put(plusWeeks(clock.now(), 6), INACTIVE)
.build());
runCommand("TEST_FEATURE", "MINIMUM_DATASET_CONTACTS_OPTIONAL");
assertInStdout(
@@ -114,15 +94,7 @@ public class GetFeatureFlagCommandTest extends CommandTestCase<GetFeatureFlagCom
@Test
void testFailure_oneFlagDoesNotExist() {
persistResource(
new FeatureFlag.Builder()
.setFeatureName(TEST_FEATURE)
.setStatusMap(
ImmutableSortedMap.<Instant, FeatureStatus>naturalOrder()
.put(START_INSTANT, INACTIVE)
.put(plusWeeks(clock.now(), 8), ACTIVE)
.build())
.build());
persistFeatureFlag(TEST_FEATURE, INACTIVE, plusWeeks(clock.now(), 8), ACTIVE);
assertThrows(
FeatureFlagNotFoundException.class,
() -> runCommand("TEST_FEATURE", "MINIMUM_DATASET_CONTACTS_OPTIONAL"));
@@ -18,14 +18,13 @@ import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATAS
import static google.registry.model.common.FeatureFlag.FeatureName.TEST_FEATURE;
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.DatabaseHelper.persistFeatureFlag;
import static google.registry.testing.TestDataHelper.loadFile;
import static google.registry.util.DateTimeUtils.START_INSTANT;
import static google.registry.util.DateTimeUtils.plusWeeks;
import com.google.common.collect.ImmutableSortedMap;
import google.registry.model.EntityYamlUtils;
import google.registry.model.common.FeatureFlag;
import google.registry.model.common.FeatureFlag.FeatureStatus;
import java.time.Instant;
import org.junit.jupiter.api.BeforeEach;
@@ -41,49 +40,23 @@ public class ListFeatureFlagsCommandTest extends CommandTestCase<ListFeatureFlag
@Test
void testSuccess_oneFlag() throws Exception {
persistResource(
new FeatureFlag.Builder()
.setFeatureName(TEST_FEATURE)
.setStatusMap(
ImmutableSortedMap.<Instant, FeatureStatus>naturalOrder()
.put(START_INSTANT, INACTIVE)
.put(plusWeeks(fakeClock.now(), 8), ACTIVE)
.build())
.build());
persistFeatureFlag(TEST_FEATURE, INACTIVE, plusWeeks(fakeClock.now(), 8), ACTIVE);
runCommand();
assertInStdout(loadFile(getClass(), "oneFlag.yaml"));
}
@Test
void test_success_manyFlags() throws Exception {
persistResource(
new FeatureFlag.Builder()
.setFeatureName(TEST_FEATURE)
.setStatusMap(
ImmutableSortedMap.<Instant, FeatureStatus>naturalOrder()
.put(START_INSTANT, INACTIVE)
.put(plusWeeks(fakeClock.now(), 8), ACTIVE)
.build())
.build());
persistResource(
new FeatureFlag.Builder()
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
.setStatusMap(
ImmutableSortedMap.<Instant, FeatureStatus>naturalOrder()
.put(START_INSTANT, INACTIVE)
.put(plusWeeks(fakeClock.now(), 1), ACTIVE)
.put(plusWeeks(fakeClock.now(), 8), INACTIVE)
.put(plusWeeks(fakeClock.now(), 10), ACTIVE)
.build())
.build());
persistResource(
new FeatureFlag.Builder()
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
.setStatusMap(
ImmutableSortedMap.<Instant, FeatureStatus>naturalOrder()
.put(START_INSTANT, ACTIVE)
.build())
persistFeatureFlag(TEST_FEATURE, INACTIVE, plusWeeks(fakeClock.now(), 8), ACTIVE);
persistFeatureFlag(
MINIMUM_DATASET_CONTACTS_OPTIONAL,
ImmutableSortedMap.<Instant, FeatureStatus>naturalOrder()
.put(START_INSTANT, INACTIVE)
.put(plusWeeks(fakeClock.now(), 1), ACTIVE)
.put(plusWeeks(fakeClock.now(), 8), INACTIVE)
.put(plusWeeks(fakeClock.now(), 10), ACTIVE)
.build());
persistFeatureFlag(MINIMUM_DATASET_CONTACTS_PROHIBITED, ACTIVE);
runCommand();
assertInStdout(loadFile(getClass(), "threeFlags.yaml"));
}
@@ -26,6 +26,7 @@ import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponse;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import google.registry.request.Action.Service;
import java.io.ByteArrayInputStream;
import org.junit.jupiter.api.Test;
@@ -33,10 +34,12 @@ import org.junit.jupiter.api.Test;
/** Unit tests for {@link google.registry.tools.ServiceConnection}. */
public class ServiceConnectionTest {
private static final Gson GSON = GsonUtils.provideGson();
@Test
void testSuccess_serverUrl_notCanary() {
ServiceConnection connection =
new ServiceConnection(false, null).withService(Service.FRONTEND, false);
new ServiceConnection(false, null, GSON).withService(Service.FRONTEND, false);
String serverUrl = connection.getServer().toString();
assertThat(serverUrl).isEqualTo("https://frontend.registry.test"); // See default-config.yaml
}
@@ -52,7 +55,7 @@ public class ServiceConnectionTest {
when(request.execute()).thenReturn(response);
when(response.getContent()).thenReturn(ByteArrayInputStream.nullInputStream());
ServiceConnection connection =
new ServiceConnection(false, factory).withService(Service.PUBAPI, true);
new ServiceConnection(false, factory, GSON).withService(Service.PUBAPI, true);
String serverUrl = connection.getServer().toString();
assertThat(serverUrl).isEqualTo("https://pubapi.registry.test");
connection.sendGetRequest("/path", ImmutableMap.of());
@@ -79,7 +79,7 @@ class UniformRapidSuspensionCommandTest
runCommandForced(
"--domain_name=evil.tld",
"--hosts=urs1.example.com,urs2.example.com",
"--dsdata=1 1 1 A94A8FE5CCB19BA61C4C0873D391E987982FBBD3",
"--dsdata=1 1 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--renew_one_year=false");
eppVerifier
.expectRegistrarId("CharlestonRoad")
@@ -107,7 +107,7 @@ class UniformRapidSuspensionCommandTest
runCommandForced(
"--domain_name=evil.tld",
"--hosts=urs1.example.com,urs2.example.com",
"--dsdata=1 1 1 A94A8FE5CCB19BA61C4C0873D391E987982FBBD3",
"--dsdata=1 1 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--renew_one_year=false");
eppVerifier
.expectRegistrarId("CharlestonRoad")
@@ -179,7 +179,7 @@ class UniformRapidSuspensionCommandTest
runCommandForced(
"--domain_name=evil.tld",
"--hosts=urs1.example.com,urs2.example.com",
"--dsdata=1 1 1 A94A8FE5CCB19BA61C4C0873D391E987982FBBD3",
"--dsdata=1 1 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--renew_one_year=false");
eppVerifier
.expectRegistrarId("CharlestonRoad")
@@ -198,7 +198,7 @@ class UniformRapidSuspensionCommandTest
runCommandForced(
"--domain_name=evil.tld",
"--hosts=URS[1-2].example.com",
"--dsdata=1 1 1 A94A8FE5CCB19BA61C4C0873D391E987982FBBD3",
"--dsdata=1 1 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--renew_one_year=false");
eppVerifier
.expectRegistrarId("CharlestonRoad")
@@ -15,6 +15,7 @@
package google.registry.tools;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.common.FeatureFlag.FeatureName.FORBID_INSECURE_ALGORITHMS_RFC_9904;
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;
@@ -22,6 +23,7 @@ import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_CREATE;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistActiveHost;
import static google.registry.testing.DatabaseHelper.persistFeatureFlag;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.TestLogHandlerUtils.assertLogMessage;
import static google.registry.testing.TestLogHandlerUtils.assertNoLogMessage;
@@ -36,6 +38,7 @@ import com.google.common.collect.ImmutableSet;
import google.registry.model.billing.BillingBase.Flag;
import google.registry.model.billing.BillingBase.Reason;
import google.registry.model.billing.BillingRecurrence;
import google.registry.model.common.FeatureFlag.FeatureStatus;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.GracePeriod;
@@ -76,10 +79,11 @@ class UpdateDomainCommandTest extends EppToolCommandTestCase<UpdateDomainCommand
"--add_nameservers=ns1.zdns.google,ns2.zdns.google",
"--add_statuses=serverDeleteProhibited",
"--add_ds_records=1 2 2 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08,4"
+ " 5 1 A94A8FE5CCB19BA61C4C0873D391E987982FBBD3",
+ " 5 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--remove_nameservers=ns3.zdns.google,ns4.zdns.google",
"--remove_statuses=serverHold",
"--remove_ds_records=7 8 1 A94A8FE5CCB19BA61C4C0873D391E987982FBBD3,6 5 4"
"--remove_ds_records=7 8 2"
+ " D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A,6 5 4"
+ " 768412320F7B0AA5812FCE428DC4706B3CAE50E02A64CAA16A782249BFE8EFC4B7EF1CCB126255D196047DFEDF17A0A9",
"--password=2fooBAR",
"example.tld");
@@ -93,10 +97,11 @@ class UpdateDomainCommandTest extends EppToolCommandTestCase<UpdateDomainCommand
"--add_nameservers=NS[1-2].zdns.google",
"--add_statuses=serverDeleteProhibited",
"--add_ds_records=1 2 2 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08,4"
+ " 5 1 A94A8FE5CCB19BA61C4C0873D391E987982FBBD3",
+ " 5 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--remove_nameservers=ns[3-4].zdns.google",
"--remove_statuses=serverHold",
"--remove_ds_records=7 8 1 A94A8FE5CCB19BA61C4C0873D391E987982FBBD3,6 5 4"
"--remove_ds_records=7 8 2"
+ " D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A,6 5 4"
+ " 768412320F7B0AA5812FCE428DC4706B3CAE50E02A64CAA16A782249BFE8EFC4B7EF1CCB126255D196047DFEDF17A0A9",
"--password=2fooBAR",
"example.tld");
@@ -112,10 +117,11 @@ class UpdateDomainCommandTest extends EppToolCommandTestCase<UpdateDomainCommand
"--add_nameservers=ns1.zdns.google,ns2.zdns.google",
"--add_statuses=serverDeleteProhibited",
"--add_ds_records=1 2 2 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08,4"
+ " 5 1 A94A8FE5CCB19BA61C4C0873D391E987982FBBD3",
+ " 5 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--remove_nameservers=ns[3-4].zdns.google",
"--remove_statuses=serverHold",
"--remove_ds_records=7 8 1 A94A8FE5CCB19BA61C4C0873D391E987982FBBD3,6 5 4"
"--remove_ds_records=7 8 2"
+ " D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A,6 5 4"
+ " 768412320F7B0AA5812FCE428DC4706B3CAE50E02A64CAA16A782249BFE8EFC4B7EF1CCB126255D196047DFEDF17A0A9",
"--password=2fooBAR",
"example.tld",
@@ -163,7 +169,7 @@ class UpdateDomainCommandTest extends EppToolCommandTestCase<UpdateDomainCommand
"--add_nameservers=ns2.zdns.google,ns3.zdns.google",
"--add_statuses=serverDeleteProhibited",
"--add_ds_records=1 2 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A,4"
+ " 5 1 A94A8FE5CCB19BA61C4C0873D391E987982FBBD3",
+ " 5 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"example.tld");
eppVerifier.verifySent("domain_update_add.xml");
}
@@ -174,7 +180,8 @@ class UpdateDomainCommandTest extends EppToolCommandTestCase<UpdateDomainCommand
"--client=NewRegistrar",
"--remove_nameservers=ns4.zdns.google",
"--remove_statuses=serverHold",
"--remove_ds_records=7 8 1 A94A8FE5CCB19BA61C4C0873D391E987982FBBD3,6 5 4"
"--remove_ds_records=7 8 2"
+ " D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A,6 5 4"
+ " 768412320F7B0AA5812FCE428DC4706B3CAE50E02A64CAA16A782249BFE8EFC4B7EF1CCB126255D196047DFEDF17A0A9",
"example.tld");
eppVerifier.verifySent("domain_update_remove.xml");
@@ -230,8 +237,8 @@ class UpdateDomainCommandTest extends EppToolCommandTestCase<UpdateDomainCommand
void testSuccess_setDsRecords() throws Exception {
runCommandForced(
"--client=NewRegistrar",
"--ds_records=1 2 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A,4 5 1"
+ " A94A8FE5CCB19BA61C4C0873D391E987982FBBD3",
"--ds_records=1 2 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A,4 5 2"
+ " D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"example.tld");
eppVerifier.verifySent("domain_update_set_ds_records.xml");
}
@@ -240,8 +247,8 @@ class UpdateDomainCommandTest extends EppToolCommandTestCase<UpdateDomainCommand
void testSuccess_setDsRecords_withUnneededClear() throws Exception {
runCommandForced(
"--client=NewRegistrar",
"--ds_records=1 2 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A,4 5 1"
+ " A94A8FE5CCB19BA61C4C0873D391E987982FBBD3",
"--ds_records=1 2 2 D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A,4 5 2"
+ " D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--clear_ds_records",
"example.tld");
eppVerifier.verifySent("domain_update_set_ds_records.xml");
@@ -532,15 +539,28 @@ class UpdateDomainCommandTest extends EppToolCommandTestCase<UpdateDomainCommand
}
@Test
void testFailure_invalidDigestLength() {
void testFailure_forbiddenDsRecordAlgorithm() {
persistFeatureFlag(FORBID_INSECURE_ALGORITHMS_RFC_9904, FeatureStatus.ACTIVE);
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
runCommandForced(
"--client=NewRegistrar",
"--ds_records=1 2 1 abcd",
"--add_ds_records=1 1 2"
+ " D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"example.tld"));
assertThat(thrown).hasMessageThat().isEqualTo("DS record uses an unrecognized algorithm: 1");
}
@Test
void testFailure_invalidDigestLength() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
runCommandForced(
"--client=NewRegistrar", "--ds_records=1 2 2 abcd", "example.tld"));
assertThat(thrown).hasMessageThat().isEqualTo("DS record has an invalid digest length: ABCD");
}
@@ -554,7 +574,8 @@ class UpdateDomainCommandTest extends EppToolCommandTestCase<UpdateDomainCommand
"--client=NewRegistrar",
"--add_ds_records=1 2 2"
+ " D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--ds_records=4 5 1 A94A8FE5CCB19BA61C4C0873D391E987982FBBD3",
"--ds_records=4 5 2"
+ " D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"example.tld"));
assertThat(thrown)
.hasMessageThat()
@@ -571,8 +592,10 @@ class UpdateDomainCommandTest extends EppToolCommandTestCase<UpdateDomainCommand
() ->
runCommandForced(
"--client=NewRegistrar",
"--remove_ds_records=7 8 1 A94A8FE5CCB19BA61C4C0873D391E987982FBBD3",
"--ds_records=4 5 1 A94A8FE5CCB19BA61C4C0873D391E987982FBBD3",
"--remove_ds_records=7 8 2"
+ " D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--ds_records=4 5 2"
+ " D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"example.tld"));
assertThat(thrown)
.hasMessageThat()
@@ -608,7 +631,8 @@ class UpdateDomainCommandTest extends EppToolCommandTestCase<UpdateDomainCommand
() ->
runCommandForced(
"--client=NewRegistrar",
"--remove_ds_records=7 8 1 A94A8FE5CCB19BA61C4C0873D391E987982FBBD3",
"--remove_ds_records=7 8 2"
+ " D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A",
"--clear_ds_records",
"example.tld"));
assertThat(thrown)
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gson.reflect.TypeToken;
import google.registry.model.OteStatsTestHelper;
import google.registry.model.console.GlobalRole;
import google.registry.model.console.User;
@@ -44,7 +45,6 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.json.simple.JSONArray;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -131,7 +131,7 @@ class ConsoleOteActionTest extends ConsoleActionBaseTestCase {
Optional.of(new OteCreateData("theregistrar", "contact@registry.example")));
action.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
action.run();
var obsResponse = GSON.fromJson(response.getPayload(), Map.class);
Map<String, Object> obsResponse = GSON.fromJson(response.getPayload(), new TypeToken<>() {});
assertThat(
ImmutableMap.of(
"theregistrar-1", "theregistrar-sunrise",
@@ -175,7 +175,7 @@ class ConsoleOteActionTest extends ConsoleActionBaseTestCase {
Action.Method.GET, authResult, "theregistrar-1", Optional.empty(), Optional.empty());
action.run();
List<Map<String, ?>> responseMaps = GSON.fromJson(response.getPayload(), JSONArray.class);
List<Map<String, ?>> responseMaps = GSON.fromJson(response.getPayload(), new TypeToken<>() {});
assertThat(response.getStatus()).isEqualTo(SC_OK);
assertTrue(
responseMaps.stream().allMatch(status -> Boolean.TRUE.equals(status.get("completed"))));
@@ -191,7 +191,7 @@ class ConsoleOteActionTest extends ConsoleActionBaseTestCase {
Action.Method.GET, authResult, "theregistrar-1", Optional.empty(), Optional.empty());
action.run();
List<Map<String, ?>> responseMaps = GSON.fromJson(response.getPayload(), JSONArray.class);
List<Map<String, ?>> responseMaps = GSON.fromJson(response.getPayload(), new TypeToken<>() {});
assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(
responseMaps.stream()
@@ -174,7 +174,9 @@ public class ConsoleScreenshotTest {
// Script that set cursor to transparent to prevent blanking cursor flakiness when comparing
// screenshots
String script =
"document.styleSheets[0].insertRule(\"html * {caret-color: transparent !important;}\")";
"var style = document.createElement('style');"
+ "style.innerHTML = 'html * {caret-color: transparent !important;}';"
+ "document.head.appendChild(style);";
driver.executeScript(script);
}
@@ -4,7 +4,6 @@
<rde:rdeMenu>
<rde:version>1.0</rde:version>
<rde:objURI>urn:ietf:params:xml:ns:rdeDomain-1.0</rde:objURI>
<rde:objURI>urn:ietf:params:xml:ns:rdeEppParams-1.0</rde:objURI>
<rde:objURI>urn:ietf:params:xml:ns:rdeHeader-1.0</rde:objURI>
<rde:objURI>urn:ietf:params:xml:ns:rdeRegistrar-1.0</rde:objURI>
</rde:rdeMenu>
@@ -12,45 +11,10 @@
<rdeDomain:domain/>
<rdeRegistrar:registrar/>
<rdeEppParams:eppParams>
<rdeEppParams:version>1.0</rdeEppParams:version>
<rdeEppParams:lang>en</rdeEppParams:lang>
<rdeEppParams:objURI>urn:ietf:params:xml:ns:rdeDomain-1.0</rdeEppParams:objURI>
<rdeEppParams:objURI>urn:ietf:params:xml:ns:rdeHost-1.0</rdeEppParams:objURI>
<rdeEppParams:svcExtension>
<epp:extURI>urn:ietf:params:xml:ns:launch-1.0</epp:extURI>
<epp:extURI>urn:ietf:params:xml:ns:rgp-1.0</epp:extURI>
<epp:extURI>urn:ietf:params:xml:ns:secDNS-1.1</epp:extURI>
<epp:extURI>urn:ietf:params:xml:ns:fee-0.6</epp:extURI>
<epp:extURI>urn:ietf:params:xml:ns:fee-0.11</epp:extURI>
<epp:extURI>urn:ietf:params:xml:ns:fee-0.12</epp:extURI>
<epp:extURI>urn:ietf:params:xml:ns:epp:fee-1.0</epp:extURI>
</rdeEppParams:svcExtension>
<rdeEppParams:dcp>
<epp:access>
<epp:all/>
</epp:access>
<epp:statement>
<epp:purpose>
<epp:admin/>
<epp:prov/>
</epp:purpose>
<epp:recipient>
<epp:ours/>
<epp:public/>
</epp:recipient>
<epp:retention>
<epp:stated/>
</epp:retention>
</epp:statement>
</rdeEppParams:dcp>
</rdeEppParams:eppParams>
<rdeHeader:header>
<rdeHeader:tld>soy</rdeHeader:tld>
<rdeHeader:count uri="urn:ietf:params:xml:ns:rdeDomain-1.0">1</rdeHeader:count>
<rdeHeader:count uri="urn:ietf:params:xml:ns:rdeRegistrar-1.0">1</rdeHeader:count>
<rdeHeader:count uri="urn:ietf:params:xml:ns:rdeEppParams-1.0">1</rdeHeader:count>
</rdeHeader:header>
</rde:contents>
@@ -22,7 +22,7 @@
<secDNS:maxSigLife>604800</secDNS:maxSigLife>
<secDNS:dsData>
<secDNS:keyTag>12345</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
@@ -21,51 +21,51 @@
xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1">
<secDNS:dsData>
<secDNS:keyTag>12345</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12346</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12347</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12348</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12349</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12350</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12351</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12352</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
</secDNS:create>
</extension>
@@ -21,55 +21,55 @@
xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1">
<secDNS:dsData>
<secDNS:keyTag>12345</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12346</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12347</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12348</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12349</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12350</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12351</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12352</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12353</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
@@ -27,19 +27,19 @@
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12346</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12347</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12348</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
@@ -51,19 +51,19 @@
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12350</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12351</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12352</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
@@ -21,49 +21,49 @@
xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1">
<secDNS:dsData>
<secDNS:keyTag>12345</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>100</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12346</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>3</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12347</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12348</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12349</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12350</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12351</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>12352</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<command>
<create>
<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:ns>
<domain:hostObj>ns1.example.net</domain:hostObj>
<domain:hostObj>ns2.example.net</domain:hostObj>
</domain:ns>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:create>
</create>
<extension>
<secDNS:create
xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1">
<secDNS:dsData>
<secDNS:keyTag>12345</secDNS:keyTag>
<secDNS:alg>1</secDNS:alg>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
</secDNS:create>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>
@@ -21,9 +21,9 @@
xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1">
<secDNS:dsData>
<secDNS:keyTag>12345</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
</secDNS:create>
</extension>
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<command>
<create>
<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:ns>
<domain:hostObj>ns1.example.net</domain:hostObj>
<domain:hostObj>ns2.example.net</domain:hostObj>
</domain:ns>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:create>
</create>
<extension>
<secDNS:create
xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1">
<secDNS:dsData>
<secDNS:keyTag>12345</secDNS:keyTag>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
</secDNS:create>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>
@@ -20,7 +20,7 @@
<secDNS:add>
<secDNS:dsData>
<secDNS:keyTag>12345</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
@@ -34,9 +34,9 @@
xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1">
<secDNS:dsData>
<secDNS:keyTag>12345</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
</secDNS:infData>
</extension>
@@ -33,9 +33,9 @@
xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1">
<secDNS:dsData>
<secDNS:keyTag>12345</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
</secDNS:infData>
<rgp:infData xmlns:rgp="urn:ietf:params:xml:ns:rgp-1.0">
@@ -14,17 +14,17 @@
<secDNS:rem>
<secDNS:dsData>
<secDNS:keyTag>12345</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
</secDNS:rem>
<secDNS:add>
<secDNS:dsData>
<secDNS:keyTag>12346</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
</secDNS:add>
</secDNS:update>
@@ -14,7 +14,7 @@
<secDNS:rem>
<secDNS:dsData>
<secDNS:keyTag>12345</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
</secDNS:dsData>
@@ -22,7 +22,7 @@
<secDNS:add>
<secDNS:dsData>
<secDNS:keyTag>12345</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
</secDNS:dsData>
@@ -14,9 +14,9 @@
<secDNS:rem>
<secDNS:dsData>
<secDNS:keyTag>12346</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
</secDNS:rem>
</secDNS:update>
@@ -17,7 +17,7 @@
<secDNS:add>
<secDNS:dsData>
<secDNS:keyTag>12346</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>38EC35D5B3A34B44C39B</secDNS:digest>
</secDNS:dsData>
@@ -16,7 +16,7 @@
<secDNS:add>
<secDNS:dsData>
<secDNS:keyTag>12346</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>38EC35D5B3A34B44C39B</secDNS:digest>
</secDNS:dsData>
@@ -13,7 +13,7 @@
xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1">
<secDNS:dsData>
<secDNS:keyTag>12345</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
@@ -14,9 +14,9 @@
<secDNS:add>
<secDNS:dsData>
<secDNS:keyTag>12346</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
</secDNS:add>
</secDNS:update>
@@ -14,9 +14,9 @@
<secDNS:rem>
<secDNS:dsData>
<secDNS:keyTag>12346</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
</secDNS:rem>
</secDNS:update>
@@ -23,7 +23,7 @@
<secDNS:maxSigLife>604800</secDNS:maxSigLife>
<secDNS:dsData>
<secDNS:keyTag>12345</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>49FD46E6C4B45C55D4AC</secDNS:digest>
</secDNS:dsData>
@@ -16,7 +16,7 @@
<secDNS:add>
<secDNS:dsData>
<secDNS:keyTag>12346</secDNS:keyTag>
<secDNS:alg>3</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>38EC35D5B3A34B44C39B</secDNS:digest>
</secDNS:dsData>
@@ -27,13 +27,13 @@
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>4</secDNS:keyTag>
<secDNS:alg>5</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>60485</secDNS:keyTag>
<secDNS:alg>5</secDNS:alg>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
@@ -0,0 +1,32 @@
<?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:period unit="y">1</domain:period>
<domain:ns>
<domain:hostObj>ns1.zdns.google</domain:hostObj>
<domain:hostObj>ns2.zdns.google</domain:hostObj>
<domain:hostObj>ns3.zdns.google</domain:hostObj>
<domain:hostObj>ns4.zdns.google</domain:hostObj>
</domain:ns>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:create>
</create>
<extension>
<secDNS:create xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1">
<secDNS:dsData>
<secDNS:keyTag>1</secDNS:keyTag>
<secDNS:alg>2</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
</secDNS:dsData>
</secDNS:create>
</extension>
<clTRID>RegistryTool</clTRID>
</command>
</epp>
@@ -26,8 +26,8 @@
<secDNS:dsData>
<secDNS:keyTag>4</secDNS:keyTag>
<secDNS:alg>5</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
</secDNS:add>
</secDNS:update>
@@ -32,8 +32,8 @@
<secDNS:dsData>
<secDNS:keyTag>7</secDNS:keyTag>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>6</secDNS:keyTag>
@@ -52,8 +52,8 @@
<secDNS:dsData>
<secDNS:keyTag>4</secDNS:keyTag>
<secDNS:alg>5</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
</secDNS:add>
</secDNS:update>
@@ -32,8 +32,8 @@
<secDNS:dsData>
<secDNS:keyTag>7</secDNS:keyTag>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>6</secDNS:keyTag>
@@ -52,8 +52,8 @@
<secDNS:dsData>
<secDNS:keyTag>4</secDNS:keyTag>
<secDNS:alg>5</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
</secDNS:add>
</secDNS:update>
@@ -19,8 +19,8 @@
<secDNS:dsData>
<secDNS:keyTag>7</secDNS:keyTag>
<secDNS:alg>8</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
<secDNS:dsData>
<secDNS:keyTag>6</secDNS:keyTag>
@@ -22,8 +22,8 @@
<secDNS:dsData>
<secDNS:keyTag>4</secDNS:keyTag>
<secDNS:alg>5</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
</secDNS:add>
</secDNS:update>
@@ -0,0 +1,31 @@
<?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:ns>
<domain:hostObj>ns2.zdns.google</domain:hostObj>
<domain:hostObj>ns3.zdns.google</domain:hostObj>
</domain:ns>
<domain:status s="serverDeleteProhibited"/>
</domain:add>
</domain:update>
</update>
<extension>
<secDNS:update xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1">
<secDNS:add>
<secDNS:dsData>
<secDNS:keyTag>1</secDNS:keyTag>
<secDNS:alg>2</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
</secDNS:dsData>
</secDNS:add>
</secDNS:update>
</extension>
<clTRID>RegistryTool</clTRID>
</command>
</epp>
@@ -30,8 +30,8 @@
<secDNS:dsData>
<secDNS:keyTag>1</secDNS:keyTag>
<secDNS:alg>1</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
</secDNS:add>
</secDNS:update>
@@ -31,8 +31,8 @@
<secDNS:dsData>
<secDNS:keyTag>1</secDNS:keyTag>
<secDNS:alg>1</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
</secDNS:add>
</secDNS:update>
@@ -29,8 +29,8 @@
<secDNS:dsData>
<secDNS:keyTag>1</secDNS:keyTag>
<secDNS:alg>1</secDNS:alg>
<secDNS:digestType>1</secDNS:digestType>
<secDNS:digest>A94A8FE5CCB19BA61C4C0873D391E987982FBBD3</secDNS:digest>
<secDNS:digestType>2</secDNS:digestType>
<secDNS:digest>D4B7D520E7BB5F0F67674A0CCEB1E3E0614B93C4F9E99B8383F6A1E4469DA50A</secDNS:digest>
</secDNS:dsData>
</secDNS:add>
</secDNS:update>
@@ -333,7 +333,7 @@
);
create table "FeatureFlag" (
feature_name text not null check ((feature_name in ('TEST_FEATURE','FEE_EXTENSION_1_DOT_0_IN_PROD','MINIMUM_DATASET_CONTACTS_OPTIONAL','MINIMUM_DATASET_CONTACTS_PROHIBITED','INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS','PROHIBIT_CONTACT_OBJECTS_ON_LOGIN'))),
feature_name text not null check ((feature_name in ('TEST_FEATURE','FEE_EXTENSION_1_DOT_0_IN_PROD','MINIMUM_DATASET_CONTACTS_OPTIONAL','MINIMUM_DATASET_CONTACTS_PROHIBITED','INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS','PROHIBIT_CONTACT_OBJECTS_ON_LOGIN','FORBID_INSECURE_ALGORITHMS_RFC_9904'))),
status hstore not null,
primary key (feature_name)
);
-1
View File
@@ -116,7 +116,6 @@ ext {
'com.google.oauth-client:google-oauth-client:[1.31.4,)',
'com.google.re2j:re2j:[1.6,)',
'com.google.truth:truth:[1.1.2,)',
'com.googlecode.json-simple:json-simple:[1.1.1,)',
'com.squareup.okhttp3:okhttp:[4.10.0,)',
'org.freemarker:freemarker:[2.3.32,)',
'com.ibm.icu:icu4j:[68.2,)',
+44 -25
View File
@@ -77,35 +77,54 @@ task extractSqlIntegrationTestSuite (type: Copy) {
}
}
// TODO(weiminyu): inherit from FilteringTest (defined in :core).
task sqlIntegrationTest(type: Test) {
// Use JUnit 5 Platform for local tests since the suite has been migrated to @Suite.
// However, Kokoro runs cross-version compatibility tests against older, deployed nomulus
// artifacts that were compiled using the legacy JUnit 4 @RunWith(JUnitPlatform.class) runner.
// We must fall back to the classic JUnit 4 runner for those remote environments to prevent
// NoClassDefFoundError and test discovery failures.
// TODO: Remove this fallback and use useJUnitPlatform() unconditionally once all deployed
// environments (sandbox and production) are running a Nomulus release built after the
// JUnit 5 @Suite migration.
if (nomulus_env == USE_LOCAL) {
useJUnitPlatform()
} else {
useJUnit()
task removeUnpackedTests {
doLast {
delete file(unpackedTestDir)
}
}
task sqlIntegrationTestLegacy(type: Test) {
useJUnit()
testClassesDirs = files(unpackedTestDir)
classpath = configurations.testRuntimeClasspath
include 'google/registry/schema/integration/SqlIntegrationTestSuite.*'
dependsOn extractSqlIntegrationTestSuite
finalizedBy tasks.create('removeUnpackedTests') {
doLast {
delete file(unpackedTestDir)
}
}
// Disable incremental build/test since Gradle cannot detect changes
// in dependencies on its own. Will not fix since this test is typically
// run once (in presubmit or ci tests).
// Prevent build failures when evaluating newer JUnit 5 artifacts that have no JUnit 4 tests.
failOnNoDiscoveredTests = false
outputs.upToDateWhen { false }
}
task sqlIntegrationTestModern(type: Test) {
useJUnitPlatform()
testClassesDirs = files(unpackedTestDir)
classpath = configurations.testRuntimeClasspath
include 'google/registry/schema/integration/SqlIntegrationTestSuite.*'
dependsOn extractSqlIntegrationTestSuite
// Prevent build failures when evaluating older JUnit 4 artifacts that have no JUnit 5 tests.
failOnNoDiscoveredTests = false
outputs.upToDateWhen { false }
}
// TODO(weiminyu): inherit from FilteringTest (defined in :core).
task sqlIntegrationTest {
// Kokoro runs cross-version compatibility tests against both older deployed artifacts
// (which use the legacy JUnit 4 @RunWith wrapper) and newer deployed artifacts (which use
// the JUnit 5 @Suite annotation). We cannot statically configure the test runner because
// we do not know which runner the downloaded artifact expects, nor can we inject the
// modern junit-platform-suite engine dependency without causing a classpath collision
// with older embedded engine APIs.
// To solve this, we execute both test runners sequentially and ignore "no tests discovered"
// errors. The runner compatible with the artifact will discover and execute the tests,
// while the incompatible runner will safely no-op.
//
// TODO: Remove this split fallback once all deployed environments (sandbox, qa, production)
// are running a Nomulus release built after the JUnit 5 @Suite migration.
// When that happens:
// 1. Delete the 'sqlIntegrationTestLegacy' and 'sqlIntegrationTestModern' tasks entirely.
// 2. Change this task back to: task sqlIntegrationTest(type: Test) { ... }
// 3. Add 'useJUnitPlatform()' unconditionally inside it.
// 4. Move the 'testClassesDirs', 'classpath', 'include', and 'outputs.upToDateWhen'
// configurations back into it.
dependsOn sqlIntegrationTestLegacy, sqlIntegrationTestModern
finalizedBy removeUnpackedTests
}
+1 -2
View File
@@ -55,8 +55,7 @@ configurations {
matching {
it.name in ['runtimeClasspath', 'compileClasspath']
}.all {
// JUnit is from org.apache.beam:beam-runners-google-cloud-dataflow-java,
// and json-simple.
// JUnit is from org.apache.beam:beam-runners-google-cloud-dataflow-java
exclude group: 'junit'
// Mockito is from org.apache.beam:beam-runners-google-cloud-dataflow-java
// See https://issues.apache.org/jira/browse/BEAM-8862
+1
View File
@@ -148,4 +148,5 @@ tasks.register('getEndpoints', Exec) {
commandLine './get-endpoints.py', "${rootProject.gcpProject}"
}
project.build.dependsOn(tasks.named('buildNomulusImage'))
rootProject.deploy.dependsOn(tasks.named('deployNomulus'))
-1
View File
@@ -162,7 +162,6 @@ com.google.protobuf:protobuf-java:4.33.2=annotationProcessor,testAnnotationProce
com.google.protobuf:protobuf-java:4.35.0=deploy_jar,runtimeClasspath,testRuntimeClasspath
com.google.re2j:re2j:1.8=deploy_jar,runtimeClasspath,testRuntimeClasspath
com.google.truth:truth:1.4.5=deploy_jar,runtimeClasspath,testRuntimeClasspath
com.googlecode.json-simple:json-simple:1.1.1=deploy_jar,runtimeClasspath,testRuntimeClasspath
com.ibm.icu:icu4j:73.2=deploy_jar,runtimeClasspath,testRuntimeClasspath
com.lmax:disruptor:3.4.2=deploy_jar,runtimeClasspath,testRuntimeClasspath
com.puppycrawl.tools:checkstyle:10.24.0=checkstyle
@@ -177,6 +177,8 @@ public class EppMessage {
new StreamSource(readResource(path + "launch.xsd")),
};
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
schemaFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
eppSchema = schemaFactory.newSchema(sources);
} catch (SAXException | IOException e) {
throw new ExceptionInInitializerError(e);
@@ -271,7 +273,9 @@ public class EppMessage {
return null;
}
try {
Transformer transformer = TransformerFactory.newInstance().newTransformer();
TransformerFactory tf = TransformerFactory.newInstance();
tf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
Transformer transformer = tf.newTransformer();
StreamResult result = new StreamResult(new StringWriter());
DOMSource source = new DOMSource(xml);
transformer.transform(source, result);
@@ -291,7 +295,9 @@ public class EppMessage {
*/
public static byte[] xmlDocToByteArray(Document xml) throws EppClientException {
try {
Transformer transformer = TransformerFactory.newInstance().newTransformer();
TransformerFactory tf = TransformerFactory.newInstance();
tf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
Transformer transformer = tf.newTransformer();
StreamResult result = new StreamResult(new StringWriter());
DOMSource source = new DOMSource(xml);
transformer.transform(source, result);
@@ -383,6 +383,10 @@ public class ProxyModule {
@Singleton
@Provides
static Random provideRandom() {
// Note: We intentionally use java.util.Random instead of SecureRandom here.
// This Random instance is injected into the hot path of the proxy exclusively for
// stochastic metrics sampling. Using SecureRandom would introduce severe lock
// contention and exhaust the system entropy pool under high load, causing DoS.
return new Random();
}
+6 -6
View File
@@ -38,7 +38,7 @@ steps:
- -c
- |
set -e
for env in crash; do
for env in crash sandbox; do
config_file="release/clouddeploy/${env}-config.yaml"
if [ -f "$config_file" ]; then
echo "Extracting checks from $config_file..."
@@ -52,8 +52,8 @@ steps:
' "$config_file" > checks.tmp
# Insert the checks where the placeholder is located and remove the placeholder
sed -i '/stableDeploymentAlertPolicyChecks/r checks.tmp' release/clouddeploy/delivery-pipeline.yaml
sed -i '/stableDeploymentAlertPolicyChecks/d' release/clouddeploy/delivery-pipeline.yaml
sed -i "/${env}StableDeploymentAlertPolicyChecks/r checks.tmp" release/clouddeploy/delivery-pipeline.yaml
sed -i "/${env}StableDeploymentAlertPolicyChecks/d" release/clouddeploy/delivery-pipeline.yaml
rm -f checks.tmp
# Extract only the indented block under partialDeploymentAlertPolicyChecks.
@@ -68,8 +68,8 @@ steps:
' "$config_file" > partial_checks.tmp
# Insert the checks where the placeholder is located and remove the placeholder
sed -i '/partialDeploymentAlertPolicyChecks/r partial_checks.tmp' release/clouddeploy/delivery-pipeline.yaml
sed -i '/partialDeploymentAlertPolicyChecks/d' release/clouddeploy/delivery-pipeline.yaml
sed -i "/${env}PartialDeploymentAlertPolicyChecks/r partial_checks.tmp" release/clouddeploy/delivery-pipeline.yaml
sed -i "/${env}PartialDeploymentAlertPolicyChecks/d" release/clouddeploy/delivery-pipeline.yaml
rm -f partial_checks.tmp
fi
@@ -95,7 +95,7 @@ steps:
- -c
- |
set -e
for env in crash; do
for env in crash sandbox; do
target_file="release/clouddeploy/${env}-target.yaml"
if [ -f "$target_file" ]; then
echo "Applying target $target_file..."
+49 -3
View File
@@ -19,7 +19,7 @@ serialPipeline:
duration: 600s
googleCloud:
alertPolicyChecks:
partialDeploymentAlertPolicyChecks
crashPartialDeploymentAlertPolicyChecks
- phaseId: "canary-5"
profiles: ["crash-partial-phase-5"]
percentage: 50
@@ -28,7 +28,7 @@ serialPipeline:
duration: 600s
googleCloud:
alertPolicyChecks:
partialDeploymentAlertPolicyChecks
crashPartialDeploymentAlertPolicyChecks
- phaseId: "stable"
profiles: ["crash"]
percentage: 100
@@ -51,4 +51,50 @@ serialPipeline:
duration: 600s
googleCloud:
alertPolicyChecks:
stableDeploymentAlertPolicyChecks
crashStableDeploymentAlertPolicyChecks
- targetId: sandbox
strategy:
canary:
customCanaryDeployment:
phaseConfigs:
- phaseId: "canary-1"
profiles: ["sandbox-partial-phase-1"]
percentage: 10
analysis:
# 10 minutes.
duration: 600s
googleCloud:
alertPolicyChecks:
sandboxPartialDeploymentAlertPolicyChecks
- phaseId: "canary-5"
profiles: ["sandbox-partial-phase-5"]
percentage: 50
analysis:
# 10 minutes.
duration: 600s
googleCloud:
alertPolicyChecks:
sandboxPartialDeploymentAlertPolicyChecks
- phaseId: "stable"
profiles: ["sandbox"]
percentage: 100
postdeploy:
tasks:
- type: container
image: gcr.io/google.com/cloudsdktool/google-cloud-cli:stable
env:
DEPLOYED_IMAGE: ${{ deploy_params['deployed_image'] }}
BASE_IMAGE: ${{ deploy_params['base_image'] }}
TARGET_ID: ${{ target.id }}
command: ["/bin/bash"]
args:
- "-c"
- |
gcloud artifacts docker tags add $DEPLOYED_IMAGE \
${BASE_IMAGE}:live-cd-${TARGET_ID}
analysis:
# 10 minutes.
duration: 600s
googleCloud:
alertPolicyChecks:
sandboxStableDeploymentAlertPolicyChecks

Some files were not shown because too many files have changed in this diff Show More