diff --git a/java/google/registry/flows/domain/DomainUpdateFlow.java b/java/google/registry/flows/domain/DomainUpdateFlow.java index 8f6b623c5..6517b12a0 100644 --- a/java/google/registry/flows/domain/DomainUpdateFlow.java +++ b/java/google/registry/flows/domain/DomainUpdateFlow.java @@ -135,7 +135,11 @@ import org.joda.time.DateTime; public final class DomainUpdateFlow implements TransactionalFlow { /** - * Note that CLIENT_UPDATE_PROHIBITED is intentionally not in this list. This is because it + * A list of {@link StatusValue}s that prohibit updates. + * + *

Superusers can override these statuses and perform updates anyway. + * + *

Note that CLIENT_UPDATE_PROHIBITED is intentionally not in this list. This is because it * requires special checking, since you must be able to clear the status off the object with an * update. */ @@ -208,12 +212,12 @@ public final class DomainUpdateFlow implements TransactionalFlow { /** Fail if the object doesn't exist or was deleted. */ private void verifyUpdateAllowed(Update command, DomainResource existingDomain, DateTime now) throws EppException { - verifyNoDisallowedStatuses(existingDomain, UPDATE_DISALLOWED_STATUSES); verifyOptionalAuthInfo(authInfo, existingDomain); AddRemove add = command.getInnerAdd(); AddRemove remove = command.getInnerRemove(); String tld = existingDomain.getTld(); if (!isSuperuser) { + verifyNoDisallowedStatuses(existingDomain, UPDATE_DISALLOWED_STATUSES); verifyResourceOwnership(clientId, existingDomain); verifyClientUpdateNotProhibited(command, existingDomain); verifyAllStatusesAreClientSettable(union(add.getStatusValues(), remove.getStatusValues())); diff --git a/java/google/registry/model/eppcommon/StatusValue.java b/java/google/registry/model/eppcommon/StatusValue.java index 540a19b26..0902ff0b9 100644 --- a/java/google/registry/model/eppcommon/StatusValue.java +++ b/java/google/registry/model/eppcommon/StatusValue.java @@ -43,11 +43,9 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; * @see RFC 5731 (Domain) Section 2.3 * @see RFC 5732 (Host) Section 2.3 * @see RFC 5733 (Contact) Section 2.2 - */ @XmlJavaTypeAdapter(StatusValueAdapter.class) public enum StatusValue implements EppEnum { - CLIENT_DELETE_PROHIBITED(AllowedOn.ALL), CLIENT_HOLD(AllowedOn.ALL), CLIENT_RENEW_PROHIBITED(AllowedOn.ALL), @@ -114,10 +112,17 @@ public enum StatusValue implements EppEnum { */ PENDING_UPDATE(AllowedOn.NONE), + + /** A non-client-settable status that prevents deletes of EPP resources. */ SERVER_DELETE_PROHIBITED(AllowedOn.ALL), + SERVER_HOLD(AllowedOn.ALL), SERVER_RENEW_PROHIBITED(AllowedOn.ALL), + + /** A non-client-settable status that prevents transfers of EPP resources. */ SERVER_TRANSFER_PROHIBITED(AllowedOn.ALL), + + /** A non-client-settable status that prevents updates of EPP resources, except by superusers. */ SERVER_UPDATE_PROHIBITED(AllowedOn.ALL); private final String xmlName = UPPER_UNDERSCORE.to(LOWER_CAMEL, name()); diff --git a/java/google/registry/tools/LockDomainCommand.java b/java/google/registry/tools/LockDomainCommand.java new file mode 100644 index 000000000..49d0fdcff --- /dev/null +++ b/java/google/registry/tools/LockDomainCommand.java @@ -0,0 +1,76 @@ +// 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 google.registry.model.EppResourceUtils.loadByForeignKey; +import static google.registry.util.FormattingLogger.getLoggerForCallerClass; +import static java.util.stream.Collectors.toList; +import static org.joda.time.DateTimeZone.UTC; + +import com.beust.jcommander.Parameters; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import com.google.template.soy.data.SoyMapData; +import google.registry.model.domain.DomainResource; +import google.registry.model.eppcommon.StatusValue; +import google.registry.tools.soy.DomainUpdateSoyInfo; +import google.registry.util.FormattingLogger; +import org.joda.time.DateTime; + +/** + * A command to registry lock domain names via EPP. + * + *

A registry lock consists of server-side statuses preventing deletes, updates, and transfers. + */ +@Parameters(separators = " =", commandDescription = "Registry lock a domain via EPP.") +public class LockDomainCommand extends LockOrUnlockDomainCommand { + + private static final FormattingLogger logger = getLoggerForCallerClass(); + + @Override + protected void initMutatingEppToolCommand() throws Exception { + // Project all domains as of the same time so that argument order doesn't affect behavior. + DateTime now = DateTime.now(UTC); + for (String domain : getDomains()) { + DomainResource domainResource = loadByForeignKey(DomainResource.class, domain, now); + checkArgument(domainResource != null, "Domain '%s' does not exist", domain); + ImmutableSet statusesToAdd = + Sets.difference(REGISTRY_LOCK_STATUSES, domainResource.getStatusValues()).immutableCopy(); + if (statusesToAdd.isEmpty()) { + logger.infofmt("Domain '%s' is already locked and needs no updates.", domain); + continue; + } + + setSoyTemplate(DomainUpdateSoyInfo.getInstance(), DomainUpdateSoyInfo.DOMAINUPDATE); + addSoyRecord( + clientId, + new SoyMapData( + "domain", domain, + "add", true, + "addNameservers", ImmutableList.of(), + "addAdmins", ImmutableList.of(), + "addTechs", ImmutableList.of(), + "addStatuses", statusesToAdd.stream().map(StatusValue::getXmlName).collect(toList()), + "remove", false, + "removeNameservers", ImmutableList.of(), + "removeAdmins", ImmutableList.of(), + "removeTechs", ImmutableList.of(), + "removeStatuses", ImmutableList.of(), + "change", false)); + } + } +} diff --git a/java/google/registry/tools/LockOrUnlockDomainCommand.java b/java/google/registry/tools/LockOrUnlockDomainCommand.java new file mode 100644 index 000000000..a5a2e3f65 --- /dev/null +++ b/java/google/registry/tools/LockOrUnlockDomainCommand.java @@ -0,0 +1,60 @@ +// 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 google.registry.model.eppcommon.StatusValue.SERVER_DELETE_PROHIBITED; +import static google.registry.model.eppcommon.StatusValue.SERVER_TRANSFER_PROHIBITED; +import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED; +import static google.registry.util.CollectionUtils.findDuplicates; + +import com.beust.jcommander.Parameter; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableSet; +import google.registry.model.eppcommon.StatusValue; +import java.util.List; + +/** Shared base class for commands to registry lock or unlock a domain via EPP. */ +public abstract class LockOrUnlockDomainCommand extends MutatingEppToolCommand { + + protected static final ImmutableSet REGISTRY_LOCK_STATUSES = + ImmutableSet.of( + SERVER_DELETE_PROHIBITED, SERVER_TRANSFER_PROHIBITED, SERVER_UPDATE_PROHIBITED); + + @Parameter( + names = {"-c", "--client"}, + description = "Client identifier of the registrar to execute the command as", + required = true + ) + String clientId; + + @Parameter(description = "Names of the domains", required = true) + private List mainParameters; + + protected ImmutableSet getDomains() { + return ImmutableSet.copyOf(mainParameters); + } + + @Override + protected void initEppToolCommand() throws Exception { + // Superuser status is required to update registry lock statuses. + superuser = true; + String duplicates = Joiner.on(", ").join(findDuplicates(mainParameters)); + checkArgument(duplicates.isEmpty(), "Duplicate domain arguments found: '%s'", duplicates); + initMutatingEppToolCommand(); + System.out.println( + "== ENSURE THAT YOU HAVE AUTHENTICATED THE REGISTRAR BEFORE RUNNING THIS COMMAND =="); + } +} diff --git a/java/google/registry/tools/RegistryTool.java b/java/google/registry/tools/RegistryTool.java index 4682a5f3c..36ca98165 100644 --- a/java/google/registry/tools/RegistryTool.java +++ b/java/google/registry/tools/RegistryTool.java @@ -95,6 +95,7 @@ public final class RegistryTool { .put("list_tlds", ListTldsCommand.class) .put("load_snapshot", LoadSnapshotCommand.class) .put("load_test", LoadTestCommand.class) + .put("lock_domain", LockDomainCommand.class) .put("login", LoginCommand.class) .put("logout", LogoutCommand.class) .put("make_billing_tables", MakeBillingTablesCommand.class) diff --git a/java/google/registry/tools/RegistryToolComponent.java b/java/google/registry/tools/RegistryToolComponent.java index 83e0d1f21..8d5d3cc15 100644 --- a/java/google/registry/tools/RegistryToolComponent.java +++ b/java/google/registry/tools/RegistryToolComponent.java @@ -94,9 +94,7 @@ interface RegistryToolComponent { void inject(SendEscrowReportToIcannCommand command); void inject(SetupOteCommand command); void inject(UpdateCursorsCommand command); - void inject(UpdateDomainCommand command); - void inject(UpdateKmsKeyringCommand command); void inject(UpdateTldCommand command); void inject(ValidateEscrowDepositCommand command); diff --git a/java/google/registry/tools/UpdateDomainCommand.java b/java/google/registry/tools/UpdateDomainCommand.java index 8661c033a..4daec5114 100644 --- a/java/google/registry/tools/UpdateDomainCommand.java +++ b/java/google/registry/tools/UpdateDomainCommand.java @@ -17,6 +17,7 @@ package google.registry.tools; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.ImmutableSet.toImmutableSet; import static google.registry.model.EppResourceUtils.loadByForeignKey; +import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED; import static google.registry.model.ofy.ObjectifyService.ofy; import static org.joda.time.DateTimeZone.UTC; @@ -127,6 +128,11 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand { DateTime now = DateTime.now(UTC); DomainResource domainResource = loadByForeignKey(DomainResource.class, domain, now); checkArgument(domainResource != null, "Domain '%s' does not exist", domain); + checkArgument( + !domainResource.getStatusValues().contains(SERVER_UPDATE_PROHIBITED), + "The domain '%s' has status SERVER_UPDATE_PROHIBITED. Verify that you are allowed " + + "to make updates, and if so, use the domain_unlock command to enable updates.", + domain); if (!nameservers.isEmpty()) { ImmutableSortedSet existingNameservers = domainResource.loadNameserverFullyQualifiedHostNames(); diff --git a/javatests/google/registry/flows/domain/DomainUpdateFlowTest.java b/javatests/google/registry/flows/domain/DomainUpdateFlowTest.java index d48c2ed52..d20bb08e3 100644 --- a/javatests/google/registry/flows/domain/DomainUpdateFlowTest.java +++ b/javatests/google/registry/flows/domain/DomainUpdateFlowTest.java @@ -18,6 +18,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.EppResourceUtils.loadByForeignKey; +import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED; import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.testing.DatastoreHelper.assertBillingEvents; import static google.registry.testing.DatastoreHelper.assertNoBillingEvents; @@ -34,6 +35,7 @@ import static google.registry.testing.DatastoreHelper.persistReservedList; import static google.registry.testing.DatastoreHelper.persistResource; import static google.registry.testing.DomainResourceSubject.assertAboutDomains; import static google.registry.testing.HistoryEntrySubject.assertAboutHistoryEntries; +import static google.registry.testing.JUnitBackports.expectThrows; import static google.registry.testing.TaskQueueHelper.assertDnsTasksEnqueued; import static org.joda.money.CurrencyUnit.USD; @@ -892,9 +894,46 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase + + + + example.tld + + + ns2.example.foo + + Add registry lock for domain. + + + + ns1.example.foo + + + + sh8013 + + 2BARfoo + + + + + ABC-12345 + + diff --git a/javatests/google/registry/testing/TestDataHelper.java b/javatests/google/registry/testing/TestDataHelper.java index 83ce3f273..fef7f391e 100644 --- a/javatests/google/registry/testing/TestDataHelper.java +++ b/javatests/google/registry/testing/TestDataHelper.java @@ -20,9 +20,7 @@ import static google.registry.util.ResourceUtils.readResourceUtf8; import java.util.Map; import java.util.Map.Entry; -/** - * Contains helper methods for dealing with test data. - */ +/** Contains helper methods for dealing with test data. */ public final class TestDataHelper { /** @@ -31,7 +29,11 @@ public final class TestDataHelper { */ public static String loadFileWithSubstitutions( Class context, String filename, Map substitutions) { - String fileContents = readResourceUtf8(context, "testdata/" + filename); + return applySubstitutions(readResourceUtf8(context, "testdata/" + filename), substitutions); + } + + /** Applies the given substitutions to the given string and returns the result. */ + public static String applySubstitutions(String fileContents, Map substitutions) { for (Entry entry : nullToEmpty(substitutions).entrySet()) { fileContents = fileContents.replaceAll("%" + entry.getKey() + "%", entry.getValue()); } diff --git a/javatests/google/registry/tools/CommandTestCase.java b/javatests/google/registry/tools/CommandTestCase.java index 8858e8768..b33acc0b1 100644 --- a/javatests/google/registry/tools/CommandTestCase.java +++ b/javatests/google/registry/tools/CommandTestCase.java @@ -14,6 +14,7 @@ package google.registry.tools; +import static com.google.common.collect.Iterables.concat; import static com.google.common.collect.Iterables.toArray; import static com.google.common.truth.Truth.assertThat; import static google.registry.model.ofy.ObjectifyService.ofy; @@ -22,6 +23,8 @@ import static java.nio.charset.StandardCharsets.UTF_8; import com.beust.jcommander.JCommander; import com.google.common.base.Joiner; import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; import com.google.common.collect.ObjectArrays; import com.google.common.io.Files; import com.google.common.reflect.TypeToken; @@ -95,11 +98,21 @@ public abstract class CommandTestCase { runCommandInEnvironment(RegistryToolEnvironment.UNITTEST, args); } + protected void runCommand(Iterable args) throws Exception { + runCommandInEnvironment( + RegistryToolEnvironment.UNITTEST, Iterables.toArray(args, String.class)); + } + /** Adds "--force" as the first parameter, then runs the command. */ protected void runCommandForced(String... args) throws Exception { runCommand(ObjectArrays.concat("--force", args)); } + /** Adds "--force" as the first parameter, then runs the command. */ + protected void runCommandForced(Iterable args) throws Exception { + runCommand(concat(ImmutableList.of("--force"), args)); + } + /** Writes the data to a named temporary file and then returns a path to the file. */ String writeToNamedTmpFile(String filename, byte[] data) throws IOException { File tmpFile = tmpDir.newFile(filename); diff --git a/javatests/google/registry/tools/EppToolVerifier.java b/javatests/google/registry/tools/EppToolVerifier.java index bd17335ed..6750fa693 100644 --- a/javatests/google/registry/tools/EppToolVerifier.java +++ b/javatests/google/registry/tools/EppToolVerifier.java @@ -14,12 +14,14 @@ package google.registry.tools; +import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.truth.Truth.assertThat; import static google.registry.xml.XmlTestUtils.assertXmlEquals; import static java.nio.charset.StandardCharsets.UTF_8; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableMap; @@ -27,6 +29,7 @@ import com.google.common.net.MediaType; import google.registry.tools.ServerSideCommand.Connection; import google.registry.tools.server.ToolsTestData; import java.net.URLDecoder; +import java.util.Arrays; import java.util.List; import java.util.Map; import org.mockito.ArgumentCaptor; @@ -67,29 +70,34 @@ public class EppToolVerifier { return new EppToolVerifier(connection, clientId, superuser, true); } - void verifySent(String... xmlToMatch) throws Exception { + void verifySent(String... expectedXmlFiles) throws Exception { + verifySentContents( + Arrays.stream(expectedXmlFiles).map(ToolsTestData::loadUtf8).collect(toImmutableList())); + } + + void verifySentContents(List expectedXmlContents) throws Exception { ArgumentCaptor params = ArgumentCaptor.forClass(byte[].class); - verify(connection, times(xmlToMatch.length)).send( + verify(connection, times(expectedXmlContents.size())).send( eq("/_dr/epptool"), eq(ImmutableMap.of()), eq(MediaType.FORM_DATA), params.capture()); List capturedParams = params.getAllValues(); - assertThat(capturedParams).hasSize(xmlToMatch.length); - for (int i = 0; i < xmlToMatch.length; i++) { - String xml = xmlToMatch[i]; + assertThat(capturedParams).hasSize(expectedXmlContents.size()); + for (int i = 0; i < expectedXmlContents.size(); i++) { byte[] capturedParam = capturedParams.get(i); Map map = - Splitter.on('&') - .withKeyValueSeparator('=') - .split(new String(capturedParam, UTF_8)); + Splitter.on('&').withKeyValueSeparator('=').split(new String(capturedParam, UTF_8)); assertThat(map).hasSize(4); assertXmlEquals( - ToolsTestData.loadUtf8(xml), - URLDecoder.decode(map.get("xml"), UTF_8.toString())); + expectedXmlContents.get(i), URLDecoder.decode(map.get("xml"), UTF_8.toString())); assertThat(map).containsEntry("dryRun", Boolean.toString(dryRun)); assertThat(map).containsEntry("clientId", clientId); assertThat(map).containsEntry("superuser", Boolean.toString(superuser)); } } + + void verifyNothingSent() { + verifyZeroInteractions(connection); + } } diff --git a/javatests/google/registry/tools/LockDomainCommandTest.java b/javatests/google/registry/tools/LockDomainCommandTest.java new file mode 100644 index 000000000..b6714c659 --- /dev/null +++ b/javatests/google/registry/tools/LockDomainCommandTest.java @@ -0,0 +1,112 @@ +// 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.truth.Truth.assertThat; +import static google.registry.model.eppcommon.StatusValue.SERVER_TRANSFER_PROHIBITED; +import static google.registry.testing.DatastoreHelper.newDomainResource; +import static google.registry.testing.DatastoreHelper.persistActiveDomain; +import static google.registry.testing.DatastoreHelper.persistResource; +import static google.registry.testing.JUnitBackports.expectThrows; +import static google.registry.testing.TestDataHelper.applySubstitutions; +import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES; +import static google.registry.tools.server.ToolsTestData.loadUtf8; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import java.util.ArrayList; +import java.util.List; +import org.junit.Test; + +/** Unit tests for {@link LockDomainCommand}. */ +public class LockDomainCommandTest extends EppToolCommandTestCase { + + /** Gets an overridden eppVerifier that has superuser set to true on it. */ + @Override + EppToolVerifier eppVerifier() { + return new EppToolVerifier() + .withConnection(connection) + .withClientId("NewRegistrar") + .asSuperuser(); + } + + @Test + public void testSuccess_sendsCorrectEppXml() throws Exception { + persistActiveDomain("example.tld"); + runCommandForced("--client=NewRegistrar", "example.tld"); + eppVerifier() + .verifySentContents( + ImmutableList.of( + loadUtf8("domain_lock.xml", ImmutableMap.of("DOMAIN", "example.tld")))); + } + + @Test + public void testSuccess_partiallyUpdatesStatuses() throws Exception { + persistResource( + newDomainResource("example.tld") + .asBuilder() + .addStatusValue(SERVER_TRANSFER_PROHIBITED) + .build()); + runCommandForced("--client=NewRegistrar", "example.tld"); + eppVerifier().verifySent("domain_lock_partial_statuses.xml"); + } + + @Test + public void testSuccess_manyDomains() throws Exception { + List params = new ArrayList<>(); + List expectedXmls = new ArrayList<>(); + params.add("--client=NewRegistrar"); + String updateDomainXml = loadUtf8("domain_lock.xml"); + // Create 26 domains -- one more than the number of entity groups allowed in a transaction (in + // case that was going to be the failure point). + for (int n = 0; n < 26; n++) { + String domain = String.format("domain%d.tld", n); + persistActiveDomain(domain); + params.add(domain); + expectedXmls.add(applySubstitutions(updateDomainXml, ImmutableMap.of("DOMAIN", domain))); + } + runCommandForced(params); + eppVerifier().verifySentContents(expectedXmls); + } + + @Test + public void testFailure_domainDoesntExist() throws Exception { + IllegalArgumentException e = + expectThrows( + IllegalArgumentException.class, + () -> runCommandForced("--client=NewRegistrar", "missing.tld")); + assertThat(e).hasMessageThat().isEqualTo("Domain 'missing.tld' does not exist"); + } + + @Test + public void testSuccess_alreadyLockedDomain_performsNoAction() throws Exception { + persistResource( + newDomainResource("example.tld") + .asBuilder() + .addStatusValues(REGISTRY_LOCK_STATUSES) + .build()); + runCommandForced("--client=NewRegistrar", "example.tld"); + eppVerifier().verifyNothingSent(); + } + + @Test + public void testFailure_duplicateDomainsAreSpecified() throws Exception { + IllegalArgumentException e = + expectThrows( + IllegalArgumentException.class, + () -> runCommandForced("--client=NewRegistrar", "dupe.tld", "dupe.tld")); + assertThat(e).hasMessageThat().isEqualTo("Duplicate domain arguments found: 'dupe.tld'"); + } +} diff --git a/javatests/google/registry/tools/UpdateDomainCommandTest.java b/javatests/google/registry/tools/UpdateDomainCommandTest.java index bee5e7b07..43c68f104 100644 --- a/javatests/google/registry/tools/UpdateDomainCommandTest.java +++ b/javatests/google/registry/tools/UpdateDomainCommandTest.java @@ -14,10 +14,13 @@ package google.registry.tools; +import static com.google.common.truth.Truth.assertThat; +import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED; import static google.registry.testing.DatastoreHelper.newContactResource; import static google.registry.testing.DatastoreHelper.newDomainResource; import static google.registry.testing.DatastoreHelper.persistActiveHost; import static google.registry.testing.DatastoreHelper.persistResource; +import static google.registry.testing.JUnitBackports.expectThrows; import com.beust.jcommander.ParameterException; import com.google.common.collect.ImmutableSet; @@ -151,7 +154,7 @@ public class UpdateDomainCommandTest extends EppToolCommandTestCase> nameservers = ImmutableSet.of(Key.create(host)); + persistResource( + newDomainResource("example.tld") + .asBuilder() + .setStatusValues(ImmutableSet.of(SERVER_UPDATE_PROHIBITED)) + .setNameservers(nameservers) + .build()); + + Exception e = + expectThrows( + IllegalArgumentException.class, + () -> + runCommandForced( + "--client=NewRegistrar", + "--statuses=clientRenewProhibited,serverHold", + "--superuser", + "example.tld")); + assertThat(e) + .hasMessageThat() + .containsMatch("The domain 'example.tld' has status SERVER_UPDATE_PROHIBITED"); + eppVerifier().verifyNothingSent(); + } + @Test public void testFailure_duplicateDomains() throws Exception { thrown.expect(IllegalArgumentException.class, "Duplicate arguments found"); diff --git a/javatests/google/registry/tools/server/ToolsTestData.java b/javatests/google/registry/tools/server/ToolsTestData.java index 82116f2f6..a946e5c0b 100644 --- a/javatests/google/registry/tools/server/ToolsTestData.java +++ b/javatests/google/registry/tools/server/ToolsTestData.java @@ -14,12 +14,12 @@ package google.registry.tools.server; -import static java.nio.charset.StandardCharsets.UTF_8; - +import com.google.common.collect.ImmutableMap; import com.google.common.io.ByteSource; import com.google.common.io.Resources; -import java.io.IOException; +import google.registry.testing.TestDataHelper; import java.net.URL; +import java.util.Map; /** Utility class providing easy access to contents of the {@code testdata/} directory. */ public final class ToolsTestData { @@ -30,12 +30,17 @@ public final class ToolsTestData { } /** - * Loads data from file in {@code tools/server/testdata/} as a String (assuming file is UTF-8). - * - * @throws IOException if the file couldn't be loaded from the jar. + * Loads data from file in {@code tools/server/testdata/} as a UTF-8 String. */ - public static String loadUtf8(String filename) throws IOException { - return Resources.asCharSource(getUrl(filename), UTF_8).read(); + public static String loadUtf8(String filename) { + return loadUtf8(filename, ImmutableMap.of()); + } + + /** + * Loads data from file in {@code tools/server/testdata/} as a UTF-8 String, with substitutions. + */ + public static String loadUtf8(String filename, Map substitutions) { + return TestDataHelper.loadFileWithSubstitutions(ToolsTestData.class, filename, substitutions); } private static URL getUrl(String filename) { diff --git a/javatests/google/registry/tools/server/testdata/domain_lock.xml b/javatests/google/registry/tools/server/testdata/domain_lock.xml new file mode 100644 index 000000000..aa1329a20 --- /dev/null +++ b/javatests/google/registry/tools/server/testdata/domain_lock.xml @@ -0,0 +1,16 @@ + + + + + + %DOMAIN% + + + + + + + + RegistryTool + + diff --git a/javatests/google/registry/tools/server/testdata/domain_lock_partial_statuses.xml b/javatests/google/registry/tools/server/testdata/domain_lock_partial_statuses.xml new file mode 100644 index 000000000..0a4843905 --- /dev/null +++ b/javatests/google/registry/tools/server/testdata/domain_lock_partial_statuses.xml @@ -0,0 +1,15 @@ + + + + + + example.tld + + + + + + + RegistryTool + + diff --git a/javatests/google/registry/tools/server/testdata/domain_update_set_statuses.xml b/javatests/google/registry/tools/server/testdata/domain_update_set_statuses.xml index 707274e3d..6bdb3352d 100644 --- a/javatests/google/registry/tools/server/testdata/domain_update_set_statuses.xml +++ b/javatests/google/registry/tools/server/testdata/domain_update_set_statuses.xml @@ -9,7 +9,7 @@ - +