mirror of
https://github.com/google/nomulus.git
synced 2025-07-09 12:43:24 +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
|
@ -61,6 +61,11 @@
|
|||
<url-pattern>/registry-lock-get</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>frontend-servlet</servlet-name>
|
||||
<url-pattern>/registry-lock-post</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>frontend-servlet</servlet-name>
|
||||
<url-pattern>/registry-lock-verify</url-pattern>
|
||||
|
|
|
@ -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.RegistryLockPostAction;
|
||||
import google.registry.ui.server.registrar.RegistryLockVerifyAction;
|
||||
|
||||
/** Dagger component with per-request lifetime for "default" App Engine module. */
|
||||
|
@ -54,6 +55,8 @@ interface FrontendRequestComponent {
|
|||
|
||||
RegistryLockGetAction registryLockGetAction();
|
||||
|
||||
RegistryLockPostAction registryLockPostAction();
|
||||
|
||||
RegistryLockVerifyAction registryLockVerifyAction();
|
||||
|
||||
@Subcomponent.Builder
|
||||
|
|
|
@ -0,0 +1,214 @@
|
|||
// 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.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.security.JsonResponseHelper.Status.ERROR;
|
||||
import static google.registry.security.JsonResponseHelper.Status.SUCCESS;
|
||||
import static google.registry.ui.server.registrar.RegistrarConsoleModule.PARAM_CLIENT_ID;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarContact;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.Method;
|
||||
import google.registry.request.JsonActionRunner;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor.RegistrarAccessDeniedException;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.schema.domain.RegistryLock;
|
||||
import google.registry.security.JsonResponseHelper;
|
||||
import google.registry.tools.DomainLockUtils;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.SendEmailService;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import javax.mail.internet.AddressException;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
|
||||
/**
|
||||
* UI action that allows for creating registry locks. Locks / unlocks must be verified separately
|
||||
* before they are written permanently.
|
||||
*
|
||||
* <p>Note: at the moment we have no mechanism for JSON GET/POSTs in the same class or at the same
|
||||
* URL, which is why this is distinct from the {@link RegistryLockGetAction}.
|
||||
*/
|
||||
@Action(
|
||||
service = Action.Service.DEFAULT,
|
||||
path = RegistryLockPostAction.PATH,
|
||||
method = Method.POST,
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public class RegistryLockPostAction implements Runnable, JsonActionRunner.JsonAction {
|
||||
public static final String PATH = "/registry-lock-post";
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private static final Gson GSON = new Gson();
|
||||
|
||||
private static final URL URL_BASE = RegistryConfig.getDefaultServer();
|
||||
private static final String VERIFICATION_EMAIL_TEMPLATE =
|
||||
"Please click the link below to perform the lock / unlock action on domain %s. Note: "
|
||||
+ "this code will expire in one hour.\n\n%s";
|
||||
|
||||
private final JsonActionRunner jsonActionRunner;
|
||||
private final AuthResult authResult;
|
||||
private final AuthenticatedRegistrarAccessor registrarAccessor;
|
||||
private final SendEmailService sendEmailService;
|
||||
private final Clock clock;
|
||||
private final InternetAddress gSuiteOutgoingEmailAddress;
|
||||
|
||||
@Inject
|
||||
RegistryLockPostAction(
|
||||
JsonActionRunner jsonActionRunner,
|
||||
AuthResult authResult,
|
||||
AuthenticatedRegistrarAccessor registrarAccessor,
|
||||
SendEmailService sendEmailService,
|
||||
Clock clock,
|
||||
@Config("gSuiteOutgoingEmailAddress") InternetAddress gSuiteOutgoingEmailAddress) {
|
||||
this.jsonActionRunner = jsonActionRunner;
|
||||
this.authResult = authResult;
|
||||
this.registrarAccessor = registrarAccessor;
|
||||
this.sendEmailService = sendEmailService;
|
||||
this.clock = clock;
|
||||
this.gSuiteOutgoingEmailAddress = gSuiteOutgoingEmailAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
jsonActionRunner.run(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ?> handleJsonRequest(Map<String, ?> input) {
|
||||
try {
|
||||
checkArgumentNotNull(input, "Null JSON");
|
||||
RegistryLockPostInput postInput =
|
||||
GSON.fromJson(GSON.toJsonTree(input), RegistryLockPostInput.class);
|
||||
checkArgument(
|
||||
!Strings.isNullOrEmpty(postInput.clientId),
|
||||
"Missing key for client: %s",
|
||||
PARAM_CLIENT_ID);
|
||||
checkArgument(
|
||||
!Strings.isNullOrEmpty(postInput.fullyQualifiedDomainName),
|
||||
"Missing key for fullyQualifiedDomainName");
|
||||
checkNotNull(postInput.isLock, "Missing key for isLock");
|
||||
checkArgumentPresent(authResult.userAuthInfo(), "User is not logged in");
|
||||
|
||||
boolean isAdmin = authResult.userAuthInfo().get().isUserAdmin();
|
||||
verifyRegistryLockPassword(postInput);
|
||||
jpaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
RegistryLock registryLock =
|
||||
postInput.isLock
|
||||
? DomainLockUtils.createRegistryLockRequest(
|
||||
postInput.fullyQualifiedDomainName,
|
||||
postInput.clientId,
|
||||
postInput.pocId,
|
||||
isAdmin,
|
||||
clock)
|
||||
: DomainLockUtils.createRegistryUnlockRequest(
|
||||
postInput.fullyQualifiedDomainName, postInput.clientId, isAdmin, clock);
|
||||
sendVerificationEmail(registryLock, postInput.isLock);
|
||||
});
|
||||
String action = postInput.isLock ? "lock" : "unlock";
|
||||
return JsonResponseHelper.create(SUCCESS, String.format("Successful %s", action));
|
||||
} catch (Throwable e) {
|
||||
logger.atWarning().withCause(e).log("Failed to lock/unlock domain with input %s", input);
|
||||
return JsonResponseHelper.create(
|
||||
ERROR,
|
||||
Optional.ofNullable(Throwables.getRootCause(e).getMessage()).orElse("Unspecified error"));
|
||||
}
|
||||
}
|
||||
|
||||
private void sendVerificationEmail(RegistryLock lock, boolean isLock) {
|
||||
try {
|
||||
String url =
|
||||
new URIBuilder()
|
||||
.setScheme("https")
|
||||
.setHost(URL_BASE.getHost())
|
||||
.setPath("registry-lock-verify")
|
||||
.setParameter("lockVerificationCode", lock.getVerificationCode())
|
||||
.setParameter("isLock", String.valueOf(isLock))
|
||||
.build()
|
||||
.toString();
|
||||
String body = String.format(VERIFICATION_EMAIL_TEMPLATE, lock.getDomainName(), url);
|
||||
ImmutableList<InternetAddress> recipients =
|
||||
ImmutableList.of(
|
||||
new InternetAddress(authResult.userAuthInfo().get().user().getEmail(), true));
|
||||
String action = isLock ? "lock" : "unlock";
|
||||
sendEmailService.sendEmail(
|
||||
EmailMessage.newBuilder()
|
||||
.setBody(body)
|
||||
.setSubject(String.format("Registry %s verification", action))
|
||||
.setRecipients(recipients)
|
||||
.setFrom(gSuiteOutgoingEmailAddress)
|
||||
.build());
|
||||
} catch (AddressException | URISyntaxException e) {
|
||||
throw new RuntimeException(e); // caught above -- this is so we can run in a transaction
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyRegistryLockPassword(RegistryLockPostInput postInput)
|
||||
throws RegistrarAccessDeniedException {
|
||||
// Verify that the user can access the registrar and that the user is either an admin or has
|
||||
// registry lock enabled and provided a correct password
|
||||
checkArgument(authResult.userAuthInfo().isPresent(), "Auth result not present");
|
||||
Registrar registrar = registrarAccessor.getRegistrar(postInput.clientId);
|
||||
checkArgument(
|
||||
registrar.isRegistryLockAllowed(), "Registry lock not allowed for this registrar");
|
||||
UserAuthInfo userAuthInfo = authResult.userAuthInfo().get();
|
||||
if (!userAuthInfo.isUserAdmin()) {
|
||||
checkArgument(!Strings.isNullOrEmpty(postInput.pocId), "Missing key for pocId");
|
||||
checkArgument(!Strings.isNullOrEmpty(postInput.password), "Missing key for password");
|
||||
RegistrarContact registrarContact =
|
||||
registrar.getContacts().stream()
|
||||
.filter(contact -> contact.getEmailAddress().equals(postInput.pocId))
|
||||
.findFirst()
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
String.format("Unknown registrar POC ID %s", postInput.pocId)));
|
||||
checkArgument(
|
||||
registrarContact.verifyRegistryLockPassword(postInput.password),
|
||||
"Incorrect registry lock password for contact");
|
||||
}
|
||||
}
|
||||
|
||||
/** Value class that represents the expected input body from the UI request. */
|
||||
private static class RegistryLockPostInput {
|
||||
private String clientId;
|
||||
private String fullyQualifiedDomainName;
|
||||
private Boolean isLock;
|
||||
private String pocId;
|
||||
private String password;
|
||||
}
|
||||
}
|
|
@ -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