Skip to content

Commit b8a6ac7

Browse files
authored
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.
1 parent b602aac commit b8a6ac7

9 files changed

Lines changed: 337 additions & 43 deletions

File tree

core/src/main/java/google/registry/module/RequestComponent.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@
114114
import google.registry.ui.server.console.ConsoleDumDownloadAction;
115115
import google.registry.ui.server.console.ConsoleEppPasswordAction;
116116
import google.registry.ui.server.console.ConsoleRegistryLockAction;
117+
import google.registry.ui.server.console.ConsoleRegistryLockVerifyAction;
117118
import google.registry.ui.server.console.ConsoleUpdateRegistrarAction;
118119
import google.registry.ui.server.console.ConsoleUserDataAction;
119120
import google.registry.ui.server.console.RegistrarsAction;
@@ -191,6 +192,8 @@ interface RequestComponent {
191192

192193
ConsoleRegistryLockAction consoleRegistryLockAction();
193194

195+
ConsoleRegistryLockVerifyAction consoleRegistryLockVerifyAction();
196+
194197
ConsoleUiAction consoleUiAction();
195198

196199
ConsoleUpdateRegistrarAction consoleUpdateRegistrarAction();

core/src/main/java/google/registry/module/frontend/FrontendRequestComponent.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import google.registry.ui.server.console.ConsoleDumDownloadAction;
3131
import google.registry.ui.server.console.ConsoleEppPasswordAction;
3232
import google.registry.ui.server.console.ConsoleRegistryLockAction;
33+
import google.registry.ui.server.console.ConsoleRegistryLockVerifyAction;
3334
import google.registry.ui.server.console.ConsoleUpdateRegistrarAction;
3435
import google.registry.ui.server.console.ConsoleUserDataAction;
3536
import google.registry.ui.server.console.RegistrarsAction;
@@ -69,6 +70,8 @@ public interface FrontendRequestComponent {
6970

7071
ConsoleRegistryLockAction consoleRegistryLockAction();
7172

73+
ConsoleRegistryLockVerifyAction consoleRegistryLockVerifyAction();
74+
7275
ConsoleUiAction consoleUiAction();
7376

7477
ConsoleUpdateRegistrarAction consoleUpdateRegistrarAction();

core/src/main/java/google/registry/ui/server/console/ConsoleRegistryLockAction.java

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,8 @@
4747
import jakarta.mail.internet.AddressException;
4848
import jakarta.mail.internet.InternetAddress;
4949
import jakarta.servlet.http.HttpServletRequest;
50-
import java.net.URISyntaxException;
5150
import java.util.Optional;
5251
import javax.inject.Inject;
53-
import org.apache.http.client.utils.URIBuilder;
5452
import org.joda.time.Duration;
5553

5654
/**
@@ -153,14 +151,9 @@ protected void postHandler(User user) {
153151
private void sendVerificationEmail(RegistryLock lock, String userEmail, boolean isLock) {
154152
try {
155153
String url =
156-
new URIBuilder()
157-
.setScheme("https")
158-
.setHost(consoleApiParams.request().getServerName())
159-
// TODO: replace this with the PATH in ConsoleRegistryLockVerifyAction once it exists
160-
.setPath("/console-api/registry-lock-verify")
161-
.setParameter("lockVerificationCode", lock.getVerificationCode())
162-
.build()
163-
.toString();
154+
String.format(
155+
"https://%s/console/#/registry-lock-verify?lockVerificationCode=%s",
156+
consoleApiParams.request().getServerName(), lock.getVerificationCode());
164157
String body = String.format(VERIFICATION_EMAIL_TEMPLATE, lock.getDomainName(), url);
165158
ImmutableList<InternetAddress> recipients =
166159
ImmutableList.of(new InternetAddress(userEmail, true));
@@ -171,7 +164,7 @@ private void sendVerificationEmail(RegistryLock lock, String userEmail, boolean
171164
.setSubject(String.format("Registry %s verification", action))
172165
.setRecipients(recipients)
173166
.build());
174-
} catch (AddressException | URISyntaxException e) {
167+
} catch (AddressException e) {
175168
throw new RuntimeException(e); // caught above -- this is so we can run in a transaction
176169
}
177170
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Copyright 2024 The Nomulus Authors. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package google.registry.ui.server.console;
16+
17+
import static google.registry.request.Action.Method.GET;
18+
19+
import com.google.common.base.Ascii;
20+
import com.google.gson.Gson;
21+
import com.google.gson.annotations.Expose;
22+
import google.registry.model.console.User;
23+
import google.registry.model.domain.RegistryLock;
24+
import google.registry.request.Action;
25+
import google.registry.request.Parameter;
26+
import google.registry.request.auth.Auth;
27+
import google.registry.tools.DomainLockUtils;
28+
import google.registry.ui.server.registrar.ConsoleApiParams;
29+
import jakarta.servlet.http.HttpServletResponse;
30+
import javax.inject.Inject;
31+
32+
/** Handler for verifying registry lock requests, a form of 2FA. */
33+
@Action(
34+
service = Action.Service.DEFAULT,
35+
path = ConsoleRegistryLockVerifyAction.PATH,
36+
method = {GET},
37+
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
38+
public class ConsoleRegistryLockVerifyAction extends ConsoleApiAction {
39+
40+
static final String PATH = "/console-api/registry-lock-verify";
41+
42+
private final DomainLockUtils domainLockUtils;
43+
private final Gson gson;
44+
private final String lockVerificationCode;
45+
46+
@Inject
47+
public ConsoleRegistryLockVerifyAction(
48+
ConsoleApiParams consoleApiParams,
49+
DomainLockUtils domainLockUtils,
50+
Gson gson,
51+
@Parameter("lockVerificationCode") String lockVerificationCode) {
52+
super(consoleApiParams);
53+
this.domainLockUtils = domainLockUtils;
54+
this.gson = gson;
55+
this.lockVerificationCode = lockVerificationCode;
56+
}
57+
58+
@Override
59+
protected void getHandler(User user) {
60+
RegistryLock lock =
61+
domainLockUtils.verifyVerificationCode(lockVerificationCode, user.getUserRoles().isAdmin());
62+
RegistryLockAction action =
63+
lock.getLockCompletionTime().isPresent()
64+
? RegistryLockAction.UNLOCKED
65+
: RegistryLockAction.LOCKED;
66+
RegistryLockVerificationResponse lockResponse =
67+
new RegistryLockVerificationResponse(
68+
Ascii.toLowerCase(action.toString()), lock.getDomainName(), lock.getRegistrarId());
69+
consoleApiParams.response().setPayload(gson.toJson(lockResponse));
70+
consoleApiParams.response().setStatus(HttpServletResponse.SC_OK);
71+
}
72+
73+
private enum RegistryLockAction {
74+
LOCKED,
75+
UNLOCKED
76+
}
77+
78+
private record RegistryLockVerificationResponse(
79+
@Expose String action, @Expose String domainName, @Expose String registrarId) {}
80+
}

core/src/test/java/google/registry/testing/FakeResponse.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.google.common.net.MediaType;
2424
import google.registry.request.Response;
2525
import jakarta.servlet.http.Cookie;
26+
import jakarta.servlet.http.HttpServletResponse;
2627
import java.io.IOException;
2728
import java.io.PrintWriter;
2829
import java.io.StringWriter;
@@ -69,7 +70,7 @@ public StringWriter getStringWriter() {
6970

7071
@Override
7172
public void sendRedirect(String url) throws IOException {
72-
status = 302;
73+
status = HttpServletResponse.SC_FOUND;
7374
this.payload = String.format("Redirected to %s", url);
7475
}
7576

core/src/test/java/google/registry/ui/server/console/ConsoleRegistryLockActionTest.java

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public class ConsoleRegistryLockActionTest {
8080
Please click the link below to perform the lock / unlock action on domain example.test. \
8181
Note: this code will expire in one hour.
8282
83-
https://registrarconsole.tld/console-api/registry-lock-verify?lockVerificationCode=\
83+
https://registrarconsole.tld/console/#/registry-lock-verify?lockVerificationCode=\
8484
123456789ABCDEFGHJKLMNPQRSTUVWXY""";
8585

8686
private static final Gson GSON = RequestModule.provideGson();
@@ -122,15 +122,7 @@ void afterEach() {
122122

123123
@Test
124124
void testGet_simpleLock() {
125-
saveRegistryLock(
126-
new RegistryLock.Builder()
127-
.setRepoId("repoId")
128-
.setDomainName("example.test")
129-
.setRegistrarId("TheRegistrar")
130-
.setVerificationCode("123456789ABCDEFGHJKLMNPQRSTUVWXY")
131-
.setRegistrarPocId("johndoe@theregistrar.com")
132-
.setLockCompletionTime(fakeClock.nowUtc())
133-
.build());
125+
saveRegistryLock(createDefaultLockBuilder().setLockCompletionTime(fakeClock.nowUtc()).build());
134126
action.run();
135127
assertThat(response.getStatus()).isEqualTo(SC_OK);
136128
assertThat(response.getPayload())

0 commit comments

Comments
 (0)