From db1b92638fca23a3971e6e0ee8cfbc4d1072f2fd Mon Sep 17 00:00:00 2001
From: Pavlo Tkach <3469726+ptkach@users.noreply.github.com>
Date: Wed, 31 May 2023 16:34:57 -0400
Subject: [PATCH] Create console settings contact endpoints (#2033)
---
.../model/registrar/RegistrarPoc.java | 19 +-
.../frontend/FrontendRequestComponent.java | 3 +
.../registry/request/OptionalJsonPayload.java | 29 +++
.../registry/request/RequestModule.java | 16 +-
.../console/settings/ContactAction.java | 145 +++++++++++
.../registrar/RegistrarConsoleModule.java | 25 ++
.../registrar/RegistrarSettingsAction.java | 2 +-
.../console/settings/ContactActionTest.java | 231 ++++++++++++++++++
.../module/frontend/frontend_routing.txt | 23 +-
9 files changed, 470 insertions(+), 23 deletions(-)
create mode 100644 core/src/main/java/google/registry/request/OptionalJsonPayload.java
create mode 100644 core/src/main/java/google/registry/ui/server/console/settings/ContactAction.java
create mode 100644 core/src/test/java/google/registry/ui/server/console/settings/ContactActionTest.java
diff --git a/core/src/main/java/google/registry/model/registrar/RegistrarPoc.java b/core/src/main/java/google/registry/model/registrar/RegistrarPoc.java
index 1f9ac9e7c..b5c8d9b43 100644
--- a/core/src/main/java/google/registry/model/registrar/RegistrarPoc.java
+++ b/core/src/main/java/google/registry/model/registrar/RegistrarPoc.java
@@ -29,6 +29,7 @@ import static java.util.stream.Collectors.joining;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
+import com.google.gson.annotations.Expose;
import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
import google.registry.model.JsonMapBuilder;
@@ -94,7 +95,7 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
}
/** The name of the contact. */
- String name;
+ @Expose String name;
/**
* The contact email address of the contact.
@@ -102,24 +103,24 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
*
This is different from the login email which is assgined to the regstrar and cannot be
* changed.
*/
- @Id String emailAddress;
+ @Expose @Id String emailAddress;
- @Id String registrarId;
+ @Expose @Id public String registrarId;
/** External email address of this contact used for registry lock confirmations. */
String registryLockEmailAddress;
/** The voice number of the contact. */
- String phoneNumber;
+ @Expose String phoneNumber;
/** The fax number of the contact. */
- String faxNumber;
+ @Expose String faxNumber;
/**
* Multiple types are used to associate the registrar contact with various mailing groups. This
* data is internal to the registry.
*/
- Set types;
+ @Expose Set types;
/** A GAIA email address that was assigned to the registrar for console login purpose. */
String loginEmailAddress;
@@ -127,19 +128,19 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
/**
* Whether this contact is publicly visible in WHOIS registrar query results as an Admin contact.
*/
- boolean visibleInWhoisAsAdmin = false;
+ @Expose boolean visibleInWhoisAsAdmin = false;
/**
* Whether this contact is publicly visible in WHOIS registrar query results as a Technical
* contact.
*/
- boolean visibleInWhoisAsTech = false;
+ @Expose boolean visibleInWhoisAsTech = false;
/**
* Whether this contact's phone number and email address is publicly visible in WHOIS domain query
* results as registrar abuse contact info.
*/
- boolean visibleInDomainWhoisAsAbuse = false;
+ @Expose boolean visibleInDomainWhoisAsAbuse = false;
/**
* Whether the contact is allowed to set their registry lock password through the registrar
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 a0bd284ff..c157ee1df 100644
--- a/core/src/main/java/google/registry/module/frontend/FrontendRequestComponent.java
+++ b/core/src/main/java/google/registry/module/frontend/FrontendRequestComponent.java
@@ -26,6 +26,7 @@ import google.registry.request.RequestComponentBuilder;
import google.registry.request.RequestModule;
import google.registry.request.RequestScope;
import google.registry.ui.server.console.ConsoleDomainGetAction;
+import google.registry.ui.server.console.settings.ContactAction;
import google.registry.ui.server.registrar.ConsoleOteSetupAction;
import google.registry.ui.server.registrar.ConsoleRegistrarCreatorAction;
import google.registry.ui.server.registrar.ConsoleUiAction;
@@ -64,6 +65,8 @@ interface FrontendRequestComponent {
ConsoleDomainGetAction consoleDomainGetAction();
+ ContactAction contactAction();
+
@Subcomponent.Builder
abstract class Builder implements RequestComponentBuilder {
@Override public abstract Builder requestModule(RequestModule requestModule);
diff --git a/core/src/main/java/google/registry/request/OptionalJsonPayload.java b/core/src/main/java/google/registry/request/OptionalJsonPayload.java
new file mode 100644
index 000000000..86e3b6d90
--- /dev/null
+++ b/core/src/main/java/google/registry/request/OptionalJsonPayload.java
@@ -0,0 +1,29 @@
+// Copyright 2023 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.request;
+
+import java.lang.annotation.Documented;
+import javax.inject.Qualifier;
+
+/**
+ * Dagger qualifier for the HTTP request payload as parsed JSON wrapped in Optional. Can be used for
+ * any kind of request methods - GET, POST, etc. Will provide Optional.empty() if body is not
+ * present.
+ *
+ * @see RequestModule
+ */
+@Qualifier
+@Documented
+public @interface OptionalJsonPayload {}
diff --git a/core/src/main/java/google/registry/request/RequestModule.java b/core/src/main/java/google/registry/request/RequestModule.java
index 175ff845e..30c7bf4d2 100644
--- a/core/src/main/java/google/registry/request/RequestModule.java
+++ b/core/src/main/java/google/registry/request/RequestModule.java
@@ -31,6 +31,8 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.io.ByteStreams;
import com.google.common.io.CharStreams;
import com.google.common.net.MediaType;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
import com.google.protobuf.ByteString;
import dagger.Module;
import dagger.Provides;
@@ -194,8 +196,7 @@ public final class RequestModule {
@JsonPayload
@SuppressWarnings("unchecked")
static Map provideJsonPayload(
- @Header("Content-Type") MediaType contentType,
- @Payload String payload) {
+ @Header("Content-Type") MediaType contentType, @Payload String payload) {
if (!JSON_UTF_8.is(contentType.withCharset(UTF_8))) {
throw new UnsupportedMediaTypeException(
String.format("Expected %s Content-Type", JSON_UTF_8.withoutParameters()));
@@ -247,4 +248,15 @@ public final class RequestModule {
static Optional provideCloudTasksRetryCount(HttpServletRequest req) {
return extractOptionalHeader(req, CLOUD_TASKS_RETRY_HEADER).map(Integer::parseInt);
}
+
+ @Provides
+ @OptionalJsonPayload
+ public static Optional provideJsonBody(HttpServletRequest req, Gson gson) {
+ try {
+ JsonObject body = gson.fromJson(req.getReader(), JsonObject.class);
+ return Optional.of(body);
+ } catch (Exception e) {
+ return Optional.empty();
+ }
+ }
}
diff --git a/core/src/main/java/google/registry/ui/server/console/settings/ContactAction.java b/core/src/main/java/google/registry/ui/server/console/settings/ContactAction.java
new file mode 100644
index 000000000..2c3b6cd24
--- /dev/null
+++ b/core/src/main/java/google/registry/ui/server/console/settings/ContactAction.java
@@ -0,0 +1,145 @@
+// Copyright 2023 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.settings;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
+import static google.registry.request.Action.Method.GET;
+import static google.registry.request.Action.Method.POST;
+
+import com.google.api.client.http.HttpStatusCodes;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.flogger.FluentLogger;
+import com.google.gson.Gson;
+import google.registry.model.console.ConsolePermission;
+import google.registry.model.console.User;
+import google.registry.model.registrar.Registrar;
+import google.registry.model.registrar.RegistrarPoc;
+import google.registry.persistence.transaction.QueryComposer.Comparator;
+import google.registry.request.Action;
+import google.registry.request.Parameter;
+import google.registry.request.Response;
+import google.registry.request.auth.Auth;
+import google.registry.request.auth.AuthResult;
+import google.registry.ui.forms.FormException;
+import google.registry.ui.server.registrar.JsonGetAction;
+import google.registry.ui.server.registrar.RegistrarSettingsAction;
+import java.util.Collections;
+import java.util.Optional;
+import javax.inject.Inject;
+import javax.servlet.http.HttpServletRequest;
+
+@Action(
+ service = Action.Service.DEFAULT,
+ path = ContactAction.PATH,
+ method = {GET, POST},
+ auth = Auth.AUTH_PUBLIC_LOGGED_IN)
+public class ContactAction implements JsonGetAction {
+ static final String PATH = "/console-api/settings/contacts/";
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ private final HttpServletRequest req;
+ private final AuthResult authResult;
+ private final Response response;
+ private final Gson gson;
+ private final Optional> contacts;
+ private final String registrarId;
+
+ @Inject
+ public ContactAction(
+ HttpServletRequest req,
+ AuthResult authResult,
+ Response response,
+ Gson gson,
+ @Parameter("registrarId") String registrarId,
+ @Parameter("contacts") Optional> contacts) {
+ this.authResult = authResult;
+ this.response = response;
+ this.gson = gson;
+ this.registrarId = registrarId;
+ this.contacts = contacts;
+ this.req = req;
+ }
+
+ @Override
+ public void run() {
+ User user = authResult.userAuthInfo().get().consoleUser().get();
+ if (req.getMethod().equals(GET.toString())) {
+ getHandler(user);
+ } else {
+ postHandler(user);
+ }
+ }
+
+ private void getHandler(User user) {
+ if (!user.getUserRoles().hasPermission(registrarId, ConsolePermission.VIEW_REGISTRAR_DETAILS)) {
+ response.setStatus(HttpStatusCodes.STATUS_CODE_FORBIDDEN);
+ return;
+ }
+
+ ImmutableList am =
+ tm().transact(
+ () ->
+ tm().createQueryComposer(RegistrarPoc.class)
+ .where("registrarId", Comparator.EQ, registrarId)
+ .list());
+
+ response.setStatus(HttpStatusCodes.STATUS_CODE_OK);
+ response.setPayload(gson.toJson(am));
+ }
+
+ private void postHandler(User user) {
+ if (!user.getUserRoles().hasPermission(registrarId, ConsolePermission.EDIT_REGISTRAR_DETAILS)) {
+ response.setStatus(HttpStatusCodes.STATUS_CODE_FORBIDDEN);
+ return;
+ }
+
+ if (!contacts.isPresent()) {
+ response.setStatus(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
+ response.setPayload(gson.toJson("Contacts parameter is not present"));
+ return;
+ }
+
+ Registrar registrar =
+ Registrar.loadByRegistrarId(registrarId)
+ .orElseThrow(
+ () ->
+ new IllegalArgumentException(
+ String.format("Unknown registrar %s", registrarId)));
+
+ ImmutableSet oldContacts = registrar.getContacts();
+ // TODO: @ptkach - refactor out contacts update functionality after RegistrarSettingsAction is
+ // deprecated
+ ImmutableSet updatedContacts =
+ RegistrarSettingsAction.readContacts(
+ registrar,
+ oldContacts,
+ Collections.singletonMap(
+ "contacts",
+ contacts.get().stream().map(c -> c.toJsonMap()).collect(toImmutableList())));
+ try {
+ RegistrarSettingsAction.checkContactRequirements(oldContacts, updatedContacts);
+ } catch (FormException e) {
+ logger.atWarning().withCause(e).log(
+ "Error processing contacts post request for registrar: %s", registrarId);
+ response.setStatus(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
+ response.setPayload(e.getMessage());
+ return;
+ }
+
+ RegistrarPoc.updateContacts(registrar, updatedContacts);
+ response.setStatus(HttpStatusCodes.STATUS_CODE_OK);
+ }
+}
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 f78ca9c94..9061cb9ab 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
@@ -19,8 +19,13 @@ import static google.registry.request.RequestParameters.extractOptionalIntParame
import static google.registry.request.RequestParameters.extractOptionalParameter;
import static google.registry.request.RequestParameters.extractRequiredParameter;
+import com.google.common.collect.ImmutableSet;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
import dagger.Module;
import dagger.Provides;
+import google.registry.model.registrar.RegistrarPoc;
+import google.registry.request.OptionalJsonPayload;
import google.registry.request.Parameter;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
@@ -162,4 +167,24 @@ public final class RegistrarConsoleModule {
static String provideDomain(HttpServletRequest req) {
return extractRequiredParameter(req, "domain");
}
+
+ @Provides
+ @Parameter("contacts")
+ public static Optional> provideContacts(
+ Gson gson, @OptionalJsonPayload Optional payload) {
+
+ if (payload.isPresent() && payload.get().has("contacts")) {
+ return Optional.of(
+ ImmutableSet.copyOf(
+ gson.fromJson(payload.get().get("contacts").getAsJsonArray(), RegistrarPoc[].class)));
+ }
+
+ return Optional.empty();
+ }
+
+ @Provides
+ @Parameter("registrarId")
+ static String provideRegistrarId(HttpServletRequest req) {
+ return extractRequiredParameter(req, "registrarId");
+ }
}
diff --git a/core/src/main/java/google/registry/ui/server/registrar/RegistrarSettingsAction.java b/core/src/main/java/google/registry/ui/server/registrar/RegistrarSettingsAction.java
index f45cae0b5..e8ce54132 100644
--- a/core/src/main/java/google/registry/ui/server/registrar/RegistrarSettingsAction.java
+++ b/core/src/main/java/google/registry/ui/server/registrar/RegistrarSettingsAction.java
@@ -473,7 +473,7 @@ public class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonA
*
* @throws FormException if the checks fail.
*/
- void checkContactRequirements(
+ public static void checkContactRequirements(
ImmutableSet existingContacts, ImmutableSet updatedContacts) {
// Check that no two contacts use the same email address.
Set emails = new HashSet<>();
diff --git a/core/src/test/java/google/registry/ui/server/console/settings/ContactActionTest.java b/core/src/test/java/google/registry/ui/server/console/settings/ContactActionTest.java
new file mode 100644
index 000000000..921a68e4a
--- /dev/null
+++ b/core/src/test/java/google/registry/ui/server/console/settings/ContactActionTest.java
@@ -0,0 +1,231 @@
+// Copyright 2023 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.settings;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.truth.Truth.assertThat;
+import static google.registry.model.registrar.RegistrarPoc.Type.WHOIS;
+import static google.registry.testing.DatabaseHelper.insertInDb;
+import static google.registry.testing.DatabaseHelper.loadAllOf;
+import static google.registry.testing.SqlHelper.saveRegistrar;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.api.client.http.HttpStatusCodes;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.gson.Gson;
+import google.registry.model.console.GlobalRole;
+import google.registry.model.console.RegistrarRole;
+import google.registry.model.console.User;
+import google.registry.model.console.UserRoles;
+import google.registry.model.registrar.Registrar;
+import google.registry.model.registrar.RegistrarPoc;
+import google.registry.persistence.transaction.JpaTestExtensions;
+import google.registry.request.Action;
+import google.registry.request.RequestModule;
+import google.registry.request.auth.AuthResult;
+import google.registry.request.auth.AuthSettings.AuthLevel;
+import google.registry.request.auth.UserAuthInfo;
+import google.registry.testing.FakeResponse;
+import google.registry.ui.server.registrar.RegistrarConsoleModule;
+import google.registry.util.UtilsModule;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Optional;
+import javax.servlet.http.HttpServletRequest;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+/** Tests for {@link google.registry.ui.server.console.settings.ContactAction}. */
+class ContactActionTest {
+ private static String jsonRegistrar1 =
+ "{\"name\":\"Test Registrar 1\","
+ + "\"emailAddress\":\"test.registrar1@example.com\","
+ + "\"registrarId\":\"registrarId\","
+ + "\"phoneNumber\":\"+1.9999999999\",\"faxNumber\":\"+1.9999999991\","
+ + "\"types\":[\"WHOIS\"],\"visibleInWhoisAsAdmin\":true,"
+ + "\"visibleInWhoisAsTech\":false,\"visibleInDomainWhoisAsAbuse\":false}";
+
+ private static String jsonRegistrar2 =
+ "{\"name\":\"Test Registrar 2\","
+ + "\"emailAddress\":\"test.registrar2@example.com\","
+ + "\"registrarId\":\"registrarId\","
+ + "\"phoneNumber\":\"+1.1234567890\",\"faxNumber\":\"+1.1234567891\","
+ + "\"types\":[\"WHOIS\"],\"visibleInWhoisAsAdmin\":true,"
+ + "\"visibleInWhoisAsTech\":false,\"visibleInDomainWhoisAsAbuse\":false}";
+
+ private Registrar testRegistrar;
+ private final HttpServletRequest request = mock(HttpServletRequest.class);
+ private RegistrarPoc testRegistrarPoc;
+ private static final Gson GSON = UtilsModule.provideGson();
+ private static final FakeResponse RESPONSE = new FakeResponse();
+
+ @RegisterExtension
+ final JpaTestExtensions.JpaIntegrationTestExtension jpa =
+ new JpaTestExtensions.Builder().buildIntegrationTestExtension();
+
+ @BeforeEach
+ void beforeEach() {
+ testRegistrar = saveRegistrar("registrarId");
+ testRegistrarPoc =
+ new RegistrarPoc.Builder()
+ .setRegistrar(testRegistrar)
+ .setName("Test Registrar 1")
+ .setEmailAddress("test.registrar1@example.com")
+ .setPhoneNumber("+1.9999999999")
+ .setFaxNumber("+1.9999999991")
+ .setTypes(ImmutableSet.of(WHOIS))
+ .setVisibleInWhoisAsAdmin(true)
+ .setVisibleInWhoisAsTech(false)
+ .setVisibleInDomainWhoisAsAbuse(false)
+ .build();
+ }
+
+ @Test
+ void testSuccess_getContactInfo() throws IOException {
+ insertInDb(testRegistrarPoc);
+ ContactAction action =
+ createAction(
+ Action.Method.GET,
+ AuthResult.create(
+ AuthLevel.USER,
+ UserAuthInfo.create(
+ createUser(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build()))),
+ testRegistrar.getRegistrarId(),
+ null);
+ action.run();
+ assertThat(RESPONSE.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_OK);
+ assertThat(RESPONSE.getPayload()).isEqualTo("[" + jsonRegistrar1 + "]");
+ }
+
+ @Test
+ void testSuccess_postCreateContactInfo() throws IOException {
+ ContactAction action =
+ createAction(
+ Action.Method.POST,
+ AuthResult.create(
+ AuthLevel.USER,
+ UserAuthInfo.create(
+ createUser(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build()))),
+ testRegistrar.getRegistrarId(),
+ "[" + jsonRegistrar1 + "," + jsonRegistrar2 + "]");
+ action.run();
+ assertThat(RESPONSE.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_OK);
+ assertThat(
+ loadAllOf(RegistrarPoc.class).stream()
+ .filter(r -> r.registrarId.equals(testRegistrar.getRegistrarId()))
+ .map(r -> r.getName())
+ .collect(toImmutableList()))
+ .containsExactly("Test Registrar 1", "Test Registrar 2");
+ }
+
+ @Test
+ void testSuccess_postUpdateContactInfo() throws IOException {
+ testRegistrarPoc = testRegistrarPoc.asBuilder().setEmailAddress("incorrect@email.com").build();
+ insertInDb(testRegistrarPoc);
+ ContactAction action =
+ createAction(
+ Action.Method.POST,
+ AuthResult.create(
+ AuthLevel.USER,
+ UserAuthInfo.create(
+ createUser(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build()))),
+ testRegistrar.getRegistrarId(),
+ "[" + jsonRegistrar1 + "," + jsonRegistrar2 + "]");
+ action.run();
+ assertThat(RESPONSE.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_OK);
+ HashMap testResult = new HashMap<>();
+ loadAllOf(RegistrarPoc.class).stream()
+ .filter(r -> r.registrarId.equals(testRegistrar.getRegistrarId()))
+ .forEach(r -> testResult.put(r.getName(), r.getEmailAddress()));
+ assertThat(testResult)
+ .containsExactly(
+ "Test Registrar 1",
+ "test.registrar1@example.com",
+ "Test Registrar 2",
+ "test.registrar2@example.com");
+ }
+
+ @Test
+ void testSuccess_postDeleteContactInfo() throws IOException {
+ insertInDb(testRegistrarPoc);
+ ContactAction action =
+ createAction(
+ Action.Method.POST,
+ AuthResult.create(
+ AuthLevel.USER,
+ UserAuthInfo.create(
+ createUser(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build()))),
+ testRegistrar.getRegistrarId(),
+ "[" + jsonRegistrar2 + "]");
+ action.run();
+ assertThat(RESPONSE.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_OK);
+ assertThat(
+ loadAllOf(RegistrarPoc.class).stream()
+ .filter(r -> r.registrarId.equals(testRegistrar.getRegistrarId()))
+ .map(r -> r.getName())
+ .collect(toImmutableList()))
+ .containsExactly("Test Registrar 2");
+ }
+
+ @Test
+ void testFailure_postDeleteContactInfo_missingPermission() throws IOException {
+ insertInDb(testRegistrarPoc);
+ ContactAction action =
+ createAction(
+ Action.Method.POST,
+ AuthResult.create(
+ AuthLevel.USER,
+ UserAuthInfo.create(
+ createUser(
+ new UserRoles.Builder()
+ .setRegistrarRoles(
+ ImmutableMap.of(
+ testRegistrar.getRegistrarId(), RegistrarRole.ACCOUNT_MANAGER))
+ .build()))),
+ testRegistrar.getRegistrarId(),
+ "[" + jsonRegistrar2 + "]");
+ action.run();
+ assertThat(RESPONSE.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_FORBIDDEN);
+ }
+
+ private User createUser(UserRoles userRoles) {
+ return new User.Builder()
+ .setEmailAddress("email@email.com")
+ .setGaiaId("gaiaId")
+ .setUserRoles(userRoles)
+ .build();
+ }
+
+ private ContactAction createAction(
+ Action.Method method, AuthResult authResult, String registrarId, String contacts)
+ throws IOException {
+ when(request.getMethod()).thenReturn(method.toString());
+ if (method.equals(Action.Method.GET)) {
+ return new ContactAction(request, authResult, RESPONSE, GSON, registrarId, Optional.empty());
+ } else {
+ when(request.getReader())
+ .thenReturn(new BufferedReader(new StringReader("{\"contacts\":" + contacts + "}")));
+ Optional> maybeContacts =
+ RegistrarConsoleModule.provideContacts(
+ GSON, RequestModule.provideJsonBody(request, GSON));
+ return new ContactAction(request, authResult, RESPONSE, GSON, registrarId, maybeContacts);
+ }
+ }
+}
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 fbc0f9ba7..cd25f3d1a 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
@@ -1,11 +1,12 @@
-PATH CLASS METHODS OK AUTH_METHODS MIN USER_POLICY
-/_dr/epp EppTlsAction POST n INTERNAL,API APP PUBLIC
-/console-api/domain ConsoleDomainGetAction GET n API,LEGACY USER PUBLIC
-/registrar ConsoleUiAction GET n INTERNAL,API,LEGACY NONE PUBLIC
-/registrar-create ConsoleRegistrarCreatorAction POST,GET n INTERNAL,API,LEGACY NONE PUBLIC
-/registrar-ote-setup ConsoleOteSetupAction POST,GET n INTERNAL,API,LEGACY NONE PUBLIC
-/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
+PATH CLASS METHODS OK AUTH_METHODS MIN USER_POLICY
+/_dr/epp EppTlsAction POST n INTERNAL,API APP PUBLIC
+/console-api/domain ConsoleDomainGetAction GET n API,LEGACY USER PUBLIC
+/console-api/settings/contacts/ ContactAction GET,POST n API,LEGACY USER PUBLIC
+/registrar ConsoleUiAction GET n INTERNAL,API,LEGACY NONE PUBLIC
+/registrar-create ConsoleRegistrarCreatorAction POST,GET n INTERNAL,API,LEGACY NONE PUBLIC
+/registrar-ote-setup ConsoleOteSetupAction POST,GET n INTERNAL,API,LEGACY NONE PUBLIC
+/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