From 24c11dfdca3034f9d1ab99c3090de935739531a9 Mon Sep 17 00:00:00 2001 From: Corey Goldfeder Date: Wed, 18 May 2016 08:09:21 -0700 Subject: [PATCH] Add support for dsdata to the URS tool ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=122631925 --- .../tools/UniformRapidSuspensionCommand.java | 61 ++++++++- .../tools/soy/UniformRapidSuspension.soy | 18 +++ .../UniformRapidSuspensionCommandTest.java | 118 +++++++++++------- .../testdata/uniform_rapid_suspension.xml | 13 ++ ...uniform_rapid_suspension_existing_host.xml | 5 + .../uniform_rapid_suspension_undo.xml | 5 + ...uniform_rapid_suspension_undo_preserve.xml | 5 + 7 files changed, 179 insertions(+), 46 deletions(-) diff --git a/java/google/registry/tools/UniformRapidSuspensionCommand.java b/java/google/registry/tools/UniformRapidSuspensionCommand.java index 27577435e..9b6eabcaa 100644 --- a/java/google/registry/tools/UniformRapidSuspensionCommand.java +++ b/java/google/registry/tools/UniformRapidSuspensionCommand.java @@ -15,12 +15,14 @@ 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.Sets.difference; import static google.registry.model.EppResourceUtils.checkResourcesExist; import static google.registry.model.EppResourceUtils.loadByUniqueId; import static org.joda.time.DateTimeZone.UTC; import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import com.google.template.soy.data.SoyMapData; @@ -30,17 +32,24 @@ import com.beust.jcommander.Parameters; import com.googlecode.objectify.Ref; import google.registry.model.domain.DomainResource; +import google.registry.model.domain.secdns.DelegationSignerData; import google.registry.model.eppcommon.StatusValue; import google.registry.model.host.HostResource; import google.registry.tools.Command.GtechCommand; import google.registry.tools.soy.UniformRapidSuspensionSoyInfo; import org.joda.time.DateTime; +import org.json.simple.JSONArray; +import org.json.simple.JSONValue; +import org.json.simple.parser.ParseException; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Set; +import javax.xml.bind.annotation.adapters.HexBinaryAdapter; + /** A command to suspend a domain for the Uniform Rapid Suspension process. */ @Parameters(separators = " =", commandDescription = "Suspend a domain for Uniform Rapid Suspension.") @@ -54,6 +63,9 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand impleme /** 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.", @@ -67,7 +79,14 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand impleme private List newHosts = new ArrayList<>(); @Parameter( - names = {"-p", "--preserve"}, + 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; + + @Parameter( + names = {"-p", "--locks_to_preserve"}, description = "Comma-delimited set of locks to preserve (only valid with --undo). " + "Valid locks: serverDeleteProhibited, serverTransferProhibited, serverUpdateProhibited") private List locksToPreserve = new ArrayList<>(); @@ -83,11 +102,29 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand impleme /** Set of existing nameservers that need to be restored during undo, sorted for nicer output. */ ImmutableSortedSet existingNameservers; + /** Set of existing dsdata jsons that need to be restored during undo, sorted for nicer output. */ + ImmutableSortedSet existingDsData; + @Override - protected void initMutatingEppToolCommand() { + protected void initMutatingEppToolCommand() throws ParseException { 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); + } DomainResource domain = loadByUniqueId(DomainResource.class, domainName, now); checkArgument(domain != null, "Domain '%s' does not exist", domainName); Set missingHosts = @@ -98,6 +135,7 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand impleme "Locks can only be preserved when running with --undo"); existingNameservers = getExistingNameservers(domain); existingLocks = getExistingLocks(domain); + existingDsData = getExistingDsData(domain); setSoyTemplate( UniformRapidSuspensionSoyInfo.getInstance(), UniformRapidSuspensionSoyInfo.UNIFORMRAPIDSUSPENSION); @@ -108,6 +146,7 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand impleme "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")); } @@ -129,6 +168,19 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand impleme return locks.build(); } + private ImmutableSortedSet getExistingDsData(DomainResource domain) { + ImmutableSortedSet.Builder dsDataJsons = ImmutableSortedSet.naturalOrder(); + 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())))); + } + return dsDataJsons.build(); + } + @Override protected String postExecute() throws Exception { if (undo) { @@ -142,7 +194,10 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand impleme undoBuilder.append(" --hosts ").append(Joiner.on(',').join(existingNameservers)); } if (!existingLocks.isEmpty()) { - undoBuilder.append(" --preserve ").append(Joiner.on(',').join(existingLocks)); + undoBuilder.append(" --locks_to_preserve ").append(Joiner.on(',').join(existingLocks)); + } + if (!existingDsData.isEmpty()) { + undoBuilder.append(" --dsdata ").append(Joiner.on(',').join(existingDsData)); } return undoBuilder.toString(); } diff --git a/java/google/registry/tools/soy/UniformRapidSuspension.soy b/java/google/registry/tools/soy/UniformRapidSuspension.soy index 5a83602ad..4cb76d827 100644 --- a/java/google/registry/tools/soy/UniformRapidSuspension.soy +++ b/java/google/registry/tools/soy/UniformRapidSuspension.soy @@ -9,6 +9,7 @@ {@param hostsToRemove: list} {@param locksToApply: list} {@param locksToRemove: list} +{@param newDsData: list<[keyTag:int, alg:int, digestType:int, digest:string]>} {@param reason: string} @@ -43,6 +44,23 @@ + + + true + + {if length($newDsData) > 0} + + {foreach $ds in $newDsData} + + {$ds.keyTag} + {$ds.alg} + {$ds.digestType} + {$ds.digest} + + {/foreach} + + {/if} + {$reason} false diff --git a/javatests/google/registry/tools/UniformRapidSuspensionCommandTest.java b/javatests/google/registry/tools/UniformRapidSuspensionCommandTest.java index f71b52d80..988ef0570 100644 --- a/javatests/google/registry/tools/UniformRapidSuspensionCommandTest.java +++ b/javatests/google/registry/tools/UniformRapidSuspensionCommandTest.java @@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableSet; import com.beust.jcommander.ParameterException; import com.googlecode.objectify.Ref; +import google.registry.model.domain.secdns.DelegationSignerData; import google.registry.model.eppcommon.StatusValue; import google.registry.model.host.HostResource; import google.registry.model.registrar.Registrar; @@ -31,16 +32,27 @@ import google.registry.model.registrar.Registrar; import org.junit.Before; import org.junit.Test; +import javax.xml.bind.annotation.adapters.HexBinaryAdapter; + /** Unit tests for {@link UniformRapidSuspensionCommand}. */ public class UniformRapidSuspensionCommandTest extends EppToolCommandTestCase { + HostResource ns1; + HostResource ns2; + HostResource urs1; + HostResource urs2; + @Before - public void initRegistrar() { + public void initResources() { // Since the command's history client ID must be CharlestonRoad, resave TheRegistrar that way. persistResource(Registrar.loadByClientId("TheRegistrar").asBuilder() .setClientIdentifier("CharlestonRoad") .build()); + ns1 = persistActiveHost("ns1.example.com"); + ns2 = persistActiveHost("ns2.example.com"); + urs1 = persistActiveHost("urs1.example.com"); + urs2 = persistActiveHost("urs2.example.com"); } private void persistDomainWithHosts(HostResource... hosts) { @@ -50,71 +62,70 @@ public class UniformRapidSuspensionCommandTest } persistResource(newDomainResource("evil.tld").asBuilder() .setNameservers(hostRefs.build()) + .setDsData(ImmutableSet.of( + DelegationSignerData.create(1, 2, 3, new HexBinaryAdapter().unmarshal("dead")), + DelegationSignerData.create(4, 5, 6, new HexBinaryAdapter().unmarshal("beef")))) .build()); } @Test - public void testCommand_addsLocksReplacesHostsPrintsUndo() throws Exception { - persistActiveHost("urs1.example.com"); - persistActiveHost("urs2.example.com"); - persistDomainWithHosts( - persistActiveHost("ns1.example.com"), - persistActiveHost("ns2.example.com")); - runCommandForced("--domain_name=evil.tld", "--hosts=urs1.example.com,urs2.example.com"); + public void testCommand_addsLocksReplacesHostsAndDsDataPrintsUndo() throws Exception { + persistDomainWithHosts(ns1, ns2); + runCommandForced( + "--domain_name=evil.tld", + "--hosts=urs1.example.com,urs2.example.com", + "--dsdata={\"keyTag\":1,\"alg\":1,\"digestType\":1,\"digest\":\"abc\"}"); eppVerifier() .setClientIdentifier("CharlestonRoad") .asSuperuser() .verifySent("testdata/uniform_rapid_suspension.xml"); - assertInStdout("uniform_rapid_suspension " - + "--undo " - + "--domain_name evil.tld " - + "--hosts ns1.example.com,ns2.example.com"); + 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\"}"); + assertNotInStdout("--locks_to_preserve"); } @Test public void testCommand_respectsExistingHost() throws Exception { - persistActiveHost("urs1.example.com"); - persistDomainWithHosts( - persistActiveHost("urs2.example.com"), - persistActiveHost("ns1.example.com")); + persistDomainWithHosts(urs2, ns1); runCommandForced("--domain_name=evil.tld", "--hosts=urs1.example.com,urs2.example.com"); eppVerifier() .setClientIdentifier("CharlestonRoad") .asSuperuser() .verifySent("testdata/uniform_rapid_suspension_existing_host.xml"); - assertInStdout("uniform_rapid_suspension " - + "--undo " - + "--domain_name evil.tld " - + "--hosts ns1.example.com,urs2.example.com"); + assertInStdout("uniform_rapid_suspension --undo "); + assertInStdout("--domain_name evil.tld"); + assertInStdout("--hosts ns1.example.com,urs2.example.com"); + assertNotInStdout("--locks_to_preserve"); } @Test public void testCommand_generatesUndoForUndelegatedDomain() throws Exception { - persistActiveHost("urs1.example.com"); - persistActiveHost("urs2.example.com"); persistActiveDomain("evil.tld"); runCommandForced("--domain_name=evil.tld", "--hosts=urs1.example.com,urs2.example.com"); - assertInStdout("uniform_rapid_suspension --undo --domain_name evil.tld"); + assertInStdout("uniform_rapid_suspension --undo"); + assertInStdout("--domain_name evil.tld"); + assertNotInStdout("--locks_to_preserve"); } @Test - public void testCommand_generatesUndoWithPreserve() throws Exception { + public void testCommand_generatesUndoWithLocksToPreserve() throws Exception { persistResource( newDomainResource("evil.tld").asBuilder() .addStatusValue(StatusValue.SERVER_DELETE_PROHIBITED) .build()); runCommandForced("--domain_name=evil.tld"); - assertInStdout( - "uniform_rapid_suspension --undo --domain_name evil.tld --preserve serverDeleteProhibited"); + assertInStdout("uniform_rapid_suspension --undo"); + assertInStdout("--domain_name evil.tld"); + assertInStdout("--locks_to_preserve serverDeleteProhibited"); } @Test - public void testUndo_removesLocksReplacesHosts() throws Exception { - persistActiveHost("ns1.example.com"); - persistActiveHost("ns2.example.com"); - persistDomainWithHosts( - persistActiveHost("urs1.example.com"), - persistActiveHost("urs2.example.com")); + public void testUndo_removesLocksReplacesHostsAndDsData() throws Exception { + persistDomainWithHosts(urs1, urs2); runCommandForced( "--domain_name=evil.tld", "--undo", "--hosts=ns1.example.com,ns2.example.com"); eppVerifier() @@ -125,16 +136,12 @@ public class UniformRapidSuspensionCommandTest } @Test - public void testUndo_respectsPreserveFlag() throws Exception { - persistActiveHost("ns1.example.com"); - persistActiveHost("ns2.example.com"); - persistDomainWithHosts( - persistActiveHost("urs1.example.com"), - persistActiveHost("urs2.example.com")); + public void testUndo_respectsLocksToPreserveFlag() throws Exception { + persistDomainWithHosts(urs1, urs2); runCommandForced( "--domain_name=evil.tld", "--undo", - "--preserve=serverDeleteProhibited", + "--locks_to_preserve=serverDeleteProhibited", "--hosts=ns1.example.com,ns2.example.com"); eppVerifier() .setClientIdentifier("CharlestonRoad") @@ -144,18 +151,43 @@ public class UniformRapidSuspensionCommandTest } @Test - public void testFailure_preserveWithoutUndo() throws Exception { + public void testFailure_locksToPreserveWithoutUndo() throws Exception { persistActiveDomain("evil.tld"); thrown.expect(IllegalArgumentException.class, "--undo"); - runCommandForced("--domain_name=evil.tld", "--preserve=serverDeleteProhibited"); + runCommandForced("--domain_name=evil.tld", "--locks_to_preserve=serverDeleteProhibited"); } @Test public void testFailure_domainNameRequired() throws Exception { - persistActiveHost("urs1.example.com"); - persistActiveHost("urs2.example.com"); persistActiveDomain("evil.tld"); thrown.expect(ParameterException.class, "--domain_name"); runCommandForced("--hosts=urs1.example.com,urs2.example.com"); } + + @Test + public void testFailure_extraFieldInDsData() throws Exception { + persistActiveDomain("evil.tld"); + thrown.expect(IllegalArgumentException.class, "Incorrect fields on --dsdata JSON"); + runCommandForced( + "--domain_name=evil.tld", + "--dsdata={\"keyTag\":1,\"alg\":1,\"digestType\":1,\"digest\":\"abc\",\"foo\":1}"); + } + + @Test + public void testFailure_missingFieldInDsData() throws Exception { + persistActiveDomain("evil.tld"); + thrown.expect(IllegalArgumentException.class, "Incorrect fields on --dsdata JSON"); + runCommandForced( + "--domain_name=evil.tld", + "--dsdata={\"keyTag\":1,\"alg\":1,\"digestType\":1}"); + } + + @Test + public void testFailure_malformedDsData() throws Exception { + persistActiveDomain("evil.tld"); + thrown.expect(IllegalArgumentException.class, "Invalid --dsdata JSON"); + runCommandForced( + "--domain_name=evil.tld", + "--dsdata=[1,2,3]"); + } } diff --git a/javatests/google/registry/tools/testdata/uniform_rapid_suspension.xml b/javatests/google/registry/tools/testdata/uniform_rapid_suspension.xml index 8f5b2d06b..d15d6b02a 100644 --- a/javatests/google/registry/tools/testdata/uniform_rapid_suspension.xml +++ b/javatests/google/registry/tools/testdata/uniform_rapid_suspension.xml @@ -22,6 +22,19 @@ + + + true + + + + 1 + 1 + 1 + abc + + + Uniform Rapid Suspension false diff --git a/javatests/google/registry/tools/testdata/uniform_rapid_suspension_existing_host.xml b/javatests/google/registry/tools/testdata/uniform_rapid_suspension_existing_host.xml index 4f098fb84..fe44c4e77 100644 --- a/javatests/google/registry/tools/testdata/uniform_rapid_suspension_existing_host.xml +++ b/javatests/google/registry/tools/testdata/uniform_rapid_suspension_existing_host.xml @@ -20,6 +20,11 @@ + + + true + + Uniform Rapid Suspension false diff --git a/javatests/google/registry/tools/testdata/uniform_rapid_suspension_undo.xml b/javatests/google/registry/tools/testdata/uniform_rapid_suspension_undo.xml index 734ac602a..98122cf4b 100644 --- a/javatests/google/registry/tools/testdata/uniform_rapid_suspension_undo.xml +++ b/javatests/google/registry/tools/testdata/uniform_rapid_suspension_undo.xml @@ -22,6 +22,11 @@ + + + true + + Undo Uniform Rapid Suspension false diff --git a/javatests/google/registry/tools/testdata/uniform_rapid_suspension_undo_preserve.xml b/javatests/google/registry/tools/testdata/uniform_rapid_suspension_undo_preserve.xml index 403dabcf1..e7a7200ca 100644 --- a/javatests/google/registry/tools/testdata/uniform_rapid_suspension_undo_preserve.xml +++ b/javatests/google/registry/tools/testdata/uniform_rapid_suspension_undo_preserve.xml @@ -21,6 +21,11 @@ + + + true + + Undo Uniform Rapid Suspension false