From 270fe06c34925723975d05f7a1db38640ff61ab5 Mon Sep 17 00:00:00 2001 From: Pavlo Tkach <3469726+ptkach@users.noreply.github.com> Date: Fri, 9 Jun 2023 16:57:26 -0400 Subject: [PATCH] Add console /registrars GET endpoint (#2050) --- .../app/settings/contact/contact.service.ts | 12 +- .../app/shared/services/backend.service.ts | 15 ++- .../model/console/ConsolePermission.java | 2 + .../model/console/ConsoleRoleDefinitions.java | 1 + .../frontend/FrontendRequestComponent.java | 3 + .../ui/server/console/RegistrarsAction.java | 68 +++++++++++ .../server/console/RegistrarsActionTest.java | 112 ++++++++++++++++++ .../module/frontend/frontend_routing.txt | 1 + 8 files changed, 204 insertions(+), 10 deletions(-) create mode 100644 core/src/main/java/google/registry/ui/server/console/RegistrarsAction.java create mode 100644 core/src/test/java/google/registry/ui/server/console/RegistrarsActionTest.java diff --git a/console-webapp/src/app/settings/contact/contact.service.ts b/console-webapp/src/app/settings/contact/contact.service.ts index 27b16c065..9a269c4fd 100644 --- a/console-webapp/src/app/settings/contact/contact.service.ts +++ b/console-webapp/src/app/settings/contact/contact.service.ts @@ -49,13 +49,11 @@ export class ContactService { contacts: Contact[], registrarId?: string ): Observable { - return this.backend - .postContacts(registrarId || 'default', contacts) - .pipe( - tap((_) => { - this.contacts = contacts; - }) - ); + return this.backend.postContacts(registrarId || 'default', contacts).pipe( + tap((_) => { + this.contacts = contacts; + }) + ); } updateContact(index: number, contact: Contact) { diff --git a/console-webapp/src/app/shared/services/backend.service.ts b/console-webapp/src/app/shared/services/backend.service.ts index 89fb40540..4c06afda9 100644 --- a/console-webapp/src/app/shared/services/backend.service.ts +++ b/console-webapp/src/app/shared/services/backend.service.ts @@ -62,11 +62,20 @@ export class BackendService { }, ]; return this.http - .get(`/console-api/settings/contacts?registrarId=${registrarId}`) - .pipe(catchError((err) => this.errorCatcher(err, mockData))); + .get( + `/console-api/settings/contacts?registrarId=${registrarId}` + ) + .pipe( + catchError((err) => + this.errorCatcher(err, mockData) + ) + ); } - postContacts(registrarId: string, contacts: Contact[]): Observable { + postContacts( + registrarId: string, + contacts: Contact[] + ): Observable { return this.http.post( `/console-api/settings/contacts?registrarId=${registrarId}`, { contacts } diff --git a/core/src/main/java/google/registry/model/console/ConsolePermission.java b/core/src/main/java/google/registry/model/console/ConsolePermission.java index a21ac980e..16a472887 100644 --- a/core/src/main/java/google/registry/model/console/ConsolePermission.java +++ b/core/src/main/java/google/registry/model/console/ConsolePermission.java @@ -22,6 +22,8 @@ public enum ConsolePermission { EDIT_REGISTRAR_DETAILS, /** Add, update, or remove other console users. */ MANAGE_USERS, + /** View registrars. */ + VIEW_REGISTRARS, /** Add, update, or remove registrars. */ MANAGE_REGISTRARS, /** Manage related registrars, e.g. when one registrar owns another. */ diff --git a/core/src/main/java/google/registry/model/console/ConsoleRoleDefinitions.java b/core/src/main/java/google/registry/model/console/ConsoleRoleDefinitions.java index 5c51801e1..e2c23b0ce 100644 --- a/core/src/main/java/google/registry/model/console/ConsoleRoleDefinitions.java +++ b/core/src/main/java/google/registry/model/console/ConsoleRoleDefinitions.java @@ -27,6 +27,7 @@ public class ConsoleRoleDefinitions { /** Permissions for a registry support agent. */ static final ImmutableSet SUPPORT_AGENT_PERMISSIONS = ImmutableSet.of( + ConsolePermission.VIEW_REGISTRARS, ConsolePermission.VIEW_REGISTRAR_DETAILS, ConsolePermission.EDIT_REGISTRAR_DETAILS, ConsolePermission.MANAGE_USERS, 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 c157ee1df..e2d7ac0b5 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.RegistrarsAction; import google.registry.ui.server.console.settings.ContactAction; import google.registry.ui.server.registrar.ConsoleOteSetupAction; import google.registry.ui.server.registrar.ConsoleRegistrarCreatorAction; @@ -67,6 +68,8 @@ interface FrontendRequestComponent { ContactAction contactAction(); + RegistrarsAction registrarsAction(); + @Subcomponent.Builder abstract class Builder implements RequestComponentBuilder { @Override public abstract Builder requestModule(RequestModule requestModule); diff --git a/core/src/main/java/google/registry/ui/server/console/RegistrarsAction.java b/core/src/main/java/google/registry/ui/server/console/RegistrarsAction.java new file mode 100644 index 000000000..18ba31ec1 --- /dev/null +++ b/core/src/main/java/google/registry/ui/server/console/RegistrarsAction.java @@ -0,0 +1,68 @@ +// 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; + +import static google.registry.request.Action.Method.GET; + +import com.google.api.client.http.HttpStatusCodes; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Streams; +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.request.Action; +import google.registry.request.Response; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthResult; +import google.registry.ui.server.registrar.JsonGetAction; +import javax.inject.Inject; + +@Action( + service = Action.Service.DEFAULT, + path = RegistrarsAction.PATH, + method = {GET}, + auth = Auth.AUTH_PUBLIC_LOGGED_IN) +public class RegistrarsAction implements JsonGetAction { + static final String PATH = "/console-api/registrars"; + + private final AuthResult authResult; + private final Response response; + private final Gson gson; + + @Inject + public RegistrarsAction(AuthResult authResult, Response response, Gson gson) { + this.authResult = authResult; + this.response = response; + this.gson = gson; + } + + @Override + public void run() { + User user = authResult.userAuthInfo().get().consoleUser().get(); + if (!user.getUserRoles().hasGlobalPermission(ConsolePermission.VIEW_REGISTRARS)) { + response.setStatus(HttpStatusCodes.STATUS_CODE_FORBIDDEN); + return; + } + ImmutableList registrarIds = + Streams.stream(Registrar.loadAllCached()) + .filter(r -> r.getType() == Registrar.Type.REAL) + .map(Registrar::getRegistrarId) + .collect(ImmutableList.toImmutableList()); + + response.setPayload(gson.toJson(registrarIds)); + response.setStatus(HttpStatusCodes.STATUS_CODE_OK); + } +} diff --git a/core/src/test/java/google/registry/ui/server/console/RegistrarsActionTest.java b/core/src/test/java/google/registry/ui/server/console/RegistrarsActionTest.java new file mode 100644 index 000000000..f0cc5de2f --- /dev/null +++ b/core/src/test/java/google/registry/ui/server/console/RegistrarsActionTest.java @@ -0,0 +1,112 @@ +// 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; + +import static com.google.common.truth.Truth.assertThat; +import static google.registry.testing.DatabaseHelper.persistNewRegistrar; +import static google.registry.testing.DatabaseHelper.persistResource; +import static google.registry.testing.SqlHelper.saveRegistrar; + +import com.google.api.client.http.HttpStatusCodes; +import com.google.common.collect.ImmutableMap; +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.persistence.transaction.JpaTestExtensions; +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.util.UtilsModule; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +/** Tests for {@link google.registry.ui.server.console.RegistrarsAction}. */ +class RegistrarsActionTest { + + private static final Gson GSON = UtilsModule.provideGson(); + private FakeResponse response; + + @RegisterExtension + final JpaTestExtensions.JpaIntegrationTestExtension jpa = + new JpaTestExtensions.Builder().buildIntegrationTestExtension(); + + @Test + void testSuccess_onlyRealRegistrars() { + Registrar registrar = persistNewRegistrar("registrarId"); + registrar = registrar.asBuilder().setType(Registrar.Type.TEST).setIanaIdentifier(null).build(); + persistResource(registrar); + RegistrarsAction action = + createAction( + AuthResult.create( + AuthLevel.USER, + UserAuthInfo.create( + createUser( + new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_LEAD).build())))); + action.run(); + assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_OK); + assertThat(response.getPayload()).isEqualTo("[\"NewRegistrar\",\"TheRegistrar\"]"); + } + + @Test + void testSuccess_getRegistrarIds() { + saveRegistrar("registrarId"); + RegistrarsAction action = + createAction( + AuthResult.create( + AuthLevel.USER, + UserAuthInfo.create( + createUser(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())))); + action.run(); + assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_OK); + assertThat(response.getPayload()) + .isEqualTo("[\"NewRegistrar\",\"TheRegistrar\",\"registrarId\"]"); + } + + @Test + void testFailure_getRegistrarIds() { + saveRegistrar("registrarId"); + RegistrarsAction action = + createAction( + AuthResult.create( + AuthLevel.USER, + UserAuthInfo.create( + createUser( + new UserRoles.Builder() + .setRegistrarRoles( + ImmutableMap.of( + "registrarId", + RegistrarRole.ACCOUNT_MANAGER_WITH_REGISTRY_LOCK)) + .build())))); + 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 RegistrarsAction createAction(AuthResult authResult) { + response = new FakeResponse(); + return new RegistrarsAction(authResult, response, GSON); + } +} 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 fc3fa4064..2dd94f983 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,6 +1,7 @@ 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/registrars RegistrarsAction 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