diff --git a/core/src/main/java/google/registry/tools/CreateOrUpdateDomainCommand.java b/core/src/main/java/google/registry/tools/CreateOrUpdateDomainCommand.java index 3de1ee37c..2696fbeca 100644 --- a/core/src/main/java/google/registry/tools/CreateOrUpdateDomainCommand.java +++ b/core/src/main/java/google/registry/tools/CreateOrUpdateDomainCommand.java @@ -15,20 +15,11 @@ package google.registry.tools; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.ImmutableList.toImmutableList; import static google.registry.util.CollectionUtils.findDuplicates; -import com.beust.jcommander.IStringConverter; import com.beust.jcommander.Parameter; -import com.google.auto.value.AutoValue; -import com.google.common.base.Ascii; -import com.google.common.base.CharMatcher; import com.google.common.base.Joiner; -import com.google.common.base.Splitter; import com.google.common.collect.ImmutableSet; -import com.google.common.io.BaseEncoding; -import com.google.template.soy.data.SoyListData; -import com.google.template.soy.data.SoyMapData; import google.registry.tools.params.NameserversParameter; import java.util.ArrayList; import java.util.HashSet; @@ -80,77 +71,15 @@ abstract class CreateOrUpdateDomainCommand extends MutatingEppToolCommand { String password; @Parameter( - names = "--ds_records", - description = - "Comma-separated list of DS records. Each DS record is given as " - + " , in order, as it appears in the Zonefile.", - converter = DsRecordConverter.class - ) + names = "--ds_records", + description = + "Comma-separated list of DS records. Each DS record is given as " + + " , in order, as it appears in the Zonefile.", + converter = DsRecord.Converter.class) List dsRecords = new ArrayList<>(); Set domains; - @AutoValue - abstract static class DsRecord { - private static final Splitter SPLITTER = - Splitter.on(CharMatcher.whitespace()).omitEmptyStrings(); - - public abstract int keyTag(); - public abstract int alg(); - public abstract int digestType(); - public abstract String digest(); - - private static DsRecord create(int keyTag, int alg, int digestType, String digest) { - digest = Ascii.toUpperCase(digest); - checkArgument( - BaseEncoding.base16().canDecode(digest), - "digest should be even-lengthed hex, but is %s (length %s)", - digest, - digest.length()); - return new AutoValue_CreateOrUpdateDomainCommand_DsRecord(keyTag, alg, digestType, digest); - } - - /** - * Parses a string representation of the DS record. - * - *

The string format accepted is "[keyTag] [alg] [digestType] [digest]" (i.e., the 4 - * arguments separated by any number of spaces, as it appears in the Zone file) - */ - public static DsRecord parse(String dsRecord) { - List elements = SPLITTER.splitToList(dsRecord); - checkArgument( - elements.size() == 4, - "dsRecord %s should have 4 parts, but has %s", - dsRecord, - elements.size()); - return DsRecord.create( - Integer.parseUnsignedInt(elements.get(0)), - Integer.parseUnsignedInt(elements.get(1)), - Integer.parseUnsignedInt(elements.get(2)), - elements.get(3)); - } - - public SoyMapData toSoyData() { - return new SoyMapData( - "keyTag", keyTag(), - "alg", alg(), - "digestType", digestType(), - "digest", digest()); - } - - public static SoyListData convertToSoy(List dsRecords) { - return new SoyListData( - dsRecords.stream().map(DsRecord::toSoyData).collect(toImmutableList())); - } - } - - public static class DsRecordConverter implements IStringConverter { - @Override - public DsRecord convert(String dsRecord) { - return DsRecord.parse(dsRecord); - } - } - @Override protected void initEppToolCommand() throws Exception { checkArgument(nameservers.size() <= 13, "There can be at most 13 nameservers."); diff --git a/core/src/main/java/google/registry/tools/DsRecord.java b/core/src/main/java/google/registry/tools/DsRecord.java new file mode 100644 index 000000000..ebbfdfc97 --- /dev/null +++ b/core/src/main/java/google/registry/tools/DsRecord.java @@ -0,0 +1,90 @@ +// Copyright 2017 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package google.registry.tools; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.ImmutableList.toImmutableList; + +import com.beust.jcommander.IStringConverter; +import com.google.auto.value.AutoValue; +import com.google.common.base.Ascii; +import com.google.common.base.CharMatcher; +import com.google.common.base.Splitter; +import com.google.common.io.BaseEncoding; +import com.google.template.soy.data.SoyListData; +import com.google.template.soy.data.SoyMapData; +import java.util.List; + +@AutoValue +abstract class DsRecord { + private static final Splitter SPLITTER = Splitter.on(CharMatcher.whitespace()).omitEmptyStrings(); + + public abstract int keyTag(); + + public abstract int alg(); + + public abstract int digestType(); + + public abstract String digest(); + + private static DsRecord create(int keyTag, int alg, int digestType, String digest) { + digest = Ascii.toUpperCase(digest); + checkArgument( + BaseEncoding.base16().canDecode(digest), + "digest should be even-lengthed hex, but is %s (length %s)", + digest, + digest.length()); + return new AutoValue_DsRecord(keyTag, alg, digestType, digest); + } + + /** + * Parses a string representation of the DS record. + * + *

The string format accepted is "[keyTag] [alg] [digestType] [digest]" (i.e., the 4 arguments + * separated by any number of spaces, as it appears in the Zone file) + */ + public static DsRecord parse(String dsRecord) { + List elements = SPLITTER.splitToList(dsRecord); + checkArgument( + elements.size() == 4, + "dsRecord %s should have 4 parts, but has %s", + dsRecord, + elements.size()); + return DsRecord.create( + Integer.parseUnsignedInt(elements.get(0)), + Integer.parseUnsignedInt(elements.get(1)), + Integer.parseUnsignedInt(elements.get(2)), + elements.get(3)); + } + + public SoyMapData toSoyData() { + return new SoyMapData( + "keyTag", keyTag(), + "alg", alg(), + "digestType", digestType(), + "digest", digest()); + } + + public static SoyListData convertToSoy(List dsRecords) { + return new SoyListData(dsRecords.stream().map(DsRecord::toSoyData).collect(toImmutableList())); + } + + public static class Converter implements IStringConverter { + @Override + public DsRecord convert(String dsRecord) { + return DsRecord.parse(dsRecord); + } + } +} diff --git a/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java b/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java index 2eb2c51cc..5a202af86 100644 --- a/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java +++ b/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java @@ -15,7 +15,7 @@ package google.registry.tools; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Strings.nullToEmpty; +import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.Sets.difference; import static google.registry.model.EppResourceUtils.checkResourcesExist; import static google.registry.model.EppResourceUtils.loadByForeignKey; @@ -26,9 +26,11 @@ import static org.joda.time.DateTimeZone.UTC; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; +import com.google.template.soy.data.SoyListData; import com.google.template.soy.data.SoyMapData; import google.registry.model.domain.DomainBase; import google.registry.model.domain.secdns.DelegationSignerData; @@ -37,14 +39,10 @@ import google.registry.model.host.HostResource; import google.registry.tools.soy.UniformRapidSuspensionSoyInfo; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.Set; import javax.xml.bind.annotation.adapters.HexBinaryAdapter; import org.joda.time.DateTime; -import org.json.simple.JSONArray; -import org.json.simple.JSONValue; -import org.json.simple.parser.ParseException; /** A command to suspend a domain for the Uniform Rapid Suspension process. */ @Parameters(separators = " =", @@ -59,9 +57,6 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand { /** Client id that made this change. Only recorded in the history entry. **/ private static final String CLIENT_ID = "CharlestonRoad"; - private static final ImmutableSet DSDATA_FIELDS = - ImmutableSet.of("keyTag", "alg", "digestType", "digest"); - @Parameter( names = {"-n", "--domain_name"}, description = "Domain to suspend.", @@ -76,10 +71,12 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand { @Parameter( names = {"-s", "--dsdata"}, - description = "Comma-delimited set of dsdata to replace the current dsdata on the domain, " - + "where each dsdata is represented as a JSON object with fields 'keyTag', 'alg', " - + "'digestType' and 'digest'.") - private String newDsData; + description = + "Comma-delimited set of dsdata to replace the current dsdata on the domain, " + + "Each DS record is given as , in order, as it " + + "appears in the Zonefile.", + converter = DsRecord.Converter.class) + private List newDsData; @Parameter( names = {"-p", "--locks_to_preserve"}, @@ -88,6 +85,13 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand { + "locks: serverDeleteProhibited, serverTransferProhibited, serverUpdateProhibited") private List locksToPreserve = new ArrayList<>(); + @Parameter( + names = {"--restore_client_hold"}, + description = + "Restores a CLIENT_HOLD status that was previously removed for a URS suspension (only " + + "valid with --undo).") + private boolean restoreClientHold; + @Parameter( names = {"--undo"}, description = "Flag indicating that is is an undo command, which removes locks.") @@ -100,28 +104,16 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand { ImmutableSortedSet existingNameservers; /** Set of existing dsdata jsons that need to be restored during undo, sorted for nicer output. */ - ImmutableSortedSet existingDsData; + ImmutableList> existingDsData; + + /** Set of status values to remove. */ + ImmutableSet removeStatuses; @Override protected void initMutatingEppToolCommand() { superuser = true; DateTime now = DateTime.now(UTC); ImmutableSet newHostsSet = ImmutableSet.copyOf(newHosts); - ImmutableSet.Builder> newDsDataBuilder = new ImmutableSet.Builder<>(); - try { - // Add brackets around newDsData to convert it to a parsable JSON array. - String jsonArrayString = String.format("[%s]", nullToEmpty(newDsData)); - for (Object dsData : (JSONArray) JSONValue.parseWithException(jsonArrayString)) { - @SuppressWarnings("unchecked") - Map dsDataJson = (Map) dsData; - checkArgument( - dsDataJson.keySet().equals(DSDATA_FIELDS), - "Incorrect fields on --dsdata JSON: " + JSONValue.toJSONString(dsDataJson)); - newDsDataBuilder.add(dsDataJson); - } - } catch (ClassCastException | ParseException e) { - throw new IllegalArgumentException("Invalid --dsdata JSON", e); - } Optional domain = loadByForeignKey(DomainBase.class, domainName, now); checkArgumentPresent(domain, "Domain '%s' does not exist or is deleted", domainName); Set missingHosts = @@ -133,18 +125,39 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand { existingNameservers = getExistingNameservers(domain.get()); existingLocks = getExistingLocks(domain.get()); existingDsData = getExistingDsData(domain.get()); + removeStatuses = + (hasClientHold(domain.get()) && !undo) + ? ImmutableSet.of(StatusValue.CLIENT_HOLD.getXmlName()) + : ImmutableSet.of(); + ImmutableSet statusesToApply; + if (undo) { + statusesToApply = + restoreClientHold + ? ImmutableSet.of(StatusValue.CLIENT_HOLD.getXmlName()) + : ImmutableSet.of(); + } else { + statusesToApply = URS_LOCKS; + } setSoyTemplate( UniformRapidSuspensionSoyInfo.getInstance(), UniformRapidSuspensionSoyInfo.UNIFORMRAPIDSUSPENSION); - addSoyRecord(CLIENT_ID, new SoyMapData( - "domainName", domainName, - "hostsToAdd", difference(newHostsSet, existingNameservers), - "hostsToRemove", difference(existingNameservers, newHostsSet), - "locksToApply", undo ? ImmutableSet.of() : URS_LOCKS, - "locksToRemove", - undo ? difference(URS_LOCKS, ImmutableSet.copyOf(locksToPreserve)) : ImmutableSet.of(), - "newDsData", newDsDataBuilder.build(), - "reason", (undo ? "Undo " : "") + "Uniform Rapid Suspension")); + addSoyRecord( + CLIENT_ID, + new SoyMapData( + "domainName", + domainName, + "hostsToAdd", + difference(newHostsSet, existingNameservers), + "hostsToRemove", + difference(existingNameservers, newHostsSet), + "statusesToApply", + statusesToApply, + "statusesToRemove", + undo ? difference(URS_LOCKS, ImmutableSet.copyOf(locksToPreserve)) : removeStatuses, + "newDsData", + newDsData != null ? DsRecord.convertToSoy(newDsData) : new SoyListData(), + "reason", + (undo ? "Undo " : "") + "Uniform Rapid Suspension")); } private ImmutableSortedSet getExistingNameservers(DomainBase domain) { @@ -165,15 +178,25 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand { return locks.build(); } - private ImmutableSortedSet getExistingDsData(DomainBase domain) { - ImmutableSortedSet.Builder dsDataJsons = ImmutableSortedSet.naturalOrder(); + private boolean hasClientHold(DomainBase domain) { + for (StatusValue status : domain.getStatusValues()) { + if (status == StatusValue.CLIENT_HOLD) { + return true; + } + } + return false; + } + + private ImmutableList> getExistingDsData(DomainBase domain) { + ImmutableList.Builder> dsDataJsons = new ImmutableList.Builder(); HexBinaryAdapter hexBinaryAdapter = new HexBinaryAdapter(); for (DelegationSignerData dsData : domain.getDsData()) { - dsDataJsons.add(JSONValue.toJSONString(ImmutableMap.of( - "keyTag", dsData.getKeyTag(), - "algorithm", dsData.getAlgorithm(), - "digestType", dsData.getDigestType(), - "digest", hexBinaryAdapter.marshal(dsData.getDigest())))); + dsDataJsons.add( + ImmutableMap.of( + "keyTag", dsData.getKeyTag(), + "algorithm", dsData.getAlgorithm(), + "digestType", dsData.getDigestType(), + "digest", hexBinaryAdapter.marshal(dsData.getDigest()))); } return dsDataJsons.build(); } @@ -194,8 +217,23 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand { if (!existingLocks.isEmpty()) { undoBuilder.append(" --locks_to_preserve ").append(Joiner.on(',').join(existingLocks)); } + if (removeStatuses.contains(StatusValue.CLIENT_HOLD.getXmlName())) { + undoBuilder.append(" --restore_client_hold"); + } if (!existingDsData.isEmpty()) { - undoBuilder.append(" --dsdata ").append(Joiner.on(',').join(existingDsData)); + ImmutableList formattedDsRecords = + existingDsData.stream() + .map( + rec -> + String.format( + "%s %s %s %s", + rec.get("keyTag"), + rec.get("algorithm"), + rec.get("digestType"), + rec.get("digest"))) + .sorted() + .collect(toImmutableList()); + undoBuilder.append(" --dsdata ").append(Joiner.on(',').join(formattedDsRecords)); } return undoBuilder.toString(); } diff --git a/core/src/main/java/google/registry/tools/UpdateDomainCommand.java b/core/src/main/java/google/registry/tools/UpdateDomainCommand.java index 6c5bb0ec7..52834e107 100644 --- a/core/src/main/java/google/registry/tools/UpdateDomainCommand.java +++ b/core/src/main/java/google/registry/tools/UpdateDomainCommand.java @@ -76,10 +76,10 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand { private List addStatuses = new ArrayList<>(); @Parameter( - names = "--add_ds_records", - description = "DS records to add. Cannot be set if --ds_records or --clear_ds_records is set.", - converter = DsRecordConverter.class - ) + names = "--add_ds_records", + description = + "DS records to add. Cannot be set if --ds_records or --clear_ds_records is set.", + converter = DsRecord.Converter.class) private List addDsRecords = new ArrayList<>(); @Parameter( @@ -110,11 +110,10 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand { private List removeStatuses = new ArrayList<>(); @Parameter( - names = "--remove_ds_records", - description = - "DS records to remove. Cannot be set if --ds_records or --clear_ds_records is set.", - converter = DsRecordConverter.class - ) + names = "--remove_ds_records", + description = + "DS records to remove. Cannot be set if --ds_records or --clear_ds_records is set.", + converter = DsRecord.Converter.class) private List removeDsRecords = new ArrayList<>(); @Parameter( diff --git a/core/src/main/resources/google/registry/tools/soy/UniformRapidSuspension.soy b/core/src/main/resources/google/registry/tools/soy/UniformRapidSuspension.soy index 153c2fde2..dab159e5d 100644 --- a/core/src/main/resources/google/registry/tools/soy/UniformRapidSuspension.soy +++ b/core/src/main/resources/google/registry/tools/soy/UniformRapidSuspension.soy @@ -21,8 +21,8 @@ {@param domainName: string} {@param hostsToAdd: list} {@param hostsToRemove: list} -{@param locksToApply: list} -{@param locksToRemove: list} +{@param statusesToApply: list} +{@param statusesToRemove: list} {@param newDsData: list<[keyTag:int, alg:int, digestType:int, digest:string]>} {@param reason: string} @@ -39,7 +39,7 @@ {/for} {/if} - {for $la in $locksToApply} + {for $la in $statusesToApply} {/for} @@ -51,7 +51,7 @@ {/for} {/if} - {for $lr in $locksToRemove} + {for $lr in $statusesToRemove} {/for} diff --git a/core/src/test/java/google/registry/tools/UniformRapidSuspensionCommandTest.java b/core/src/test/java/google/registry/tools/UniformRapidSuspensionCommandTest.java index 15e73ddb6..ca9db3740 100644 --- a/core/src/test/java/google/registry/tools/UniformRapidSuspensionCommandTest.java +++ b/core/src/test/java/google/registry/tools/UniformRapidSuspensionCommandTest.java @@ -71,7 +71,7 @@ class UniformRapidSuspensionCommandTest runCommandForced( "--domain_name=evil.tld", "--hosts=urs1.example.com,urs2.example.com", - "--dsdata={\"keyTag\":1,\"alg\":1,\"digestType\":1,\"digest\":\"abc\"}"); + "--dsdata=1 1 1 abcd"); eppVerifier .expectClientId("CharlestonRoad") .expectSuperuser() @@ -79,10 +79,9 @@ class UniformRapidSuspensionCommandTest assertInStdout("uniform_rapid_suspension --undo"); assertInStdout("--domain_name evil.tld"); assertInStdout("--hosts ns1.example.com,ns2.example.com"); - assertInStdout("--dsdata " - + "{\"keyTag\":1,\"algorithm\":2,\"digestType\":3,\"digest\":\"DEAD\"}," - + "{\"keyTag\":4,\"algorithm\":5,\"digestType\":6,\"digest\":\"BEEF\"}"); + assertInStdout("--dsdata 1 2 3 DEAD,4 5 6 BEEF"); assertNotInStdout("--locks_to_preserve"); + assertNotInStdout("--restore_client_hold"); } @Test @@ -122,6 +121,29 @@ class UniformRapidSuspensionCommandTest assertInStdout("--locks_to_preserve serverDeleteProhibited"); } + @Test + void testCommand_removeClientHold() throws Exception { + persistResource( + newDomainBase("evil.tld") + .asBuilder() + .addStatusValue(StatusValue.CLIENT_HOLD) + .addNameserver(ns1.createVKey()) + .addNameserver(ns2.createVKey()) + .build()); + runCommandForced( + "--domain_name=evil.tld", + "--hosts=urs1.example.com,urs2.example.com", + "--dsdata=1 1 1 abcd"); + eppVerifier + .expectClientId("CharlestonRoad") + .expectSuperuser() + .verifySent("uniform_rapid_suspension_with_client_hold.xml"); + assertInStdout("uniform_rapid_suspension --undo"); + assertInStdout("--domain_name evil.tld"); + assertInStdout("--hosts ns1.example.com,ns2.example.com"); + assertInStdout("--restore_client_hold"); + } + @Test void testUndo_removesLocksReplacesHostsAndDsData() throws Exception { persistDomainWithHosts(urs1, urs2); @@ -149,6 +171,21 @@ class UniformRapidSuspensionCommandTest assertNotInStdout("--undo"); // Undo shouldn't print a new undo command. } + @Test + void testUndo_restoresClientHolds() throws Exception { + persistDomainWithHosts(urs1, urs2); + runCommandForced( + "--domain_name=evil.tld", + "--undo", + "--hosts=ns1.example.com,ns2.example.com", + "--restore_client_hold"); + eppVerifier + .expectClientId("CharlestonRoad") + .expectSuperuser() + .verifySent("uniform_rapid_suspension_undo_client_hold.xml"); + assertNotInStdout("--undo"); // Undo shouldn't print a new undo command. + } + @Test void testFailure_locksToPreserveWithoutUndo() { persistActiveDomain("evil.tld"); @@ -177,11 +214,10 @@ class UniformRapidSuspensionCommandTest IllegalArgumentException thrown = assertThrows( IllegalArgumentException.class, - () -> - runCommandForced( - "--domain_name=evil.tld", - "--dsdata={\"keyTag\":1,\"alg\":1,\"digestType\":1,\"digest\":\"abc\",\"foo\":1}")); - assertThat(thrown).hasMessageThat().contains("Incorrect fields on --dsdata JSON"); + () -> runCommandForced("--domain_name=evil.tld", "--dsdata=1 1 1 abc 1")); + assertThat(thrown) + .hasMessageThat() + .contains("dsRecord 1 1 1 abc 1 should have 4 parts, but has 5"); } @Test @@ -190,11 +226,8 @@ class UniformRapidSuspensionCommandTest IllegalArgumentException thrown = assertThrows( IllegalArgumentException.class, - () -> - runCommandForced( - "--domain_name=evil.tld", - "--dsdata={\"keyTag\":1,\"alg\":1,\"digestType\":1}")); - assertThat(thrown).hasMessageThat().contains("Incorrect fields on --dsdata JSON"); + () -> runCommandForced("--domain_name=evil.tld", "--dsdata=1 1 1")); + assertThat(thrown).hasMessageThat().contains("dsRecord 1 1 1 should have 4 parts, but has 3"); } @Test @@ -203,7 +236,7 @@ class UniformRapidSuspensionCommandTest IllegalArgumentException thrown = assertThrows( IllegalArgumentException.class, - () -> runCommandForced("--domain_name=evil.tld", "--dsdata=[1,2,3]")); - assertThat(thrown).hasMessageThat().contains("Invalid --dsdata JSON"); + () -> runCommandForced("--domain_name=evil.tld", "--dsdata=1,2,3")); + assertThat(thrown).hasMessageThat().contains("dsRecord 1 should have 4 parts, but has 1"); } } diff --git a/core/src/test/resources/google/registry/tools/server/uniform_rapid_suspension.xml b/core/src/test/resources/google/registry/tools/server/uniform_rapid_suspension.xml index ecaf9a6d0..57c08b20c 100644 --- a/core/src/test/resources/google/registry/tools/server/uniform_rapid_suspension.xml +++ b/core/src/test/resources/google/registry/tools/server/uniform_rapid_suspension.xml @@ -31,7 +31,7 @@ 1 1 1 - abc + ABCD diff --git a/core/src/test/resources/google/registry/tools/server/uniform_rapid_suspension_undo_client_hold.xml b/core/src/test/resources/google/registry/tools/server/uniform_rapid_suspension_undo_client_hold.xml new file mode 100644 index 000000000..c6acf397f --- /dev/null +++ b/core/src/test/resources/google/registry/tools/server/uniform_rapid_suspension_undo_client_hold.xml @@ -0,0 +1,38 @@ + + + + + + evil.tld + + + ns1.example.com + ns2.example.com + + + + + + urs1.example.com + urs2.example.com + + + + + + + + + + + true + + + + Undo Uniform Rapid Suspension + false + + + RegistryTool + + diff --git a/core/src/test/resources/google/registry/tools/server/uniform_rapid_suspension_with_client_hold.xml b/core/src/test/resources/google/registry/tools/server/uniform_rapid_suspension_with_client_hold.xml new file mode 100644 index 000000000..9c2461688 --- /dev/null +++ b/core/src/test/resources/google/registry/tools/server/uniform_rapid_suspension_with_client_hold.xml @@ -0,0 +1,46 @@ + + + + + + evil.tld + + + urs1.example.com + urs2.example.com + + + + + + + + ns2.example.com + ns1.example.com + + + + + + + + + true + + + + 1 + 1 + 1 + ABCD + + + + + Uniform Rapid Suspension + false + + + RegistryTool + +