mirror of
https://github.com/google/nomulus.git
synced 2025-07-09 04:33:28 +02:00
Add a RegistryLockPostAction and tests (#459)
* Add a RegistryLockPostAction and tests * Response to CR and more robust email content test * Fix typo * Run save + email in a JPA transaction * Use action in subject
This commit is contained in:
parent
36e46097e3
commit
36787c37fb
6 changed files with 632 additions and 0 deletions
|
@ -84,6 +84,7 @@ public final class RegistryTestServer {
|
|||
route("/registrar-ote-status", FrontendServlet.class),
|
||||
route("/registrar-settings", FrontendServlet.class),
|
||||
route("/registry-lock-get", FrontendServlet.class),
|
||||
route("/registry-lock-post", FrontendServlet.class),
|
||||
route("/registry-lock-verify", FrontendServlet.class));
|
||||
|
||||
private static final ImmutableList<Class<? extends Filter>> FILTERS = ImmutableList.of(
|
||||
|
|
|
@ -0,0 +1,408 @@
|
|||
// 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.EppResourceUtils.loadByForeignKey;
|
||||
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.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import com.google.appengine.api.users.User;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.registry.RegistryLockDao;
|
||||
import google.registry.persistence.transaction.JpaTestRules;
|
||||
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationTestRule;
|
||||
import google.registry.request.JsonActionRunner;
|
||||
import google.registry.request.JsonResponse;
|
||||
import google.registry.request.ResponseImpl;
|
||||
import google.registry.request.auth.AuthLevel;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor.Role;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.schema.domain.RegistryLock;
|
||||
import google.registry.testing.AppEngineRule;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.SendEmailService;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
public final class RegistryLockPostActionTest {
|
||||
|
||||
private static final String EMAIL_MESSAGE_TEMPLATE =
|
||||
"Please click the link below to perform the lock \\/ unlock action on domain example.tld. "
|
||||
+ "Note: this code will expire in one hour.\n\n"
|
||||
+ "https:\\/\\/localhost\\/registry-lock-verify\\?lockVerificationCode="
|
||||
+ "[0-9a-zA-Z_\\-]+&isLock=(true|false)";
|
||||
|
||||
@Rule public final AppEngineRule appEngineRule = AppEngineRule.builder().withDatastore().build();
|
||||
|
||||
@Rule
|
||||
public final JpaIntegrationTestRule jpaRule =
|
||||
new JpaTestRules.Builder().buildIntegrationTestRule();
|
||||
|
||||
@Rule public final MockitoRule mocks = MockitoJUnit.rule();
|
||||
|
||||
private final User userWithoutPermission =
|
||||
new User("johndoe@theregistrar.com", "gmail.com", "31337");
|
||||
// Marla Singer has registry lock auth permissions
|
||||
private final User userWithLockPermission =
|
||||
new User("Marla.Singer@crr.com", "gmail.com", "31337");
|
||||
private final FakeClock clock = new FakeClock();
|
||||
|
||||
private InternetAddress outgoingAddress;
|
||||
private DomainBase domain;
|
||||
private RegistryLockPostAction action;
|
||||
|
||||
@Mock SendEmailService emailService;
|
||||
@Mock HttpServletResponse mockResponse;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
createTld("tld");
|
||||
domain = persistResource(newDomainBase("example.tld"));
|
||||
outgoingAddress = new InternetAddress("domain-registry@example.com");
|
||||
|
||||
action =
|
||||
createAction(
|
||||
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithLockPermission, false)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_lock() throws Exception {
|
||||
Map<String, ?> response = action.handleJsonRequest(lockRequest());
|
||||
assertSuccess(response, "lock", "Marla.Singer@crr.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_unlock() throws Exception {
|
||||
RegistryLockDao.save(
|
||||
createLock().asBuilder().setLockCompletionTimestamp(clock.nowUtc()).build());
|
||||
persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
|
||||
Map<String, ?> response = action.handleJsonRequest(unlockRequest());
|
||||
assertSuccess(response, "unlock", "Marla.Singer@crr.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_unlock_adminUnlockingAdmin() throws Exception {
|
||||
RegistryLockDao.save(
|
||||
createLock()
|
||||
.asBuilder()
|
||||
.isSuperuser(true)
|
||||
.setLockCompletionTimestamp(clock.nowUtc())
|
||||
.build());
|
||||
persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
|
||||
action =
|
||||
createAction(
|
||||
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithoutPermission, true)));
|
||||
Map<String, ?> response = action.handleJsonRequest(unlockRequest());
|
||||
assertSuccess(response, "unlock", "johndoe@theregistrar.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_unlock_noLock() {
|
||||
persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
|
||||
Map<String, ?> response = action.handleJsonRequest(unlockRequest());
|
||||
assertFailureWithMessage(response, "No lock object for domain example.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_unlock_alreadyUnlocked() {
|
||||
persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
|
||||
RegistryLockDao.save(
|
||||
createLock()
|
||||
.asBuilder()
|
||||
.setLockCompletionTimestamp(clock.nowUtc())
|
||||
.setUnlockRequestTimestamp(clock.nowUtc())
|
||||
.build());
|
||||
Map<String, ?> response = action.handleJsonRequest(unlockRequest());
|
||||
assertFailureWithMessage(response, "A pending unlock action already exists for example.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_unlock_nonAdminUnlockingAdmin() {
|
||||
RegistryLockDao.save(
|
||||
createLock()
|
||||
.asBuilder()
|
||||
.isSuperuser(true)
|
||||
.setLockCompletionTimestamp(clock.nowUtc())
|
||||
.build());
|
||||
persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
|
||||
Map<String, ?> response = action.handleJsonRequest(unlockRequest());
|
||||
assertFailureWithMessage(
|
||||
response, "Non-admin user cannot unlock admin-locked domain example.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_adminUser() throws Exception {
|
||||
// Admin user should be able to lock/unlock regardless
|
||||
action =
|
||||
createAction(
|
||||
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithoutPermission, true)));
|
||||
Map<String, ?> response = action.handleJsonRequest(lockRequest());
|
||||
assertSuccess(response, "lock", "johndoe@theregistrar.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_noInput() {
|
||||
Map<String, ?> response = action.handleJsonRequest(null);
|
||||
assertFailureWithMessage(response, "Null JSON");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_noClientId() {
|
||||
Map<String, ?> response = action.handleJsonRequest(ImmutableMap.of());
|
||||
assertFailureWithMessage(response, "Missing key for client: clientId");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_emptyClientId() {
|
||||
Map<String, ?> response = action.handleJsonRequest(ImmutableMap.of("clientId", ""));
|
||||
assertFailureWithMessage(response, "Missing key for client: clientId");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_noDomainName() {
|
||||
Map<String, ?> response =
|
||||
action.handleJsonRequest(
|
||||
ImmutableMap.of("clientId", "TheRegistrar", "password", "hi", "isLock", true));
|
||||
assertFailureWithMessage(response, "Missing key for fullyQualifiedDomainName");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_noLockParam() {
|
||||
Map<String, ?> response =
|
||||
action.handleJsonRequest(
|
||||
ImmutableMap.of(
|
||||
"clientId", "TheRegistrar",
|
||||
"fullyQualifiedDomainName", "example.tld",
|
||||
"password", "hi"));
|
||||
assertFailureWithMessage(response, "Missing key for isLock");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_notAllowedOnRegistrar() {
|
||||
persistResource(
|
||||
loadRegistrar("TheRegistrar").asBuilder().setRegistryLockAllowed(false).build());
|
||||
Map<String, ?> response = action.handleJsonRequest(lockRequest());
|
||||
assertFailureWithMessage(response, "Registry lock not allowed for this registrar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_noPassword() {
|
||||
Map<String, ?> response =
|
||||
action.handleJsonRequest(
|
||||
ImmutableMap.of(
|
||||
"clientId", "TheRegistrar",
|
||||
"fullyQualifiedDomainName", "example.tld",
|
||||
"isLock", true,
|
||||
"pocId", "Marla.Singer@crr.com"));
|
||||
assertFailureWithMessage(response, "Missing key for password");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_notEnabledForRegistrarContact() {
|
||||
Map<String, ?> response =
|
||||
action.handleJsonRequest(
|
||||
ImmutableMap.of(
|
||||
"clientId", "TheRegistrar",
|
||||
"fullyQualifiedDomainName", "example.tld",
|
||||
"isLock", true,
|
||||
"pocId", "johndoe@theregistrar.com",
|
||||
"password", "hi"));
|
||||
assertFailureWithMessage(response, "Incorrect registry lock password for contact");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_badPassword() {
|
||||
Map<String, ?> response =
|
||||
action.handleJsonRequest(
|
||||
ImmutableMap.of(
|
||||
"clientId", "TheRegistrar",
|
||||
"fullyQualifiedDomainName", "example.tld",
|
||||
"isLock", true,
|
||||
"pocId", "Marla.Singer@crr.com",
|
||||
"password", "badPassword"));
|
||||
assertFailureWithMessage(response, "Incorrect registry lock password for contact");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_invalidDomain() {
|
||||
Map<String, ?> response =
|
||||
action.handleJsonRequest(
|
||||
ImmutableMap.of(
|
||||
"clientId", "TheRegistrar",
|
||||
"fullyQualifiedDomainName", "bad.tld",
|
||||
"isLock", true,
|
||||
"pocId", "Marla.Singer@crr.com",
|
||||
"password", "hi"));
|
||||
assertFailureWithMessage(response, "Unknown domain bad.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_noPocId() {
|
||||
Map<String, ?> response =
|
||||
action.handleJsonRequest(
|
||||
ImmutableMap.of(
|
||||
"clientId", "TheRegistrar",
|
||||
"fullyQualifiedDomainName", "bad.tld",
|
||||
"isLock", true,
|
||||
"password", "hi"));
|
||||
assertFailureWithMessage(response, "Missing key for pocId");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_invalidPocId() {
|
||||
Map<String, ?> response =
|
||||
action.handleJsonRequest(
|
||||
ImmutableMap.of(
|
||||
"clientId", "TheRegistrar",
|
||||
"fullyQualifiedDomainName", "bad.tld",
|
||||
"isLock", true,
|
||||
"pocId", "someotherpoc@crr.com",
|
||||
"password", "hi"));
|
||||
assertFailureWithMessage(response, "Unknown registrar POC ID someotherpoc@crr.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_previousLockUnlocked() throws Exception {
|
||||
RegistryLockDao.save(
|
||||
createLock()
|
||||
.asBuilder()
|
||||
.setLockCompletionTimestamp(clock.nowUtc().minusMinutes(1))
|
||||
.setUnlockRequestTimestamp(clock.nowUtc().minusMinutes(1))
|
||||
.setUnlockCompletionTimestamp(clock.nowUtc().minusMinutes(1))
|
||||
.build());
|
||||
|
||||
Map<String, ?> response = action.handleJsonRequest(lockRequest());
|
||||
assertSuccess(response, "lock", "Marla.Singer@crr.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_previousLockExpired() throws Exception {
|
||||
RegistryLock previousLock = RegistryLockDao.save(createLock());
|
||||
previousLock = RegistryLockDao.getByVerificationCode(previousLock.getVerificationCode()).get();
|
||||
clock.setTo(previousLock.getLockRequestTimestamp().plusHours(2));
|
||||
Map<String, ?> response = action.handleJsonRequest(lockRequest());
|
||||
assertSuccess(response, "lock", "Marla.Singer@crr.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_alreadyPendingLock() {
|
||||
RegistryLockDao.save(createLock());
|
||||
Map<String, ?> response = action.handleJsonRequest(lockRequest());
|
||||
assertFailureWithMessage(
|
||||
response, "A pending or completed lock action already exists for example.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_alreadyLocked() {
|
||||
persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
|
||||
Map<String, ?> response = action.handleJsonRequest(lockRequest());
|
||||
assertFailureWithMessage(response, "Domain example.tld is already locked");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_alreadyUnlocked() {
|
||||
Map<String, ?> response = action.handleJsonRequest(unlockRequest());
|
||||
assertFailureWithMessage(response, "Domain example.tld is already unlocked");
|
||||
}
|
||||
|
||||
private ImmutableMap<String, Object> lockRequest() {
|
||||
return fullRequest(true);
|
||||
}
|
||||
|
||||
private ImmutableMap<String, Object> unlockRequest() {
|
||||
return fullRequest(false);
|
||||
}
|
||||
|
||||
private ImmutableMap<String, Object> fullRequest(boolean lock) {
|
||||
return ImmutableMap.of(
|
||||
"isLock", lock,
|
||||
"clientId", "TheRegistrar",
|
||||
"fullyQualifiedDomainName", "example.tld",
|
||||
"pocId", "Marla.Singer@crr.com",
|
||||
"password", "hi");
|
||||
}
|
||||
|
||||
private RegistryLock createLock() {
|
||||
DomainBase domain = loadByForeignKey(DomainBase.class, "example.tld", clock.nowUtc()).get();
|
||||
return new RegistryLock.Builder()
|
||||
.setDomainName("example.tld")
|
||||
.isSuperuser(false)
|
||||
.setVerificationCode(UUID.randomUUID().toString())
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.setRepoId(domain.getRepoId())
|
||||
.setRegistrarPocId("Marla.Singer@crr.com")
|
||||
.build();
|
||||
}
|
||||
|
||||
private void assertSuccess(Map<String, ?> response, String lockAction, String recipient)
|
||||
throws Exception {
|
||||
assertThat(response)
|
||||
.containsExactly(
|
||||
"status", "SUCCESS",
|
||||
"results", ImmutableList.of(),
|
||||
"message", String.format("Successful %s", lockAction));
|
||||
verifyEmail(recipient);
|
||||
}
|
||||
|
||||
private void assertFailureWithMessage(Map<String, ?> response, String message) {
|
||||
assertThat(response)
|
||||
.containsExactly("status", "ERROR", "results", ImmutableList.of(), "message", message);
|
||||
verifyNoMoreInteractions(emailService);
|
||||
}
|
||||
|
||||
private void verifyEmail(String recipient) throws Exception {
|
||||
ArgumentCaptor<EmailMessage> emailCaptor = ArgumentCaptor.forClass(EmailMessage.class);
|
||||
verify(emailService).sendEmail(emailCaptor.capture());
|
||||
EmailMessage sentMessage = emailCaptor.getValue();
|
||||
assertThat(sentMessage.subject()).matches("Registry (un)?lock verification");
|
||||
assertThat(sentMessage.body()).matches(EMAIL_MESSAGE_TEMPLATE);
|
||||
assertThat(sentMessage.from()).isEqualTo(new InternetAddress("domain-registry@example.com"));
|
||||
assertThat(sentMessage.recipients()).containsExactly(new InternetAddress(recipient));
|
||||
}
|
||||
|
||||
private RegistryLockPostAction createAction(AuthResult authResult) {
|
||||
AuthenticatedRegistrarAccessor registrarAccessor =
|
||||
AuthenticatedRegistrarAccessor.createForTesting(
|
||||
ImmutableSetMultimap.of("TheRegistrar", Role.OWNER, "NewRegistrar", Role.OWNER));
|
||||
JsonActionRunner jsonActionRunner =
|
||||
new JsonActionRunner(ImmutableMap.of(), new JsonResponse(new ResponseImpl(mockResponse)));
|
||||
return new RegistryLockPostAction(
|
||||
jsonActionRunner, authResult, registrarAccessor, emailService, clock, outgoingAddress);
|
||||
}
|
||||
}
|
|
@ -6,4 +6,5 @@ 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-post RegistryLockPostAction POST n API,LEGACY USER PUBLIC
|
||||
/registry-lock-verify RegistryLockVerifyAction GET n API,LEGACY USER PUBLIC
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue