diff --git a/console-webapp/src/app/history/historyList.component.ts b/console-webapp/src/app/history/historyList.component.ts index 76ca96ad4..bb1e2e973 100644 --- a/console-webapp/src/app/history/historyList.component.ts +++ b/console-webapp/src/app/history/historyList.component.ts @@ -56,7 +56,7 @@ export class HistoryListComponent { return { main: 'N/A', detail: null }; } const parts = description.split('|'); - const detail = parts.length > 1 ? parts[1].replace(/_/g, ' ') : null; + const detail = parts.length > 1 ? parts[1].replace(/_/g, ' ') : parts[0]; return { main: parts[0], diff --git a/console-webapp/src/app/settings/contact/contactDetails.component.html b/console-webapp/src/app/settings/contact/contactDetails.component.html index d621920f5..8c5d1242d 100644 --- a/console-webapp/src/app/settings/contact/contactDetails.component.html +++ b/console-webapp/src/app/settings/contact/contactDetails.component.html @@ -57,6 +57,11 @@ [(ngModel)]="contactService.contactInEdit.emailAddress" [ngModelOptions]="{ standalone: true }" [disabled]="emailAddressIsDisabled()" + [matTooltip]=" + emailAddressIsDisabled() + ? 'Reach out to registry customer support to update email address' + : '' + " /> @@ -84,6 +89,7 @@

Contact Type

errorRequired to select at least one + (primary contact can't be updated)

consoleUserEmail; + private final String supportEmail; + @Inject public ConsoleHistoryDataAction( ConsoleApiParams consoleApiParams, + @Config("supportEmail") String supportEmail, @Parameter("registrarId") String registrarId, @Parameter("consoleUserEmail") Optional consoleUserEmail) { super(consoleApiParams); this.registrarId = registrarId; this.consoleUserEmail = consoleUserEmail; + this.supportEmail = supportEmail; } @Override @@ -95,7 +103,9 @@ public class ConsoleHistoryDataAction extends ConsoleApiAction { .setHint("org.hibernate.fetchSize", 1000) .getResultList()); - consoleApiParams.response().setPayload(consoleApiParams.gson().toJson(queryResult)); + List formattedHistoryList = + replaceActiveUserIfNecessary(queryResult, user); + consoleApiParams.response().setPayload(consoleApiParams.gson().toJson(formattedHistoryList)); consoleApiParams.response().setStatus(SC_OK); } @@ -110,7 +120,39 @@ public class ConsoleHistoryDataAction extends ConsoleApiAction { .setParameter("registrarId", registrarId) .setHint("org.hibernate.fetchSize", 1000) .getResultList()); - consoleApiParams.response().setPayload(consoleApiParams.gson().toJson(queryResult)); + + List formattedHistoryList = + replaceActiveUserIfNecessary(queryResult, user); + consoleApiParams.response().setPayload(consoleApiParams.gson().toJson(formattedHistoryList)); consoleApiParams.response().setStatus(SC_OK); } + + /** Anonymizes support users in history logs if the user viewing the log is a registrar user. */ + private List replaceActiveUserIfNecessary( + List historyList, User requestingUser) { + // Check if the user *viewing* the history is a registrar user (not a support user) + if (GlobalRole.NONE.equals(requestingUser.getUserRoles().getGlobalRole())) { + User genericSupportUser = // Fixed typo + new User.Builder() + .setEmailAddress(this.supportEmail) + .setUserRoles(new UserRoles.Builder().build()) // Simplified roles + .build(); + + return historyList.stream() + .map( + history -> { + // Check if the user who performed the action was a support user + if (!GlobalRole.NONE.equals( + history.getActingUser().getUserRoles().getGlobalRole())) { + return history.asBuilder().setActingUser(genericSupportUser).build(); + } + // If acting user was a registrar user show them as-is + return history; + }) + .collect(toImmutableList()); + } + + // If the viewing user is a support user, return the list unmodified + return historyList; + } } diff --git a/core/src/main/java/google/registry/ui/server/console/ConsoleUpdateRegistrarAction.java b/core/src/main/java/google/registry/ui/server/console/ConsoleUpdateRegistrarAction.java index 6f72372eb..22d7c7185 100644 --- a/core/src/main/java/google/registry/ui/server/console/ConsoleUpdateRegistrarAction.java +++ b/core/src/main/java/google/registry/ui/server/console/ConsoleUpdateRegistrarAction.java @@ -23,6 +23,7 @@ import static org.apache.http.HttpStatus.SC_OK; import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; +import com.google.common.flogger.FluentLogger; import google.registry.model.console.ConsolePermission; import google.registry.model.console.ConsoleUpdateHistory; import google.registry.model.console.User; @@ -47,6 +48,8 @@ import org.joda.time.DateTime; method = {POST}, auth = Auth.AUTH_PUBLIC_LOGGED_IN) public class ConsoleUpdateRegistrarAction extends ConsoleApiAction { + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + private static final String CHANGE_LOG_ENTRY = "%s updated on %s, old -> %s, new -> %s"; static final String PATH = "/console-api/registrar"; private final Optional registrar; @@ -124,6 +127,9 @@ public class ConsoleUpdateRegistrarAction extends ConsoleApiAction { new ConsoleUpdateHistory.Builder() .setType(ConsoleUpdateHistory.Type.REGISTRAR_UPDATE) .setDescription(updatedRegistrar.getRegistrarId())); + + logConsoleChangesIfNecessary(updatedRegistrar, existingRegistrar.get()); + sendExternalUpdatesIfNecessary( EmailInfo.create( existingRegistrar.get(), @@ -134,4 +140,25 @@ public class ConsoleUpdateRegistrarAction extends ConsoleApiAction { consoleApiParams.response().setStatus(SC_OK); } + + private void logConsoleChangesIfNecessary( + Registrar updatedRegistrar, Registrar existingRegistrar) { + if (!updatedRegistrar.getAllowedTlds().containsAll(existingRegistrar.getAllowedTlds())) { + logger.atInfo().log( + CHANGE_LOG_ENTRY, + "Allowed TLDs", + updatedRegistrar.getRegistrarId(), + existingRegistrar.getAllowedTlds(), + updatedRegistrar.getAllowedTlds()); + } + + if (updatedRegistrar.isRegistryLockAllowed() != existingRegistrar.isRegistryLockAllowed()) { + logger.atInfo().log( + CHANGE_LOG_ENTRY, + "Registry lock", + updatedRegistrar.getRegistrarId(), + existingRegistrar.isRegistryLockAllowed(), + updatedRegistrar.isRegistryLockAllowed()); + } + } } diff --git a/core/src/test/java/google/registry/ui/server/console/ConsoleHistoryDataActionTest.java b/core/src/test/java/google/registry/ui/server/console/ConsoleHistoryDataActionTest.java index b513f139d..10ef6c230 100644 --- a/core/src/test/java/google/registry/ui/server/console/ConsoleHistoryDataActionTest.java +++ b/core/src/test/java/google/registry/ui/server/console/ConsoleHistoryDataActionTest.java @@ -40,8 +40,10 @@ import org.junit.jupiter.api.Test; class ConsoleHistoryDataActionTest extends ConsoleActionBaseTestCase { + private static final String SUPPORT_EMAIL = "supportEmailTest@test.com"; private static final Gson GSON = new Gson(); private User noPermissionUser; + private User registrarPrimaryUser; @BeforeEach void beforeEach() { @@ -56,6 +58,17 @@ class ConsoleHistoryDataActionTest extends ConsoleActionBaseTestCase { .build()) .build()); + registrarPrimaryUser = + DatabaseHelper.persistResource( + new User.Builder() + .setEmailAddress("primary@example.com") + .setUserRoles( + new UserRoles.Builder() + .setRegistrarRoles( + ImmutableMap.of("TheRegistrar", RegistrarRole.PRIMARY_CONTACT)) + .build()) + .build()); + DatabaseHelper.persistResources( ImmutableList.of( new ConsoleUpdateHistory.Builder() @@ -85,7 +98,7 @@ class ConsoleHistoryDataActionTest extends ConsoleActionBaseTestCase { } @Test - void testSuccess_getByRegistrar() { + void testSuccess_getByRegistrar_notAnonymizedForSupportUser() { ConsoleHistoryDataAction action = createAction(AuthResult.createUser(fteUser), "TheRegistrar", Optional.empty()); action.run(); @@ -93,6 +106,35 @@ class ConsoleHistoryDataActionTest extends ConsoleActionBaseTestCase { List> payload = GSON.fromJson(response.getPayload(), List.class); assertThat(payload.stream().map(record -> record.get("description")).collect(toImmutableList())) .containsExactly("TheRegistrar|Some change", "TheRegistrar|Another change"); + + // Assert that the support user sees the real acting users + List actingUserEmails = + payload.stream() + .map(record -> (Map) record.get("actingUser")) + .map(userMap -> (String) userMap.get("emailAddress")) + .collect(toImmutableList()); + + assertThat(actingUserEmails).containsExactly("fte@email.tld", "no.perms@example.com"); + } + + @Test + void testSuccess_getByRegistrar_anonymizedForRegistrarUser() { + ConsoleHistoryDataAction action = + createAction(AuthResult.createUser(registrarPrimaryUser), "TheRegistrar", Optional.empty()); + action.run(); + assertThat(response.getStatus()).isEqualTo(SC_OK); + List> payload = GSON.fromJson(response.getPayload(), List.class); + assertThat(payload.stream().map(record -> record.get("description")).collect(toImmutableList())) + .containsExactly("TheRegistrar|Some change", "TheRegistrar|Another change"); + + // Assert that the registrar user sees the anonymized support user + List actingUserEmails = + payload.stream() + .map(record -> (Map) record.get("actingUser")) + .map(userMap -> (String) userMap.get("emailAddress")) + .collect(toImmutableList()); + + assertThat(actingUserEmails).containsExactly(SUPPORT_EMAIL, "no.perms@example.com"); } @Test @@ -104,6 +146,13 @@ class ConsoleHistoryDataActionTest extends ConsoleActionBaseTestCase { List> payload = GSON.fromJson(response.getPayload(), List.class); assertThat(payload.stream().map(record -> record.get("description")).collect(toImmutableList())) .containsExactly("TheRegistrar|Some change", "OtherRegistrar|Some change"); + + List actingUserEmails = + payload.stream() + .map(record -> (Map) record.get("actingUser")) + .map(userMap -> (String) userMap.get("emailAddress")) + .collect(toImmutableList()); + assertThat(actingUserEmails).containsExactly("fte@email.tld", "fte@email.tld"); } @Test @@ -148,6 +197,7 @@ class ConsoleHistoryDataActionTest extends ConsoleActionBaseTestCase { consoleApiParams = ConsoleApiParamsUtils.createFake(authResult); when(consoleApiParams.request().getMethod()).thenReturn("GET"); response = (FakeResponse) consoleApiParams.response(); - return new ConsoleHistoryDataAction(consoleApiParams, registrarId, consoleUserEmail); + return new ConsoleHistoryDataAction( + consoleApiParams, SUPPORT_EMAIL, registrarId, consoleUserEmail); } }