From af8ef38b0af8961aa98b0b26c9d4906d1ad9582c Mon Sep 17 00:00:00 2001 From: gbrodman Date: Wed, 29 Jan 2020 16:36:39 -0500 Subject: [PATCH] Add RegistryLockVerifyAction (#461) * Add RegistryLockVerifyAction The action takes two parameters - isLock is a boolean, determining whether we're trying to lock or unlock a domain - lockVerificationCode is the UUID by which we'll look up the lock object in question. The lock in question must not be expired and must be in a valid lockable / unlockable state * Some responses to CR * Add slash and move test method * Add more data and tests * Fix screenshot --- .../env/common/default/WEB-INF/web.xml | 5 + .../frontend/FrontendRequestComponent.java | 3 + .../tools/LockOrUnlockDomainCommand.java | 2 +- .../registrar/RegistrarConsoleModule.java | 14 +- .../registrar/RegistryLockVerifyAction.java | 97 +++++ .../registrar/RegistryLockVerification.soy | 71 ++++ .../integration/SqlIntegrationTestSuite.java | 2 + .../registry/server/RegistryTestServer.java | 3 +- .../RegistryLockVerifyActionTest.java | 345 ++++++++++++++++++ .../RegistrarConsoleScreenshotTest.java | 39 +- .../module/frontend/frontend_routing.txt | 1 + ...otTest_registryLockVerify_success_page.png | Bin 0 -> 30226 bytes ...st_registryLockVerify_unknownLock_page.png | Bin 0 -> 30034 bytes 13 files changed, 578 insertions(+), 4 deletions(-) create mode 100644 core/src/main/java/google/registry/ui/server/registrar/RegistryLockVerifyAction.java create mode 100644 core/src/main/resources/google/registry/ui/soy/registrar/RegistryLockVerification.soy create mode 100644 core/src/test/java/google/registry/ui/server/registrar/RegistryLockVerifyActionTest.java create mode 100644 core/src/test/resources/google/registry/webdriver/goldens/chrome-linux/RegistrarConsoleScreenshotTest_registryLockVerify_success_page.png create mode 100644 core/src/test/resources/google/registry/webdriver/goldens/chrome-linux/RegistrarConsoleScreenshotTest_registryLockVerify_unknownLock_page.png diff --git a/core/src/main/java/google/registry/env/common/default/WEB-INF/web.xml b/core/src/main/java/google/registry/env/common/default/WEB-INF/web.xml index 80d6601e7..b2417bec1 100644 --- a/core/src/main/java/google/registry/env/common/default/WEB-INF/web.xml +++ b/core/src/main/java/google/registry/env/common/default/WEB-INF/web.xml @@ -61,6 +61,11 @@ /registry-lock-get + + frontend-servlet + /registry-lock-verify + + diff --git a/core/src/main/java/google/registry/module/frontend/FrontendRequestComponent.java b/core/src/main/java/google/registry/module/frontend/FrontendRequestComponent.java index a9b529051..75b1632dc 100644 --- a/core/src/main/java/google/registry/module/frontend/FrontendRequestComponent.java +++ b/core/src/main/java/google/registry/module/frontend/FrontendRequestComponent.java @@ -31,6 +31,7 @@ import google.registry.ui.server.registrar.OteStatusAction; import google.registry.ui.server.registrar.RegistrarConsoleModule; import google.registry.ui.server.registrar.RegistrarSettingsAction; import google.registry.ui.server.registrar.RegistryLockGetAction; +import google.registry.ui.server.registrar.RegistryLockVerifyAction; /** Dagger component with per-request lifetime for "default" App Engine module. */ @RequestScope @@ -53,6 +54,8 @@ interface FrontendRequestComponent { RegistryLockGetAction registryLockGetAction(); + RegistryLockVerifyAction registryLockVerifyAction(); + @Subcomponent.Builder abstract class Builder implements RequestComponentBuilder { @Override public abstract Builder requestModule(RequestModule requestModule); diff --git a/core/src/main/java/google/registry/tools/LockOrUnlockDomainCommand.java b/core/src/main/java/google/registry/tools/LockOrUnlockDomainCommand.java index d66173cab..245dab7c8 100644 --- a/core/src/main/java/google/registry/tools/LockOrUnlockDomainCommand.java +++ b/core/src/main/java/google/registry/tools/LockOrUnlockDomainCommand.java @@ -38,7 +38,7 @@ public abstract class LockOrUnlockDomainCommand extends ConfirmingCommand private static final FluentLogger logger = FluentLogger.forEnclosingClass(); - static final ImmutableSet REGISTRY_LOCK_STATUSES = + public static final ImmutableSet REGISTRY_LOCK_STATUSES = ImmutableSet.of( SERVER_DELETE_PROHIBITED, SERVER_TRANSFER_PROHIBITED, SERVER_UPDATE_PROHIBITED); diff --git a/core/src/main/java/google/registry/ui/server/registrar/RegistrarConsoleModule.java b/core/src/main/java/google/registry/ui/server/registrar/RegistrarConsoleModule.java index 63b1a48e8..6c1ddc61b 100644 --- a/core/src/main/java/google/registry/ui/server/registrar/RegistrarConsoleModule.java +++ b/core/src/main/java/google/registry/ui/server/registrar/RegistrarConsoleModule.java @@ -14,7 +14,7 @@ package google.registry.ui.server.registrar; - +import static google.registry.request.RequestParameters.extractBooleanParameter; import static google.registry.request.RequestParameters.extractOptionalIntParameter; import static google.registry.request.RequestParameters.extractOptionalParameter; import static google.registry.request.RequestParameters.extractRequiredParameter; @@ -144,4 +144,16 @@ public final class RegistrarConsoleModule { static Optional provideOptionalPasscode(HttpServletRequest req) { return extractOptionalParameter(req, "passcode"); } + + @Provides + @Parameter("lockVerificationCode") + static String provideLockVerificationCode(HttpServletRequest req) { + return extractRequiredParameter(req, "lockVerificationCode"); + } + + @Provides + @Parameter("isLock") + static Boolean provideIsLock(HttpServletRequest req) { + return extractBooleanParameter(req, "isLock"); + } } diff --git a/core/src/main/java/google/registry/ui/server/registrar/RegistryLockVerifyAction.java b/core/src/main/java/google/registry/ui/server/registrar/RegistryLockVerifyAction.java new file mode 100644 index 000000000..6d77e4142 --- /dev/null +++ b/core/src/main/java/google/registry/ui/server/registrar/RegistryLockVerifyAction.java @@ -0,0 +1,97 @@ +// Copyright 2020 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.ui.server.registrar; + +import static google.registry.ui.server.SoyTemplateUtils.CSS_RENAMING_MAP_SUPPLIER; + +import com.google.common.base.Supplier; +import com.google.common.base.Throwables; +import com.google.common.flogger.FluentLogger; +import com.google.template.soy.tofu.SoyTofu; +import google.registry.request.Action; +import google.registry.request.Parameter; +import google.registry.request.auth.Auth; +import google.registry.schema.domain.RegistryLock; +import google.registry.tools.DomainLockUtils; +import google.registry.ui.server.SoyTemplateUtils; +import google.registry.ui.soy.registrar.RegistryLockVerificationSoyInfo; +import google.registry.util.Clock; +import java.util.HashMap; +import javax.inject.Inject; + +/** Action that allows for verification of registry lock / unlock requests */ +@Action( + service = Action.Service.DEFAULT, + path = RegistryLockVerifyAction.PATH, + auth = Auth.AUTH_PUBLIC_LOGGED_IN) +public final class RegistryLockVerifyAction extends HtmlAction { + + public static final String PATH = "/registry-lock-verify"; + + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + + private static final Supplier TOFU_SUPPLIER = + SoyTemplateUtils.createTofuSupplier( + google.registry.ui.soy.ConsoleSoyInfo.getInstance(), + google.registry.ui.soy.AnalyticsSoyInfo.getInstance(), + google.registry.ui.soy.registrar.RegistryLockVerificationSoyInfo.getInstance()); + + private final Clock clock; + private final String lockVerificationCode; + private final Boolean isLock; + + @Inject + public RegistryLockVerifyAction( + Clock clock, + @Parameter("lockVerificationCode") String lockVerificationCode, + @Parameter("isLock") Boolean isLock) { + this.clock = clock; + this.lockVerificationCode = lockVerificationCode; + this.isLock = isLock; + } + + @Override + public void runAfterLogin(HashMap data) { + try { + boolean isAdmin = authResult.userAuthInfo().get().isUserAdmin(); + final RegistryLock resultLock; + if (isLock) { + resultLock = DomainLockUtils.verifyAndApplyLock(lockVerificationCode, isAdmin, clock); + } else { + resultLock = DomainLockUtils.verifyAndApplyUnlock(lockVerificationCode, isAdmin, clock); + } + data.put("isLock", isLock); + data.put("success", true); + data.put("fullyQualifiedDomainName", resultLock.getDomainName()); + } catch (Throwable t) { + logger.atWarning().withCause(t).log( + "Error when verifying verification code %s", lockVerificationCode); + data.put("success", false); + data.put("errorMessage", Throwables.getRootCause(t).getMessage()); + } + response.setPayload( + TOFU_SUPPLIER + .get() + .newRenderer(RegistryLockVerificationSoyInfo.VERIFICATION_PAGE) + .setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get()) + .setData(data) + .render()); + } + + @Override + public String getPath() { + return PATH; + } +} diff --git a/core/src/main/resources/google/registry/ui/soy/registrar/RegistryLockVerification.soy b/core/src/main/resources/google/registry/ui/soy/registrar/RegistryLockVerification.soy new file mode 100644 index 000000000..d18d674be --- /dev/null +++ b/core/src/main/resources/google/registry/ui/soy/registrar/RegistryLockVerification.soy @@ -0,0 +1,71 @@ +// Copyright 2019 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +{namespace registry.soy.registrar.registrylock.verification} + + +/** + * Results page for a registry lock/unlock verification. + */ +{template .verificationPage} + {@param username: string} /** Arbitrary username to display. */ + {@param analyticsConfig: [googleAnalyticsId: string|null]} + {@param success: bool} + {@param? errorMessage: string} + {@param? isLock: bool} + {@param? fullyQualifiedDomainName: string} + {call registry.soy.console.header} + {param app: 'registrar' /} + {param subtitle: 'Verify Registry Lock' /} + {param analyticsConfig: $analyticsConfig /} + {/call} + {call registry.soy.console.googlebar data="all" /} + +{/template} + +/** + * Result page for failure, e.g. the UUID was invalid + */ +{template .failure} + {@param? errorMessage: string} +

Failed: {if isNonnull($errorMessage)} + {$errorMessage} + {else} + Undefined error message + {/if} +

+{/template} + +/** + * Result page for a successful lock / unlock. + */ +{template .success} + {@param? isLock: bool} + {@param? fullyQualifiedDomainName: string} +

+ Success: {if $isLock}lock{else}unlock{/if} has been applied to {$fullyQualifiedDomainName} +

+{/template} + diff --git a/core/src/test/java/google/registry/schema/integration/SqlIntegrationTestSuite.java b/core/src/test/java/google/registry/schema/integration/SqlIntegrationTestSuite.java index ef5771cd3..dcfa32e1d 100644 --- a/core/src/test/java/google/registry/schema/integration/SqlIntegrationTestSuite.java +++ b/core/src/test/java/google/registry/schema/integration/SqlIntegrationTestSuite.java @@ -30,6 +30,7 @@ import google.registry.tools.UpdateReservedListCommandTest; import google.registry.tools.server.CreatePremiumListActionTest; import google.registry.tools.server.UpdatePremiumListActionTest; import google.registry.ui.server.registrar.RegistryLockGetActionTest; +import google.registry.ui.server.registrar.RegistryLockVerifyActionTest; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.ClassRule; @@ -60,6 +61,7 @@ import org.junit.runners.Suite.SuiteClasses; PremiumListDaoTest.class, RegistryLockDaoTest.class, RegistryLockGetActionTest.class, + RegistryLockVerifyActionTest.class, ReservedListDaoTest.class, UnlockDomainCommandTest.class, UpdatePremiumListActionTest.class, diff --git a/core/src/test/java/google/registry/server/RegistryTestServer.java b/core/src/test/java/google/registry/server/RegistryTestServer.java index 0d36cfbbe..7214be456 100644 --- a/core/src/test/java/google/registry/server/RegistryTestServer.java +++ b/core/src/test/java/google/registry/server/RegistryTestServer.java @@ -83,7 +83,8 @@ public final class RegistryTestServer { route("/registrar-ote-setup", FrontendServlet.class), route("/registrar-ote-status", FrontendServlet.class), route("/registrar-settings", FrontendServlet.class), - route("/registry-lock-get", FrontendServlet.class)); + route("/registry-lock-get", FrontendServlet.class), + route("/registry-lock-verify", FrontendServlet.class)); private static final ImmutableList> FILTERS = ImmutableList.of( ObjectifyFilter.class, diff --git a/core/src/test/java/google/registry/ui/server/registrar/RegistryLockVerifyActionTest.java b/core/src/test/java/google/registry/ui/server/registrar/RegistryLockVerifyActionTest.java new file mode 100644 index 000000000..c6dfd85af --- /dev/null +++ b/core/src/test/java/google/registry/ui/server/registrar/RegistryLockVerifyActionTest.java @@ -0,0 +1,345 @@ +// Copyright 2020 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.ui.server.registrar; + +import static com.google.common.truth.Truth.assertThat; +import static google.registry.model.ofy.ObjectifyService.ofy; +import static google.registry.testing.DatastoreHelper.createTlds; +import static google.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType; +import static google.registry.testing.DatastoreHelper.newDomainBase; +import static google.registry.testing.DatastoreHelper.persistActiveHost; +import static google.registry.testing.DatastoreHelper.persistResource; +import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES; +import static javax.servlet.http.HttpServletResponse.SC_MOVED_TEMPORARILY; +import static javax.servlet.http.HttpServletResponse.SC_OK; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.google.appengine.api.users.User; +import com.google.appengine.api.users.UserService; +import com.google.appengine.api.users.UserServiceFactory; +import com.google.common.collect.ImmutableMap; +import google.registry.model.billing.BillingEvent; +import google.registry.model.billing.BillingEvent.Reason; +import google.registry.model.domain.DomainBase; +import google.registry.model.host.HostResource; +import google.registry.model.registry.Registry; +import google.registry.model.registry.RegistryLockDao; +import google.registry.model.reporting.HistoryEntry; +import google.registry.persistence.transaction.JpaTestRules; +import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageRule; +import google.registry.request.auth.AuthLevel; +import google.registry.request.auth.AuthResult; +import google.registry.request.auth.UserAuthInfo; +import google.registry.schema.domain.RegistryLock; +import google.registry.security.XsrfTokenManager; +import google.registry.testing.AppEngineRule; +import google.registry.testing.DatastoreHelper; +import google.registry.testing.FakeClock; +import google.registry.testing.FakeResponse; +import google.registry.testing.InjectRule; +import google.registry.testing.UserInfo; +import java.util.UUID; +import javax.servlet.http.HttpServletRequest; +import org.joda.time.Duration; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public final class RegistryLockVerifyActionTest { + + private final FakeClock fakeClock = new FakeClock(); + + @Rule + public final AppEngineRule appEngineRule = + AppEngineRule.builder() + .withDatastore() + .withUserService(UserInfo.create("marla.singer@example.com", "12345")) + .build(); + + @Rule + public final JpaIntegrationWithCoverageRule jpaRule = + new JpaTestRules.Builder().withClock(fakeClock).buildIntegrationWithCoverageRule(); + + @Rule public final InjectRule inject = new InjectRule(); + + private final HttpServletRequest request = mock(HttpServletRequest.class); + private final UserService userService = UserServiceFactory.getUserService(); + private final User user = new User("marla.singer@example.com", "gmail.com", "12345"); + private final String lockId = "f1be78a2-2d61-458c-80f0-9dd8f2f8625f"; + + private FakeResponse response; + private DomainBase domain; + private AuthResult authResult; + private RegistryLockVerifyAction action; + + @Before + public void setup() { + createTlds("tld", "net"); + HostResource host = persistActiveHost("ns1.example.net"); + domain = persistResource(newDomainBase("example.tld", host)); + when(request.getRequestURI()).thenReturn("https://registry.example/registry-lock-verification"); + action = createAction(lockId, true); + } + + @Test + public void testSuccess_lockDomain() { + RegistryLockDao.save(createLock()); + action.run(); + assertThat(response.getStatus()).isEqualTo(SC_OK); + assertThat(reloadDomain().getStatusValues()).containsExactlyElementsIn(REGISTRY_LOCK_STATUSES); + assertThat(response.getPayload()).contains("Success: lock has been applied to example.tld"); + HistoryEntry historyEntry = getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_UPDATE); + assertThat(historyEntry.getRequestedByRegistrar()).isTrue(); + assertThat(historyEntry.getBySuperuser()).isFalse(); + assertThat(historyEntry.getReason()) + .isEqualTo("Lock or unlock of a domain through a RegistryLock operation"); + assertBillingEvent(historyEntry); + } + + @Test + public void testSuccess_unlockDomain() { + action = createAction(lockId, false); + domain = persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build()); + RegistryLockDao.save( + createLock().asBuilder().setUnlockRequestTimestamp(fakeClock.nowUtc()).build()); + action.run(); + assertThat(response.getStatus()).isEqualTo(SC_OK); + assertThat(response.getPayload()).contains("Success: unlock has been applied to example.tld"); + assertThat(reloadDomain().getStatusValues()).containsNoneIn(REGISTRY_LOCK_STATUSES); + HistoryEntry historyEntry = getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_UPDATE); + assertThat(historyEntry.getRequestedByRegistrar()).isTrue(); + assertThat(historyEntry.getBySuperuser()).isFalse(); + assertThat(historyEntry.getReason()) + .isEqualTo("Lock or unlock of a domain through a RegistryLock operation"); + assertBillingEvent(historyEntry); + } + + @Test + public void testSuccess_adminLock_createsOnlyHistoryEntry() { + action.authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, true)); + RegistryLockDao.save(createLock().asBuilder().isSuperuser(true).build()); + + action.run(); + HistoryEntry historyEntry = getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_UPDATE); + assertThat(historyEntry.getRequestedByRegistrar()).isFalse(); + assertThat(historyEntry.getBySuperuser()).isTrue(); + DatastoreHelper.assertNoBillingEvents(); + } + + @Test + public void testFailure_badVerificationCode() { + RegistryLockDao.save( + createLock().asBuilder().setVerificationCode(UUID.randomUUID().toString()).build()); + action.run(); + assertThat(response.getPayload()).contains("Failed: Invalid verification code"); + assertNoDomainChanges(); + } + + @Test + public void testFailure_alreadyVerified() { + RegistryLockDao.save( + createLock().asBuilder().setLockCompletionTimestamp(fakeClock.nowUtc()).build()); + action.run(); + assertThat(response.getPayload()).contains("Failed: Domain example.tld is already locked"); + assertNoDomainChanges(); + } + + @Test + public void testFailure_expired() { + RegistryLockDao.save(createLock()); + fakeClock.advanceBy(Duration.standardHours(2)); + action.run(); + assertThat(response.getPayload()) + .contains("Failed: The pending lock has expired; please try again"); + assertNoDomainChanges(); + } + + @Test + public void testFailure_nonAdmin_verifyingAdminLock() { + RegistryLockDao.save(createLock().asBuilder().isSuperuser(true).build()); + action.run(); + assertThat(response.getPayload()).contains("Failed: Non-admin user cannot complete admin lock"); + assertNoDomainChanges(); + } + + @Test + public void testFailure_alreadyUnlocked() { + action = createAction(lockId, false); + RegistryLockDao.save( + createLock() + .asBuilder() + .setLockCompletionTimestamp(fakeClock.nowUtc()) + .setUnlockRequestTimestamp(fakeClock.nowUtc()) + .setUnlockCompletionTimestamp(fakeClock.nowUtc()) + .build()); + action.run(); + assertThat(response.getPayload()).contains("Failed: Domain example.tld is already unlocked"); + assertNoDomainChanges(); + } + + @Test + public void testFailure_alreadyLocked() { + RegistryLockDao.save(createLock()); + domain = persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build()); + action.run(); + assertThat(response.getPayload()).contains("Failed: Domain example.tld is already locked"); + assertNoDomainChanges(); + } + + @Test + public void testFailure_notLoggedIn() { + action.authResult = AuthResult.NOT_AUTHENTICATED; + action.run(); + assertThat(response.getStatus()).isEqualTo(SC_MOVED_TEMPORARILY); + assertThat(response.getHeaders()).containsKey("Location"); + assertNoDomainChanges(); + } + + @Test + public void testFailure_doesNotChangeLockObject() { + // A failure when performing Datastore actions means that no actions should be taken in the + // Cloud SQL RegistryLock object + RegistryLock lock = createLock(); + RegistryLockDao.save(lock); + // reload the lock to pick up creation time + lock = RegistryLockDao.getByVerificationCode(lock.getVerificationCode()).get(); + fakeClock.advanceOneMilli(); + domain = persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build()); + action.run(); + // we would have failed during the Datastore segment of the action + assertThat(response.getPayload()).contains("Failed: Domain example.tld is already locked"); + + // verify that the changes to the SQL object were rolled back + RegistryLock afterAction = + RegistryLockDao.getByVerificationCode(lock.getVerificationCode()).get(); + assertThat(afterAction).isEqualTo(lock); + } + + @Test + public void testFailure_isLockTrue_shouldBeFalse() { + domain = persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build()); + RegistryLockDao.save( + createLock() + .asBuilder() + .setLockCompletionTimestamp(fakeClock.nowUtc()) + .setUnlockRequestTimestamp(fakeClock.nowUtc()) + .build()); + action.run(); + assertThat(response.getPayload()).contains("Failed: Domain example.tld is already locked"); + } + + @Test + public void testFailure_isLockFalse_shouldBeTrue() { + action = createAction(lockId, false); + RegistryLockDao.save(createLock()); + action.run(); + assertThat(response.getPayload()).contains("Failed: Domain example.tld is already unlocked"); + } + + @Test + public void testFailure_lock_unlock_lockAgain() { + RegistryLock lock = RegistryLockDao.save(createLock()); + action.run(); + assertThat(response.getPayload()).contains("Success: lock has been applied to example.tld"); + String unlockVerificationCode = "some-unlock-code"; + RegistryLockDao.save( + lock.asBuilder() + .setVerificationCode(unlockVerificationCode) + .setUnlockRequestTimestamp(fakeClock.nowUtc()) + .build()); + action = createAction(unlockVerificationCode, false); + action.run(); + assertThat(response.getPayload()).contains("Success: unlock has been applied to example.tld"); + action = createAction(lockId, true); + action.run(); + assertThat(response.getPayload()).contains("Failed: Invalid verification code"); + } + + @Test + public void testFailure_lock_lockAgain() { + RegistryLockDao.save(createLock()); + action.run(); + assertThat(response.getPayload()).contains("Success: lock has been applied to example.tld"); + action = createAction(lockId, true); + action.run(); + assertThat(response.getPayload()).contains("Failed: Domain example.tld is already locked"); + } + + @Test + public void testFailure_unlock_unlockAgain() { + action = createAction(lockId, false); + domain = persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build()); + RegistryLockDao.save( + createLock().asBuilder().setUnlockRequestTimestamp(fakeClock.nowUtc()).build()); + action.run(); + assertThat(response.getStatus()).isEqualTo(SC_OK); + assertThat(response.getPayload()).contains("Success: unlock has been applied to example.tld"); + action = createAction(lockId, false); + action.run(); + assertThat(response.getPayload()).contains("Failed: Domain example.tld is already unlocked"); + } + + private RegistryLock createLock() { + return new RegistryLock.Builder() + .setDomainName("example.tld") + .setRegistrarId("TheRegistrar") + .setRepoId("repoId") + .setRegistrarPocId("marla.singer@example.com") + .isSuperuser(false) + .setVerificationCode(lockId) + .build(); + } + + private DomainBase reloadDomain() { + return ofy().load().entity(domain).now(); + } + + private void assertNoDomainChanges() { + assertThat(reloadDomain()).isEqualTo(domain); + } + + private void assertBillingEvent(HistoryEntry historyEntry) { + DatastoreHelper.assertBillingEvents( + new BillingEvent.OneTime.Builder() + .setReason(Reason.SERVER_STATUS) + .setTargetId(domain.getForeignKey()) + .setClientId(domain.getCurrentSponsorClientId()) + .setCost(Registry.get(domain.getTld()).getServerStatusChangeCost()) + .setEventTime(fakeClock.nowUtc()) + .setBillingTime(fakeClock.nowUtc()) + .setParent(historyEntry) + .build()); + } + + private RegistryLockVerifyAction createAction(String lockVerificationCode, Boolean isLock) { + response = new FakeResponse(); + RegistryLockVerifyAction action = + new RegistryLockVerifyAction(fakeClock, lockVerificationCode, isLock); + authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, false)); + action.req = request; + action.response = response; + action.authResult = authResult; + action.userService = userService; + action.logoFilename = "logo.png"; + action.productName = "Nomulus"; + action.analyticsConfig = ImmutableMap.of("googleAnalyticsId", "sampleId"); + action.xsrfTokenManager = new XsrfTokenManager(new FakeClock(), action.userService); + return action; + } +} diff --git a/core/src/test/java/google/registry/webdriver/RegistrarConsoleScreenshotTest.java b/core/src/test/java/google/registry/webdriver/RegistrarConsoleScreenshotTest.java index f7a1fd13a..e2430d974 100644 --- a/core/src/test/java/google/registry/webdriver/RegistrarConsoleScreenshotTest.java +++ b/core/src/test/java/google/registry/webdriver/RegistrarConsoleScreenshotTest.java @@ -18,7 +18,9 @@ import static google.registry.server.Fixture.BASIC; import static google.registry.server.Route.route; import static google.registry.testing.AppEngineRule.makeRegistrar2; import static google.registry.testing.AppEngineRule.makeRegistrarContact2; +import static google.registry.testing.DatastoreHelper.createTld; import static google.registry.testing.DatastoreHelper.loadRegistrar; +import static google.registry.testing.DatastoreHelper.newDomainBase; import static google.registry.testing.DatastoreHelper.persistResource; import static google.registry.util.DateTimeUtils.START_OF_TIME; @@ -26,7 +28,9 @@ import com.google.common.collect.ImmutableMap; import com.googlecode.objectify.ObjectifyFilter; import google.registry.model.ofy.OfyFilter; import google.registry.model.registrar.Registrar.State; +import google.registry.model.registry.RegistryLockDao; import google.registry.module.frontend.FrontendServlet; +import google.registry.schema.domain.RegistryLock; import google.registry.server.RegistryTestServer; import google.registry.testing.CertificateSamples; import org.junit.Rule; @@ -46,7 +50,8 @@ public class RegistrarConsoleScreenshotTest extends WebDriverTestCase { .setRoutes( route("/registrar", FrontendServlet.class), route("/registrar-ote-status", FrontendServlet.class), - route("/registrar-settings", FrontendServlet.class)) + route("/registrar-settings", FrontendServlet.class), + route("/registry-lock-verify", FrontendServlet.class)) .setFilters(ObjectifyFilter.class, OfyFilter.class) .setFixtures(BASIC) .setEmail("Marla.Singer@google.com") @@ -370,4 +375,36 @@ public class RegistrarConsoleScreenshotTest extends WebDriverTestCase { Thread.sleep(500); driver.diffPage("page"); } + + @Test + public void registryLockVerify_success() throws Throwable { + String lockVerificationCode = "f1be78a2-2d61-458c-80f0-9dd8f2f8625f"; + server.runInAppEngineEnvironment( + () -> { + createTld("tld"); + persistResource(newDomainBase("example.tld")); + RegistryLockDao.save( + new RegistryLock.Builder() + .setRegistrarPocId("johndoe@theregistrar.com") + .setRepoId("repoId") + .setRegistrarId("TheRegistrar") + .setVerificationCode("f1be78a2-2d61-458c-80f0-9dd8f2f8625f") + .isSuperuser(false) + .setDomainName("example.tld") + .build()); + return null; + }); + driver.get( + server.getUrl( + "/registry-lock-verify?isLock=true&lockVerificationCode=" + lockVerificationCode)); + driver.waitForElement(By.id("reg-content")); + driver.diffPage("page"); + } + + @Test + public void registryLockVerify_unknownLock() throws Throwable { + driver.get(server.getUrl("/registry-lock-verify?isLock=true&lockVerificationCode=asdfasdf")); + driver.waitForElement(By.id("reg-content")); + driver.diffPage("page"); + } } diff --git a/core/src/test/resources/google/registry/module/frontend/frontend_routing.txt b/core/src/test/resources/google/registry/module/frontend/frontend_routing.txt index 3f94daeeb..6a7f9066e 100644 --- a/core/src/test/resources/google/registry/module/frontend/frontend_routing.txt +++ b/core/src/test/resources/google/registry/module/frontend/frontend_routing.txt @@ -6,3 +6,4 @@ PATH CLASS METHODS OK AUTH_METHODS /registrar-ote-status OteStatusAction POST n API,LEGACY USER PUBLIC /registrar-settings RegistrarSettingsAction POST n API,LEGACY USER PUBLIC /registry-lock-get RegistryLockGetAction GET n API,LEGACY USER PUBLIC +/registry-lock-verify RegistryLockVerifyAction GET n API,LEGACY USER PUBLIC diff --git a/core/src/test/resources/google/registry/webdriver/goldens/chrome-linux/RegistrarConsoleScreenshotTest_registryLockVerify_success_page.png b/core/src/test/resources/google/registry/webdriver/goldens/chrome-linux/RegistrarConsoleScreenshotTest_registryLockVerify_success_page.png new file mode 100644 index 0000000000000000000000000000000000000000..76acb814d0e43e1636f73937d0828dc394c44778 GIT binary patch literal 30226 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 Ke9c+!JO2yqQLc0V literal 0 HcmV?d00001