mirror of
https://github.com/google/nomulus
synced 2026-02-11 23:31:37 +00:00
Compare commits
3 Commits
nomulus-20
...
nomulus-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2e9d4cfc7 | ||
|
|
2948dcc1be | ||
|
|
c5644d5c8b |
@@ -16,6 +16,8 @@ package google.registry.model.console;
|
||||
|
||||
/** Permissions that users may have in the UI, either per-registrar or globally. */
|
||||
public enum ConsolePermission {
|
||||
AUDIT_ACTIVITY_BY_USER,
|
||||
AUDIT_ACTIVITY_BY_REGISTRAR,
|
||||
/** View basic information about a registrar. */
|
||||
VIEW_REGISTRAR_DETAILS,
|
||||
/** Edit basic information about a registrar. */
|
||||
|
||||
@@ -55,6 +55,8 @@ public class ConsoleRoleDefinitions {
|
||||
new ImmutableSet.Builder<ConsolePermission>()
|
||||
.addAll(SUPPORT_AGENT_PERMISSIONS)
|
||||
.add(
|
||||
ConsolePermission.AUDIT_ACTIVITY_BY_USER,
|
||||
ConsolePermission.AUDIT_ACTIVITY_BY_REGISTRAR,
|
||||
ConsolePermission.MANAGE_REGISTRARS,
|
||||
ConsolePermission.GET_REGISTRANT_EMAIL,
|
||||
ConsolePermission.SUSPEND_DOMAIN,
|
||||
@@ -111,6 +113,7 @@ public class ConsoleRoleDefinitions {
|
||||
new ImmutableSet.Builder<ConsolePermission>()
|
||||
.addAll(TECH_CONTACT_PERMISSIONS)
|
||||
.add(ConsolePermission.MANAGE_USERS)
|
||||
.add(ConsolePermission.AUDIT_ACTIVITY_BY_REGISTRAR)
|
||||
.build();
|
||||
|
||||
private ConsoleRoleDefinitions() {}
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.model.console;
|
||||
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.annotations.IdAllocation;
|
||||
@@ -45,6 +46,7 @@ public class ConsoleUpdateHistory extends ImmutableObject implements Buildable {
|
||||
@Id @IdAllocation @Column Long revisionId;
|
||||
|
||||
@Column(nullable = false)
|
||||
@Expose
|
||||
DateTime modificationTime;
|
||||
|
||||
/** The HTTP method (e.g. POST, PUT) used to make this modification. */
|
||||
@@ -54,6 +56,7 @@ public class ConsoleUpdateHistory extends ImmutableObject implements Buildable {
|
||||
/** The type of modification. */
|
||||
@Column(nullable = false)
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Expose
|
||||
Type type;
|
||||
|
||||
/** The URL of the action that was used to make the modification. */
|
||||
@@ -61,11 +64,12 @@ public class ConsoleUpdateHistory extends ImmutableObject implements Buildable {
|
||||
String url;
|
||||
|
||||
/** An optional further description of the action. */
|
||||
String description;
|
||||
@Expose String description;
|
||||
|
||||
/** The user that performed the modification. */
|
||||
@JoinColumn(name = "actingUser", referencedColumnName = "emailAddress", nullable = false)
|
||||
@ManyToOne
|
||||
@Expose
|
||||
User actingUser;
|
||||
|
||||
public Long getRevisionId() {
|
||||
@@ -102,18 +106,24 @@ public class ConsoleUpdateHistory extends ImmutableObject implements Buildable {
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
DUM_DOWNLOAD,
|
||||
DOMAIN_DELETE,
|
||||
DOMAIN_SUSPEND,
|
||||
DOMAIN_UNSUSPEND,
|
||||
EPP_PASSWORD_UPDATE,
|
||||
REGISTRAR_CREATE,
|
||||
REGISTRAR_CONTACTS_UPDATE,
|
||||
REGISTRAR_SECURITY_UPDATE,
|
||||
REGISTRAR_UPDATE,
|
||||
REGISTRY_LOCK,
|
||||
REGISTRY_UNLOCK,
|
||||
USER_CREATE,
|
||||
USER_DELETE,
|
||||
USER_UPDATE
|
||||
USER_UPDATE,
|
||||
}
|
||||
|
||||
public static final String DESCRIPTION_SEPARATOR = "|";
|
||||
|
||||
public static class Builder extends Buildable.Builder<ConsoleUpdateHistory> {
|
||||
public Builder() {}
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ import google.registry.ui.server.console.ConsoleDomainGetAction;
|
||||
import google.registry.ui.server.console.ConsoleDomainListAction;
|
||||
import google.registry.ui.server.console.ConsoleDumDownloadAction;
|
||||
import google.registry.ui.server.console.ConsoleEppPasswordAction;
|
||||
import google.registry.ui.server.console.ConsoleHistoryDataAction;
|
||||
import google.registry.ui.server.console.ConsoleModule;
|
||||
import google.registry.ui.server.console.ConsoleOteAction;
|
||||
import google.registry.ui.server.console.ConsoleRegistryLockAction;
|
||||
@@ -122,6 +123,8 @@ import google.registry.ui.server.console.ConsoleRegistryLockVerifyAction;
|
||||
import google.registry.ui.server.console.ConsoleUpdateRegistrarAction;
|
||||
import google.registry.ui.server.console.ConsoleUserDataAction;
|
||||
import google.registry.ui.server.console.ConsoleUsersAction;
|
||||
import google.registry.ui.server.console.PasswordResetRequestAction;
|
||||
import google.registry.ui.server.console.PasswordResetVerifyAction;
|
||||
import google.registry.ui.server.console.RegistrarsAction;
|
||||
import google.registry.ui.server.console.domains.ConsoleBulkDomainAction;
|
||||
import google.registry.ui.server.console.settings.ContactAction;
|
||||
@@ -183,6 +186,8 @@ interface RequestComponent {
|
||||
|
||||
ConsoleEppPasswordAction consoleEppPasswordAction();
|
||||
|
||||
ConsoleHistoryDataAction consoleHistoryDataAction();
|
||||
|
||||
ConsoleOteAction consoleOteAction();
|
||||
|
||||
ConsoleRegistryLockAction consoleRegistryLockAction();
|
||||
@@ -249,6 +254,10 @@ interface RequestComponent {
|
||||
|
||||
NordnVerifyAction nordnVerifyAction();
|
||||
|
||||
PasswordResetRequestAction passwordResetRequestAction();
|
||||
|
||||
PasswordResetVerifyAction passwordResetVerifyAction();
|
||||
|
||||
PublishDnsUpdatesAction publishDnsUpdatesAction();
|
||||
|
||||
PublishInvoicesAction uploadInvoicesAction();
|
||||
@@ -281,6 +290,8 @@ interface RequestComponent {
|
||||
|
||||
RdapNameserverSearchAction rdapNameserverSearchAction();
|
||||
|
||||
RdapRegistrarFieldsAction rdapRegistrarFieldsAction();
|
||||
|
||||
RdeReportAction rdeReportAction();
|
||||
|
||||
RdeReporter rdeReporter();
|
||||
@@ -332,9 +343,7 @@ interface RequestComponent {
|
||||
WhoisAction whoisAction();
|
||||
|
||||
WhoisHttpAction whoisHttpAction();
|
||||
|
||||
RdapRegistrarFieldsAction rdapRegistrarFieldsAction();
|
||||
|
||||
|
||||
WipeOutContactHistoryPiiAction wipeOutContactHistoryPiiAction();
|
||||
|
||||
@Subcomponent.Builder
|
||||
|
||||
@@ -38,6 +38,8 @@ import google.registry.ui.server.console.ConsoleRegistryLockVerifyAction;
|
||||
import google.registry.ui.server.console.ConsoleUpdateRegistrarAction;
|
||||
import google.registry.ui.server.console.ConsoleUserDataAction;
|
||||
import google.registry.ui.server.console.ConsoleUsersAction;
|
||||
import google.registry.ui.server.console.PasswordResetRequestAction;
|
||||
import google.registry.ui.server.console.PasswordResetVerifyAction;
|
||||
import google.registry.ui.server.console.RegistrarsAction;
|
||||
import google.registry.ui.server.console.domains.ConsoleBulkDomainAction;
|
||||
import google.registry.ui.server.console.settings.ContactAction;
|
||||
@@ -84,6 +86,12 @@ public interface FrontendRequestComponent {
|
||||
|
||||
FlowComponent.Builder flowComponentBuilder();
|
||||
|
||||
PasswordResetRequestAction passwordResetRequestAction();
|
||||
|
||||
PasswordResetVerifyAction passwordResetVerifyAction();
|
||||
|
||||
RdapRegistrarFieldsAction rdapRegistrarFieldsAction();
|
||||
|
||||
ReadinessProbeActionFrontend readinessProbeActionFrontend();
|
||||
|
||||
ReadinessProbeConsoleAction readinessProbeConsoleAction();
|
||||
@@ -92,8 +100,6 @@ public interface FrontendRequestComponent {
|
||||
|
||||
SecurityAction securityAction();
|
||||
|
||||
RdapRegistrarFieldsAction rdapRegistrarFieldsAction();
|
||||
|
||||
@Subcomponent.Builder
|
||||
abstract class Builder implements RequestComponentBuilder<FrontendRequestComponent> {
|
||||
@Override public abstract Builder requestModule(RequestModule requestModule);
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
package google.registry.ui.server.console;
|
||||
|
||||
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 org.joda.time.DateTimeZone.UTC;
|
||||
@@ -24,6 +23,7 @@ import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
import google.registry.model.console.ConsoleUpdateHistory;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.GaeService;
|
||||
@@ -34,7 +34,6 @@ import google.registry.util.Clock;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVPrinter;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -100,27 +99,40 @@ public class ConsoleDumDownloadAction extends ConsoleApiAction {
|
||||
consoleApiParams.response().setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
tm().transact(
|
||||
() -> {
|
||||
finishAndPersistConsoleUpdateHistory(
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
.setType(ConsoleUpdateHistory.Type.DUM_DOWNLOAD)
|
||||
.setDescription(registrarId));
|
||||
});
|
||||
consoleApiParams.response().setStatus(HttpServletResponse.SC_OK);
|
||||
}
|
||||
|
||||
private void writeCsv(CSVPrinter printer) throws IOException {
|
||||
String sql = SQL_TEMPLATE.replaceAll(":now", clock.nowUtc().toString());
|
||||
|
||||
// We deliberately don't want to use ImmutableList.copyOf because underlying list may contain
|
||||
// large amount of records and that will degrade performance.
|
||||
List<String> queryResult =
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().getEntityManager()
|
||||
.createNativeQuery(sql)
|
||||
.setParameter("registrarId", registrarId)
|
||||
.setHint("org.hibernate.fetchSize", 1000)
|
||||
.getResultList());
|
||||
|
||||
ImmutableList<String[]> formattedRecords =
|
||||
queryResult.stream().map(r -> r.split(",")).collect(toImmutableList());
|
||||
printer.printRecord(
|
||||
ImmutableList.of("Domain Name", "Creation Time", "Expiration Time", "Domain Statuses"));
|
||||
printer.printRecords(formattedRecords);
|
||||
|
||||
tm().transact(
|
||||
() -> {
|
||||
try (var resultStream =
|
||||
tm().getEntityManager()
|
||||
.createNativeQuery(sql, String.class)
|
||||
.setParameter("registrarId", registrarId)
|
||||
.setHint("org.hibernate.fetchSize", 1000)
|
||||
.getResultStream()) {
|
||||
|
||||
resultStream.forEach(
|
||||
row -> {
|
||||
try {
|
||||
printer.printRecord((Object[]) ((String) row).split(","));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
// Copyright 2025 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.GET;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
import google.registry.model.console.ConsoleUpdateHistory;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.GaeService;
|
||||
import google.registry.request.Action.GkeService;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Action(
|
||||
service = GaeService.DEFAULT,
|
||||
gkeService = GkeService.CONSOLE,
|
||||
path = ConsoleHistoryDataAction.PATH,
|
||||
method = {GET},
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public class ConsoleHistoryDataAction extends ConsoleApiAction {
|
||||
|
||||
private static final String SQL_USER_HISTORY =
|
||||
"""
|
||||
SELECT * FROM "ConsoleUpdateHistory"
|
||||
WHERE acting_user = :actingUser
|
||||
""";
|
||||
|
||||
private static final String SQL_REGISTRAR_HISTORY =
|
||||
"""
|
||||
SELECT *
|
||||
FROM "ConsoleUpdateHistory"
|
||||
WHERE SPLIT_PART(description, '|', 1) = :registrarId;
|
||||
""";
|
||||
|
||||
public static final String PATH = "/console-api/history";
|
||||
|
||||
private final String registrarId;
|
||||
private final Optional<String> consoleUserEmail;
|
||||
|
||||
@Inject
|
||||
public ConsoleHistoryDataAction(
|
||||
ConsoleApiParams consoleApiParams,
|
||||
@Parameter("registrarId") String registrarId,
|
||||
@Parameter("consoleUserEmail") Optional<String> consoleUserEmail) {
|
||||
super(consoleApiParams);
|
||||
this.registrarId = registrarId;
|
||||
this.consoleUserEmail = consoleUserEmail;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getHandler(User user) {
|
||||
if (this.consoleUserEmail.isPresent()) {
|
||||
this.historyByUser(user, this.consoleUserEmail.get());
|
||||
return;
|
||||
}
|
||||
|
||||
this.historyByRegistrarId(user, this.registrarId);
|
||||
}
|
||||
|
||||
private void historyByUser(User user, String consoleUserEmail) {
|
||||
if (!user.getUserRoles().hasGlobalPermission(ConsolePermission.AUDIT_ACTIVITY_BY_USER)) {
|
||||
setFailedResponse(
|
||||
"User doesn't have a permission to check audit activity by user", SC_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
List<ConsoleUpdateHistory> queryResult =
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().getEntityManager()
|
||||
.createNativeQuery(SQL_USER_HISTORY, ConsoleUpdateHistory.class)
|
||||
.setParameter("actingUser", consoleUserEmail)
|
||||
.setHint("org.hibernate.fetchSize", 1000)
|
||||
.getResultList());
|
||||
|
||||
consoleApiParams.response().setPayload(consoleApiParams.gson().toJson(queryResult));
|
||||
consoleApiParams.response().setStatus(SC_OK);
|
||||
}
|
||||
|
||||
private void historyByRegistrarId(User user, String registrarId) {
|
||||
checkArgument(!Strings.isNullOrEmpty(registrarId), "Empty registrarId param");
|
||||
checkPermission(user, registrarId, ConsolePermission.AUDIT_ACTIVITY_BY_REGISTRAR);
|
||||
List<ConsoleUpdateHistory> queryResult =
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().getEntityManager()
|
||||
.createNativeQuery(SQL_REGISTRAR_HISTORY, ConsoleUpdateHistory.class)
|
||||
.setParameter("registrarId", registrarId)
|
||||
.setHint("org.hibernate.fetchSize", 1000)
|
||||
.getResultList());
|
||||
consoleApiParams.response().setPayload(consoleApiParams.gson().toJson(queryResult));
|
||||
consoleApiParams.response().setStatus(SC_OK);
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ import google.registry.ui.server.console.ConsoleEppPasswordAction.EppPasswordDat
|
||||
import google.registry.ui.server.console.ConsoleOteAction.OteCreateData;
|
||||
import google.registry.ui.server.console.ConsoleRegistryLockAction.ConsoleRegistryLockPostInput;
|
||||
import google.registry.ui.server.console.ConsoleUsersAction.UserData;
|
||||
import google.registry.ui.server.console.PasswordResetRequestAction.PasswordResetRequestData;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -246,6 +247,12 @@ public final class ConsoleModule {
|
||||
return extractRequiredParameter(req, "bulkDomainAction");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("resetRequestVerificationCode")
|
||||
public static String provideResetRequestVerificationCode(HttpServletRequest req) {
|
||||
return extractRequiredParameter(req, "resetRequestVerificationCode");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("eppPasswordChangeRequest")
|
||||
public static Optional<EppPasswordData> provideEppPasswordChangeRequest(
|
||||
@@ -273,4 +280,21 @@ public final class ConsoleModule {
|
||||
Gson gson, @OptionalJsonPayload Optional<JsonElement> payload) {
|
||||
return payload.map(e -> gson.fromJson(e, ConsoleRegistryLockPostInput.class));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("passwordResetRequestData")
|
||||
public static PasswordResetRequestData providePasswordResetRequestData(
|
||||
Gson gson, @OptionalJsonPayload Optional<JsonElement> payload) {
|
||||
return payload
|
||||
.map(e -> gson.fromJson(e, PasswordResetRequestData.class))
|
||||
.orElseThrow(
|
||||
() -> new IllegalArgumentException("Must provide password request reset data"));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("newPassword")
|
||||
public static Optional<String> provideNewPassword(
|
||||
Gson gson, @OptionalJsonPayload Optional<JsonElement> payload) {
|
||||
return payload.map(e -> gson.fromJson(e, String.class));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,12 @@
|
||||
|
||||
package google.registry.ui.server.console;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.model.console.ConsoleUpdateHistory;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.domain.RegistryLock;
|
||||
import google.registry.request.Action;
|
||||
@@ -64,6 +66,21 @@ public class ConsoleRegistryLockVerifyAction extends ConsoleApiAction {
|
||||
RegistryLockVerificationResponse lockResponse =
|
||||
new RegistryLockVerificationResponse(
|
||||
Ascii.toLowerCase(action.toString()), lock.getDomainName(), lock.getRegistrarId());
|
||||
tm().transact(
|
||||
() -> {
|
||||
finishAndPersistConsoleUpdateHistory(
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
.setType(
|
||||
action == RegistryLockAction.LOCKED
|
||||
? ConsoleUpdateHistory.Type.REGISTRY_LOCK
|
||||
: ConsoleUpdateHistory.Type.REGISTRY_UNLOCK)
|
||||
.setDescription(
|
||||
String.format(
|
||||
"%s%s%s",
|
||||
lock.getRegistrarId(),
|
||||
ConsoleUpdateHistory.DESCRIPTION_SEPARATOR,
|
||||
lockResponse)));
|
||||
});
|
||||
consoleApiParams.response().setPayload(consoleApiParams.gson().toJson(lockResponse));
|
||||
consoleApiParams.response().setStatus(HttpServletResponse.SC_OK);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
import google.registry.model.console.ConsoleUpdateHistory;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
@@ -177,6 +178,12 @@ public class ConsoleUsersAction extends ConsoleApiAction {
|
||||
tm().delete(key);
|
||||
User.revokeIapPermission(email, maybeGroupEmailAddress, cloudTasksUtils, null, iamClient);
|
||||
sendConfirmationEmail(registrarId, email, "Deleted user");
|
||||
finishAndPersistConsoleUpdateHistory(
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
.setType(ConsoleUpdateHistory.Type.USER_DELETE)
|
||||
.setDescription(
|
||||
String.format(
|
||||
"%s%s%s", registrarId, ConsoleUpdateHistory.DESCRIPTION_SEPARATOR, email)));
|
||||
}
|
||||
|
||||
consoleApiParams.response().setStatus(SC_OK);
|
||||
@@ -231,6 +238,12 @@ public class ConsoleUsersAction extends ConsoleApiAction {
|
||||
consoleApiParams
|
||||
.gson()
|
||||
.toJson(new UserData(newEmail, ACCOUNT_MANAGER.toString(), newUser.getPassword())));
|
||||
finishAndPersistConsoleUpdateHistory(
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
.setType(ConsoleUpdateHistory.Type.USER_CREATE)
|
||||
.setDescription(
|
||||
String.format(
|
||||
"%s%s%s", registrarId, ConsoleUpdateHistory.DESCRIPTION_SEPARATOR, newEmail)));
|
||||
}
|
||||
|
||||
private void runUpdateInTransaction() {
|
||||
@@ -245,6 +258,15 @@ public class ConsoleUsersAction extends ConsoleApiAction {
|
||||
|
||||
sendConfirmationEmail(registrarId, this.userData.get().emailAddress, "Updated user");
|
||||
consoleApiParams.response().setStatus(SC_OK);
|
||||
finishAndPersistConsoleUpdateHistory(
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
.setType(ConsoleUpdateHistory.Type.USER_UPDATE)
|
||||
.setDescription(
|
||||
String.format(
|
||||
"%s%s%s",
|
||||
registrarId,
|
||||
ConsoleUpdateHistory.DESCRIPTION_SEPARATOR,
|
||||
this.userData.get().emailAddress)));
|
||||
}
|
||||
|
||||
private boolean isModifyingRequestValid() {
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
// Copyright 2025 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 com.google.gson.annotations.Expose;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
import google.registry.model.console.PasswordResetRequest;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.persistence.transaction.QueryComposer;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.EmailMessage;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.mail.internet.AddressException;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@Action(
|
||||
service = Action.GaeService.DEFAULT,
|
||||
gkeService = Action.GkeService.CONSOLE,
|
||||
path = PasswordResetRequestAction.PATH,
|
||||
method = Action.Method.POST,
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public class PasswordResetRequestAction extends ConsoleApiAction {
|
||||
|
||||
static final String PATH = "/console-api/password-reset-request";
|
||||
static final String VERIFICATION_EMAIL_TEMPLATE =
|
||||
"""
|
||||
Please click the link below to perform the requested password reset. Note: this\
|
||||
code will expire in one hour.
|
||||
|
||||
%s\
|
||||
""";
|
||||
|
||||
private final PasswordResetRequestData passwordResetRequestData;
|
||||
|
||||
@Inject
|
||||
public PasswordResetRequestAction(
|
||||
ConsoleApiParams consoleApiParams,
|
||||
@Parameter("passwordResetRequestData") PasswordResetRequestData passwordResetRequestData) {
|
||||
super(consoleApiParams);
|
||||
this.passwordResetRequestData = passwordResetRequestData;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postHandler(User user) {
|
||||
// Temporary flag when testing email sending etc
|
||||
if (!user.getUserRoles().isAdmin()) {
|
||||
setFailedResponse("", HttpServletResponse.SC_FORBIDDEN);
|
||||
}
|
||||
tm().transact(() -> performRequest(user));
|
||||
consoleApiParams.response().setStatus(HttpServletResponse.SC_OK);
|
||||
}
|
||||
|
||||
private void performRequest(User user) {
|
||||
checkArgument(passwordResetRequestData.type != null, "Type cannot be null");
|
||||
checkArgument(passwordResetRequestData.registrarId != null, "Registrar ID cannot be null");
|
||||
PasswordResetRequest.Type type = passwordResetRequestData.type;
|
||||
String registrarId = passwordResetRequestData.registrarId;
|
||||
|
||||
ConsolePermission requiredPermission;
|
||||
String destinationEmail;
|
||||
String emailSubject;
|
||||
switch (type) {
|
||||
case EPP:
|
||||
requiredPermission = ConsolePermission.EDIT_REGISTRAR_DETAILS;
|
||||
destinationEmail = getAdminPocEmail(registrarId);
|
||||
emailSubject = "EPP password reset request";
|
||||
break;
|
||||
case REGISTRY_LOCK:
|
||||
checkArgument(
|
||||
passwordResetRequestData.registryLockEmail != null,
|
||||
"Must provide registry lock email to reset");
|
||||
requiredPermission = ConsolePermission.MANAGE_USERS;
|
||||
destinationEmail = passwordResetRequestData.registryLockEmail;
|
||||
checkUserExistsWithRegistryLockEmail(destinationEmail);
|
||||
emailSubject = "Registry lock password reset request";
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown type " + type);
|
||||
}
|
||||
|
||||
checkPermission(user, registrarId, requiredPermission);
|
||||
|
||||
InternetAddress destinationAddress;
|
||||
try {
|
||||
destinationAddress = new InternetAddress(destinationEmail);
|
||||
} catch (AddressException e) {
|
||||
// Shouldn't happen
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
PasswordResetRequest resetRequest =
|
||||
new PasswordResetRequest.Builder()
|
||||
.setRequester(user.getEmailAddress())
|
||||
.setRegistrarId(registrarId)
|
||||
.setType(type)
|
||||
.setDestinationEmail(destinationEmail)
|
||||
.build();
|
||||
tm().put(resetRequest);
|
||||
String verificationUrl =
|
||||
String.format(
|
||||
"https://%s/console/#/password-reset-verify?resetRequestVerificationCode=%s",
|
||||
consoleApiParams.request().getServerName(), resetRequest.getVerificationCode());
|
||||
String body = String.format(VERIFICATION_EMAIL_TEMPLATE, verificationUrl);
|
||||
consoleApiParams
|
||||
.sendEmailUtils()
|
||||
.gmailClient
|
||||
.sendEmail(EmailMessage.create(emailSubject, body, destinationAddress));
|
||||
}
|
||||
|
||||
static User checkUserExistsWithRegistryLockEmail(String destinationEmail) {
|
||||
return tm().createQueryComposer(User.class)
|
||||
.where("registryLockEmailAddress", QueryComposer.Comparator.EQ, destinationEmail)
|
||||
.first()
|
||||
.orElseThrow(
|
||||
() -> new IllegalArgumentException("Unknown user with lock email " + destinationEmail));
|
||||
}
|
||||
|
||||
private String getAdminPocEmail(String registrarId) {
|
||||
return RegistrarPoc.loadForRegistrar(registrarId).stream()
|
||||
.filter(poc -> poc.getTypes().contains(RegistrarPoc.Type.ADMIN))
|
||||
.map(RegistrarPoc::getEmailAddress)
|
||||
.findAny()
|
||||
.orElseThrow(() -> new IllegalStateException("No admin contacts found for " + registrarId));
|
||||
}
|
||||
|
||||
public record PasswordResetRequestData(
|
||||
@Expose PasswordResetRequest.Type type,
|
||||
@Expose String registrarId,
|
||||
@Expose @Nullable String registryLockEmail) {}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
// Copyright 2025 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.GET;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.ui.server.console.PasswordResetRequestAction.checkUserExistsWithRegistryLockEmail;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
import google.registry.model.console.PasswordResetRequest;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
@Action(
|
||||
service = Action.GaeService.DEFAULT,
|
||||
gkeService = Action.GkeService.CONSOLE,
|
||||
path = PasswordResetVerifyAction.PATH,
|
||||
method = {GET, POST},
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public class PasswordResetVerifyAction extends ConsoleApiAction {
|
||||
|
||||
static final String PATH = "/console-api/password-reset-verify";
|
||||
|
||||
private final String verificationCode;
|
||||
private final Optional<String> newPassword;
|
||||
|
||||
@Inject
|
||||
public PasswordResetVerifyAction(
|
||||
ConsoleApiParams consoleApiParams,
|
||||
@Parameter("resetRequestVerificationCode") String verificationCode,
|
||||
@Parameter("newPassword") Optional<String> newPassword) {
|
||||
super(consoleApiParams);
|
||||
this.verificationCode = verificationCode;
|
||||
this.newPassword = newPassword;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getHandler(User user) {
|
||||
// Temporary flag when testing email sending etc
|
||||
if (!user.getUserRoles().isAdmin()) {
|
||||
setFailedResponse("", HttpServletResponse.SC_FORBIDDEN);
|
||||
}
|
||||
PasswordResetRequest request = tm().transact(() -> loadAndValidateResetRequest(user));
|
||||
ImmutableMap<String, ?> result =
|
||||
ImmutableMap.of("type", request.getType(), "registrarId", request.getRegistrarId());
|
||||
consoleApiParams.response().setPayload(consoleApiParams.gson().toJson(result));
|
||||
consoleApiParams.response().setStatus(HttpServletResponse.SC_OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postHandler(User user) {
|
||||
// Temporary flag when testing email sending etc
|
||||
if (!user.getUserRoles().isAdmin()) {
|
||||
setFailedResponse("", HttpServletResponse.SC_FORBIDDEN);
|
||||
}
|
||||
checkArgument(!Strings.isNullOrEmpty(newPassword.orElse(null)), "Password must be provided");
|
||||
tm().transact(
|
||||
() -> {
|
||||
PasswordResetRequest request = loadAndValidateResetRequest(user);
|
||||
switch (request.getType()) {
|
||||
case EPP -> handleEppPasswordReset(request);
|
||||
case REGISTRY_LOCK -> handleRegistryLockPasswordReset(request);
|
||||
}
|
||||
tm().put(request.asBuilder().setFulfillmentTime(tm().getTransactionTime()).build());
|
||||
});
|
||||
consoleApiParams.response().setStatus(HttpServletResponse.SC_OK);
|
||||
}
|
||||
|
||||
private void handleEppPasswordReset(PasswordResetRequest request) {
|
||||
Registrar registrar = Registrar.loadByRegistrarId(request.getRegistrarId()).get();
|
||||
tm().put(registrar.asBuilder().setPassword(newPassword.get()).build());
|
||||
}
|
||||
|
||||
private void handleRegistryLockPasswordReset(PasswordResetRequest request) {
|
||||
User affectedUser = checkUserExistsWithRegistryLockEmail(request.getDestinationEmail());
|
||||
tm().put(
|
||||
affectedUser
|
||||
.asBuilder()
|
||||
.removeRegistryLockPassword()
|
||||
.setRegistryLockPassword(newPassword.get())
|
||||
.build());
|
||||
}
|
||||
|
||||
private PasswordResetRequest loadAndValidateResetRequest(User user) {
|
||||
PasswordResetRequest request =
|
||||
tm().loadByKeyIfPresent(VKey.create(PasswordResetRequest.class, verificationCode))
|
||||
.orElseThrow(this::createVerificationCodeException);
|
||||
ConsolePermission requiredVerifyPermission =
|
||||
switch (request.getType()) {
|
||||
case EPP -> ConsolePermission.MANAGE_USERS;
|
||||
case REGISTRY_LOCK -> ConsolePermission.REGISTRY_LOCK;
|
||||
};
|
||||
checkPermission(user, request.getRegistrarId(), requiredVerifyPermission);
|
||||
if (request
|
||||
.getRequestTime()
|
||||
.plus(Duration.standardHours(1))
|
||||
.isBefore(tm().getTransactionTime())) {
|
||||
throw createVerificationCodeException();
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
private IllegalArgumentException createVerificationCodeException() {
|
||||
return new IllegalArgumentException(
|
||||
"Unknown, invalid, or expired verification code " + verificationCode);
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
import google.registry.model.console.ConsoleUpdateHistory;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
@@ -84,6 +85,7 @@ public class ContactAction extends ConsoleApiAction {
|
||||
protected void deleteHandler(User user) {
|
||||
updateContacts(
|
||||
user,
|
||||
"Deleted " + contact.get().getEmailAddress(),
|
||||
(registrar, oldContacts) ->
|
||||
oldContacts.stream()
|
||||
.filter(
|
||||
@@ -96,6 +98,7 @@ public class ContactAction extends ConsoleApiAction {
|
||||
protected void postHandler(User user) {
|
||||
updateContacts(
|
||||
user,
|
||||
"Created " + contact.get().getEmailAddress(),
|
||||
(registrar, oldContacts) -> {
|
||||
RegistrarPoc newContact = contact.get();
|
||||
return ImmutableSet.<RegistrarPoc>builder()
|
||||
@@ -121,6 +124,7 @@ public class ContactAction extends ConsoleApiAction {
|
||||
protected void putHandler(User user) {
|
||||
updateContacts(
|
||||
user,
|
||||
"Updated " + contact.get().getEmailAddress(),
|
||||
(registrar, oldContacts) -> {
|
||||
RegistrarPoc updatedContact = contact.get();
|
||||
return oldContacts.stream()
|
||||
@@ -146,6 +150,7 @@ public class ContactAction extends ConsoleApiAction {
|
||||
|
||||
private void updateContacts(
|
||||
User user,
|
||||
String historyDescription,
|
||||
BiFunction<Registrar, ImmutableSet<RegistrarPoc>, ImmutableSet<RegistrarPoc>>
|
||||
contactsUpdater) {
|
||||
checkPermission(user, registrarId, ConsolePermission.EDIT_REGISTRAR_DETAILS);
|
||||
@@ -176,6 +181,15 @@ public class ContactAction extends ConsoleApiAction {
|
||||
tm().put(updatedRegistrar);
|
||||
sendExternalUpdatesIfNecessary(
|
||||
EmailInfo.create(registrar, updatedRegistrar, oldContacts, newContacts));
|
||||
finishAndPersistConsoleUpdateHistory(
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
.setType(ConsoleUpdateHistory.Type.REGISTRAR_CONTACTS_UPDATE)
|
||||
.setDescription(
|
||||
String.format(
|
||||
"%s%s%s",
|
||||
registrarId,
|
||||
ConsoleUpdateHistory.DESCRIPTION_SEPARATOR,
|
||||
historyDescription)));
|
||||
});
|
||||
consoleApiParams.response().setStatus(SC_OK);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import google.registry.ui.server.console.ConsoleApiAction;
|
||||
import google.registry.ui.server.console.ConsoleApiParams;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
/**
|
||||
* Console action for editing fields on a registrar that are visible in WHOIS/RDAP.
|
||||
@@ -82,19 +83,37 @@ public class RdapRegistrarFieldsAction extends ConsoleApiAction {
|
||||
return;
|
||||
}
|
||||
|
||||
Registrar newRegistrar =
|
||||
savedRegistrar
|
||||
.asBuilder()
|
||||
.setLocalizedAddress(providedRegistrar.getLocalizedAddress())
|
||||
.setPhoneNumber(providedRegistrar.getPhoneNumber())
|
||||
.setFaxNumber(providedRegistrar.getFaxNumber())
|
||||
.setEmailAddress(providedRegistrar.getEmailAddress())
|
||||
.build();
|
||||
StringJoiner updates = new StringJoiner(",");
|
||||
|
||||
var newRegistrarBuilder = savedRegistrar.asBuilder();
|
||||
|
||||
if (!providedRegistrar.getLocalizedAddress().equals(savedRegistrar.getLocalizedAddress())) {
|
||||
newRegistrarBuilder.setLocalizedAddress(providedRegistrar.getLocalizedAddress());
|
||||
updates.add("ADDRESS");
|
||||
}
|
||||
if (!providedRegistrar.getPhoneNumber().equals(savedRegistrar.getPhoneNumber())) {
|
||||
newRegistrarBuilder.setPhoneNumber(providedRegistrar.getPhoneNumber());
|
||||
updates.add("PHONE");
|
||||
}
|
||||
if (!providedRegistrar.getFaxNumber().equals(savedRegistrar.getPhoneNumber())) {
|
||||
newRegistrarBuilder.setFaxNumber(providedRegistrar.getFaxNumber());
|
||||
updates.add("FAX");
|
||||
}
|
||||
if (!providedRegistrar.getEmailAddress().equals(savedRegistrar.getEmailAddress())) {
|
||||
newRegistrarBuilder.setEmailAddress(providedRegistrar.getEmailAddress());
|
||||
updates.add("EMAIL");
|
||||
}
|
||||
var newRegistrar = newRegistrarBuilder.build();
|
||||
tm().put(newRegistrar);
|
||||
finishAndPersistConsoleUpdateHistory(
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
.setType(ConsoleUpdateHistory.Type.REGISTRAR_UPDATE)
|
||||
.setDescription(newRegistrar.getRegistrarId()));
|
||||
.setDescription(
|
||||
String.format(
|
||||
"%s%s%s",
|
||||
newRegistrar.getRegistrarId(),
|
||||
ConsoleUpdateHistory.DESCRIPTION_SEPARATOR,
|
||||
updates)));
|
||||
sendExternalUpdatesIfNecessary(
|
||||
EmailInfo.create(
|
||||
savedRegistrar,
|
||||
|
||||
@@ -39,6 +39,7 @@ import google.registry.ui.server.console.ConsoleApiAction;
|
||||
import google.registry.ui.server.console.ConsoleApiParams;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
@Action(
|
||||
service = GaeService.DEFAULT,
|
||||
@@ -86,10 +87,15 @@ public class SecurityAction extends ConsoleApiAction {
|
||||
|
||||
private void setResponse(Registrar savedRegistrar) {
|
||||
Registrar registrarParameter = registrar.get();
|
||||
Registrar.Builder updatedRegistrarBuilder =
|
||||
savedRegistrar
|
||||
.asBuilder()
|
||||
.setIpAddressAllowList(registrarParameter.getIpAddressAllowList());
|
||||
Registrar.Builder updatedRegistrarBuilder = savedRegistrar.asBuilder();
|
||||
StringJoiner updates = new StringJoiner(",");
|
||||
|
||||
if (!savedRegistrar
|
||||
.getIpAddressAllowList()
|
||||
.equals(registrarParameter.getIpAddressAllowList())) {
|
||||
updatedRegistrarBuilder.setIpAddressAllowList(registrarParameter.getIpAddressAllowList());
|
||||
updates.add("IP_CHANGE");
|
||||
}
|
||||
|
||||
try {
|
||||
if (!savedRegistrar
|
||||
@@ -99,6 +105,7 @@ public class SecurityAction extends ConsoleApiAction {
|
||||
String newClientCert = registrarParameter.getClientCertificate().get();
|
||||
certificateChecker.validateCertificate(newClientCert);
|
||||
updatedRegistrarBuilder.setClientCertificate(newClientCert, tm().getTransactionTime());
|
||||
updates.add("PRIMARY_SSL_CERT_CHANGE");
|
||||
}
|
||||
}
|
||||
if (!savedRegistrar
|
||||
@@ -109,6 +116,7 @@ public class SecurityAction extends ConsoleApiAction {
|
||||
certificateChecker.validateCertificate(newFailoverCert);
|
||||
updatedRegistrarBuilder.setFailoverClientCertificate(
|
||||
newFailoverCert, tm().getTransactionTime());
|
||||
updates.add("FAILOVER_SSL_CERT_CHANGE");
|
||||
}
|
||||
}
|
||||
} catch (InsecureCertificateException e) {
|
||||
@@ -121,7 +129,9 @@ public class SecurityAction extends ConsoleApiAction {
|
||||
finishAndPersistConsoleUpdateHistory(
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
.setType(ConsoleUpdateHistory.Type.REGISTRAR_SECURITY_UPDATE)
|
||||
.setDescription(registrarId));
|
||||
.setDescription(
|
||||
String.format(
|
||||
"%s%s%s", registrarId, ConsoleUpdateHistory.DESCRIPTION_SEPARATOR, updates)));
|
||||
|
||||
sendExternalUpdatesIfNecessary(
|
||||
EmailInfo.create(savedRegistrar, updatedRegistrar, ImmutableSet.of(), ImmutableSet.of()));
|
||||
|
||||
@@ -29,6 +29,7 @@ import google.registry.testing.TestDataHelper;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link RequestComponent}. */
|
||||
@@ -49,6 +50,7 @@ public class RequestComponentTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("To be removed with GAE components")
|
||||
void testGaeToJettyRoutingCoverage() {
|
||||
Set<Route> jettyRoutes = getRoutes(RequestComponent.class, "routing.txt");
|
||||
Set<Route> gaeRoutes = new HashSet<>();
|
||||
|
||||
@@ -1042,6 +1042,8 @@ public final class DatabaseHelper {
|
||||
.setGlobalRole(GlobalRole.FTE)
|
||||
.setIsAdmin(true)
|
||||
.build())
|
||||
.setRegistryLockEmailAddress("registrylock" + emailAddress)
|
||||
.setRegistryLockPassword("password")
|
||||
.build();
|
||||
tm().put(user);
|
||||
return user;
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
// Copyright 2025 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.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.model.console.ConsoleUpdateHistory;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.testing.ConsoleApiParamsUtils;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class ConsoleHistoryDataActionTest extends ConsoleActionBaseTestCase {
|
||||
|
||||
private static final Gson GSON = new Gson();
|
||||
private User noPermissionUser;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
noPermissionUser =
|
||||
DatabaseHelper.persistResource(
|
||||
new User.Builder()
|
||||
.setEmailAddress("no.perms@example.com")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder()
|
||||
.setRegistrarRoles(
|
||||
ImmutableMap.of("TheRegistrar", RegistrarRole.ACCOUNT_MANAGER))
|
||||
.build())
|
||||
.build());
|
||||
|
||||
DatabaseHelper.persistResources(
|
||||
ImmutableList.of(
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
.setType(ConsoleUpdateHistory.Type.REGISTRAR_UPDATE)
|
||||
.setActingUser(fteUser)
|
||||
.setDescription("TheRegistrar|Some change")
|
||||
.setModificationTime(clock.nowUtc())
|
||||
.setUrl("/test")
|
||||
.setMethod("POST")
|
||||
.build(),
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
.setType(ConsoleUpdateHistory.Type.REGISTRAR_UPDATE)
|
||||
.setActingUser(noPermissionUser)
|
||||
.setDescription("TheRegistrar|Another change")
|
||||
.setModificationTime(clock.nowUtc())
|
||||
.setUrl("/test")
|
||||
.setMethod("POST")
|
||||
.build(),
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
.setType(ConsoleUpdateHistory.Type.REGISTRAR_UPDATE)
|
||||
.setActingUser(fteUser)
|
||||
.setDescription("OtherRegistrar|Some change")
|
||||
.setModificationTime(clock.nowUtc())
|
||||
.setUrl("/test")
|
||||
.setMethod("POST")
|
||||
.build()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_getByRegistrar() {
|
||||
ConsoleHistoryDataAction action =
|
||||
createAction(AuthResult.createUser(fteUser), "TheRegistrar", Optional.empty());
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
List<Map<String, Object>> payload = GSON.fromJson(response.getPayload(), List.class);
|
||||
assertThat(payload.stream().map(record -> record.get("description")).collect(toImmutableList()))
|
||||
.containsExactly("TheRegistrar|Some change", "TheRegistrar|Another change");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_getByUser() {
|
||||
ConsoleHistoryDataAction action =
|
||||
createAction(AuthResult.createUser(fteUser), "TheRegistrar", Optional.of("fte@email.tld"));
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
List<Map<String, Object>> payload = GSON.fromJson(response.getPayload(), List.class);
|
||||
assertThat(payload.stream().map(record -> record.get("description")).collect(toImmutableList()))
|
||||
.containsExactly("TheRegistrar|Some change", "OtherRegistrar|Some change");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_noResults() {
|
||||
ConsoleHistoryDataAction action =
|
||||
createAction(AuthResult.createUser(fteUser), "NoHistoryRegistrar", Optional.empty());
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
assertThat(response.getPayload()).isEqualTo("[]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_getByRegistrar_noPermission() {
|
||||
ConsoleHistoryDataAction action =
|
||||
createAction(AuthResult.createUser(noPermissionUser), "TheRegistrar", Optional.empty());
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_getByUser_noPermission() {
|
||||
ConsoleHistoryDataAction action =
|
||||
createAction(
|
||||
AuthResult.createUser(noPermissionUser), "TheRegistrar", Optional.of("fte@email.tld"));
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(response.getPayload())
|
||||
.contains("User doesn't have a permission to check audit activity by user");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_emptyRegistrarId() {
|
||||
ConsoleHistoryDataAction action =
|
||||
createAction(AuthResult.createUser(fteUser), "", Optional.empty());
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(response.getPayload()).contains("Empty registrarId param");
|
||||
}
|
||||
|
||||
private ConsoleHistoryDataAction createAction(
|
||||
AuthResult authResult, String registrarId, Optional<String> consoleUserEmail) {
|
||||
consoleApiParams = ConsoleApiParamsUtils.createFake(authResult);
|
||||
when(consoleApiParams.request().getMethod()).thenReturn("GET");
|
||||
response = (FakeResponse) consoleApiParams.response();
|
||||
return new ConsoleHistoryDataAction(consoleApiParams, registrarId, consoleUserEmail);
|
||||
}
|
||||
}
|
||||
@@ -56,16 +56,17 @@ public class ConsoleRegistryLockVerifyActionTest extends ConsoleActionBaseTestCa
|
||||
createTld("test");
|
||||
defaultDomain = persistActiveDomain("example.test");
|
||||
user =
|
||||
new User.Builder()
|
||||
.setEmailAddress("user@theregistrar.com")
|
||||
.setRegistryLockEmailAddress("registrylock@theregistrar.com")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder()
|
||||
.setRegistrarRoles(
|
||||
ImmutableMap.of("TheRegistrar", RegistrarRole.PRIMARY_CONTACT))
|
||||
.build())
|
||||
.setRegistryLockPassword("registryLockPassword")
|
||||
.build();
|
||||
persistResource(
|
||||
new User.Builder()
|
||||
.setEmailAddress("user@theregistrar.com")
|
||||
.setRegistryLockEmailAddress("registrylock@theregistrar.com")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder()
|
||||
.setRegistrarRoles(
|
||||
ImmutableMap.of("TheRegistrar", RegistrarRole.PRIMARY_CONTACT))
|
||||
.build())
|
||||
.setRegistryLockPassword("registryLockPassword")
|
||||
.build());
|
||||
action = createAction(DEFAULT_CODE);
|
||||
}
|
||||
|
||||
|
||||
@@ -215,7 +215,7 @@ class ConsoleUpdateRegistrarActionTest extends ConsoleActionBaseTestCase {
|
||||
return ConsoleApiParamsUtils.createFake(authResult);
|
||||
}
|
||||
|
||||
ConsoleUpdateRegistrarAction createAction(String requestData) throws IOException {
|
||||
private ConsoleUpdateRegistrarAction createAction(String requestData) throws IOException {
|
||||
when(consoleApiParams.request().getMethod()).thenReturn(Action.Method.POST.toString());
|
||||
doReturn(new BufferedReader(new StringReader(requestData)))
|
||||
.when(consoleApiParams.request())
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
// Copyright 2025 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.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.console.PasswordResetRequest;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.testing.ConsoleApiParamsUtils;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.ui.server.console.PasswordResetRequestAction.PasswordResetRequestData;
|
||||
import google.registry.util.EmailMessage;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import javax.annotation.Nullable;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Tests for {@link PasswordResetRequestAction}. */
|
||||
public class PasswordResetRequestActionTest extends ConsoleActionBaseTestCase {
|
||||
|
||||
@Test
|
||||
void testSuccess_epp() throws Exception {
|
||||
PasswordResetRequestAction action =
|
||||
createAction(PasswordResetRequest.Type.EPP, "TheRegistrar", null);
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||
PasswordResetRequest actualRequest =
|
||||
DatabaseHelper.loadSingleton(PasswordResetRequest.class).get();
|
||||
assertAboutImmutableObjects()
|
||||
.that(actualRequest)
|
||||
.isEqualExceptFields(
|
||||
new PasswordResetRequest.Builder()
|
||||
.setDestinationEmail("johndoe@theregistrar.com")
|
||||
.setRequester("fte@email.tld")
|
||||
.setType(PasswordResetRequest.Type.EPP)
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.build(),
|
||||
"requestTime",
|
||||
"verificationCode");
|
||||
EmailMessage expectedMessage =
|
||||
EmailMessage.create(
|
||||
"EPP password reset request",
|
||||
"""
|
||||
Please click the link below to perform the requested password reset. Note: this\
|
||||
code will expire in one hour.
|
||||
|
||||
https://registrarconsole.tld/console/#/password-reset-verify?resetRequestVerificationCode=\
|
||||
"""
|
||||
+ actualRequest.getVerificationCode(),
|
||||
new InternetAddress("johndoe@theregistrar.com"));
|
||||
verify(consoleApiParams.sendEmailUtils().gmailClient).sendEmail(expectedMessage);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_registryLock() throws Exception {
|
||||
DatabaseHelper.persistResource(
|
||||
new User.Builder()
|
||||
.setEmailAddress("email@registry.tld")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder()
|
||||
.setRegistrarRoles(
|
||||
ImmutableMap.of(
|
||||
"TheRegistrar", RegistrarRole.ACCOUNT_MANAGER_WITH_REGISTRY_LOCK))
|
||||
.build())
|
||||
.setRegistryLockEmailAddress("registrylock@theregistrar.com")
|
||||
.setRegistryLockPassword("password")
|
||||
.build());
|
||||
PasswordResetRequestAction action =
|
||||
createAction(
|
||||
PasswordResetRequest.Type.REGISTRY_LOCK,
|
||||
"TheRegistrar",
|
||||
"registrylock@theregistrar.com");
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||
PasswordResetRequest actualRequest =
|
||||
DatabaseHelper.loadSingleton(PasswordResetRequest.class).get();
|
||||
assertAboutImmutableObjects()
|
||||
.that(actualRequest)
|
||||
.isEqualExceptFields(
|
||||
new PasswordResetRequest.Builder()
|
||||
.setDestinationEmail("registrylock@theregistrar.com")
|
||||
.setRequester("fte@email.tld")
|
||||
.setType(PasswordResetRequest.Type.REGISTRY_LOCK)
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.build(),
|
||||
"requestTime",
|
||||
"verificationCode");
|
||||
EmailMessage expectedMessage =
|
||||
EmailMessage.create(
|
||||
"Registry lock password reset request",
|
||||
"""
|
||||
Please click the link below to perform the requested password reset. Note: this\
|
||||
code will expire in one hour.
|
||||
|
||||
https://registrarconsole.tld/console/#/password-reset-verify?resetRequestVerificationCode=\
|
||||
"""
|
||||
+ actualRequest.getVerificationCode(),
|
||||
new InternetAddress("registrylock@theregistrar.com"));
|
||||
verify(consoleApiParams.sendEmailUtils().gmailClient).sendEmail(expectedMessage);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_nullType() throws Exception {
|
||||
PasswordResetRequestAction action = createAction(null, "TheRegistrar", "email@email.test");
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
|
||||
assertThat(response.getPayload()).isEqualTo("Type cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_nullRegistrarId() throws Exception {
|
||||
PasswordResetRequestAction action =
|
||||
createAction(PasswordResetRequest.Type.EPP, null, "email@email.test");
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
|
||||
assertThat(response.getPayload()).isEqualTo("Registrar ID cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_registryLock_nullEmail() throws Exception {
|
||||
PasswordResetRequestAction action =
|
||||
createAction(PasswordResetRequest.Type.REGISTRY_LOCK, "TheRegistrar", null);
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
|
||||
assertThat(response.getPayload()).isEqualTo("Must provide registry lock email to reset");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_registryLock_invalidEmail() throws Exception {
|
||||
PasswordResetRequestAction action =
|
||||
createAction(
|
||||
PasswordResetRequest.Type.REGISTRY_LOCK, "TheRegistrar", "nonexistent@email.com");
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo("Unknown user with lock email nonexistent@email.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("Enable when testing is done in sandbox and isAdmin check is removed")
|
||||
void testFailure_epp_noPermission() throws Exception {
|
||||
User user =
|
||||
new User.Builder()
|
||||
.setEmailAddress("email@email.test")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder()
|
||||
.setRegistrarRoles(
|
||||
ImmutableMap.of("TheRegistrar", RegistrarRole.ACCOUNT_MANAGER))
|
||||
.build())
|
||||
.build();
|
||||
PasswordResetRequestAction action =
|
||||
createAction(user, PasswordResetRequest.Type.EPP, "TheRegistrar", null);
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("Enable when testing is done in sandbox and isAdmin check is removed")
|
||||
void testFailure_lock_noPermission() throws Exception {
|
||||
User user =
|
||||
new User.Builder()
|
||||
.setEmailAddress("email@email.test")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder()
|
||||
.setRegistrarRoles(ImmutableMap.of("TheRegistrar", RegistrarRole.TECH_CONTACT))
|
||||
.build())
|
||||
.build();
|
||||
PasswordResetRequestAction action =
|
||||
createAction(
|
||||
user,
|
||||
PasswordResetRequest.Type.REGISTRY_LOCK,
|
||||
"TheRegistrar",
|
||||
"registrylockfte@email.tld");
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
private PasswordResetRequestAction createAction(
|
||||
User user,
|
||||
PasswordResetRequest.Type type,
|
||||
String registrarId,
|
||||
@Nullable String registryLockEmail) {
|
||||
consoleApiParams = ConsoleApiParamsUtils.createFake(AuthResult.createUser(user));
|
||||
return createAction(type, registrarId, registryLockEmail);
|
||||
}
|
||||
|
||||
private PasswordResetRequestAction createAction(
|
||||
PasswordResetRequest.Type type, String registrarId, @Nullable String registryLockEmail) {
|
||||
when(consoleApiParams.request().getMethod()).thenReturn(Action.Method.POST.toString());
|
||||
when(consoleApiParams.request().getServerName()).thenReturn("registrarconsole.tld");
|
||||
response = (FakeResponse) consoleApiParams.response();
|
||||
PasswordResetRequestData data =
|
||||
new PasswordResetRequestData(type, registrarId, registryLockEmail);
|
||||
return new PasswordResetRequestAction(consoleApiParams, data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
// Copyright 2025 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.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.console.PasswordResetRequest;
|
||||
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.request.auth.AuthResult;
|
||||
import google.registry.testing.ConsoleApiParamsUtils;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Tests for {@link PasswordResetVerifyAction}. */
|
||||
public class PasswordResetVerifyActionTest extends ConsoleActionBaseTestCase {
|
||||
|
||||
private String verificationCode;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
verificationCode = saveRequest(PasswordResetRequest.Type.EPP).getVerificationCode();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_get_epp() throws Exception {
|
||||
createAction("GET", verificationCode, null).run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||
assertThat(GSON.fromJson(response.getPayload(), Map.class))
|
||||
.isEqualTo(ImmutableMap.of("registrarId", "TheRegistrar", "type", "EPP"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_get_lock() throws Exception {
|
||||
verificationCode = saveRequest(PasswordResetRequest.Type.REGISTRY_LOCK).getVerificationCode();
|
||||
createAction("GET", verificationCode, null).run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||
assertThat(GSON.fromJson(response.getPayload(), Map.class))
|
||||
.isEqualTo(ImmutableMap.of("registrarId", "TheRegistrar", "type", "REGISTRY_LOCK"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_post_epp() throws Exception {
|
||||
assertThat(Registrar.loadByRegistrarId("TheRegistrar").get().verifyPassword("password2"))
|
||||
.isTrue();
|
||||
createAction("POST", verificationCode, "newEppPassword").run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||
assertThat(Registrar.loadByRegistrarId("TheRegistrar").get().verifyPassword("password2"))
|
||||
.isFalse();
|
||||
assertThat(Registrar.loadByRegistrarId("TheRegistrar").get().verifyPassword("newEppPassword"))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_post_lock() throws Exception {
|
||||
assertThat(loadByEntity(fteUser).verifyRegistryLockPassword("password")).isTrue();
|
||||
verificationCode = saveRequest(PasswordResetRequest.Type.REGISTRY_LOCK).getVerificationCode();
|
||||
createAction("POST", verificationCode, "newRegistryLockPassword").run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||
assertThat(loadByEntity(fteUser).verifyRegistryLockPassword("newRegistryLockPassword"))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_get_invalidVerificationCode() throws Exception {
|
||||
createAction("GET", "invalid", null).run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_post_invalidVerificationCode() throws Exception {
|
||||
createAction("POST", "invalid", "newPassword").run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_nullPassword() throws Exception {
|
||||
createAction("POST", verificationCode, null).run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_emptyPassword() throws Exception {
|
||||
createAction("POST", verificationCode, "").run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("Enable when testing is done in sandbox and isAdmin check is removed")
|
||||
void testFailure_get_epp_badPermission() throws Exception {
|
||||
createAction(createTechUser(), "GET", verificationCode, null).run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("Enable when testing is done in sandbox and isAdmin check is removed")
|
||||
void testFailure_get_lock_badPermission() throws Exception {
|
||||
createAction(createAccountManager(), "GET", verificationCode, null).run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("Enable when testing is done in sandbox and isAdmin check is removed")
|
||||
void testFailure_post_epp_badPermission() throws Exception {
|
||||
createAction(createTechUser(), "POST", verificationCode, "newPassword").run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("Enable when testing is done in sandbox and isAdmin check is removed")
|
||||
void testFailure_post_lock_badPermission() throws Exception {
|
||||
createAction(createAccountManager(), "POST", verificationCode, "newPassword").run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_get_expired() throws Exception {
|
||||
clock.advanceBy(Duration.standardDays(1));
|
||||
createAction("GET", verificationCode, null).run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_post_expired() throws Exception {
|
||||
clock.advanceBy(Duration.standardDays(1));
|
||||
createAction("POST", verificationCode, "newPassword").run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
|
||||
}
|
||||
|
||||
private User createTechUser() {
|
||||
return new User.Builder()
|
||||
.setEmailAddress("tech@example.tld")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder()
|
||||
.setRegistrarRoles(ImmutableMap.of("TheRegistrar", RegistrarRole.TECH_CONTACT))
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
private User createAccountManager() {
|
||||
return new User.Builder()
|
||||
.setEmailAddress("accountmanager@example.tld")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder()
|
||||
.setRegistrarRoles(ImmutableMap.of("TheRegistrar", RegistrarRole.ACCOUNT_MANAGER))
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
private PasswordResetRequest saveRequest(PasswordResetRequest.Type type) {
|
||||
return persistResource(
|
||||
new PasswordResetRequest.Builder()
|
||||
// use the built-in user registry lock email
|
||||
.setDestinationEmail("registrylockfte@email.tld")
|
||||
.setRequester("requester@email.tld")
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.setType(type)
|
||||
.build());
|
||||
}
|
||||
|
||||
private PasswordResetVerifyAction createAction(
|
||||
User user, String method, String verificationCode, @Nullable String newPassword) {
|
||||
consoleApiParams = ConsoleApiParamsUtils.createFake(AuthResult.createUser(user));
|
||||
return createAction(method, verificationCode, newPassword);
|
||||
}
|
||||
|
||||
private PasswordResetVerifyAction createAction(
|
||||
String method, String verificationCode, @Nullable String newPassword) {
|
||||
when(consoleApiParams.request().getMethod()).thenReturn(method);
|
||||
response = (FakeResponse) consoleApiParams.response();
|
||||
return new PasswordResetVerifyAction(
|
||||
consoleApiParams, verificationCode, Optional.ofNullable(newPassword));
|
||||
}
|
||||
}
|
||||
@@ -112,7 +112,7 @@ public class RdapRegistrarFieldsActionTest extends ConsoleActionBaseTestCase {
|
||||
.isEqualExceptFields(oldRegistrar, "localizedAddress", "phoneNumber", "faxNumber");
|
||||
ConsoleUpdateHistory history = loadSingleton(ConsoleUpdateHistory.class).get();
|
||||
assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.REGISTRAR_UPDATE);
|
||||
assertThat(history.getDescription()).hasValue("TheRegistrar");
|
||||
assertThat(history.getDescription()).hasValue("TheRegistrar|ADDRESS,PHONE,FAX");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -87,7 +87,7 @@ class SecurityActionTest extends ConsoleActionBaseTestCase {
|
||||
assertThat(r.getIpAddressAllowList().get(0).getNetmask()).isEqualTo(32);
|
||||
ConsoleUpdateHistory history = loadSingleton(ConsoleUpdateHistory.class).get();
|
||||
assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.REGISTRAR_SECURITY_UPDATE);
|
||||
assertThat(history.getDescription()).hasValue("registrarId");
|
||||
assertThat(history.getDescription()).hasValue("registrarId|IP_CHANGE,PRIMARY_SSL_CERT_CHANGE");
|
||||
}
|
||||
|
||||
private SecurityAction createAction(String registrarId) throws IOException {
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
SERVICE PATH CLASS METHODS OK MIN USER_POLICY
|
||||
FRONTEND /_dr/epp EppTlsAction POST n APP ADMIN
|
||||
FRONTEND /ready/frontend ReadinessProbeActionFrontend GET n NONE PUBLIC
|
||||
CONSOLE /console-api/bulk-domain ConsoleBulkDomainAction POST n USER PUBLIC
|
||||
CONSOLE /console-api/domain ConsoleDomainGetAction GET n USER PUBLIC
|
||||
CONSOLE /console-api/domain-list ConsoleDomainListAction GET n USER PUBLIC
|
||||
CONSOLE /console-api/dum-download ConsoleDumDownloadAction GET n USER PUBLIC
|
||||
CONSOLE /console-api/eppPassword ConsoleEppPasswordAction POST n USER PUBLIC
|
||||
CONSOLE /console-api/ote ConsoleOteAction GET,POST n USER PUBLIC
|
||||
CONSOLE /console-api/registrar ConsoleUpdateRegistrarAction POST n USER PUBLIC
|
||||
CONSOLE /console-api/registrars RegistrarsAction GET,POST n USER PUBLIC
|
||||
CONSOLE /console-api/registry-lock ConsoleRegistryLockAction GET,POST n USER PUBLIC
|
||||
CONSOLE /console-api/registry-lock-verify ConsoleRegistryLockVerifyAction GET n USER PUBLIC
|
||||
CONSOLE /console-api/settings/contacts ContactAction GET,POST,DELETE,PUT n USER PUBLIC
|
||||
CONSOLE /console-api/settings/rdap-fields RdapRegistrarFieldsAction POST n USER PUBLIC
|
||||
CONSOLE /console-api/settings/security SecurityAction POST n USER PUBLIC
|
||||
CONSOLE /console-api/userdata ConsoleUserDataAction GET n USER PUBLIC
|
||||
CONSOLE /console-api/users ConsoleUsersAction GET,POST,DELETE,PUT n USER PUBLIC
|
||||
CONSOLE /ready/console ReadinessProbeConsoleAction GET n NONE PUBLIC
|
||||
SERVICE PATH CLASS METHODS OK MIN USER_POLICY
|
||||
FRONTEND /_dr/epp EppTlsAction POST n APP ADMIN
|
||||
FRONTEND /ready/frontend ReadinessProbeActionFrontend GET n NONE PUBLIC
|
||||
CONSOLE /console-api/bulk-domain ConsoleBulkDomainAction POST n USER PUBLIC
|
||||
CONSOLE /console-api/domain ConsoleDomainGetAction GET n USER PUBLIC
|
||||
CONSOLE /console-api/domain-list ConsoleDomainListAction GET n USER PUBLIC
|
||||
CONSOLE /console-api/dum-download ConsoleDumDownloadAction GET n USER PUBLIC
|
||||
CONSOLE /console-api/eppPassword ConsoleEppPasswordAction POST n USER PUBLIC
|
||||
CONSOLE /console-api/ote ConsoleOteAction GET,POST n USER PUBLIC
|
||||
CONSOLE /console-api/password-reset-request PasswordResetRequestAction POST n USER PUBLIC
|
||||
CONSOLE /console-api/password-reset-verify PasswordResetVerifyAction GET,POST n USER PUBLIC
|
||||
CONSOLE /console-api/registrar ConsoleUpdateRegistrarAction POST n USER PUBLIC
|
||||
CONSOLE /console-api/registrars RegistrarsAction GET,POST n USER PUBLIC
|
||||
CONSOLE /console-api/registry-lock ConsoleRegistryLockAction GET,POST n USER PUBLIC
|
||||
CONSOLE /console-api/registry-lock-verify ConsoleRegistryLockVerifyAction GET n USER PUBLIC
|
||||
CONSOLE /console-api/settings/contacts ContactAction GET,POST,DELETE,PUT n USER PUBLIC
|
||||
CONSOLE /console-api/settings/rdap-fields RdapRegistrarFieldsAction POST n USER PUBLIC
|
||||
CONSOLE /console-api/settings/security SecurityAction POST n USER PUBLIC
|
||||
CONSOLE /console-api/userdata ConsoleUserDataAction GET n USER PUBLIC
|
||||
CONSOLE /console-api/users ConsoleUsersAction GET,POST,DELETE,PUT n USER PUBLIC
|
||||
CONSOLE /ready/console ReadinessProbeConsoleAction GET n NONE PUBLIC
|
||||
|
||||
@@ -74,7 +74,10 @@ CONSOLE /console-api/domain ConsoleDomainGetActi
|
||||
CONSOLE /console-api/domain-list ConsoleDomainListAction GET n USER PUBLIC
|
||||
CONSOLE /console-api/dum-download ConsoleDumDownloadAction GET n USER PUBLIC
|
||||
CONSOLE /console-api/eppPassword ConsoleEppPasswordAction POST n USER PUBLIC
|
||||
CONSOLE /console-api/history ConsoleHistoryDataAction GET n USER PUBLIC
|
||||
CONSOLE /console-api/ote ConsoleOteAction GET,POST n USER PUBLIC
|
||||
CONSOLE /console-api/password-reset-request PasswordResetRequestAction POST n USER PUBLIC
|
||||
CONSOLE /console-api/password-reset-verify PasswordResetVerifyAction GET,POST n USER PUBLIC
|
||||
CONSOLE /console-api/registrar ConsoleUpdateRegistrarAction POST n USER PUBLIC
|
||||
CONSOLE /console-api/registrars RegistrarsAction GET,POST n USER PUBLIC
|
||||
CONSOLE /console-api/registry-lock ConsoleRegistryLockAction GET,POST n USER PUBLIC
|
||||
@@ -84,4 +87,4 @@ CONSOLE /console-api/settings/rdap-fields RdapRegistrarFieldsA
|
||||
CONSOLE /console-api/settings/security SecurityAction POST n USER PUBLIC
|
||||
CONSOLE /console-api/userdata ConsoleUserDataAction GET n USER PUBLIC
|
||||
CONSOLE /console-api/users ConsoleUsersAction GET,POST,DELETE,PUT n USER PUBLIC
|
||||
CONSOLE /ready/console ReadinessProbeConsoleAction GET n NONE PUBLIC
|
||||
CONSOLE /ready/console ReadinessProbeConsoleAction GET n NONE PUBLIC
|
||||
|
||||
@@ -139,7 +139,7 @@
|
||||
description text,
|
||||
method text not null,
|
||||
modification_time timestamp(6) with time zone not null,
|
||||
type text not null check (type in ('DOMAIN_DELETE','DOMAIN_SUSPEND','DOMAIN_UNSUSPEND','EPP_PASSWORD_UPDATE','REGISTRAR_CREATE','REGISTRAR_SECURITY_UPDATE','REGISTRAR_UPDATE','USER_CREATE','USER_DELETE','USER_UPDATE')),
|
||||
type text not null check (type in ('DUM_DOWNLOAD','DOMAIN_DELETE','DOMAIN_SUSPEND','DOMAIN_UNSUSPEND','EPP_PASSWORD_UPDATE','REGISTRAR_CREATE','REGISTRAR_CONTACTS_UPDATE','REGISTRAR_SECURITY_UPDATE','REGISTRAR_UPDATE','REGISTRY_LOCK','REGISTRY_UNLOCK','USER_CREATE','USER_DELETE','USER_UPDATE')),
|
||||
url text not null,
|
||||
acting_user text not null,
|
||||
primary key (revision_id)
|
||||
|
||||
Reference in New Issue
Block a user