Skip to content

Commit

Permalink
Add console backend for editing registrar (#2452)
Browse files Browse the repository at this point in the history
  • Loading branch information
ptkach committed May 24, 2024
1 parent e88ff77 commit bee4284
Show file tree
Hide file tree
Showing 2 changed files with 326 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// 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.base.Preconditions.checkArgument;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.request.Action.Method.POST;
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;

import com.google.api.client.http.HttpStatusCodes;
import com.google.common.base.Strings;
import google.registry.groups.GmailClient;
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.HttpException.BadRequestException;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.ui.server.registrar.ConsoleApiParams;
import google.registry.util.DomainNameUtils;
import google.registry.util.EmailMessage;
import google.registry.util.RegistryEnvironment;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;

@Action(
service = Action.Service.DEFAULT,
path = ConsoleEppPasswordAction.PATH,
method = {POST},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
public class ConsoleUpdateRegistrarAction extends ConsoleApiAction {
static final String PATH = "/console-api/registrar";
private static final String EMAIL_SUBJ = "Registrar %s has been updated";
private static final String EMAIL_BODY =
"The following changes were made in registry %s environment to the registrar %s:";
private final Optional<Registrar> registrar;

private final GmailClient gmailClient;

@Inject
ConsoleUpdateRegistrarAction(
ConsoleApiParams consoleApiParams,
GmailClient gmailClient,
@Parameter("registrar") Optional<Registrar> registrar) {
super(consoleApiParams);
this.registrar = registrar;
this.gmailClient = gmailClient;
}

@Override
protected void postHandler(User user) {
var errorMsg = "Missing param(s): %s";
Registrar updatedRegistrar =
registrar.orElseThrow(() -> new BadRequestException(String.format(errorMsg, "registrar")));
checkArgument(
!Strings.isNullOrEmpty(updatedRegistrar.getRegistrarId()), errorMsg, "registrarId");
checkPermission(
user, updatedRegistrar.getRegistrarId(), ConsolePermission.EDIT_REGISTRAR_DETAILS);

tm().transact(
() -> {
Optional<Registrar> existingRegistrar =
Registrar.loadByRegistrarId(updatedRegistrar.getRegistrarId());
checkArgument(
!existingRegistrar.isEmpty(),
"Registrar with registrarId %s doesn't exists",
updatedRegistrar.getRegistrarId());

// Only allow modifying allowed TLDs if we're in a non-PRODUCTION environment, if the
// registrar is not REAL, or the registrar has a WHOIS abuse contact set.
if (!updatedRegistrar.getAllowedTlds().isEmpty()) {
boolean isRealRegistrar =
Registrar.Type.REAL.equals(existingRegistrar.get().getType());
if (RegistryEnvironment.PRODUCTION.equals(RegistryEnvironment.get())
&& isRealRegistrar) {
checkArgumentPresent(
existingRegistrar.get().getWhoisAbuseContact(),
"Cannot modify allowed TLDs if there is no WHOIS abuse contact set. Please"
+ " use the \"nomulus registrar_contact\" command on this registrar to"
+ " set a WHOIS abuse contact.");
}
}

tm().put(
existingRegistrar
.get()
.asBuilder()
.setAllowedTlds(
updatedRegistrar.getAllowedTlds().stream()
.map(DomainNameUtils::canonicalizeHostname)
.collect(Collectors.toSet()))
.setRegistryLockAllowed(updatedRegistrar.isRegistryLockAllowed())
.build());

sendEmail(existingRegistrar.get(), updatedRegistrar);
});

consoleApiParams.response().setStatus(HttpStatusCodes.STATUS_CODE_OK);
}

void sendEmail(Registrar oldRegistrar, Registrar updatedRegistrar) throws AddressException {
String emailBody =
String.format(EMAIL_BODY, RegistryEnvironment.get(), oldRegistrar.getRegistrarId());

StringBuilder diff = new StringBuilder();
if (oldRegistrar.isRegistryLockAllowed() != updatedRegistrar.isRegistryLockAllowed()) {
diff.append("/n");
diff.append(
String.format(
"Registry Lock Allowed: %s -> %s",
oldRegistrar.isRegistryLockAllowed(), updatedRegistrar.isRegistryLockAllowed()));
}
if (!oldRegistrar.getAllowedTlds().equals(updatedRegistrar.getAllowedTlds())) {
diff.append("/n");
diff.append(
String.format(
"Allowed TLDs: %s -> %s",
oldRegistrar.getAllowedTlds(), updatedRegistrar.getAllowedTlds()));
}

if (diff.length() > 0) {
this.gmailClient.sendEmail(
EmailMessage.create(
String.format(EMAIL_SUBJ, oldRegistrar.getRegistrarId()),
emailBody + diff,
new InternetAddress(oldRegistrar.getEmailAddress(), true)));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// 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.model.registrar.RegistrarPocBase.Type.WHOIS;
import static google.registry.testing.DatabaseHelper.createTlds;
import static google.registry.testing.DatabaseHelper.persistNewRegistrar;
import static google.registry.testing.DatabaseHelper.persistResource;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.google.api.client.http.HttpStatusCodes;
import com.google.common.collect.ImmutableSet;
import com.google.gson.Gson;
import google.registry.groups.GmailClient;
import google.registry.model.console.GlobalRole;
import google.registry.model.console.User;
import google.registry.model.console.UserRoles;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarBase;
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.UserAuthInfo;
import google.registry.testing.FakeConsoleApiParams;
import google.registry.testing.FakeResponse;
import google.registry.testing.SystemPropertyExtension;
import google.registry.tools.GsonUtils;
import google.registry.ui.server.registrar.ConsoleApiParams;
import google.registry.ui.server.registrar.RegistrarConsoleModule;
import google.registry.util.EmailMessage;
import google.registry.util.RegistryEnvironment;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.Optional;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

/** Tests for {@link google.registry.ui.server.console.ConsoleUpdateRegistrarAction}. */
class ConsoleUpdateRegistrarActionTest {
private static final Gson GSON = GsonUtils.provideGson();

private ConsoleApiParams consoleApiParams;
private FakeResponse response;

private Registrar registrar;

private User user;

private static String registrarPostData =
"{\"registrarId\":\"%s\",\"allowedTlds\":[%s],\"registryLockAllowed\":%s}";

private GmailClient gmailClient = mock(GmailClient.class);

@RegisterExtension
@Order(Integer.MAX_VALUE)
final SystemPropertyExtension systemPropertyExtension = new SystemPropertyExtension();

@BeforeEach
void beforeEach() throws Exception {
createTlds("app", "dev");
registrar = persistNewRegistrar("registrarId");
persistResource(
registrar
.asBuilder()
.setType(RegistrarBase.Type.REAL)
.setEmailAddress("testEmail@google.com")
.build());
user =
new User.Builder()
.setEmailAddress("user@registrarId.com")
.setRegistryLockEmailAddress("registryedit@registrarId.com")
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
.build();
consoleApiParams = createParams();
}

@RegisterExtension
final JpaTestExtensions.JpaIntegrationTestExtension jpa =
new JpaTestExtensions.Builder().buildIntegrationTestExtension();

@Test
void testSuccess__updatesRegistrar() throws IOException {
var action = createAction(String.format(registrarPostData, "registrarId", "app, dev", false));
action.run();
Registrar newRegistrar = Registrar.loadByRegistrarId("registrarId").get();
assertThat(newRegistrar.getAllowedTlds()).containsExactly("app", "dev");
assertThat(newRegistrar.isRegistryLockAllowed()).isFalse();
assertThat(((FakeResponse) consoleApiParams.response()).getStatus())
.isEqualTo(HttpStatusCodes.STATUS_CODE_OK);
}

@Test
void testFails__missingWhoisContact() throws IOException {
RegistryEnvironment.PRODUCTION.setup(systemPropertyExtension);
var action = createAction(String.format(registrarPostData, "registrarId", "app, dev", false));
action.run();
assertThat(((FakeResponse) consoleApiParams.response()).getStatus())
.isEqualTo(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
assertThat((String) ((FakeResponse) consoleApiParams.response()).getPayload())
.contains("Cannot modify allowed TLDs if there is no WHOIS abuse contact set");
}

@Test
void testSuccess__presentWhoisContact() throws IOException {
RegistryEnvironment.PRODUCTION.setup(systemPropertyExtension);
RegistrarPoc contact =
new RegistrarPoc.Builder()
.setRegistrar(registrar)
.setName("Test Registrar 1")
.setEmailAddress("test.registrar1@example.com")
.setPhoneNumber("+1.9999999999")
.setFaxNumber("+1.9999999991")
.setTypes(ImmutableSet.of(WHOIS))
.setVisibleInWhoisAsAdmin(true)
.setVisibleInWhoisAsTech(true)
.setVisibleInDomainWhoisAsAbuse(true)
.build();
persistResource(contact);
var action = createAction(String.format(registrarPostData, "registrarId", "app, dev", false));
action.run();
Registrar newRegistrar = Registrar.loadByRegistrarId("registrarId").get();
assertThat(newRegistrar.getAllowedTlds()).containsExactly("app", "dev");
assertThat(newRegistrar.isRegistryLockAllowed()).isFalse();
assertThat(((FakeResponse) consoleApiParams.response()).getStatus())
.isEqualTo(HttpStatusCodes.STATUS_CODE_OK);
}

@Test
void testSuccess__sendsEmail() throws AddressException, IOException {
var action = createAction(String.format(registrarPostData, "registrarId", "app, dev", false));
action.run();
verify(gmailClient, times(1))
.sendEmail(
EmailMessage.create(
"Registrar registrarId has been updated",
"The following changes were made in registry UNITTEST environment to the registrar"
+ " registrarId:/nAllowed TLDs: [] -> [app, dev]",
new InternetAddress("testEmail@google.com")));
}

private ConsoleApiParams createParams() {
AuthResult authResult = AuthResult.createUser(UserAuthInfo.create(user));
return FakeConsoleApiParams.get(Optional.of(authResult));
}

ConsoleUpdateRegistrarAction createAction(String requestData) throws IOException {
when(consoleApiParams.request().getMethod()).thenReturn(Action.Method.POST.toString());
doReturn(new BufferedReader(new StringReader(requestData)))
.when(consoleApiParams.request())
.getReader();
Optional<Registrar> maybeRegistrarUpdateData =
RegistrarConsoleModule.provideRegistrar(
GSON, RequestModule.provideJsonBody(consoleApiParams.request(), GSON));
return new ConsoleUpdateRegistrarAction(
consoleApiParams, gmailClient, maybeRegistrarUpdateData);
}
}

0 comments on commit bee4284

Please sign in to comment.