1
0
mirror of https://github.com/google/nomulus synced 2026-06-05 14:32:51 +00:00

Add a reg-lock verification action to the new console (#2467)

The front end will have a (hidden) page that passes the verification
code to this API endpoint and displays the result.
This commit is contained in:
gbrodman
2024-07-08 17:25:22 -04:00
committed by GitHub
parent b602aac09a
commit b8a6ac72dd
9 changed files with 337 additions and 43 deletions

View File

@@ -23,6 +23,7 @@ import com.google.common.base.Throwables;
import com.google.common.net.MediaType;
import google.registry.request.Response;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -69,7 +70,7 @@ public final class FakeResponse implements Response {
@Override
public void sendRedirect(String url) throws IOException {
status = 302;
status = HttpServletResponse.SC_FOUND;
this.payload = String.format("Redirected to %s", url);
}

View File

@@ -80,7 +80,7 @@ public class ConsoleRegistryLockActionTest {
Please click the link below to perform the lock / unlock action on domain example.test. \
Note: this code will expire in one hour.
https://registrarconsole.tld/console-api/registry-lock-verify?lockVerificationCode=\
https://registrarconsole.tld/console/#/registry-lock-verify?lockVerificationCode=\
123456789ABCDEFGHJKLMNPQRSTUVWXY""";
private static final Gson GSON = RequestModule.provideGson();
@@ -122,15 +122,7 @@ public class ConsoleRegistryLockActionTest {
@Test
void testGet_simpleLock() {
saveRegistryLock(
new RegistryLock.Builder()
.setRepoId("repoId")
.setDomainName("example.test")
.setRegistrarId("TheRegistrar")
.setVerificationCode("123456789ABCDEFGHJKLMNPQRSTUVWXY")
.setRegistrarPocId("johndoe@theregistrar.com")
.setLockCompletionTime(fakeClock.nowUtc())
.build());
saveRegistryLock(createDefaultLockBuilder().setLockCompletionTime(fakeClock.nowUtc()).build());
action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getPayload())

View File

@@ -0,0 +1,220 @@
// Copyright 2024 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.console;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.loadByEntity;
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.SqlHelper.saveRegistryLock;
import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES;
import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import google.registry.model.console.RegistrarRole;
import google.registry.model.console.User;
import google.registry.model.console.UserRoles;
import google.registry.model.domain.Domain;
import google.registry.model.domain.RegistryLock;
import google.registry.model.eppcommon.StatusValue;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.request.RequestModule;
import google.registry.request.auth.AuthResult;
import google.registry.testing.CloudTasksHelper;
import google.registry.testing.ConsoleApiParamsUtils;
import google.registry.testing.DeterministicStringGenerator;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeResponse;
import google.registry.tools.DomainLockUtils;
import google.registry.ui.server.registrar.ConsoleApiParams;
import google.registry.util.StringGenerator;
import jakarta.servlet.http.HttpServletResponse;
import org.joda.time.Duration;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Tests for {@link ConsoleRegistryLockVerifyAction}. */
public class ConsoleRegistryLockVerifyActionTest {
private static final String DEFAULT_CODE = "123456789ABCDEFGHJKLMNPQRSTUUUUU";
private static final Gson GSON = RequestModule.provideGson();
private final FakeClock fakeClock = new FakeClock();
@RegisterExtension
final JpaTestExtensions.JpaIntegrationTestExtension jpa =
new JpaTestExtensions.Builder().withClock(fakeClock).buildIntegrationTestExtension();
private FakeResponse response;
private Domain defaultDomain;
private User user;
private ConsoleRegistryLockVerifyAction action;
@BeforeEach
void beforeEach() {
createTld("test");
defaultDomain = persistActiveDomain("example.test");
user =
new User.Builder()
.setEmailAddress("user@theregistrar.com")
.setRegistryLockEmailAddress("registrylock@theregistrar.com")
.setUserRoles(
new UserRoles.Builder()
.setRegistrarRoles(
ImmutableMap.of("TheRegistrar", RegistrarRole.PRIMARY_CONTACT))
.build())
.setRegistryLockPassword("registryLockPassword")
.build();
action = createAction(DEFAULT_CODE);
}
@Test
void testSuccess_lock() {
saveRegistryLock(createDefaultLockBuilder().build());
action.run();
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
assertThat(response.getPayload())
.isEqualTo(
"{\"action\":\"unlocked\",\"domainName\":\"example.test\",\"registrarId\":\"TheRegistrar\"}");
assertThat(loadByEntity(defaultDomain).getStatusValues())
.containsAtLeastElementsIn(REGISTRY_LOCK_STATUSES);
}
@Test
void testSuccess_unlock() {
persistResource(defaultDomain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
saveRegistryLock(
createDefaultLockBuilder()
.setLockCompletionTime(fakeClock.nowUtc())
.setUnlockRequestTime(fakeClock.nowUtc())
.build());
action.run();
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
assertThat(response.getPayload())
.isEqualTo(
"{\"action\":\"unlocked\",\"domainName\":\"example.test\",\"registrarId\":\"TheRegistrar\"}");
assertThat(loadByEntity(defaultDomain).getStatusValues()).containsExactly(StatusValue.INACTIVE);
}
@Test
void testSuccess_admin_lock() {
saveRegistryLock(createDefaultLockBuilder().isSuperuser(true).build());
user =
user.asBuilder()
.setUserRoles(user.getUserRoles().asBuilder().setIsAdmin(true).build())
.build();
action = createAction(DEFAULT_CODE);
action.run();
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
assertThat(response.getPayload())
.isEqualTo(
"{\"action\":\"unlocked\",\"domainName\":\"example.test\",\"registrarId\":\"TheRegistrar\"}");
assertThat(loadByEntity(defaultDomain).getStatusValues())
.containsAtLeastElementsIn(REGISTRY_LOCK_STATUSES);
}
@Test
void testSuccess_admin_unlock() {
persistResource(defaultDomain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
saveRegistryLock(
createDefaultLockBuilder()
.isSuperuser(true)
.setLockCompletionTime(fakeClock.nowUtc())
.setUnlockRequestTime(fakeClock.nowUtc())
.build());
user =
user.asBuilder()
.setUserRoles(user.getUserRoles().asBuilder().setIsAdmin(true).build())
.build();
action = createAction(DEFAULT_CODE);
action.run();
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
assertThat(response.getPayload())
.isEqualTo(
"{\"action\":\"unlocked\",\"domainName\":\"example.test\",\"registrarId\":\"TheRegistrar\"}");
assertThat(loadByEntity(defaultDomain).getStatusValues()).containsExactly(StatusValue.INACTIVE);
}
@Test
void testFailure_invalidCode() {
saveRegistryLock(createDefaultLockBuilder().setVerificationCode("foobar").build());
action.run();
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
assertThat(response.getPayload())
.isEqualTo("Invalid verification code 123456789ABCDEFGHJKLMNPQRSTUUUUU");
assertThat(loadByEntity(defaultDomain).getStatusValues()).containsExactly(StatusValue.INACTIVE);
}
@Test
void testFailure_expiredLock() {
saveRegistryLock(createDefaultLockBuilder().build());
fakeClock.advanceBy(Duration.standardDays(1));
action.run();
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
assertThat(response.getPayload()).isEqualTo("The pending lock has expired; please try again");
assertThat(loadByEntity(defaultDomain).getStatusValues()).containsExactly(StatusValue.INACTIVE);
}
@Test
void testFailure_nonAdmin_lock() {
saveRegistryLock(createDefaultLockBuilder().isSuperuser(true).build());
action.run();
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
assertThat(response.getPayload()).isEqualTo("Non-admin user cannot complete admin lock");
assertThat(loadByEntity(defaultDomain).getStatusValues()).containsExactly(StatusValue.INACTIVE);
}
@Test
void testFailure_nonAdmin_unlock() {
persistResource(defaultDomain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
saveRegistryLock(
createDefaultLockBuilder()
.isSuperuser(true)
.setLockCompletionTime(fakeClock.nowUtc())
.setUnlockRequestTime(fakeClock.nowUtc())
.build());
action.run();
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
assertThat(response.getPayload()).isEqualTo("Non-admin user cannot complete admin unlock");
assertThat(loadByEntity(defaultDomain).getStatusValues())
.containsAtLeastElementsIn(REGISTRY_LOCK_STATUSES);
}
private RegistryLock.Builder createDefaultLockBuilder() {
return new RegistryLock.Builder()
.setRepoId(defaultDomain.getRepoId())
.setDomainName(defaultDomain.getDomainName())
.setRegistrarId(defaultDomain.getCurrentSponsorRegistrarId())
.setRegistrarPocId("johndoe@theregistrar.com")
.setVerificationCode(DEFAULT_CODE);
}
private ConsoleRegistryLockVerifyAction createAction(String verificationCode) {
AuthResult authResult = AuthResult.createUser(user);
ConsoleApiParams params = ConsoleApiParamsUtils.createFake(authResult);
when(params.request().getMethod()).thenReturn("GET");
when(params.request().getServerName()).thenReturn("registrarconsole.tld");
DomainLockUtils domainLockUtils =
new DomainLockUtils(
new DeterministicStringGenerator(StringGenerator.Alphabets.BASE_58),
"adminreg",
new CloudTasksHelper(fakeClock).getTestCloudTasksUtils());
response = (FakeResponse) params.response();
return new ConsoleRegistryLockVerifyAction(params, domainLockUtils, GSON, verificationCode);
}
}

View File

@@ -1,21 +1,22 @@
PATH CLASS METHODS OK MIN USER_POLICY
/_dr/epp EppTlsAction POST n APP ADMIN
/console-api/domain ConsoleDomainGetAction GET n USER PUBLIC
/console-api/domain-list ConsoleDomainListAction GET n USER PUBLIC
/console-api/dum-download ConsoleDumDownloadAction GET n USER PUBLIC
/console-api/eppPassword ConsoleEppPasswordAction POST n USER PUBLIC
/console-api/registrar ConsoleUpdateRegistrarAction POST n USER PUBLIC
/console-api/registrars RegistrarsAction GET,POST n USER PUBLIC
/console-api/registry-lock ConsoleRegistryLockAction GET,POST n USER PUBLIC
/console-api/settings/contacts ContactAction GET,POST n USER PUBLIC
/console-api/settings/security SecurityAction POST n USER PUBLIC
/console-api/settings/whois-fields WhoisRegistrarFieldsAction POST n USER PUBLIC
/console-api/userdata ConsoleUserDataAction GET n USER PUBLIC
/registrar ConsoleUiAction GET n USER PUBLIC
/registrar-create ConsoleRegistrarCreatorAction POST,GET n USER PUBLIC
/registrar-ote-setup ConsoleOteSetupAction POST,GET n USER PUBLIC
/registrar-ote-status OteStatusAction POST n USER PUBLIC
/registrar-settings RegistrarSettingsAction POST n USER PUBLIC
/registry-lock-get RegistryLockGetAction GET n USER PUBLIC
/registry-lock-post RegistryLockPostAction POST n USER PUBLIC
/registry-lock-verify RegistryLockVerifyAction GET n USER PUBLIC
PATH CLASS METHODS OK MIN USER_POLICY
/_dr/epp EppTlsAction POST n APP ADMIN
/console-api/domain ConsoleDomainGetAction GET n USER PUBLIC
/console-api/domain-list ConsoleDomainListAction GET n USER PUBLIC
/console-api/dum-download ConsoleDumDownloadAction GET n USER PUBLIC
/console-api/eppPassword ConsoleEppPasswordAction POST n USER PUBLIC
/console-api/registrar ConsoleUpdateRegistrarAction POST n USER PUBLIC
/console-api/registrars RegistrarsAction GET,POST n USER PUBLIC
/console-api/registry-lock ConsoleRegistryLockAction GET,POST n USER PUBLIC
/console-api/registry-lock-verify ConsoleRegistryLockVerifyAction GET n USER PUBLIC
/console-api/settings/contacts ContactAction GET,POST n USER PUBLIC
/console-api/settings/security SecurityAction POST n USER PUBLIC
/console-api/settings/whois-fields WhoisRegistrarFieldsAction POST n USER PUBLIC
/console-api/userdata ConsoleUserDataAction GET n USER PUBLIC
/registrar ConsoleUiAction GET n USER PUBLIC
/registrar-create ConsoleRegistrarCreatorAction POST,GET n USER PUBLIC
/registrar-ote-setup ConsoleOteSetupAction POST,GET n USER PUBLIC
/registrar-ote-status OteStatusAction POST n USER PUBLIC
/registrar-settings RegistrarSettingsAction POST n USER PUBLIC
/registry-lock-get RegistryLockGetAction GET n USER PUBLIC
/registry-lock-post RegistryLockPostAction POST n USER PUBLIC
/registry-lock-verify RegistryLockVerifyAction GET n USER PUBLIC

View File

@@ -63,6 +63,7 @@ PATH CLASS
/console-api/registrar ConsoleUpdateRegistrarAction POST n USER PUBLIC
/console-api/registrars RegistrarsAction GET,POST n USER PUBLIC
/console-api/registry-lock ConsoleRegistryLockAction GET,POST n USER PUBLIC
/console-api/registry-lock-verify ConsoleRegistryLockVerifyAction GET n USER PUBLIC
/console-api/settings/contacts ContactAction GET,POST n USER PUBLIC
/console-api/settings/security SecurityAction POST n USER PUBLIC
/console-api/settings/whois-fields WhoisRegistrarFieldsAction POST n USER PUBLIC