From 083a9dc8c929996dab2ba9ee05f6f2cad9869f5e Mon Sep 17 00:00:00 2001 From: gbrodman Date: Fri, 18 Apr 2025 18:05:29 -0400 Subject: [PATCH] Remove old console history Java classes (#2726) 1. This doesn't remove the SQL tables yet (this is necessary to pass tests and also good practice just in case we need or want to look at history for a little bit) 2. This also removes the Registrar, RegistrarPoc, and User base classes that were only necessary because we were saving copies of those objects in the old history classes. --- ...ingCertificateNotificationEmailAction.java | 2 +- .../google/registry/beam/rde/RdePipeline.java | 2 +- .../registry/dns/PublishDnsUpdatesAction.java | 3 +- .../export/SyncGroupMembersAction.java | 5 +- .../export/sheet/SyncRegistrarsSheet.java | 17 +- .../flows/domain/DomainFlowUtils.java | 2 +- .../console/ConsoleEppActionHistory.java | 99 -- .../model/console/ConsoleUpdateHistory.java | 214 ++-- .../console/RegistrarPocUpdateHistory.java | 99 -- .../model/console/RegistrarUpdateHistory.java | 89 -- .../console/SimpleConsoleUpdateHistory.java | 153 --- .../google/registry/model/console/User.java | 171 ++- .../registry/model/console/UserBase.java | 186 --- .../model/console/UserUpdateHistory.java | 85 -- .../registry/model/registrar/Registrar.java | 1017 +++++++++++++++- .../model/registrar/RegistrarBase.java | 1040 ----------------- .../model/registrar/RegistrarPoc.java | 404 ++++++- .../model/registrar/RegistrarPocBase.java | 425 ------- .../registry/rde/RegistrarToXjcConverter.java | 5 +- .../auth/AuthenticatedRegistrarAccessor.java | 2 +- .../tools/CreateOrUpdateRegistrarCommand.java | 5 +- .../tools/CreateRegistrarCommand.java | 2 +- .../registry/tools/RegistrarPocCommand.java | 5 +- .../tools/server/CreateGroupsAction.java | 4 +- .../ui/server/RegistrarFormFields.java | 5 +- .../ui/server/console/ConsoleApiAction.java | 7 +- .../console/ConsoleEppPasswordAction.java | 3 +- .../ui/server/console/ConsoleOteAction.java | 3 +- .../console/ConsoleUpdateRegistrarAction.java | 3 +- .../ui/server/console/RegistrarsAction.java | 10 +- .../domains/ConsoleBulkDomainAction.java | 4 +- .../console/settings/ContactAction.java | 2 +- .../settings/RdapRegistrarFieldsAction.java | 3 +- .../console/settings/SecurityAction.java | 3 +- .../main/resources/META-INF/persistence.xml | 6 +- ...ertificateNotificationEmailActionTest.java | 29 +- .../registry/beam/rde/RdePipelineTest.java | 96 +- .../export/SyncGroupMembersActionTest.java | 6 +- .../export/sheet/SyncRegistrarsSheetTest.java | 98 +- .../flows/domain/DomainCreateFlowTest.java | 2 +- .../flows/domain/DomainRenewFlowTest.java | 2 +- .../domain/DomainRestoreRequestFlowTest.java | 2 +- .../domain/DomainTransferRequestFlowTest.java | 2 +- .../flows/session/LoginFlowTestCase.java | 2 +- .../console/ConsoleEppActionHistoryTest.java | 68 -- ...est.java => ConsoleUpdateHistoryTest.java} | 8 +- .../RegistrarPocUpdateHistoryTest.java | 61 - .../console/RegistrarUpdateHistoryTest.java | 55 - .../model/console/UserUpdateHistoryTest.java | 65 -- .../model/registrar/RegistrarTest.java | 17 +- .../converter/StringValueEnumeratedTest.java | 2 +- .../JpaTransactionManagerExtension.java | 9 +- .../registry/rdap/RdapJsonFormatterTest.java | 9 +- ...UpdateRegistrarRdapBaseUrlsActionTest.java | 35 +- .../rde/RegistrarToXjcConverterTest.java | 2 +- .../AuthenticatedRegistrarAccessorTest.java | 2 +- .../integration/SqlIntegrationTestSuite.java | 12 +- .../schema/registrar/RegistrarPocTest.java | 2 +- .../registry/testing/DatabaseHelper.java | 5 +- .../testing/FullFieldsTestEntityHelper.java | 10 +- .../tools/CheckDomainClaimsCommandTest.java | 2 +- .../tools/CheckDomainCommandTest.java | 2 +- .../registry/tools/LockDomainCommandTest.java | 2 +- .../tools/RegistrarPocCommandTest.java | 8 +- .../registry/tools/SetupOteCommandTest.java | 2 +- .../tools/UnlockDomainCommandTest.java | 2 +- .../tools/UpdateRegistrarCommandTest.java | 4 +- .../ValidateLoginCredentialsCommandTest.java | 4 +- .../console/ConsoleEppPasswordActionTest.java | 3 +- .../ConsoleUpdateRegistrarActionTest.java | 8 +- .../server/console/RegistrarsActionTest.java | 3 +- .../domains/ConsoleBulkDomainActionTest.java | 9 +- .../console/settings/ContactActionTest.java | 6 +- .../RdapRegistrarFieldsActionTest.java | 3 +- .../console/settings/SecurityActionTest.java | 3 +- .../whois/RegistrarWhoisResponseTest.java | 11 +- .../registry/whois/WhoisActionTest.java | 4 +- .../registry/whois/WhoisHttpActionTest.java | 2 +- .../sql/er_diagram/brief_er_diagram.html | 6 +- .../sql/er_diagram/full_er_diagram.html | 6 +- .../sql/schema/db-schema.sql.generated | 164 --- 81 files changed, 1876 insertions(+), 3064 deletions(-) delete mode 100644 core/src/main/java/google/registry/model/console/ConsoleEppActionHistory.java delete mode 100644 core/src/main/java/google/registry/model/console/RegistrarPocUpdateHistory.java delete mode 100644 core/src/main/java/google/registry/model/console/RegistrarUpdateHistory.java delete mode 100644 core/src/main/java/google/registry/model/console/SimpleConsoleUpdateHistory.java delete mode 100644 core/src/main/java/google/registry/model/console/UserBase.java delete mode 100644 core/src/main/java/google/registry/model/console/UserUpdateHistory.java delete mode 100644 core/src/main/java/google/registry/model/registrar/RegistrarBase.java delete mode 100644 core/src/main/java/google/registry/model/registrar/RegistrarPocBase.java delete mode 100644 core/src/test/java/google/registry/model/console/ConsoleEppActionHistoryTest.java rename core/src/test/java/google/registry/model/console/{SimpleConsoleUpdateHistoryTest.java => ConsoleUpdateHistoryTest.java} (91%) delete mode 100644 core/src/test/java/google/registry/model/console/RegistrarPocUpdateHistoryTest.java delete mode 100644 core/src/test/java/google/registry/model/console/RegistrarUpdateHistoryTest.java delete mode 100644 core/src/test/java/google/registry/model/console/UserUpdateHistoryTest.java diff --git a/core/src/main/java/google/registry/batch/SendExpiringCertificateNotificationEmailAction.java b/core/src/main/java/google/registry/batch/SendExpiringCertificateNotificationEmailAction.java index 709dfc8bb..b1505042a 100644 --- a/core/src/main/java/google/registry/batch/SendExpiringCertificateNotificationEmailAction.java +++ b/core/src/main/java/google/registry/batch/SendExpiringCertificateNotificationEmailAction.java @@ -33,7 +33,7 @@ import google.registry.flows.certs.CertificateChecker; import google.registry.groups.GmailClient; import google.registry.model.registrar.Registrar; import google.registry.model.registrar.RegistrarPoc; -import google.registry.model.registrar.RegistrarPocBase.Type; +import google.registry.model.registrar.RegistrarPoc.Type; import google.registry.request.Action; import google.registry.request.Action.GaeService; import google.registry.request.Response; diff --git a/core/src/main/java/google/registry/beam/rde/RdePipeline.java b/core/src/main/java/google/registry/beam/rde/RdePipeline.java index 7dbd8a708..08c0e2ca2 100644 --- a/core/src/main/java/google/registry/beam/rde/RdePipeline.java +++ b/core/src/main/java/google/registry/beam/rde/RdePipeline.java @@ -58,7 +58,7 @@ import google.registry.model.host.Host; import google.registry.model.host.HostHistory; import google.registry.model.rde.RdeMode; import google.registry.model.registrar.Registrar; -import google.registry.model.registrar.RegistrarBase.Type; +import google.registry.model.registrar.Registrar.Type; import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry.HistoryEntryId; import google.registry.persistence.PersistenceModule.TransactionIsolationLevel; diff --git a/core/src/main/java/google/registry/dns/PublishDnsUpdatesAction.java b/core/src/main/java/google/registry/dns/PublishDnsUpdatesAction.java index 66728e30f..79319b43b 100644 --- a/core/src/main/java/google/registry/dns/PublishDnsUpdatesAction.java +++ b/core/src/main/java/google/registry/dns/PublishDnsUpdatesAction.java @@ -50,7 +50,6 @@ import google.registry.model.domain.Domain; import google.registry.model.host.Host; import google.registry.model.registrar.Registrar; import google.registry.model.registrar.RegistrarPoc; -import google.registry.model.registrar.RegistrarPocBase; import google.registry.model.tld.Tld; import google.registry.request.Action; import google.registry.request.Action.GaeService; @@ -296,7 +295,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable { ImmutableList recipients = registrar.get().getContacts().stream() - .filter(c -> c.getTypes().contains(RegistrarPocBase.Type.ADMIN)) + .filter(c -> c.getTypes().contains(RegistrarPoc.Type.ADMIN)) .map(RegistrarPoc::getEmailAddress) .map(PublishDnsUpdatesAction::emailToInternetAddress) .collect(toImmutableList()); diff --git a/core/src/main/java/google/registry/export/SyncGroupMembersAction.java b/core/src/main/java/google/registry/export/SyncGroupMembersAction.java index c07477587..992d55eac 100644 --- a/core/src/main/java/google/registry/export/SyncGroupMembersAction.java +++ b/core/src/main/java/google/registry/export/SyncGroupMembersAction.java @@ -33,7 +33,6 @@ import google.registry.groups.GroupsConnection; import google.registry.groups.GroupsConnection.Role; import google.registry.model.registrar.Registrar; import google.registry.model.registrar.RegistrarPoc; -import google.registry.model.registrar.RegistrarPocBase; import google.registry.request.Action; import google.registry.request.Action.GaeService; import google.registry.request.Response; @@ -101,7 +100,7 @@ public final class SyncGroupMembersAction implements Runnable { * Returns the Google Groups email address for the given registrar ID and RegistrarContact.Type. */ public static String getGroupEmailAddressForContactType( - String registrarId, RegistrarPocBase.Type type, String gSuiteDomainName) { + String registrarId, RegistrarPoc.Type type, String gSuiteDomainName) { // Take the registrar's ID, make it lowercase, and remove all characters that aren't // alphanumeric, hyphens, or underscores. return String.format( @@ -176,7 +175,7 @@ public final class SyncGroupMembersAction implements Runnable { Set registrarPocs = registrar.getContacts(); long totalAdded = 0; long totalRemoved = 0; - for (final RegistrarPocBase.Type type : RegistrarPocBase.Type.values()) { + for (final RegistrarPoc.Type type : RegistrarPoc.Type.values()) { groupKey = getGroupEmailAddressForContactType(registrar.getRegistrarId(), type, gSuiteDomainName); Set currentMembers = groupsConnection.getMembersOfGroup(groupKey); diff --git a/core/src/main/java/google/registry/export/sheet/SyncRegistrarsSheet.java b/core/src/main/java/google/registry/export/sheet/SyncRegistrarsSheet.java index 35926d3bb..1f1159b60 100644 --- a/core/src/main/java/google/registry/export/sheet/SyncRegistrarsSheet.java +++ b/core/src/main/java/google/registry/export/sheet/SyncRegistrarsSheet.java @@ -17,13 +17,13 @@ package google.registry.export.sheet; import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.collect.ImmutableList.toImmutableList; import static google.registry.model.common.Cursor.CursorType.SYNC_REGISTRAR_SHEET; -import static google.registry.model.registrar.RegistrarPocBase.Type.ABUSE; -import static google.registry.model.registrar.RegistrarPocBase.Type.ADMIN; -import static google.registry.model.registrar.RegistrarPocBase.Type.BILLING; -import static google.registry.model.registrar.RegistrarPocBase.Type.LEGAL; -import static google.registry.model.registrar.RegistrarPocBase.Type.MARKETING; -import static google.registry.model.registrar.RegistrarPocBase.Type.TECH; -import static google.registry.model.registrar.RegistrarPocBase.Type.WHOIS; +import static google.registry.model.registrar.RegistrarPoc.Type.ABUSE; +import static google.registry.model.registrar.RegistrarPoc.Type.ADMIN; +import static google.registry.model.registrar.RegistrarPoc.Type.BILLING; +import static google.registry.model.registrar.RegistrarPoc.Type.LEGAL; +import static google.registry.model.registrar.RegistrarPoc.Type.MARKETING; +import static google.registry.model.registrar.RegistrarPoc.Type.TECH; +import static google.registry.model.registrar.RegistrarPoc.Type.WHOIS; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.util.DateTimeUtils.START_OF_TIME; @@ -36,7 +36,6 @@ import google.registry.model.common.Cursor; import google.registry.model.registrar.Registrar; import google.registry.model.registrar.RegistrarAddress; import google.registry.model.registrar.RegistrarPoc; -import google.registry.model.registrar.RegistrarPocBase; import google.registry.util.Clock; import google.registry.util.DateTimeUtils; import jakarta.inject.Inject; @@ -174,7 +173,7 @@ class SyncRegistrarsSheet { return result.toString(); } - private static Predicate byType(final RegistrarPocBase.Type type) { + private static Predicate byType(final RegistrarPoc.Type type) { return contact -> contact.getTypes().contains(type); } diff --git a/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java b/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java index 1dba91f04..d1062e0c1 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java +++ b/core/src/main/java/google/registry/flows/domain/DomainFlowUtils.java @@ -124,7 +124,7 @@ import google.registry.model.eppoutput.EppResponse.ResponseExtension; import google.registry.model.host.Host; import google.registry.model.poll.PollMessage.Autorenew; import google.registry.model.registrar.Registrar; -import google.registry.model.registrar.RegistrarBase.State; +import google.registry.model.registrar.Registrar.State; import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField; import google.registry.model.reporting.HistoryEntry.HistoryEntryId; diff --git a/core/src/main/java/google/registry/model/console/ConsoleEppActionHistory.java b/core/src/main/java/google/registry/model/console/ConsoleEppActionHistory.java deleted file mode 100644 index df5875e02..000000000 --- a/core/src/main/java/google/registry/model/console/ConsoleEppActionHistory.java +++ /dev/null @@ -1,99 +0,0 @@ -// 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.model.console; - -import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; - -import google.registry.model.reporting.HistoryEntry; -import google.registry.model.reporting.HistoryEntry.HistoryEntryId; -import google.registry.persistence.VKey; -import jakarta.persistence.Access; -import jakarta.persistence.AccessType; -import jakarta.persistence.AttributeOverride; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Index; -import jakarta.persistence.Table; - -/** - * A persisted history object representing an EPP action via the console. - * - *

In addition to the generic history fields (time, URL, etc.) we also persist a reference to the - * history entry so that we can refer to it if necessary. - */ -@Access(AccessType.FIELD) -@Entity -@Table( - indexes = { - @Index(columnList = "historyActingUser"), - @Index(columnList = "repoId"), - @Index(columnList = "revisionId") - }) -public class ConsoleEppActionHistory extends ConsoleUpdateHistory { - - @AttributeOverride(name = "repoId", column = @Column(nullable = false)) - HistoryEntryId historyEntryId; - - @Column(nullable = false) - Class historyEntryClass; - - public HistoryEntryId getHistoryEntryId() { - return historyEntryId; - } - - public Class getHistoryEntryClass() { - return historyEntryClass; - } - - /** Creates a {@link VKey} instance for this entity. */ - @Override - public VKey createVKey() { - return VKey.create(ConsoleEppActionHistory.class, getRevisionId()); - } - - @Override - public Builder asBuilder() { - return new Builder(clone(this)); - } - - /** Builder for the immutable UserUpdateHistory. */ - public static class Builder - extends ConsoleUpdateHistory.Builder { - - public Builder() {} - - public Builder(ConsoleEppActionHistory instance) { - super(instance); - } - - @Override - public ConsoleEppActionHistory build() { - checkArgumentNotNull(getInstance().historyEntryId, "History entry ID must be specified"); - checkArgumentNotNull( - getInstance().historyEntryClass, "History entry class must be specified"); - return super.build(); - } - - public Builder setHistoryEntryId(HistoryEntryId historyEntryId) { - getInstance().historyEntryId = historyEntryId; - return this; - } - - public Builder setHistoryEntryClass(Class historyEntryClass) { - getInstance().historyEntryClass = historyEntryClass; - return this; - } - } -} diff --git a/core/src/main/java/google/registry/model/console/ConsoleUpdateHistory.java b/core/src/main/java/google/registry/model/console/ConsoleUpdateHistory.java index 2a29dac83..aa08bbffc 100644 --- a/core/src/main/java/google/registry/model/console/ConsoleUpdateHistory.java +++ b/core/src/main/java/google/registry/model/console/ConsoleUpdateHistory.java @@ -1,4 +1,4 @@ -// Copyright 2024 The Nomulus Authors. All Rights Reserved. +// 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. @@ -19,26 +19,87 @@ import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; import google.registry.model.Buildable; import google.registry.model.ImmutableObject; import google.registry.model.annotations.IdAllocation; -import jakarta.persistence.Access; -import jakarta.persistence.AccessType; +import google.registry.persistence.WithVKey; import jakarta.persistence.Column; +import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; import jakarta.persistence.Id; +import jakarta.persistence.Index; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; -import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.Table; +import java.util.Optional; import org.joda.time.DateTime; -/** - * A record of a resource that was updated through the console. - * - *

This abstract class has several subclasses that (mostly) include the modified resource itself - * so that the entire object history is persisted to SQL. - */ -@Access(AccessType.FIELD) -@MappedSuperclass -public abstract class ConsoleUpdateHistory extends ImmutableObject implements Buildable { +@Entity +@WithVKey(Long.class) +@Table( + indexes = { + @Index(columnList = "actingUser", name = "idx_console_update_history_acting_user"), + @Index(columnList = "type", name = "idx_console_update_history_type"), + @Index(columnList = "modificationTime", name = "idx_console_update_history_modification_time") + }) +public class ConsoleUpdateHistory extends ImmutableObject implements Buildable { + + @Id @IdAllocation @Column Long revisionId; + + @Column(nullable = false) + DateTime modificationTime; + + /** The HTTP method (e.g. POST, PUT) used to make this modification. */ + @Column(nullable = false) + String method; + + /** The type of modification. */ + @Column(nullable = false) + @Enumerated(EnumType.STRING) + Type type; + + /** The URL of the action that was used to make the modification. */ + @Column(nullable = false) + String url; + + /** An optional further description of the action. */ + String description; + + /** The user that performed the modification. */ + @JoinColumn(name = "actingUser", referencedColumnName = "emailAddress", nullable = false) + @ManyToOne + User actingUser; + + public Long getRevisionId() { + return revisionId; + } + + public DateTime getModificationTime() { + return modificationTime; + } + + public Optional getDescription() { + return Optional.ofNullable(description); + } + + public String getMethod() { + return method; + } + + public Type getType() { + return type; + } + + public String getUrl() { + return url; + } + + public User getActingUser() { + return actingUser; + } + + @Override + public Builder asBuilder() { + return new Builder(clone(this)); + } public enum Type { DOMAIN_DELETE, @@ -53,118 +114,51 @@ public abstract class ConsoleUpdateHistory extends ImmutableObject implements Bu USER_UPDATE } - /** Autogenerated ID of this event. */ - @Id - @IdAllocation - @Column(nullable = false, name = "historyRevisionId") - protected Long revisionId; + public static class Builder extends Buildable.Builder { + public Builder() {} - /** The user that performed the modification. */ - @JoinColumn(name = "historyActingUser", referencedColumnName = "emailAddress", nullable = false) - @ManyToOne - User actingUser; - - /** The URL of the action that was used to make the modification. */ - @Column(nullable = false, name = "historyUrl") - String url; - - /** The HTTP method (e.g. POST, PUT) used to make this modification. */ - @Column(nullable = false, name = "historyMethod") - String method; - - /** The raw body of the request that was used to make this modification. */ - @Column(name = "historyRequestBody") - String requestBody; - - /** The time at which the modification was mode. */ - @Column(nullable = false, name = "historyModificationTime") - DateTime modificationTime; - - /** The type of modification. */ - @Column(nullable = false, name = "historyType") - @Enumerated(EnumType.STRING) - Type type; - - public long getRevisionId() { - return revisionId; - } - - public User getActingUser() { - return actingUser; - } - - public String getUrl() { - return url; - } - - public String getMethod() { - return method; - } - - public String getRequestBody() { - return requestBody; - } - - public DateTime getModificationTime() { - return modificationTime; - } - - public Type getType() { - return type; - } - - @Override - public abstract Builder asBuilder(); - - /** Builder for the immutable ConsoleUpdateHistory. */ - public abstract static class Builder< - T extends ConsoleUpdateHistory, B extends ConsoleUpdateHistory.Builder> - extends GenericBuilder { - - protected Builder() {} - - protected Builder(T instance) { + private Builder(ConsoleUpdateHistory instance) { super(instance); } @Override - public T build() { + public ConsoleUpdateHistory build() { + checkArgumentNotNull(getInstance().modificationTime, "Modification time must be specified"); checkArgumentNotNull(getInstance().actingUser, "Acting user must be specified"); checkArgumentNotNull(getInstance().url, "URL must be specified"); checkArgumentNotNull(getInstance().method, "HTTP method must be specified"); - checkArgumentNotNull(getInstance().modificationTime, "modificationTime must be specified"); - checkArgumentNotNull(getInstance().type, "Console History type must be specified"); + checkArgumentNotNull(getInstance().type, "ConsoleUpdateHistory type must be specified"); return super.build(); } - public B setActingUser(User actingUser) { - getInstance().actingUser = actingUser; - return thisCastToDerived(); - } - - public B setUrl(String url) { - getInstance().url = url; - return thisCastToDerived(); - } - - public B setMethod(String method) { - getInstance().method = method; - return thisCastToDerived(); - } - - public B setRequestBody(String requestBody) { - getInstance().requestBody = requestBody; - return thisCastToDerived(); - } - - public B setModificationTime(DateTime modificationTime) { + public Builder setModificationTime(DateTime modificationTime) { getInstance().modificationTime = modificationTime; - return thisCastToDerived(); + return this; } - public B setType(Type type) { + public Builder setActingUser(User actingUser) { + getInstance().actingUser = actingUser; + return this; + } + + public Builder setUrl(String url) { + getInstance().url = url; + return this; + } + + public Builder setMethod(String method) { + getInstance().method = method; + return this; + } + + public Builder setDescription(String description) { + getInstance().description = description; + return this; + } + + public Builder setType(Type type) { getInstance().type = type; - return thisCastToDerived(); + return this; } } } diff --git a/core/src/main/java/google/registry/model/console/RegistrarPocUpdateHistory.java b/core/src/main/java/google/registry/model/console/RegistrarPocUpdateHistory.java deleted file mode 100644 index 2b292bb87..000000000 --- a/core/src/main/java/google/registry/model/console/RegistrarPocUpdateHistory.java +++ /dev/null @@ -1,99 +0,0 @@ -// 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.model.console; - -import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; - -import google.registry.model.registrar.RegistrarPoc; -import google.registry.model.registrar.RegistrarPocBase; -import google.registry.persistence.VKey; -import jakarta.persistence.Access; -import jakarta.persistence.AccessType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Index; -import jakarta.persistence.PostLoad; -import jakarta.persistence.Table; - -/** - * A persisted history object representing an update to a RegistrarPoc. - * - *

In addition to the generic history fields (time, URL, etc.) we also persist a copy of the - * modified RegistrarPoc object at this point in time. - */ -@Access(AccessType.FIELD) -@Entity -@Table( - indexes = { - @Index(columnList = "historyActingUser"), - @Index(columnList = "emailAddress"), - @Index(columnList = "registrarId") - }) -public class RegistrarPocUpdateHistory extends ConsoleUpdateHistory { - - RegistrarPocBase registrarPoc; - - // These fields exist so that they can be populated in the SQL table - @Column(nullable = false) - String emailAddress; - - @Column(nullable = false) - String registrarId; - - public RegistrarPocBase getRegistrarPoc() { - return registrarPoc; - } - - @PostLoad - void postLoad() { - registrarPoc.setEmailAddress(emailAddress); - registrarPoc.setRegistrarId(registrarId); - } - - /** Creates a {@link VKey} instance for this entity. */ - @Override - public VKey createVKey() { - return VKey.create(RegistrarPocUpdateHistory.class, getRevisionId()); - } - - @Override - public Builder asBuilder() { - return new Builder(clone(this)); - } - - /** Builder for the immutable UserUpdateHistory. */ - public static class Builder - extends ConsoleUpdateHistory.Builder { - - public Builder() {} - - public Builder(RegistrarPocUpdateHistory instance) { - super(instance); - } - - @Override - public RegistrarPocUpdateHistory build() { - checkArgumentNotNull(getInstance().registrarPoc, "Registrar POC must be specified"); - return super.build(); - } - - public Builder setRegistrarPoc(RegistrarPoc registrarPoc) { - getInstance().registrarPoc = registrarPoc; - getInstance().registrarId = registrarPoc.getRegistrarId(); - getInstance().emailAddress = registrarPoc.getEmailAddress(); - return this; - } - } -} diff --git a/core/src/main/java/google/registry/model/console/RegistrarUpdateHistory.java b/core/src/main/java/google/registry/model/console/RegistrarUpdateHistory.java deleted file mode 100644 index 31ad1e753..000000000 --- a/core/src/main/java/google/registry/model/console/RegistrarUpdateHistory.java +++ /dev/null @@ -1,89 +0,0 @@ -// 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.model.console; - -import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; - -import google.registry.model.registrar.RegistrarBase; -import google.registry.persistence.VKey; -import jakarta.persistence.Access; -import jakarta.persistence.AccessType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Index; -import jakarta.persistence.PostLoad; -import jakarta.persistence.Table; - -/** - * A persisted history object representing an update to a Registrar. - * - *

In addition to the generic history fields (time, URL, etc.) we also persist a copy of the - * modified Registrar object at this point in time. - */ -@Access(AccessType.FIELD) -@Entity -@Table(indexes = {@Index(columnList = "historyActingUser"), @Index(columnList = "registrarId")}) -public class RegistrarUpdateHistory extends ConsoleUpdateHistory { - - RegistrarBase registrar; - - // This field exists so that it exists in the SQL table - @Column(nullable = false) - @SuppressWarnings("unused") - private String registrarId; - - public RegistrarBase getRegistrar() { - return registrar; - } - - @PostLoad - void postLoad() { - registrar.setRegistrarId(registrarId); - } - - /** Creates a {@link VKey} instance for this entity. */ - @Override - public VKey createVKey() { - return VKey.create(RegistrarUpdateHistory.class, getRevisionId()); - } - - @Override - public Builder asBuilder() { - return new RegistrarUpdateHistory.Builder(clone(this)); - } - - /** Builder for the immutable UserUpdateHistory. */ - public static class Builder - extends ConsoleUpdateHistory.Builder { - - public Builder() {} - - public Builder(RegistrarUpdateHistory instance) { - super(instance); - } - - @Override - public RegistrarUpdateHistory build() { - checkArgumentNotNull(getInstance().registrar, "Registrar must be specified"); - return super.build(); - } - - public Builder setRegistrar(RegistrarBase registrar) { - getInstance().registrar = registrar; - getInstance().registrarId = registrar.getRegistrarId(); - return this; - } - } -} diff --git a/core/src/main/java/google/registry/model/console/SimpleConsoleUpdateHistory.java b/core/src/main/java/google/registry/model/console/SimpleConsoleUpdateHistory.java deleted file mode 100644 index b172bc9e4..000000000 --- a/core/src/main/java/google/registry/model/console/SimpleConsoleUpdateHistory.java +++ /dev/null @@ -1,153 +0,0 @@ -// 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.model.console; - -import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; - -import google.registry.model.Buildable; -import google.registry.model.ImmutableObject; -import google.registry.model.annotations.IdAllocation; -import google.registry.persistence.WithVKey; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.Id; -import jakarta.persistence.Index; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; -import java.util.Optional; -import org.joda.time.DateTime; - -@Entity -@WithVKey(Long.class) -@Table( - name = "ConsoleUpdateHistory", - indexes = { - @Index(columnList = "actingUser", name = "idx_console_update_history_acting_user"), - @Index(columnList = "type", name = "idx_console_update_history_type"), - @Index(columnList = "modificationTime", name = "idx_console_update_history_modification_time") - }) -// TODO: rename this to ConsoleUpdateHistory when that class is removed -public class SimpleConsoleUpdateHistory extends ImmutableObject implements Buildable { - - @Id @IdAllocation @Column Long revisionId; - - @Column(nullable = false) - DateTime modificationTime; - - /** The HTTP method (e.g. POST, PUT) used to make this modification. */ - @Column(nullable = false) - String method; - - /** The type of modification. */ - @Column(nullable = false) - @Enumerated(EnumType.STRING) - ConsoleUpdateHistory.Type type; - - /** The URL of the action that was used to make the modification. */ - @Column(nullable = false) - String url; - - /** An optional further description of the action. */ - String description; - - /** The user that performed the modification. */ - @JoinColumn(name = "actingUser", referencedColumnName = "emailAddress", nullable = false) - @ManyToOne - User actingUser; - - public Long getRevisionId() { - return revisionId; - } - - public DateTime getModificationTime() { - return modificationTime; - } - - public Optional getDescription() { - return Optional.ofNullable(description); - } - - public String getMethod() { - return method; - } - - public ConsoleUpdateHistory.Type getType() { - return type; - } - - public String getUrl() { - return url; - } - - public User getActingUser() { - return actingUser; - } - - @Override - public Builder asBuilder() { - return new Builder(clone(this)); - } - - public static class Builder extends Buildable.Builder { - public Builder() {} - - private Builder(SimpleConsoleUpdateHistory instance) { - super(instance); - } - - @Override - public SimpleConsoleUpdateHistory build() { - checkArgumentNotNull(getInstance().modificationTime, "Modification time must be specified"); - checkArgumentNotNull(getInstance().actingUser, "Acting user must be specified"); - checkArgumentNotNull(getInstance().url, "URL must be specified"); - checkArgumentNotNull(getInstance().method, "HTTP method must be specified"); - checkArgumentNotNull(getInstance().type, "ConsoleUpdateHistory type must be specified"); - return super.build(); - } - - public Builder setModificationTime(DateTime modificationTime) { - getInstance().modificationTime = modificationTime; - return this; - } - - public Builder setActingUser(User actingUser) { - getInstance().actingUser = actingUser; - return this; - } - - public Builder setUrl(String url) { - getInstance().url = url; - return this; - } - - public Builder setMethod(String method) { - getInstance().method = method; - return this; - } - - public Builder setDescription(String description) { - getInstance().description = description; - return this; - } - - public Builder setType(ConsoleUpdateHistory.Type type) { - getInstance().type = type; - return this; - } - } -} diff --git a/core/src/main/java/google/registry/model/console/User.java b/core/src/main/java/google/registry/model/console/User.java index cd7390a6a..4aba7ef2d 100644 --- a/core/src/main/java/google/registry/model/console/User.java +++ b/core/src/main/java/google/registry/model/console/User.java @@ -1,4 +1,4 @@ -// Copyright 2022 The Nomulus Authors. All Rights Reserved. +// 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. @@ -15,23 +15,30 @@ package google.registry.model.console; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Strings.isNullOrEmpty; +import static com.google.common.io.BaseEncoding.base64; +import static google.registry.model.registrar.Registrar.checkValidEmail; import static google.registry.tools.server.UpdateUserGroupAction.GROUP_UPDATE_QUEUE; +import static google.registry.util.PasswordUtils.SALT_SUPPLIER; +import static google.registry.util.PasswordUtils.hashPassword; +import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; import com.google.cloud.tasks.v2.Task; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; import com.google.common.flogger.FluentLogger; import com.google.common.net.MediaType; +import com.google.gson.annotations.Expose; import google.registry.batch.CloudTasksUtils; -import google.registry.persistence.VKey; +import google.registry.model.Buildable; +import google.registry.model.UpdateAutoTimestampEntity; import google.registry.request.Action; import google.registry.tools.IamClient; import google.registry.tools.ServiceConnection; import google.registry.tools.server.UpdateUserGroupAction; -import google.registry.tools.server.UpdateUserGroupAction.Mode; +import google.registry.util.PasswordUtils; import google.registry.util.RegistryEnvironment; -import jakarta.persistence.Access; -import jakarta.persistence.AccessType; +import jakarta.persistence.Column; import jakarta.persistence.Embeddable; import jakarta.persistence.Entity; import jakarta.persistence.Id; @@ -44,11 +51,33 @@ import javax.annotation.Nullable; @Embeddable @Entity @Table -public class User extends UserBase { +public class User extends UpdateAutoTimestampEntity implements Buildable { public static final String IAP_SECURED_WEB_APP_USER_ROLE = "roles/iap.httpsResourceAccessor"; + + private static final long serialVersionUID = 6936728603828566721L; private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + /** Email address of the user in question. */ + @Id @Expose String emailAddress; + + /** Optional external email address to use for registry lock confirmation emails. */ + @Column String registryLockEmailAddress; + + /** Roles (which grant permissions) associated with this user. */ + @Expose + @Column(nullable = false) + UserRoles userRoles; + + /** + * A hashed password that exists iff this contact is registry-lock-enabled. The hash is a base64 + * encoded SHA256 string. + */ + String registryLockPasswordHash; + + /** Randomly generated hash salt. */ + String registryLockPasswordSalt; + /** * Grants the user permission to pass IAP. * @@ -113,9 +142,13 @@ public class User extends UserBase { logger.atInfo().log("Removing %s from group %s", emailAddress, groupEmailAddress.get()); if (cloudTasksUtils != null) { modifyGroupMembershipAsync( - emailAddress, groupEmailAddress.get(), cloudTasksUtils, Mode.REMOVE); + emailAddress, + groupEmailAddress.get(), + cloudTasksUtils, + UpdateUserGroupAction.Mode.REMOVE); } else { - modifyGroupMembershipSync(emailAddress, groupEmailAddress.get(), connection, Mode.REMOVE); + modifyGroupMembershipSync( + emailAddress, groupEmailAddress.get(), connection, UpdateUserGroupAction.Mode.REMOVE); } } } @@ -124,7 +157,7 @@ public class User extends UserBase { String userEmailAddress, String groupEmailAddress, CloudTasksUtils cloudTasksUtils, - Mode mode) { + UpdateUserGroupAction.Mode mode) { Task task = cloudTasksUtils.createTask( UpdateUserGroupAction.class, @@ -140,7 +173,10 @@ public class User extends UserBase { } private static void modifyGroupMembershipSync( - String userEmailAddress, String groupEmailAddress, ServiceConnection connection, Mode mode) { + String userEmailAddress, + String groupEmailAddress, + ServiceConnection connection, + UpdateUserGroupAction.Mode mode) { try { connection.sendPostRequest( UpdateUserGroupAction.PATH, @@ -158,30 +194,117 @@ public class User extends UserBase { } } - @Id - @Override - @Access(AccessType.PROPERTY) + /** + * Sets the user email address. + * + *

This should only be used for restoring an object being loaded in a PostLoad method + * (effectively, when it is still under construction by Hibernate). In all other cases, the object + * should be regarded as immutable and changes should go through a Builder. + * + *

In addition to this special case use, this method must exist to satisfy Hibernate. + */ + void setEmailAddress(String emailAddress) { + this.emailAddress = emailAddress; + } + public String getEmailAddress() { - return super.getEmailAddress(); + return emailAddress; + } + + public Optional getRegistryLockEmailAddress() { + return Optional.ofNullable(registryLockEmailAddress); + } + + public UserRoles getUserRoles() { + return userRoles; + } + + public boolean hasRegistryLockPassword() { + return !isNullOrEmpty(registryLockPasswordHash) && !isNullOrEmpty(registryLockPasswordSalt); + } + + public boolean verifyRegistryLockPassword(String registryLockPassword) { + if (isNullOrEmpty(registryLockPassword) + || isNullOrEmpty(registryLockPasswordSalt) + || isNullOrEmpty(registryLockPasswordHash)) { + return false; + } + return PasswordUtils.verifyPassword( + registryLockPassword, registryLockPasswordHash, registryLockPasswordSalt); + } + + /** + * Whether the user has the registry lock permission on any registrar or globally. + * + *

If so, they should be allowed to (re)set their registry lock password. + */ + public boolean hasAnyRegistryLockPermission() { + if (userRoles == null) { + return false; + } + if (userRoles.isAdmin() || userRoles.hasGlobalPermission(ConsolePermission.REGISTRY_LOCK)) { + return true; + } + return userRoles.getRegistrarRoles().values().stream() + .anyMatch(role -> role.hasPermission(ConsolePermission.REGISTRY_LOCK)); } @Override - public Builder asBuilder() { - return new Builder(clone(this)); - } - - @Override - public VKey createVKey() { - return VKey.create(User.class, getEmailAddress()); + public Builder asBuilder() { + return new Builder<>(clone(this)); } /** Builder for constructing immutable {@link User} objects. */ - public static class Builder extends UserBase.Builder { + public static class Builder> + extends GenericBuilder { public Builder() {} - public Builder(User user) { - super(user); + public Builder(T abstractUser) { + super(abstractUser); + } + + @Override + public T build() { + checkArgumentNotNull(getInstance().emailAddress, "Email address cannot be null"); + checkArgumentNotNull(getInstance().userRoles, "User roles cannot be null"); + return super.build(); + } + + public B setEmailAddress(String emailAddress) { + getInstance().emailAddress = checkValidEmail(emailAddress); + return thisCastToDerived(); + } + + public B setRegistryLockEmailAddress(@Nullable String registryLockEmailAddress) { + getInstance().registryLockEmailAddress = + registryLockEmailAddress == null ? null : checkValidEmail(registryLockEmailAddress); + return thisCastToDerived(); + } + + public B setUserRoles(UserRoles userRoles) { + checkArgumentNotNull(userRoles, "User roles cannot be null"); + getInstance().userRoles = userRoles; + return thisCastToDerived(); + } + + public B removeRegistryLockPassword() { + getInstance().registryLockPasswordHash = null; + getInstance().registryLockPasswordSalt = null; + return thisCastToDerived(); + } + + public B setRegistryLockPassword(String registryLockPassword) { + checkArgument( + getInstance().hasAnyRegistryLockPermission(), "User has no registry lock permission"); + checkArgument( + !getInstance().hasRegistryLockPassword(), "User already has a password, remove it first"); + checkArgument( + !isNullOrEmpty(registryLockPassword), "Registry lock password was null or empty"); + byte[] salt = SALT_SUPPLIER.get(); + getInstance().registryLockPasswordSalt = base64().encode(salt); + getInstance().registryLockPasswordHash = hashPassword(registryLockPassword, salt); + return thisCastToDerived(); } } } diff --git a/core/src/main/java/google/registry/model/console/UserBase.java b/core/src/main/java/google/registry/model/console/UserBase.java deleted file mode 100644 index 785df8982..000000000 --- a/core/src/main/java/google/registry/model/console/UserBase.java +++ /dev/null @@ -1,186 +0,0 @@ -// 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.model.console; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Strings.isNullOrEmpty; -import static com.google.common.io.BaseEncoding.base64; -import static google.registry.model.registrar.Registrar.checkValidEmail; -import static google.registry.util.PasswordUtils.SALT_SUPPLIER; -import static google.registry.util.PasswordUtils.hashPassword; -import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; - -import com.google.gson.annotations.Expose; -import google.registry.model.Buildable; -import google.registry.model.UpdateAutoTimestampEntity; -import google.registry.util.PasswordUtils; -import jakarta.persistence.Access; -import jakarta.persistence.AccessType; -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; -import jakarta.persistence.Id; -import jakarta.persistence.MappedSuperclass; -import jakarta.persistence.Transient; -import java.util.Optional; -import javax.annotation.Nullable; - -/** - * A console user, either a registry employee or a registrar partner. - * - *

This class deliberately does not include an {@link Id} so that any foreign-keyed fields can - * refer to the proper parent entity's ID, whether we're storing this in the DB itself or as part of - * another entity. - */ -@Access(AccessType.FIELD) -@Embeddable -@MappedSuperclass -public class UserBase extends UpdateAutoTimestampEntity implements Buildable { - - private static final long serialVersionUID = 6936728603828566721L; - - /** Email address of the user in question. */ - @Transient @Expose String emailAddress; - - /** Optional external email address to use for registry lock confirmation emails. */ - @Column String registryLockEmailAddress; - - /** Roles (which grant permissions) associated with this user. */ - @Expose - @Column(nullable = false) - UserRoles userRoles; - - /** - * A hashed password that exists iff this contact is registry-lock-enabled. The hash is a base64 - * encoded SHA256 string. - */ - String registryLockPasswordHash; - - /** Randomly generated hash salt. */ - String registryLockPasswordSalt; - - /** - * Sets the user email address. - * - *

This should only be used for restoring an object being loaded in a PostLoad method - * (effectively, when it is still under construction by Hibernate). In all other cases, the object - * should be regarded as immutable and changes should go through a Builder. - * - *

In addition to this special case use, this method must exist to satisfy Hibernate. - */ - void setEmailAddress(String emailAddress) { - this.emailAddress = emailAddress; - } - - public String getEmailAddress() { - return emailAddress; - } - - public Optional getRegistryLockEmailAddress() { - return Optional.ofNullable(registryLockEmailAddress); - } - - public UserRoles getUserRoles() { - return userRoles; - } - - public boolean hasRegistryLockPassword() { - return !isNullOrEmpty(registryLockPasswordHash) && !isNullOrEmpty(registryLockPasswordSalt); - } - - public boolean verifyRegistryLockPassword(String registryLockPassword) { - if (isNullOrEmpty(registryLockPassword) - || isNullOrEmpty(registryLockPasswordSalt) - || isNullOrEmpty(registryLockPasswordHash)) { - return false; - } - return PasswordUtils.verifyPassword( - registryLockPassword, registryLockPasswordHash, registryLockPasswordSalt); - } - - /** - * Whether the user has the registry lock permission on any registrar or globally. - * - *

If so, they should be allowed to (re)set their registry lock password. - */ - public boolean hasAnyRegistryLockPermission() { - if (userRoles == null) { - return false; - } - if (userRoles.isAdmin() || userRoles.hasGlobalPermission(ConsolePermission.REGISTRY_LOCK)) { - return true; - } - return userRoles.getRegistrarRoles().values().stream() - .anyMatch(role -> role.hasPermission(ConsolePermission.REGISTRY_LOCK)); - } - - @Override - public Builder asBuilder() { - return new Builder<>(clone(this)); - } - - /** Builder for constructing immutable {@link UserBase} objects. */ - public static class Builder> - extends GenericBuilder { - - public Builder() {} - - public Builder(T abstractUser) { - super(abstractUser); - } - - @Override - public T build() { - checkArgumentNotNull(getInstance().emailAddress, "Email address cannot be null"); - checkArgumentNotNull(getInstance().userRoles, "User roles cannot be null"); - return super.build(); - } - - public B setEmailAddress(String emailAddress) { - getInstance().emailAddress = checkValidEmail(emailAddress); - return thisCastToDerived(); - } - - public B setRegistryLockEmailAddress(@Nullable String registryLockEmailAddress) { - getInstance().registryLockEmailAddress = - registryLockEmailAddress == null ? null : checkValidEmail(registryLockEmailAddress); - return thisCastToDerived(); - } - - public B setUserRoles(UserRoles userRoles) { - checkArgumentNotNull(userRoles, "User roles cannot be null"); - getInstance().userRoles = userRoles; - return thisCastToDerived(); - } - - public B removeRegistryLockPassword() { - getInstance().registryLockPasswordHash = null; - getInstance().registryLockPasswordSalt = null; - return thisCastToDerived(); - } - - public B setRegistryLockPassword(String registryLockPassword) { - checkArgument( - getInstance().hasAnyRegistryLockPermission(), "User has no registry lock permission"); - checkArgument( - !getInstance().hasRegistryLockPassword(), "User already has a password, remove it first"); - checkArgument( - !isNullOrEmpty(registryLockPassword), "Registry lock password was null or empty"); - byte[] salt = SALT_SUPPLIER.get(); - getInstance().registryLockPasswordSalt = base64().encode(salt); - getInstance().registryLockPasswordHash = hashPassword(registryLockPassword, salt); - return thisCastToDerived(); - } - } -} diff --git a/core/src/main/java/google/registry/model/console/UserUpdateHistory.java b/core/src/main/java/google/registry/model/console/UserUpdateHistory.java deleted file mode 100644 index 5cb2196d8..000000000 --- a/core/src/main/java/google/registry/model/console/UserUpdateHistory.java +++ /dev/null @@ -1,85 +0,0 @@ -// 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.model.console; - -import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; - -import google.registry.persistence.VKey; -import jakarta.persistence.Access; -import jakarta.persistence.AccessType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Index; -import jakarta.persistence.PostLoad; -import jakarta.persistence.Table; - -/** - * A persisted history object representing an update to a User. - * - *

In addition to the generic history fields (time, URL, etc.) we also persist a copy of the - * modified User object at this point in time. - */ -@Access(AccessType.FIELD) -@Entity -@Table(indexes = {@Index(columnList = "historyActingUser"), @Index(columnList = "emailAddress")}) -public class UserUpdateHistory extends ConsoleUpdateHistory { - - UserBase user; - - @Column(nullable = false, name = "emailAddress") - String emailAddress; - - public UserBase getUser() { - return user; - } - - @PostLoad - void postLoad() { - user.setEmailAddress(emailAddress); - } - - /** Creates a {@link VKey} instance for this entity. */ - @Override - public VKey createVKey() { - return VKey.create(UserUpdateHistory.class, getRevisionId()); - } - - @Override - public Builder asBuilder() { - return new Builder(clone(this)); - } - - /** Builder for the immutable UserUpdateHistory. */ - public static class Builder extends ConsoleUpdateHistory.Builder { - - public Builder() {} - - public Builder(UserUpdateHistory instance) { - super(instance); - } - - @Override - public UserUpdateHistory build() { - checkArgumentNotNull(getInstance().user, "User must be specified"); - return super.build(); - } - - public Builder setUser(User user) { - getInstance().user = user; - getInstance().emailAddress = user.getEmailAddress(); - return this; - } - } -} diff --git a/core/src/main/java/google/registry/model/registrar/Registrar.java b/core/src/main/java/google/registry/model/registrar/Registrar.java index b2a5fefa3..262bc18e1 100644 --- a/core/src/main/java/google/registry/model/registrar/Registrar.java +++ b/core/src/main/java/google/registry/model/registrar/Registrar.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Nomulus Authors. All Rights Reserved. +// 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. @@ -14,17 +14,82 @@ package google.registry.model.registrar; -import jakarta.persistence.Access; -import jakarta.persistence.AccessType; +import static com.google.common.base.MoreObjects.firstNonNull; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Strings.emptyToNull; +import static com.google.common.base.Strings.nullToEmpty; +import static com.google.common.collect.ImmutableSet.toImmutableSet; +import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet; +import static com.google.common.collect.Sets.immutableEnumSet; +import static com.google.common.collect.Streams.stream; +import static com.google.common.io.BaseEncoding.base64; +import static google.registry.config.RegistryConfig.getDefaultRegistrarWhoisServer; +import static google.registry.model.CacheUtils.memoizeWithShortExpiration; +import static google.registry.model.tld.Tlds.assertTldsExist; +import static google.registry.persistence.transaction.TransactionManagerFactory.tm; +import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; +import static google.registry.util.CollectionUtils.nullToEmptyImmutableSortedCopy; +import static google.registry.util.DateTimeUtils.START_OF_TIME; +import static google.registry.util.PasswordUtils.SALT_SUPPLIER; +import static google.registry.util.PasswordUtils.hashPassword; +import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; +import static google.registry.util.X509Utils.getCertificateHash; +import static google.registry.util.X509Utils.loadCertificate; +import static java.util.Comparator.comparing; +import static java.util.function.Predicate.isEqual; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSortedMap; +import com.google.common.collect.ImmutableSortedSet; +import com.google.common.collect.Maps; +import com.google.common.collect.Range; +import com.google.common.collect.Sets; +import com.google.gson.annotations.Expose; +import com.google.re2j.Pattern; +import google.registry.model.Buildable; +import google.registry.model.CreateAutoTimestamp; +import google.registry.model.JsonMapBuilder; +import google.registry.model.Jsonifiable; +import google.registry.model.UpdateAutoTimestamp; +import google.registry.model.UpdateAutoTimestampEntity; +import google.registry.model.tld.Tld; +import google.registry.model.tld.Tld.TldType; +import google.registry.persistence.VKey; +import google.registry.persistence.converter.CidrBlockListUserType; +import google.registry.persistence.converter.CurrencyToStringMapUserType; +import google.registry.util.CidrAddressBlock; +import google.registry.util.PasswordUtils; +import jakarta.mail.internet.AddressException; +import jakarta.mail.internet.InternetAddress; import jakarta.persistence.AttributeOverride; +import jakarta.persistence.AttributeOverrides; import jakarta.persistence.Column; +import jakarta.persistence.Embedded; import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; import jakarta.persistence.Id; import jakarta.persistence.Index; import jakarta.persistence.Table; +import java.security.cert.CertificateParsingException; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Predicate; +import java.util.function.Supplier; +import javax.annotation.Nullable; +import org.joda.money.CurrencyUnit; +import org.joda.time.DateTime; /** Information about a registrar. */ -@Access(AccessType.FIELD) @Entity @Table( indexes = { @@ -34,28 +99,940 @@ import jakarta.persistence.Table; @AttributeOverride( name = "updateTimestamp.lastUpdateTime", column = @Column(nullable = false, name = "lastUpdateTime")) -public class Registrar extends RegistrarBase { +public class Registrar extends UpdateAutoTimestampEntity implements Buildable, Jsonifiable { - @Override - @Id - @Access(AccessType.PROPERTY) - public String getRegistrarId() { - return super.getRegistrarId(); - } + /** Represents the type of registrar entity. */ + public enum Type { + /** A real-world, third-party registrar. Should have non-null IANA and billing account IDs. */ + REAL(Objects::nonNull), - @Override - public Builder asBuilder() { - return new Builder(clone(this)); - } + /** + * A registrar account used by a real third-party registrar undergoing operational testing and + * evaluation. Should only be created in sandbox, and should have null IANA/billing account IDs. + */ + OTE(Objects::isNull), - /** Builder for constructing immutable {@link Registrar} objects. */ - public static class Builder extends RegistrarBase.Builder { + /** + * A registrar used for predelegation testing. Should have a null billing account ID. The IANA + * ID should be either 9995 or 9996, which are reserved for predelegation testing. + */ + PDT(n -> ImmutableSet.of(9995L, 9996L).contains(n)), - public Builder() {} + /** + * A registrar used for external monitoring by ICANN. Should have IANA ID 9997 and a null + * billing account ID. + */ + EXTERNAL_MONITORING(isEqual(9997L)), - public Builder(Registrar registrar) { - super(registrar); + /** + * A registrar used for when the registry acts as a registrar. Must have either IANA ID 9998 + * (for billable transactions) or 9999 (for non-billable transactions). + */ + // TODO(b/13786188): determine what billing account ID for this should be, if any. + INTERNAL(n -> ImmutableSet.of(9998L, 9999L).contains(n)), + + /** A registrar used for internal monitoring. Should have null IANA/billing account IDs. */ + MONITORING(Objects::isNull), + + /** A registrar used for internal testing. Should have null IANA/billing account IDs. */ + TEST(Objects::isNull); + + /** + * Predicate for validating IANA IDs for this type of registrar. + * + * @see Registrar + * IDs + */ + @SuppressWarnings("ImmutableEnumChecker") + private final Predicate ianaIdValidator; + + Type(Predicate ianaIdValidator) { + this.ianaIdValidator = ianaIdValidator; } + /** Returns true if the given IANA identifier is valid for this registrar type. */ + public boolean isValidIanaId(Long ianaId) { + return ianaIdValidator.test(ianaId); + } + } + + /** Represents the state of a persisted registrar entity. */ + public enum State { + + /** This registrar is provisioned but not yet active, and cannot log in. */ + PENDING, + + /** This is an active registrar account which is allowed to provision and modify domains. */ + ACTIVE, + + /** + * This is a suspended account which is disallowed from provisioning new domains, but can + * otherwise still perform other operations to continue operations. + */ + SUSPENDED, + + /** + * This registrar is completely disabled and cannot perform any EPP actions whatsoever, nor log + * in to the registrar console. + */ + DISABLED + } + + /** Regex for E.164 phone number format specified by {@code contact.xsd}. */ + private static final Pattern E164_PATTERN = Pattern.compile("\\+[0-9]{1,3}\\.[0-9]{1,14}"); + + /** Regex for telephone support passcode (5 digit string). */ + public static final Pattern PHONE_PASSCODE_PATTERN = Pattern.compile("\\d{5}"); + + /** The states in which a {@link Registrar} is considered {@link #isLive live}. */ + private static final ImmutableSet LIVE_STATES = + immutableEnumSet(State.ACTIVE, State.SUSPENDED); + + /** + * The types for which a {@link Registrar} should be included in WHOIS and RDAP output. We exclude + * registrars of type TEST. We considered excluding INTERNAL as well, but decided that + * troubleshooting would be easier with INTERNAL registrars visible. Before removing other types + * from view, carefully consider the effect on things like prober monitoring and OT&E. + */ + private static final ImmutableSet PUBLICLY_VISIBLE_TYPES = + immutableEnumSet( + Type.REAL, Type.PDT, Type.OTE, Type.EXTERNAL_MONITORING, Type.MONITORING, Type.INTERNAL); + + /** Compare two instances of {@link RegistrarPoc} by their email addresses lexicographically. */ + private static final Comparator CONTACT_EMAIL_COMPARATOR = + comparing(RegistrarPoc::getEmailAddress, String::compareTo); + + /** A caching {@link Supplier} of a registrarId to {@link Registrar} map. */ + private static final Supplier> CACHE_BY_REGISTRAR_ID = + memoizeWithShortExpiration( + () -> + Maps.uniqueIndex( + tm().reTransact(() -> tm().loadAllOf(Registrar.class)), + Registrar::getRegistrarId)); + + /** + * Unique registrar client id. Must conform to "clIDType" as defined in RFC5730. + * + * @see Shared Structure Schema + */ + @Id @Expose String registrarId; + + /** + * Registrar name. This is a distinct from the client identifier since there are no restrictions + * on its length. + * + *

NB: We are assuming that this field is unique across all registrar entities. This is not + * formally enforced in the database, but should be enforced by ICANN in that no two registrars + * will be accredited with the same name. + * + * @see ICANN-Accredited + * Registrars + */ + @Expose + @Column(nullable = false) + String registrarName; + + /** The type of this registrar. */ + @Expose + @Column(nullable = false) + @Enumerated(EnumType.STRING) + Type type; + + /** The state of this registrar. */ + @Enumerated(EnumType.STRING) + State state; + + /** The set of TLDs which this registrar is allowed to access. */ + @Expose Set allowedTlds; + + /** Host name of WHOIS server. */ + @Expose String whoisServer; + + /** Base URLs for the registrar's RDAP servers. */ + Set rdapBaseUrls; + + /** + * Whether registration of premium names should be blocked over EPP. If this is set to true, then + * the only way to register premium names is with the superuser flag. + */ + @Column(nullable = false) + boolean blockPremiumNames; + + // Authentication. + + /** X.509 PEM client certificate(s) used to authenticate registrar to EPP service. */ + @Expose String clientCertificate; + + /** Base64 encoded SHA256 hash of {@link #clientCertificate}. */ + String clientCertificateHash; + + /** + * Optional secondary X.509 PEM certificate to try if {@link #clientCertificate} does not work. + * + *

This allows registrars to migrate certificates without downtime. + */ + @Expose String failoverClientCertificate; + + /** Base64 encoded SHA256 hash of {@link #failoverClientCertificate}. */ + String failoverClientCertificateHash; + + /** An allow list of netmasks (in CIDR notation) which the client is allowed to connect from. */ + @org.hibernate.annotations.Type(CidrBlockListUserType.class) + @Expose + List ipAddressAllowList; + + /** A hashed password for EPP access. The hash is a base64 encoded SHA256 string. */ + String passwordHash; + + /** Randomly generated hash salt. */ + @Column(name = "password_salt") + String salt; + + // The following fields may appear redundant to the above, but are + // implied by RFC examples and should be interpreted as "for the + // Registrar as a whole". + /** + * Localized {@link RegistrarAddress} for this registrar. Contents can be represented in + * unrestricted UTF-8. + */ + @Embedded + @Expose + @AttributeOverrides({ + @AttributeOverride( + name = "streetLine1", + column = @Column(name = "localized_address_street_line1")), + @AttributeOverride( + name = "streetLine2", + column = @Column(name = "localized_address_street_line2")), + @AttributeOverride( + name = "streetLine3", + column = @Column(name = "localized_address_street_line3")), + @AttributeOverride(name = "city", column = @Column(name = "localized_address_city")), + @AttributeOverride(name = "state", column = @Column(name = "localized_address_state")), + @AttributeOverride(name = "zip", column = @Column(name = "localized_address_zip")), + @AttributeOverride( + name = "countryCode", + column = @Column(name = "localized_address_country_code")) + }) + RegistrarAddress localizedAddress; + + /** + * Internationalized {@link RegistrarAddress} for this registrar. All contained values must be + * representable in the 7-bit US-ASCII character set. + */ + @Embedded + @AttributeOverrides({ + @AttributeOverride(name = "streetLine1", column = @Column(name = "i18n_address_street_line1")), + @AttributeOverride(name = "streetLine2", column = @Column(name = "i18n_address_street_line2")), + @AttributeOverride(name = "streetLine3", column = @Column(name = "i18n_address_street_line3")), + @AttributeOverride(name = "city", column = @Column(name = "i18n_address_city")), + @AttributeOverride(name = "state", column = @Column(name = "i18n_address_state")), + @AttributeOverride(name = "zip", column = @Column(name = "i18n_address_zip")), + @AttributeOverride(name = "countryCode", column = @Column(name = "i18n_address_country_code")) + }) + RegistrarAddress internationalizedAddress; + + /** Voice number. */ + @Expose String phoneNumber; + + /** Fax number. */ + @Expose String faxNumber; + + /** Email address. */ + @Expose String emailAddress; + + // External IDs. + + /** + * Registrar identifier used for reporting to ICANN. + * + *

    + *
  • 8 is used for Testing Registrar. + *
  • 9997 is used by ICANN for SLA monitoring. + *
  • 9999 is used for cases when the registry operator acts as registrar. + *
+ * + * @see Registrar + * IDs + */ + @Expose @Nullable Long ianaIdentifier; + + /** Purchase Order number used for invoices in external billing system, if applicable. */ + @Nullable String poNumber; + + /** + * Map of currency-to-billing account for the registrar. + * + *

A registrar can have different billing accounts that are denoted in different currencies. + * This provides flexibility for billing systems that require such distinction. When this field is + * accessed by {@link #getBillingAccountMap}, a sorted map is returned to guarantee deterministic + * behavior when serializing the map, for display purpose for instance. + */ + @Expose + @Nullable + @org.hibernate.annotations.Type(CurrencyToStringMapUserType.class) + Map billingAccountMap; + + /** URL of registrar's website. */ + @Expose String url; + + /** + * ICANN referral email address. + * + *

This value is specified in the initial registrar contact. It can't be edited in the web GUI, + * and it must be specified when the registrar account is created. + */ + @Expose String icannReferralEmail; + + /** ID of the folder in drive used to publish information for this registrar. */ + @Expose String driveFolderId; + + // Metadata. + + /** The time when this registrar was created. */ + CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null); + + /** The time that the certificate was last updated. */ + DateTime lastCertificateUpdateTime; + + /** The time that an expiring certificate notification email was sent to the registrar. */ + DateTime lastExpiringCertNotificationSentDate = START_OF_TIME; + + /** + * The time that an expiring failover certificate notification email was sent to the registrar. + */ + DateTime lastExpiringFailoverCertNotificationSentDate = START_OF_TIME; + + /** Telephone support passcode (5-digit numeric) */ + String phonePasscode; + + /** + * A dirty bit for whether RegistrarContact changes have been made that haven't been synced to + * Google Groups yet. When creating a new instance, contacts require syncing by default. + */ + @Column(nullable = false) + boolean contactsRequireSyncing = true; + + /** Whether or not registry lock is allowed for this registrar. */ + @Column(nullable = false) + @Expose + boolean registryLockAllowed = false; + + public String getRegistrarId() { + return registrarId; + } + + public DateTime getCreationTime() { + return creationTime.getTimestamp(); + } + + @Nullable + public Long getIanaIdentifier() { + return ianaIdentifier; + } + + public Optional getPoNumber() { + return Optional.ofNullable(poNumber); + } + + public ImmutableSortedMap getBillingAccountMap() { + return billingAccountMap == null + ? ImmutableSortedMap.of() + : ImmutableSortedMap.copyOf(billingAccountMap); + } + + public DateTime getLastUpdateTime() { + return getUpdateTimestamp().getTimestamp(); + } + + public DateTime getLastCertificateUpdateTime() { + return lastCertificateUpdateTime; + } + + public DateTime getLastExpiringCertNotificationSentDate() { + return lastExpiringCertNotificationSentDate; + } + + public DateTime getLastExpiringFailoverCertNotificationSentDate() { + return lastExpiringFailoverCertNotificationSentDate; + } + + public String getRegistrarName() { + return registrarName; + } + + public Type getType() { + return type; + } + + public State getState() { + return state; + } + + public ImmutableSortedSet getAllowedTlds() { + return nullToEmptyImmutableSortedCopy(allowedTlds); + } + + /** + * Returns {@code true} if the registrar is live. + * + *

A live registrar is one that can have live domains/contacts/hosts in the registry, meaning + * that it is either currently active or used to be active (i.e. suspended). + */ + public boolean isLive() { + return LIVE_STATES.contains(state); + } + + /** Returns {@code true} if registrar should be visible in WHOIS results. */ + public boolean isLiveAndPubliclyVisible() { + return LIVE_STATES.contains(state) && PUBLICLY_VISIBLE_TYPES.contains(type); + } + + /** Returns the client certificate string if it has been set, or empty otherwise. */ + public Optional getClientCertificate() { + return Optional.ofNullable(clientCertificate); + } + + /** Returns the client certificate hash if it has been set, or empty otherwise. */ + public Optional getClientCertificateHash() { + return Optional.ofNullable(clientCertificateHash); + } + + /** Returns the failover client certificate string if it has been set, or empty otherwise. */ + public Optional getFailoverClientCertificate() { + return Optional.ofNullable(failoverClientCertificate); + } + + /** Returns the failover client certificate hash if it has been set, or empty otherwise. */ + public Optional getFailoverClientCertificateHash() { + return Optional.ofNullable(failoverClientCertificateHash); + } + + public ImmutableList getIpAddressAllowList() { + return nullToEmptyImmutableCopy(ipAddressAllowList); + } + + public RegistrarAddress getLocalizedAddress() { + return localizedAddress; + } + + public RegistrarAddress getInternationalizedAddress() { + return internationalizedAddress; + } + + public String getPhoneNumber() { + return phoneNumber; + } + + public String getFaxNumber() { + return faxNumber; + } + + public String getEmailAddress() { + return emailAddress; + } + + public String getWhoisServer() { + return firstNonNull(whoisServer, getDefaultRegistrarWhoisServer()); + } + + public ImmutableSet getRdapBaseUrls() { + return nullToEmptyImmutableSortedCopy(rdapBaseUrls); + } + + public boolean getBlockPremiumNames() { + return blockPremiumNames; + } + + public boolean getContactsRequireSyncing() { + return contactsRequireSyncing; + } + + public boolean isRegistryLockAllowed() { + return registryLockAllowed; + } + + public String getUrl() { + return url; + } + + public String getIcannReferralEmail() { + return nullToEmpty(icannReferralEmail); + } + + public String getDriveFolderId() { + return driveFolderId; + } + + /** + * Returns a list of all {@link RegistrarPoc} objects for this registrar sorted by their email + * address. + */ + public ImmutableSortedSet getContacts() { + return getContactPocs().stream() + .filter(Objects::nonNull) + .collect(toImmutableSortedSet(CONTACT_EMAIL_COMPARATOR)); + } + + /** + * Returns a list of {@link RegistrarPoc} objects of a given type for this registrar sorted by + * their email address. + */ + public ImmutableSortedSet getContactsOfType(final RegistrarPoc.Type type) { + return getContactPocs().stream() + .filter(Objects::nonNull) + .filter((@Nullable RegistrarPoc contact) -> contact.getTypes().contains(type)) + .collect(toImmutableSortedSet(CONTACT_EMAIL_COMPARATOR)); + } + + /** + * Returns the {@link RegistrarPoc} that is the WHOIS abuse contact for this registrar, or empty + * if one does not exist. + */ + public Optional getWhoisAbuseContact() { + return getContacts().stream().filter(RegistrarPoc::getVisibleInDomainWhoisAsAbuse).findFirst(); + } + + private ImmutableSet getContactPocs() { + return tm().transact( + () -> + tm().query("FROM RegistrarPoc WHERE registrarId = :registrarId", RegistrarPoc.class) + .setParameter("registrarId", registrarId) + .getResultStream() + .collect(toImmutableSet())); + } + + @Override + public Map toJsonMap() { + return new JsonMapBuilder() + .put("registrarId", registrarId) + .put("ianaIdentifier", ianaIdentifier) + .putString("creationTime", creationTime.getTimestamp()) + .putString("lastUpdateTime", getUpdateTimestamp().getTimestamp()) + .putString("lastCertificateUpdateTime", lastCertificateUpdateTime) + .putString("lastExpiringCertNotificationSentDate", lastExpiringCertNotificationSentDate) + .putString( + "lastExpiringFailoverCertNotificationSentDate", + lastExpiringFailoverCertNotificationSentDate) + .put("registrarName", registrarName) + .put("type", type) + .put("state", state) + .put("clientCertificate", clientCertificate) + .put("clientCertificateHash", clientCertificateHash) + .put("failoverClientCertificate", failoverClientCertificate) + .put("failoverClientCertificateHash", failoverClientCertificateHash) + .put("localizedAddress", localizedAddress) + .put("internationalizedAddress", internationalizedAddress) + .put("phoneNumber", phoneNumber) + .put("faxNumber", faxNumber) + .put("emailAddress", emailAddress) + .put("whoisServer", getWhoisServer()) + .putListOfStrings("rdapBaseUrls", getRdapBaseUrls()) + .put("blockPremiumNames", blockPremiumNames) + .put("url", url) + .put("icannReferralEmail", getIcannReferralEmail()) + .put("driveFolderId", driveFolderId) + .put("phoneNumber", phoneNumber) + .put("phonePasscode", phonePasscode) + .putListOfStrings("allowedTlds", getAllowedTlds()) + .putListOfStrings("ipAddressAllowList", getIpAddressAllowList()) + .putListOfJsonObjects("contacts", getContacts()) + .put("registryLockAllowed", registryLockAllowed) + .build(); + } + + private static String checkValidPhoneNumber(String phoneNumber) { + checkArgument( + E164_PATTERN.matcher(phoneNumber).matches(), + "Not a valid E.164 phone number: %s", + phoneNumber); + return phoneNumber; + } + + public boolean verifyPassword(String password) { + return PasswordUtils.verifyPassword(password, passwordHash, salt); + } + + public String getPhonePasscode() { + return phonePasscode; + } + + /** + * Sets the registrar ID. + * + *

This should only be used for restoring the registrar ID of an object being loaded in a + * PostLoad method (effectively, when it is still under construction by Hibernate). In all other + * cases, the object should be regarded as immutable and changes should go through a Builder. + * + *

In addition to this special case use, this method must exist to satisfy Hibernate. + */ + @SuppressWarnings("unused") + public void setRegistrarId(String registrarId) { + this.registrarId = registrarId; + } + + @Override + public Builder asBuilder() { + return new Builder<>(clone(this)); + } + + @Override + public VKey createVKey() { + return createVKey(registrarId); + } + + /** Creates a {@link VKey} for the given {@code registrarId}. */ + public static VKey createVKey(String registrarId) { + checkArgumentNotNull(registrarId, "registrarId must be specified"); + return VKey.create(Registrar.class, registrarId); + } + + /** A builder for constructing {@link Registrar}, since it is immutable. */ + public static class Builder> + extends GenericBuilder { + public Builder() {} + + public Builder(T instance) { + super(instance); + } + + public B setRegistrarId(String registrarId) { + // Registrar id must be [3,16] chars long. See "clIDType" in the base EPP schema of RFC 5730. + // (Need to validate this here as there's no matching EPP XSD for validation.) + checkArgument( + Range.closed(3, 16).contains(registrarId.length()), + "Registrar ID must be 3-16 characters long."); + getInstance().registrarId = registrarId; + return thisCastToDerived(); + } + + public B setIanaIdentifier(@Nullable Long ianaIdentifier) { + checkArgument( + ianaIdentifier == null || ianaIdentifier > 0, "IANA ID must be a positive number"); + getInstance().ianaIdentifier = ianaIdentifier; + return thisCastToDerived(); + } + + public B setPoNumber(Optional poNumber) { + getInstance().poNumber = poNumber.orElse(null); + return thisCastToDerived(); + } + + public B setBillingAccountMap(@Nullable Map billingAccountMap) { + getInstance().billingAccountMap = nullToEmptyImmutableCopy(billingAccountMap); + return thisCastToDerived(); + } + + public B setRegistrarName(String registrarName) { + getInstance().registrarName = registrarName; + return thisCastToDerived(); + } + + public B setType(Type type) { + getInstance().type = type; + return thisCastToDerived(); + } + + public B setState(State state) { + getInstance().state = state; + return thisCastToDerived(); + } + + public B setAllowedTlds(Set allowedTlds) { + getInstance().allowedTlds = ImmutableSortedSet.copyOf(assertTldsExist(allowedTlds)); + return thisCastToDerived(); + } + + /** + * Same as {@link #setAllowedTlds}, but doesn't use the cache to check if the TLDs exist. + * + *

This should be used if the TLD we want to set is persisted in the same transaction - + * meaning its existence can't be cached before we need to save the Registrar. + * + *

We can still only set the allowedTld AFTER we saved the Registry entity. Make sure to call + * {@code .now()} when saving the Registry entity to make sure it's actually saved before trying + * to set the allowed TLDs. + */ + public B setAllowedTldsUncached(Set allowedTlds) { + ImmutableSet> newTldKeys = + Sets.difference(allowedTlds, getInstance().getAllowedTlds()).stream() + .map(Tld::createVKey) + .collect(toImmutableSet()); + Set> missingTldKeys = + Sets.difference(newTldKeys, tm().loadByKeysIfPresent(newTldKeys).keySet()); + checkArgument(missingTldKeys.isEmpty(), "Trying to set nonexistent TLDs: %s", missingTldKeys); + getInstance().allowedTlds = ImmutableSortedSet.copyOf(allowedTlds); + return thisCastToDerived(); + } + + public B setClientCertificate(String clientCertificate, DateTime now) { + clientCertificate = emptyToNull(clientCertificate); + String clientCertificateHash = calculateHash(clientCertificate); + if (!Objects.equals(clientCertificate, getInstance().clientCertificate) + || !Objects.equals(clientCertificateHash, getInstance().clientCertificateHash)) { + getInstance().clientCertificate = clientCertificate; + getInstance().clientCertificateHash = clientCertificateHash; + getInstance().lastCertificateUpdateTime = now; + } + return thisCastToDerived(); + } + + public B setLastExpiringCertNotificationSentDate(DateTime now) { + checkArgumentNotNull(now, "Registrar lastExpiringCertNotificationSentDate cannot be null"); + getInstance().lastExpiringCertNotificationSentDate = now; + return thisCastToDerived(); + } + + public B setLastExpiringFailoverCertNotificationSentDate(DateTime now) { + checkArgumentNotNull( + now, "Registrar lastExpiringFailoverCertNotificationSentDate cannot be null"); + getInstance().lastExpiringFailoverCertNotificationSentDate = now; + return thisCastToDerived(); + } + + public B setFailoverClientCertificate(String clientCertificate, DateTime now) { + clientCertificate = emptyToNull(clientCertificate); + String clientCertificateHash = calculateHash(clientCertificate); + if (!Objects.equals(clientCertificate, getInstance().failoverClientCertificate) + || !Objects.equals(clientCertificateHash, getInstance().failoverClientCertificateHash)) { + getInstance().failoverClientCertificate = clientCertificate; + getInstance().failoverClientCertificateHash = clientCertificateHash; + getInstance().lastCertificateUpdateTime = now; + } + return thisCastToDerived(); + } + + private static String calculateHash(String clientCertificate) { + if (clientCertificate == null) { + return null; + } + try { + return getCertificateHash(loadCertificate(clientCertificate)); + } catch (CertificateParsingException e) { + throw new IllegalArgumentException(e); + } + } + + // Making sure there's no registrar with the same ianaId already in the system + private static boolean isNotADuplicateIanaId( + Iterable registrars, Registrar newInstance) { + // Return early if newly build registrar is not type REAL or ianaId is + // reserved by ICANN - https://www.iana.org/assignments/registrar-ids/registrar-ids.xhtml + if (!Type.REAL.equals(newInstance.type) + || ImmutableSet.of(1L, 8L).contains(newInstance.ianaIdentifier)) { + return true; + } + + return stream(registrars) + .filter(registrar -> Type.REAL.equals(registrar.getType())) + .filter(registrar -> !Objects.equals(newInstance.registrarId, registrar.getRegistrarId())) + .noneMatch( + registrar -> + Objects.equals(newInstance.ianaIdentifier, registrar.getIanaIdentifier())); + } + + public B setContactsRequireSyncing(boolean contactsRequireSyncing) { + getInstance().contactsRequireSyncing = contactsRequireSyncing; + return thisCastToDerived(); + } + + public B setIpAddressAllowList(Iterable ipAddressAllowList) { + getInstance().ipAddressAllowList = ImmutableList.copyOf(ipAddressAllowList); + return thisCastToDerived(); + } + + public B setLocalizedAddress(RegistrarAddress localizedAddress) { + getInstance().localizedAddress = localizedAddress; + return thisCastToDerived(); + } + + public B setInternationalizedAddress(RegistrarAddress internationalizedAddress) { + getInstance().internationalizedAddress = internationalizedAddress; + return thisCastToDerived(); + } + + public B setPhoneNumber(String phoneNumber) { + getInstance().phoneNumber = (phoneNumber == null) ? null : checkValidPhoneNumber(phoneNumber); + return thisCastToDerived(); + } + + public B setFaxNumber(String faxNumber) { + getInstance().faxNumber = (faxNumber == null) ? null : checkValidPhoneNumber(faxNumber); + return thisCastToDerived(); + } + + public B setEmailAddress(String emailAddress) { + getInstance().emailAddress = checkValidEmail(emailAddress); + return thisCastToDerived(); + } + + public B setWhoisServer(String whoisServer) { + getInstance().whoisServer = whoisServer; + return thisCastToDerived(); + } + + public B setRdapBaseUrls(Set rdapBaseUrls) { + getInstance().rdapBaseUrls = ImmutableSet.copyOf(rdapBaseUrls); + return thisCastToDerived(); + } + + public B setBlockPremiumNames(boolean blockPremiumNames) { + getInstance().blockPremiumNames = blockPremiumNames; + return thisCastToDerived(); + } + + public B setUrl(String url) { + getInstance().url = url; + return thisCastToDerived(); + } + + public B setIcannReferralEmail(String icannReferralEmail) { + getInstance().icannReferralEmail = checkValidEmail(icannReferralEmail); + return thisCastToDerived(); + } + + public B setDriveFolderId(@Nullable String driveFolderId) { + checkArgument( + driveFolderId == null || !driveFolderId.contains("/"), + "Drive folder ID must not be a full URL"); + getInstance().driveFolderId = driveFolderId; + return thisCastToDerived(); + } + + public B setPassword(String password) { + // Passwords must be [6,16] chars long. See "pwType" in the base EPP schema of RFC 5730. + checkArgument( + Range.closed(6, 16).contains(nullToEmpty(password).length()), + "Password must be 6-16 characters long."); + byte[] salt = SALT_SUPPLIER.get(); + getInstance().salt = base64().encode(salt); + getInstance().passwordHash = hashPassword(password, salt); + return thisCastToDerived(); + } + + /** + * Set the phone passcode. + * + * @throws IllegalArgumentException if provided passcode is not 5-digit numeric + */ + public B setPhonePasscode(String phonePasscode) { + checkArgument( + phonePasscode == null || PHONE_PASSCODE_PATTERN.matcher(phonePasscode).matches(), + "Not a valid telephone passcode (must be 5 digits long): %s", + phonePasscode); + getInstance().phonePasscode = phonePasscode; + return thisCastToDerived(); + } + + public B setRegistryLockAllowed(boolean registryLockAllowed) { + getInstance().registryLockAllowed = registryLockAllowed; + return thisCastToDerived(); + } + + /** + * This lets tests set the update timestamp in cases where setting fields resets the timestamp + * and breaks the verification that an object has not been updated since it was copied. + */ + @VisibleForTesting + public B setLastUpdateTime(DateTime timestamp) { + getInstance().setUpdateTimestamp(UpdateAutoTimestamp.create(timestamp)); + return thisCastToDerived(); + } + + /** Build the registrar, nullifying empty fields. */ + @Override + public T build() { + checkArgumentNotNull(getInstance().type, "Registrar type cannot be null"); + checkArgumentNotNull(getInstance().registrarName, "Registrar name cannot be null"); + checkArgument( + getInstance().localizedAddress != null || getInstance().internationalizedAddress != null, + "Must specify at least one of localized or internationalized address"); + checkArgument( + getInstance().type.isValidIanaId(getInstance().ianaIdentifier), + String.format( + "Supplied IANA ID is not valid for %s registrar type: %s", + getInstance().type, getInstance().ianaIdentifier)); + + // We do not allow creating Real registrars with IANA ID that's already in the system + // b/315007360 - for more details + checkArgument( + isNotADuplicateIanaId(loadAllCached(), getInstance()), + String.format( + "Rejected attempt to create a registrar with ianaId that's already in the system -" + + " %s", + getInstance().ianaIdentifier)); + + // In order to grant access to real TLDs, the registrar must have a corresponding billing + // account ID for that TLD's billing currency. + ImmutableSet nonBillableTlds = + Tld.get(getInstance().getAllowedTlds()).stream() + .filter(r -> r.getTldType() == TldType.REAL) + .filter(r -> !getInstance().getBillingAccountMap().containsKey(r.getCurrency())) + .map(Tld::getTldStr) + .collect(toImmutableSet()); + checkArgument( + nonBillableTlds.isEmpty(), + "Cannot set these allowed, real TLDs because their currency is missing " + + "from the billing account map: %s", + nonBillableTlds); + return cloneEmptyToNull(super.build()); + } + } + + /** Verifies that the email address in question is not null and has a valid format. */ + public static String checkValidEmail(String email) { + checkNotNull(email, "Provided email was null"); + try { + InternetAddress internetAddress = new InternetAddress(email, true); + internetAddress.validate(); + } catch (AddressException e) { + throw new IllegalArgumentException( + String.format("Provided email %s is not a valid email address", email)); + } + return email; + } + + /** Loads all registrar entities directly from the database. */ + public static Iterable loadAll() { + return tm().transact(() -> tm().loadAllOf(Registrar.class)); + } + + /** Loads all registrar entities using an in-memory cache. */ + public static Iterable loadAllCached() { + return CACHE_BY_REGISTRAR_ID.get().values(); + } + + /** Loads all registrar keys using an in-memory cache. */ + public static ImmutableSet> loadAllKeysCached() { + return CACHE_BY_REGISTRAR_ID.get().keySet().stream() + .map(Registrar::createVKey) + .collect(toImmutableSet()); + } + + /** Loads and returns a registrar entity by its id directly from the database. */ + public static Optional loadByRegistrarId(String registrarId) { + checkArgument(!Strings.isNullOrEmpty(registrarId), "registrarId must be specified"); + return tm().reTransact(() -> tm().loadByKeyIfPresent(createVKey(registrarId))); + } + + /** + * Loads and returns a registrar entity by its id using an in-memory cache. + * + *

Returns empty if the registrar isn't found. + */ + public static Optional loadByRegistrarIdCached(String registrarId) { + checkArgument(!Strings.isNullOrEmpty(registrarId), "registrarId must be specified"); + return Optional.ofNullable(CACHE_BY_REGISTRAR_ID.get().get(registrarId)); + } + + /** + * Loads and returns a registrar entity by its id using an in-memory cache. + * + *

Throws if the registrar isn't found. + */ + public static Registrar loadRequiredRegistrarCached(String registrarId) { + Optional registrar = loadByRegistrarIdCached(registrarId); + checkArgument(registrar.isPresent(), "couldn't find registrar '%s'", registrarId); + return registrar.get(); } } diff --git a/core/src/main/java/google/registry/model/registrar/RegistrarBase.java b/core/src/main/java/google/registry/model/registrar/RegistrarBase.java deleted file mode 100644 index ea91755a4..000000000 --- a/core/src/main/java/google/registry/model/registrar/RegistrarBase.java +++ /dev/null @@ -1,1040 +0,0 @@ -// 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.model.registrar; - -import static com.google.common.base.MoreObjects.firstNonNull; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Strings.emptyToNull; -import static com.google.common.base.Strings.nullToEmpty; -import static com.google.common.collect.ImmutableSet.toImmutableSet; -import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet; -import static com.google.common.collect.Sets.immutableEnumSet; -import static com.google.common.collect.Streams.stream; -import static com.google.common.io.BaseEncoding.base64; -import static google.registry.config.RegistryConfig.getDefaultRegistrarWhoisServer; -import static google.registry.model.CacheUtils.memoizeWithShortExpiration; -import static google.registry.model.tld.Tlds.assertTldsExist; -import static google.registry.persistence.transaction.TransactionManagerFactory.tm; -import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; -import static google.registry.util.CollectionUtils.nullToEmptyImmutableSortedCopy; -import static google.registry.util.DateTimeUtils.START_OF_TIME; -import static google.registry.util.PasswordUtils.SALT_SUPPLIER; -import static google.registry.util.PasswordUtils.hashPassword; -import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; -import static google.registry.util.X509Utils.getCertificateHash; -import static google.registry.util.X509Utils.loadCertificate; -import static java.util.Comparator.comparing; -import static java.util.function.Predicate.isEqual; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Strings; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSortedMap; -import com.google.common.collect.ImmutableSortedSet; -import com.google.common.collect.Maps; -import com.google.common.collect.Range; -import com.google.common.collect.Sets; -import com.google.gson.annotations.Expose; -import com.google.re2j.Pattern; -import google.registry.model.Buildable; -import google.registry.model.CreateAutoTimestamp; -import google.registry.model.JsonMapBuilder; -import google.registry.model.Jsonifiable; -import google.registry.model.UpdateAutoTimestamp; -import google.registry.model.UpdateAutoTimestampEntity; -import google.registry.model.tld.Tld; -import google.registry.model.tld.Tld.TldType; -import google.registry.persistence.VKey; -import google.registry.persistence.converter.CidrBlockListUserType; -import google.registry.persistence.converter.CurrencyToStringMapUserType; -import google.registry.util.CidrAddressBlock; -import google.registry.util.PasswordUtils; -import jakarta.mail.internet.AddressException; -import jakarta.mail.internet.InternetAddress; -import jakarta.persistence.Access; -import jakarta.persistence.AccessType; -import jakarta.persistence.AttributeOverride; -import jakarta.persistence.AttributeOverrides; -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; -import jakarta.persistence.Embedded; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.Id; -import jakarta.persistence.MappedSuperclass; -import jakarta.persistence.Transient; -import java.security.cert.CertificateParsingException; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.function.Predicate; -import java.util.function.Supplier; -import javax.annotation.Nullable; -import org.joda.money.CurrencyUnit; -import org.joda.time.DateTime; - -/** - * Information about a registrar. - * - *

This class deliberately does not include an {@link Id} so that any foreign-keyed fields can - * refer to the proper parent entity's ID, whether we're storing this in the DB itself or as part of - * another entity. - */ -@Access(AccessType.FIELD) -@Embeddable -@MappedSuperclass -public class RegistrarBase extends UpdateAutoTimestampEntity implements Buildable, Jsonifiable { - - /** Represents the type of registrar entity. */ - public enum Type { - /** A real-world, third-party registrar. Should have non-null IANA and billing account IDs. */ - REAL(Objects::nonNull), - - /** - * A registrar account used by a real third-party registrar undergoing operational testing and - * evaluation. Should only be created in sandbox, and should have null IANA/billing account IDs. - */ - OTE(Objects::isNull), - - /** - * A registrar used for predelegation testing. Should have a null billing account ID. The IANA - * ID should be either 9995 or 9996, which are reserved for predelegation testing. - */ - PDT(n -> ImmutableSet.of(9995L, 9996L).contains(n)), - - /** - * A registrar used for external monitoring by ICANN. Should have IANA ID 9997 and a null - * billing account ID. - */ - EXTERNAL_MONITORING(isEqual(9997L)), - - /** - * A registrar used for when the registry acts as a registrar. Must have either IANA ID 9998 - * (for billable transactions) or 9999 (for non-billable transactions). - */ - // TODO(b/13786188): determine what billing account ID for this should be, if any. - INTERNAL(n -> ImmutableSet.of(9998L, 9999L).contains(n)), - - /** A registrar used for internal monitoring. Should have null IANA/billing account IDs. */ - MONITORING(Objects::isNull), - - /** A registrar used for internal testing. Should have null IANA/billing account IDs. */ - TEST(Objects::isNull); - - /** - * Predicate for validating IANA IDs for this type of registrar. - * - * @see Registrar - * IDs - */ - @SuppressWarnings("ImmutableEnumChecker") - private final Predicate ianaIdValidator; - - Type(Predicate ianaIdValidator) { - this.ianaIdValidator = ianaIdValidator; - } - - /** Returns true if the given IANA identifier is valid for this registrar type. */ - public boolean isValidIanaId(Long ianaId) { - return ianaIdValidator.test(ianaId); - } - } - - /** Represents the state of a persisted registrar entity. */ - public enum State { - - /** This registrar is provisioned but not yet active, and cannot log in. */ - PENDING, - - /** This is an active registrar account which is allowed to provision and modify domains. */ - ACTIVE, - - /** - * This is a suspended account which is disallowed from provisioning new domains, but can - * otherwise still perform other operations to continue operations. - */ - SUSPENDED, - - /** - * This registrar is completely disabled and cannot perform any EPP actions whatsoever, nor log - * in to the registrar console. - */ - DISABLED - } - - /** Regex for E.164 phone number format specified by {@code contact.xsd}. */ - private static final Pattern E164_PATTERN = Pattern.compile("\\+[0-9]{1,3}\\.[0-9]{1,14}"); - - /** Regex for telephone support passcode (5 digit string). */ - public static final Pattern PHONE_PASSCODE_PATTERN = Pattern.compile("\\d{5}"); - - /** The states in which a {@link Registrar} is considered {@link #isLive live}. */ - private static final ImmutableSet LIVE_STATES = - immutableEnumSet(State.ACTIVE, State.SUSPENDED); - - /** - * The types for which a {@link Registrar} should be included in WHOIS and RDAP output. We exclude - * registrars of type TEST. We considered excluding INTERNAL as well, but decided that - * troubleshooting would be easier with INTERNAL registrars visible. Before removing other types - * from view, carefully consider the effect on things like prober monitoring and OT&E. - */ - private static final ImmutableSet PUBLICLY_VISIBLE_TYPES = - immutableEnumSet( - Type.REAL, Type.PDT, Type.OTE, Type.EXTERNAL_MONITORING, Type.MONITORING, Type.INTERNAL); - - /** Compare two instances of {@link RegistrarPoc} by their email addresses lexicographically. */ - private static final Comparator CONTACT_EMAIL_COMPARATOR = - comparing(RegistrarPoc::getEmailAddress, String::compareTo); - - /** A caching {@link Supplier} of a registrarId to {@link Registrar} map. */ - private static final Supplier> CACHE_BY_REGISTRAR_ID = - memoizeWithShortExpiration( - () -> - Maps.uniqueIndex( - tm().reTransact(() -> tm().loadAllOf(Registrar.class)), - Registrar::getRegistrarId)); - - /** - * Unique registrar client id. Must conform to "clIDType" as defined in RFC5730. - * - * @see Shared Structure Schema - */ - @Expose @Transient String registrarId; - - /** - * Registrar name. This is a distinct from the client identifier since there are no restrictions - * on its length. - * - *

NB: We are assuming that this field is unique across all registrar entities. This is not - * formally enforced in the database, but should be enforced by ICANN in that no two registrars - * will be accredited with the same name. - * - * @see ICANN-Accredited - * Registrars - */ - @Expose - @Column(nullable = false) - String registrarName; - - /** The type of this registrar. */ - @Expose - @Column(nullable = false) - @Enumerated(EnumType.STRING) - Type type; - - /** The state of this registrar. */ - @Enumerated(EnumType.STRING) - State state; - - /** The set of TLDs which this registrar is allowed to access. */ - @Expose Set allowedTlds; - - /** Host name of WHOIS server. */ - @Expose String whoisServer; - - /** Base URLs for the registrar's RDAP servers. */ - Set rdapBaseUrls; - - /** - * Whether registration of premium names should be blocked over EPP. If this is set to true, then - * the only way to register premium names is with the superuser flag. - */ - @Column(nullable = false) - boolean blockPremiumNames; - - // Authentication. - - /** X.509 PEM client certificate(s) used to authenticate registrar to EPP service. */ - @Expose String clientCertificate; - - /** Base64 encoded SHA256 hash of {@link #clientCertificate}. */ - String clientCertificateHash; - - /** - * Optional secondary X.509 PEM certificate to try if {@link #clientCertificate} does not work. - * - *

This allows registrars to migrate certificates without downtime. - */ - @Expose String failoverClientCertificate; - - /** Base64 encoded SHA256 hash of {@link #failoverClientCertificate}. */ - String failoverClientCertificateHash; - - /** An allow list of netmasks (in CIDR notation) which the client is allowed to connect from. */ - @org.hibernate.annotations.Type(CidrBlockListUserType.class) - @Expose - List ipAddressAllowList; - - /** A hashed password for EPP access. The hash is a base64 encoded SHA256 string. */ - String passwordHash; - - /** Randomly generated hash salt. */ - @Column(name = "password_salt") - String salt; - - // The following fields may appear redundant to the above, but are - // implied by RFC examples and should be interpreted as "for the - // Registrar as a whole". - /** - * Localized {@link RegistrarAddress} for this registrar. Contents can be represented in - * unrestricted UTF-8. - */ - @Embedded - @Expose - @AttributeOverrides({ - @AttributeOverride( - name = "streetLine1", - column = @Column(name = "localized_address_street_line1")), - @AttributeOverride( - name = "streetLine2", - column = @Column(name = "localized_address_street_line2")), - @AttributeOverride( - name = "streetLine3", - column = @Column(name = "localized_address_street_line3")), - @AttributeOverride(name = "city", column = @Column(name = "localized_address_city")), - @AttributeOverride(name = "state", column = @Column(name = "localized_address_state")), - @AttributeOverride(name = "zip", column = @Column(name = "localized_address_zip")), - @AttributeOverride( - name = "countryCode", - column = @Column(name = "localized_address_country_code")) - }) - RegistrarAddress localizedAddress; - - /** - * Internationalized {@link RegistrarAddress} for this registrar. All contained values must be - * representable in the 7-bit US-ASCII character set. - */ - @Embedded - @AttributeOverrides({ - @AttributeOverride(name = "streetLine1", column = @Column(name = "i18n_address_street_line1")), - @AttributeOverride(name = "streetLine2", column = @Column(name = "i18n_address_street_line2")), - @AttributeOverride(name = "streetLine3", column = @Column(name = "i18n_address_street_line3")), - @AttributeOverride(name = "city", column = @Column(name = "i18n_address_city")), - @AttributeOverride(name = "state", column = @Column(name = "i18n_address_state")), - @AttributeOverride(name = "zip", column = @Column(name = "i18n_address_zip")), - @AttributeOverride(name = "countryCode", column = @Column(name = "i18n_address_country_code")) - }) - RegistrarAddress internationalizedAddress; - - /** Voice number. */ - @Expose String phoneNumber; - - /** Fax number. */ - @Expose String faxNumber; - - /** Email address. */ - @Expose String emailAddress; - - // External IDs. - - /** - * Registrar identifier used for reporting to ICANN. - * - *

    - *
  • 8 is used for Testing Registrar. - *
  • 9997 is used by ICANN for SLA monitoring. - *
  • 9999 is used for cases when the registry operator acts as registrar. - *
- * - * @see Registrar - * IDs - */ - @Expose @Nullable Long ianaIdentifier; - - /** Purchase Order number used for invoices in external billing system, if applicable. */ - @Nullable String poNumber; - - /** - * Map of currency-to-billing account for the registrar. - * - *

A registrar can have different billing accounts that are denoted in different currencies. - * This provides flexibility for billing systems that require such distinction. When this field is - * accessed by {@link #getBillingAccountMap}, a sorted map is returned to guarantee deterministic - * behavior when serializing the map, for display purpose for instance. - */ - @Expose - @Nullable - @org.hibernate.annotations.Type(CurrencyToStringMapUserType.class) - Map billingAccountMap; - - /** URL of registrar's website. */ - @Expose String url; - - /** - * ICANN referral email address. - * - *

This value is specified in the initial registrar contact. It can't be edited in the web GUI, - * and it must be specified when the registrar account is created. - */ - @Expose String icannReferralEmail; - - /** ID of the folder in drive used to publish information for this registrar. */ - @Expose String driveFolderId; - - // Metadata. - - /** The time when this registrar was created. */ - CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null); - - /** The time that the certificate was last updated. */ - DateTime lastCertificateUpdateTime; - - /** The time that an expiring certificate notification email was sent to the registrar. */ - DateTime lastExpiringCertNotificationSentDate = START_OF_TIME; - - /** - * The time that an expiring failover certificate notification email was sent to the registrar. - */ - DateTime lastExpiringFailoverCertNotificationSentDate = START_OF_TIME; - - /** Telephone support passcode (5-digit numeric) */ - String phonePasscode; - - /** - * A dirty bit for whether RegistrarContact changes have been made that haven't been synced to - * Google Groups yet. When creating a new instance, contacts require syncing by default. - */ - @Column(nullable = false) - boolean contactsRequireSyncing = true; - - /** Whether or not registry lock is allowed for this registrar. */ - @Column(nullable = false) - @Expose - boolean registryLockAllowed = false; - - public String getRegistrarId() { - return registrarId; - } - - public DateTime getCreationTime() { - return creationTime.getTimestamp(); - } - - @Nullable - public Long getIanaIdentifier() { - return ianaIdentifier; - } - - public Optional getPoNumber() { - return Optional.ofNullable(poNumber); - } - - public ImmutableSortedMap getBillingAccountMap() { - return billingAccountMap == null - ? ImmutableSortedMap.of() - : ImmutableSortedMap.copyOf(billingAccountMap); - } - - public DateTime getLastUpdateTime() { - return getUpdateTimestamp().getTimestamp(); - } - - public DateTime getLastCertificateUpdateTime() { - return lastCertificateUpdateTime; - } - - public DateTime getLastExpiringCertNotificationSentDate() { - return lastExpiringCertNotificationSentDate; - } - - public DateTime getLastExpiringFailoverCertNotificationSentDate() { - return lastExpiringFailoverCertNotificationSentDate; - } - - public String getRegistrarName() { - return registrarName; - } - - public Type getType() { - return type; - } - - public State getState() { - return state; - } - - public ImmutableSortedSet getAllowedTlds() { - return nullToEmptyImmutableSortedCopy(allowedTlds); - } - - /** - * Returns {@code true} if the registrar is live. - * - *

A live registrar is one that can have live domains/contacts/hosts in the registry, meaning - * that it is either currently active or used to be active (i.e. suspended). - */ - public boolean isLive() { - return LIVE_STATES.contains(state); - } - - /** Returns {@code true} if registrar should be visible in WHOIS results. */ - public boolean isLiveAndPubliclyVisible() { - return LIVE_STATES.contains(state) && PUBLICLY_VISIBLE_TYPES.contains(type); - } - - /** Returns the client certificate string if it has been set, or empty otherwise. */ - public Optional getClientCertificate() { - return Optional.ofNullable(clientCertificate); - } - - /** Returns the client certificate hash if it has been set, or empty otherwise. */ - public Optional getClientCertificateHash() { - return Optional.ofNullable(clientCertificateHash); - } - - /** Returns the failover client certificate string if it has been set, or empty otherwise. */ - public Optional getFailoverClientCertificate() { - return Optional.ofNullable(failoverClientCertificate); - } - - /** Returns the failover client certificate hash if it has been set, or empty otherwise. */ - public Optional getFailoverClientCertificateHash() { - return Optional.ofNullable(failoverClientCertificateHash); - } - - public ImmutableList getIpAddressAllowList() { - return nullToEmptyImmutableCopy(ipAddressAllowList); - } - - public RegistrarAddress getLocalizedAddress() { - return localizedAddress; - } - - public RegistrarAddress getInternationalizedAddress() { - return internationalizedAddress; - } - - public String getPhoneNumber() { - return phoneNumber; - } - - public String getFaxNumber() { - return faxNumber; - } - - public String getEmailAddress() { - return emailAddress; - } - - public String getWhoisServer() { - return firstNonNull(whoisServer, getDefaultRegistrarWhoisServer()); - } - - public ImmutableSet getRdapBaseUrls() { - return nullToEmptyImmutableSortedCopy(rdapBaseUrls); - } - - public boolean getBlockPremiumNames() { - return blockPremiumNames; - } - - public boolean getContactsRequireSyncing() { - return contactsRequireSyncing; - } - - public boolean isRegistryLockAllowed() { - return registryLockAllowed; - } - - public String getUrl() { - return url; - } - - public String getIcannReferralEmail() { - return nullToEmpty(icannReferralEmail); - } - - public String getDriveFolderId() { - return driveFolderId; - } - - /** - * Returns a list of all {@link RegistrarPoc} objects for this registrar sorted by their email - * address. - */ - public ImmutableSortedSet getContacts() { - return getContactPocs().stream() - .filter(Objects::nonNull) - .collect(toImmutableSortedSet(CONTACT_EMAIL_COMPARATOR)); - } - - /** - * Returns a list of {@link RegistrarPoc} objects of a given type for this registrar sorted by - * their email address. - */ - public ImmutableSortedSet getContactsOfType(final RegistrarPocBase.Type type) { - return getContactPocs().stream() - .filter(Objects::nonNull) - .filter((@Nullable RegistrarPoc contact) -> contact.getTypes().contains(type)) - .collect(toImmutableSortedSet(CONTACT_EMAIL_COMPARATOR)); - } - - /** - * Returns the {@link RegistrarPoc} that is the WHOIS abuse contact for this registrar, or empty - * if one does not exist. - */ - public Optional getWhoisAbuseContact() { - return getContacts().stream().filter(RegistrarPoc::getVisibleInDomainWhoisAsAbuse).findFirst(); - } - - private ImmutableSet getContactPocs() { - return tm().transact( - () -> - tm().query("FROM RegistrarPoc WHERE registrarId = :registrarId", RegistrarPoc.class) - .setParameter("registrarId", registrarId) - .getResultStream() - .collect(toImmutableSet())); - } - - @Override - public Map toJsonMap() { - return new JsonMapBuilder() - .put("registrarId", registrarId) - .put("ianaIdentifier", ianaIdentifier) - .putString("creationTime", creationTime.getTimestamp()) - .putString("lastUpdateTime", getUpdateTimestamp().getTimestamp()) - .putString("lastCertificateUpdateTime", lastCertificateUpdateTime) - .putString("lastExpiringCertNotificationSentDate", lastExpiringCertNotificationSentDate) - .putString( - "lastExpiringFailoverCertNotificationSentDate", - lastExpiringFailoverCertNotificationSentDate) - .put("registrarName", registrarName) - .put("type", type) - .put("state", state) - .put("clientCertificate", clientCertificate) - .put("clientCertificateHash", clientCertificateHash) - .put("failoverClientCertificate", failoverClientCertificate) - .put("failoverClientCertificateHash", failoverClientCertificateHash) - .put("localizedAddress", localizedAddress) - .put("internationalizedAddress", internationalizedAddress) - .put("phoneNumber", phoneNumber) - .put("faxNumber", faxNumber) - .put("emailAddress", emailAddress) - .put("whoisServer", getWhoisServer()) - .putListOfStrings("rdapBaseUrls", getRdapBaseUrls()) - .put("blockPremiumNames", blockPremiumNames) - .put("url", url) - .put("icannReferralEmail", getIcannReferralEmail()) - .put("driveFolderId", driveFolderId) - .put("phoneNumber", phoneNumber) - .put("phonePasscode", phonePasscode) - .putListOfStrings("allowedTlds", getAllowedTlds()) - .putListOfStrings("ipAddressAllowList", getIpAddressAllowList()) - .putListOfJsonObjects("contacts", getContacts()) - .put("registryLockAllowed", registryLockAllowed) - .build(); - } - - private static String checkValidPhoneNumber(String phoneNumber) { - checkArgument( - E164_PATTERN.matcher(phoneNumber).matches(), - "Not a valid E.164 phone number: %s", - phoneNumber); - return phoneNumber; - } - - public boolean verifyPassword(String password) { - return PasswordUtils.verifyPassword(password, passwordHash, salt); - } - - public String getPhonePasscode() { - return phonePasscode; - } - - /** - * Sets the registrar ID. - * - *

This should only be used for restoring the registrar ID of an object being loaded in a - * PostLoad method (effectively, when it is still under construction by Hibernate). In all other - * cases, the object should be regarded as immutable and changes should go through a Builder. - * - *

In addition to this special case use, this method must exist to satisfy Hibernate. - */ - @SuppressWarnings("unused") - public void setRegistrarId(String registrarId) { - this.registrarId = registrarId; - } - - @Override - public Builder asBuilder() { - return new Builder<>(clone(this)); - } - - @Override - public VKey createVKey() { - return createVKey(registrarId); - } - - /** Creates a {@link VKey} for the given {@code registrarId}. */ - public static VKey createVKey(String registrarId) { - checkArgumentNotNull(registrarId, "registrarId must be specified"); - return VKey.create(Registrar.class, registrarId); - } - - /** A builder for constructing {@link Registrar}, since it is immutable. */ - public static class Builder> - extends GenericBuilder { - public Builder() {} - - public Builder(T instance) { - super(instance); - } - - public B setRegistrarId(String registrarId) { - // Registrar id must be [3,16] chars long. See "clIDType" in the base EPP schema of RFC 5730. - // (Need to validate this here as there's no matching EPP XSD for validation.) - checkArgument( - Range.closed(3, 16).contains(registrarId.length()), - "Registrar ID must be 3-16 characters long."); - getInstance().registrarId = registrarId; - return thisCastToDerived(); - } - - public B setIanaIdentifier(@Nullable Long ianaIdentifier) { - checkArgument( - ianaIdentifier == null || ianaIdentifier > 0, "IANA ID must be a positive number"); - getInstance().ianaIdentifier = ianaIdentifier; - return thisCastToDerived(); - } - - public B setPoNumber(Optional poNumber) { - getInstance().poNumber = poNumber.orElse(null); - return thisCastToDerived(); - } - - public B setBillingAccountMap(@Nullable Map billingAccountMap) { - getInstance().billingAccountMap = nullToEmptyImmutableCopy(billingAccountMap); - return thisCastToDerived(); - } - - public B setRegistrarName(String registrarName) { - getInstance().registrarName = registrarName; - return thisCastToDerived(); - } - - public B setType(Type type) { - getInstance().type = type; - return thisCastToDerived(); - } - - public B setState(State state) { - getInstance().state = state; - return thisCastToDerived(); - } - - public B setAllowedTlds(Set allowedTlds) { - getInstance().allowedTlds = ImmutableSortedSet.copyOf(assertTldsExist(allowedTlds)); - return thisCastToDerived(); - } - - /** - * Same as {@link #setAllowedTlds}, but doesn't use the cache to check if the TLDs exist. - * - *

This should be used if the TLD we want to set is persisted in the same transaction - - * meaning its existence can't be cached before we need to save the Registrar. - * - *

We can still only set the allowedTld AFTER we saved the Registry entity. Make sure to call - * {@code .now()} when saving the Registry entity to make sure it's actually saved before trying - * to set the allowed TLDs. - */ - public B setAllowedTldsUncached(Set allowedTlds) { - ImmutableSet> newTldKeys = - Sets.difference(allowedTlds, getInstance().getAllowedTlds()).stream() - .map(Tld::createVKey) - .collect(toImmutableSet()); - Set> missingTldKeys = - Sets.difference(newTldKeys, tm().loadByKeysIfPresent(newTldKeys).keySet()); - checkArgument(missingTldKeys.isEmpty(), "Trying to set nonexistent TLDs: %s", missingTldKeys); - getInstance().allowedTlds = ImmutableSortedSet.copyOf(allowedTlds); - return thisCastToDerived(); - } - - public B setClientCertificate(String clientCertificate, DateTime now) { - clientCertificate = emptyToNull(clientCertificate); - String clientCertificateHash = calculateHash(clientCertificate); - if (!Objects.equals(clientCertificate, getInstance().clientCertificate) - || !Objects.equals(clientCertificateHash, getInstance().clientCertificateHash)) { - getInstance().clientCertificate = clientCertificate; - getInstance().clientCertificateHash = clientCertificateHash; - getInstance().lastCertificateUpdateTime = now; - } - return thisCastToDerived(); - } - - public B setLastExpiringCertNotificationSentDate(DateTime now) { - checkArgumentNotNull(now, "Registrar lastExpiringCertNotificationSentDate cannot be null"); - getInstance().lastExpiringCertNotificationSentDate = now; - return thisCastToDerived(); - } - - public B setLastExpiringFailoverCertNotificationSentDate(DateTime now) { - checkArgumentNotNull( - now, "Registrar lastExpiringFailoverCertNotificationSentDate cannot be null"); - getInstance().lastExpiringFailoverCertNotificationSentDate = now; - return thisCastToDerived(); - } - - public B setFailoverClientCertificate(String clientCertificate, DateTime now) { - clientCertificate = emptyToNull(clientCertificate); - String clientCertificateHash = calculateHash(clientCertificate); - if (!Objects.equals(clientCertificate, getInstance().failoverClientCertificate) - || !Objects.equals(clientCertificateHash, getInstance().failoverClientCertificateHash)) { - getInstance().failoverClientCertificate = clientCertificate; - getInstance().failoverClientCertificateHash = clientCertificateHash; - getInstance().lastCertificateUpdateTime = now; - } - return thisCastToDerived(); - } - - private static String calculateHash(String clientCertificate) { - if (clientCertificate == null) { - return null; - } - try { - return getCertificateHash(loadCertificate(clientCertificate)); - } catch (CertificateParsingException e) { - throw new IllegalArgumentException(e); - } - } - - // Making sure there's no registrar with the same ianaId already in the system - private static boolean isNotADuplicateIanaId( - Iterable registrars, RegistrarBase newInstance) { - // Return early if newly build registrar is not type REAL or ianaId is - // reserved by ICANN - https://www.iana.org/assignments/registrar-ids/registrar-ids.xhtml - if (!Type.REAL.equals(newInstance.type) - || ImmutableSet.of(1L, 8L).contains(newInstance.ianaIdentifier)) { - return true; - } - - return stream(registrars) - .filter(registrar -> Type.REAL.equals(registrar.getType())) - .filter(registrar -> !Objects.equals(newInstance.registrarId, registrar.getRegistrarId())) - .noneMatch( - registrar -> - Objects.equals(newInstance.ianaIdentifier, registrar.getIanaIdentifier())); - } - - public B setContactsRequireSyncing(boolean contactsRequireSyncing) { - getInstance().contactsRequireSyncing = contactsRequireSyncing; - return thisCastToDerived(); - } - - public B setIpAddressAllowList(Iterable ipAddressAllowList) { - getInstance().ipAddressAllowList = ImmutableList.copyOf(ipAddressAllowList); - return thisCastToDerived(); - } - - public B setLocalizedAddress(RegistrarAddress localizedAddress) { - getInstance().localizedAddress = localizedAddress; - return thisCastToDerived(); - } - - public B setInternationalizedAddress(RegistrarAddress internationalizedAddress) { - getInstance().internationalizedAddress = internationalizedAddress; - return thisCastToDerived(); - } - - public B setPhoneNumber(String phoneNumber) { - getInstance().phoneNumber = (phoneNumber == null) ? null : checkValidPhoneNumber(phoneNumber); - return thisCastToDerived(); - } - - public B setFaxNumber(String faxNumber) { - getInstance().faxNumber = (faxNumber == null) ? null : checkValidPhoneNumber(faxNumber); - return thisCastToDerived(); - } - - public B setEmailAddress(String emailAddress) { - getInstance().emailAddress = checkValidEmail(emailAddress); - return thisCastToDerived(); - } - - public B setWhoisServer(String whoisServer) { - getInstance().whoisServer = whoisServer; - return thisCastToDerived(); - } - - public B setRdapBaseUrls(Set rdapBaseUrls) { - getInstance().rdapBaseUrls = ImmutableSet.copyOf(rdapBaseUrls); - return thisCastToDerived(); - } - - public B setBlockPremiumNames(boolean blockPremiumNames) { - getInstance().blockPremiumNames = blockPremiumNames; - return thisCastToDerived(); - } - - public B setUrl(String url) { - getInstance().url = url; - return thisCastToDerived(); - } - - public B setIcannReferralEmail(String icannReferralEmail) { - getInstance().icannReferralEmail = checkValidEmail(icannReferralEmail); - return thisCastToDerived(); - } - - public B setDriveFolderId(@Nullable String driveFolderId) { - checkArgument( - driveFolderId == null || !driveFolderId.contains("/"), - "Drive folder ID must not be a full URL"); - getInstance().driveFolderId = driveFolderId; - return thisCastToDerived(); - } - - public B setPassword(String password) { - // Passwords must be [6,16] chars long. See "pwType" in the base EPP schema of RFC 5730. - checkArgument( - Range.closed(6, 16).contains(nullToEmpty(password).length()), - "Password must be 6-16 characters long."); - byte[] salt = SALT_SUPPLIER.get(); - getInstance().salt = base64().encode(salt); - getInstance().passwordHash = hashPassword(password, salt); - return thisCastToDerived(); - } - - /** - * Set the phone passcode. - * - * @throws IllegalArgumentException if provided passcode is not 5-digit numeric - */ - public B setPhonePasscode(String phonePasscode) { - checkArgument( - phonePasscode == null || PHONE_PASSCODE_PATTERN.matcher(phonePasscode).matches(), - "Not a valid telephone passcode (must be 5 digits long): %s", - phonePasscode); - getInstance().phonePasscode = phonePasscode; - return thisCastToDerived(); - } - - public B setRegistryLockAllowed(boolean registryLockAllowed) { - getInstance().registryLockAllowed = registryLockAllowed; - return thisCastToDerived(); - } - - /** - * This lets tests set the update timestamp in cases where setting fields resets the timestamp - * and breaks the verification that an object has not been updated since it was copied. - */ - @VisibleForTesting - public B setLastUpdateTime(DateTime timestamp) { - getInstance().setUpdateTimestamp(UpdateAutoTimestamp.create(timestamp)); - return thisCastToDerived(); - } - - /** Build the registrar, nullifying empty fields. */ - @Override - public T build() { - checkArgumentNotNull(getInstance().type, "Registrar type cannot be null"); - checkArgumentNotNull(getInstance().registrarName, "Registrar name cannot be null"); - checkArgument( - getInstance().localizedAddress != null || getInstance().internationalizedAddress != null, - "Must specify at least one of localized or internationalized address"); - checkArgument( - getInstance().type.isValidIanaId(getInstance().ianaIdentifier), - String.format( - "Supplied IANA ID is not valid for %s registrar type: %s", - getInstance().type, getInstance().ianaIdentifier)); - - // We do not allow creating Real registrars with IANA ID that's already in the system - // b/315007360 - for more details - checkArgument( - isNotADuplicateIanaId(loadAllCached(), getInstance()), - String.format( - "Rejected attempt to create a registrar with ianaId that's already in the system -" - + " %s", - getInstance().ianaIdentifier)); - - // In order to grant access to real TLDs, the registrar must have a corresponding billing - // account ID for that TLD's billing currency. - ImmutableSet nonBillableTlds = - Tld.get(getInstance().getAllowedTlds()).stream() - .filter(r -> r.getTldType() == TldType.REAL) - .filter(r -> !getInstance().getBillingAccountMap().containsKey(r.getCurrency())) - .map(Tld::getTldStr) - .collect(toImmutableSet()); - checkArgument( - nonBillableTlds.isEmpty(), - "Cannot set these allowed, real TLDs because their currency is missing " - + "from the billing account map: %s", - nonBillableTlds); - return cloneEmptyToNull(super.build()); - } - } - - /** Verifies that the email address in question is not null and has a valid format. */ - public static String checkValidEmail(String email) { - checkNotNull(email, "Provided email was null"); - try { - InternetAddress internetAddress = new InternetAddress(email, true); - internetAddress.validate(); - } catch (AddressException e) { - throw new IllegalArgumentException( - String.format("Provided email %s is not a valid email address", email)); - } - return email; - } - - /** Loads all registrar entities directly from the database. */ - public static Iterable loadAll() { - return tm().transact(() -> tm().loadAllOf(Registrar.class)); - } - - /** Loads all registrar entities using an in-memory cache. */ - public static Iterable loadAllCached() { - return CACHE_BY_REGISTRAR_ID.get().values(); - } - - /** Loads all registrar keys using an in-memory cache. */ - public static ImmutableSet> loadAllKeysCached() { - return CACHE_BY_REGISTRAR_ID.get().keySet().stream() - .map(Registrar::createVKey) - .collect(toImmutableSet()); - } - - /** Loads and returns a registrar entity by its id directly from the database. */ - public static Optional loadByRegistrarId(String registrarId) { - checkArgument(!Strings.isNullOrEmpty(registrarId), "registrarId must be specified"); - return tm().reTransact(() -> tm().loadByKeyIfPresent(createVKey(registrarId))); - } - - /** - * Loads and returns a registrar entity by its id using an in-memory cache. - * - *

Returns empty if the registrar isn't found. - */ - public static Optional loadByRegistrarIdCached(String registrarId) { - checkArgument(!Strings.isNullOrEmpty(registrarId), "registrarId must be specified"); - return Optional.ofNullable(CACHE_BY_REGISTRAR_ID.get().get(registrarId)); - } - - /** - * Loads and returns a registrar entity by its id using an in-memory cache. - * - *

Throws if the registrar isn't found. - */ - public static Registrar loadRequiredRegistrarCached(String registrarId) { - Optional registrar = loadByRegistrarIdCached(registrarId); - checkArgument(registrar.isPresent(), "couldn't find registrar '%s'", registrarId); - return registrar.get(); - } -} diff --git a/core/src/main/java/google/registry/model/registrar/RegistrarPoc.java b/core/src/main/java/google/registry/model/registrar/RegistrarPoc.java index 4912c271c..5c06ebba6 100644 --- a/core/src/main/java/google/registry/model/registrar/RegistrarPoc.java +++ b/core/src/main/java/google/registry/model/registrar/RegistrarPoc.java @@ -1,4 +1,4 @@ -// Copyright 2017 The Nomulus Authors. All Rights Reserved. +// 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. @@ -14,17 +14,40 @@ package google.registry.model.registrar; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Strings.isNullOrEmpty; +import static com.google.common.collect.ImmutableSet.toImmutableSet; +import static com.google.common.io.BaseEncoding.base64; +import static google.registry.model.registrar.Registrar.checkValidEmail; +import static google.registry.persistence.transaction.TransactionManagerFactory.tm; +import static google.registry.util.CollectionUtils.nullToEmptyImmutableSortedCopy; +import static google.registry.util.PasswordUtils.SALT_SUPPLIER; +import static google.registry.util.PasswordUtils.hashPassword; +import static java.util.stream.Collectors.joining; import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSortedSet; +import com.google.gson.annotations.Expose; +import google.registry.model.Buildable.GenericBuilder; import google.registry.model.ImmutableObject; -import google.registry.model.registrar.RegistrarPoc.RegistrarPocId; +import google.registry.model.JsonMapBuilder; +import google.registry.model.Jsonifiable; +import google.registry.model.UnsafeSerializable; import google.registry.persistence.VKey; -import jakarta.persistence.Access; -import jakarta.persistence.AccessType; +import google.registry.util.PasswordUtils; +import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; import jakarta.persistence.Id; import jakarta.persistence.IdClass; import java.io.Serializable; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import javax.annotation.Nullable; /** * A contact for a Registrar. Note, equality, hashCode and comparable have been overridden to only @@ -35,21 +58,244 @@ import java.io.Serializable; * set to true. */ @Entity -@IdClass(RegistrarPocId.class) -@Access(AccessType.FIELD) -public class RegistrarPoc extends RegistrarPocBase { +@IdClass(RegistrarPoc.RegistrarPocId.class) +public class RegistrarPoc extends ImmutableObject implements Jsonifiable, UnsafeSerializable { + /** + * Registrar contacts types for partner communication tracking. + * + *

Note: These types only matter to the registry. They are not meant to be used for + * WHOIS or RDAP results. + */ + public enum Type { + ABUSE("abuse", true), + ADMIN("primary", true), + BILLING("billing", true), + LEGAL("legal", true), + MARKETING("marketing", false), + TECH("technical", true), + WHOIS("whois-inquiry", true); + + private final String displayName; + + private final boolean required; + + public String getDisplayName() { + return displayName; + } + + public boolean isRequired() { + return required; + } + + Type(String display, boolean required) { + displayName = display; + this.required = required; + } + } + + /** The name of the contact. */ + @Expose String name; + + /** The contact email address of the contact. */ + @Id @Expose String emailAddress; + + @Id @Expose public String registrarId; + + /** External email address of this contact used for registry lock confirmations. */ + String registryLockEmailAddress; + + /** The voice number of the contact. */ + @Expose String phoneNumber; + + /** The fax number of the contact. */ + @Expose String faxNumber; + + /** + * Multiple types are used to associate the registrar contact with various mailing groups. This + * data is internal to the registry. + */ + @Enumerated(EnumType.STRING) + @Expose + Set types; + + /** + * Whether this contact is publicly visible in WHOIS registrar query results as an Admin contact. + */ + @Column(nullable = false) + @Expose + boolean visibleInWhoisAsAdmin = false; + + /** + * Whether this contact is publicly visible in WHOIS registrar query results as a Technical + * contact. + */ + @Column(nullable = false) + @Expose + boolean visibleInWhoisAsTech = false; + + /** + * Whether this contact's phone number and email address is publicly visible in WHOIS domain query + * results as registrar abuse contact info. + */ + @Column(nullable = false) + @Expose + boolean visibleInDomainWhoisAsAbuse = false; + + /** + * Whether the contact is allowed to set their registry lock password through the registrar + * console. This will be set to false on contact creation and when the user sets a password. + */ + @Column(nullable = false) + boolean allowedToSetRegistryLockPassword = false; + + /** + * A hashed password that exists iff this contact is registry-lock-enabled. The hash is a base64 + * encoded SHA256 string. + */ + String registryLockPasswordHash; + + /** Randomly generated hash salt. */ + String registryLockPasswordSalt; + + /** + * Helper to update the contacts associated with a Registrar. This requires querying for the + * existing contacts, deleting existing contacts that are not part of the given {@code contacts} + * set, and then saving the given {@code contacts}. + * + *

IMPORTANT NOTE: If you call this method then it is your responsibility to also persist the + * relevant Registrar entity with the {@link Registrar#contactsRequireSyncing} field set to true. + */ + public static void updateContacts( + final Registrar registrar, final ImmutableSet contacts) { + ImmutableSet emailAddressesToKeep = + contacts.stream().map(RegistrarPoc::getEmailAddress).collect(toImmutableSet()); + tm().query( + "DELETE FROM RegistrarPoc WHERE registrarId = :registrarId AND " + + "emailAddress NOT IN :emailAddressesToKeep") + .setParameter("registrarId", registrar.getRegistrarId()) + .setParameter("emailAddressesToKeep", emailAddressesToKeep) + .executeUpdate(); + + tm().putAll(contacts); + } + + public String getName() { + return name; + } - @Id - @Access(AccessType.PROPERTY) - @Override public String getEmailAddress() { return emailAddress; } - @Id - @Access(AccessType.PROPERTY) - public String getRegistrarId() { - return registrarId; + public Optional getRegistryLockEmailAddress() { + return Optional.ofNullable(registryLockEmailAddress); + } + + public String getPhoneNumber() { + return phoneNumber; + } + + public String getFaxNumber() { + return faxNumber; + } + + public ImmutableSortedSet getTypes() { + return nullToEmptyImmutableSortedCopy(types); + } + + public boolean getVisibleInWhoisAsAdmin() { + return visibleInWhoisAsAdmin; + } + + public boolean getVisibleInWhoisAsTech() { + return visibleInWhoisAsTech; + } + + public boolean getVisibleInDomainWhoisAsAbuse() { + return visibleInDomainWhoisAsAbuse; + } + + public Builder asBuilder() { + return new Builder<>(clone(this)); + } + + public boolean isAllowedToSetRegistryLockPassword() { + return allowedToSetRegistryLockPassword; + } + + public boolean isRegistryLockAllowed() { + return !isNullOrEmpty(registryLockPasswordHash) && !isNullOrEmpty(registryLockPasswordSalt); + } + + public boolean verifyRegistryLockPassword(String registryLockPassword) { + if (isNullOrEmpty(registryLockPassword) + || isNullOrEmpty(registryLockPasswordSalt) + || isNullOrEmpty(registryLockPasswordHash)) { + return false; + } + return PasswordUtils.verifyPassword( + registryLockPassword, registryLockPasswordHash, registryLockPasswordSalt); + } + + /** + * Returns a string representation that's human friendly. + * + *

The output will look something like this: + * + *

{@code
+   * Some Person
+   * person@example.com
+   * Tel: +1.2125650666
+   * Types: [ADMIN, WHOIS]
+   * Visible in WHOIS as Admin contact: Yes
+   * Visible in WHOIS as Technical contact: No
+   * Registrar-Console access: Yes
+   * Login Email Address: person@registry.example
+   * }
+ */ + public String toStringMultilinePlainText() { + StringBuilder result = new StringBuilder(256); + result.append(getName()).append('\n'); + result.append(getEmailAddress()).append('\n'); + if (phoneNumber != null) { + result.append("Tel: ").append(getPhoneNumber()).append('\n'); + } + if (faxNumber != null) { + result.append("Fax: ").append(getFaxNumber()).append('\n'); + } + result.append("Types: ").append(getTypes()).append('\n'); + result + .append("Visible in registrar WHOIS query as Admin contact: ") + .append(getVisibleInWhoisAsAdmin() ? "Yes" : "No") + .append('\n'); + result + .append("Visible in registrar WHOIS query as Technical contact: ") + .append(getVisibleInWhoisAsTech() ? "Yes" : "No") + .append('\n'); + result + .append( + "Phone number and email visible in domain WHOIS query as " + + "Registrar Abuse contact info: ") + .append(getVisibleInDomainWhoisAsAbuse() ? "Yes" : "No") + .append('\n'); + return result.toString(); + } + + @Override + public Map toJsonMap() { + return new JsonMapBuilder() + .put("name", name) + .put("emailAddress", emailAddress) + .put("registryLockEmailAddress", registryLockEmailAddress) + .put("phoneNumber", phoneNumber) + .put("faxNumber", faxNumber) + .put("types", getTypes().stream().map(Object::toString).collect(joining(","))) + .put("visibleInWhoisAsAdmin", visibleInWhoisAsAdmin) + .put("visibleInWhoisAsTech", visibleInWhoisAsTech) + .put("visibleInDomainWhoisAsAbuse", visibleInDomainWhoisAsAbuse) + .put("allowedToSetRegistryLockPassword", allowedToSetRegistryLockPassword) + .put("registryLockAllowed", isRegistryLockAllowed()) + .build(); } @Override @@ -57,9 +303,124 @@ public class RegistrarPoc extends RegistrarPocBase { return VKey.create(RegistrarPoc.class, new RegistrarPocId(emailAddress, registrarId)); } - @Override - public Builder asBuilder() { - return new Builder(clone(this)); + /** + * These methods set the email address and registrar ID + * + *

This should only be used for restoring the fields of an object being loaded in a PostLoad + * method (effectively, when it is still under construction by Hibernate). In all other cases, the + * object should be regarded as immutable and changes should go through a Builder. + * + *

In addition to this special case use, this method must exist to satisfy Hibernate. + */ + public void setEmailAddress(String emailAddress) { + this.emailAddress = emailAddress; + } + + public void setRegistrarId(String registrarId) { + this.registrarId = registrarId; + } + + /** A builder for constructing a {@link RegistrarPoc}, since it is immutable. */ + public static class Builder> + extends GenericBuilder { + public Builder() {} + + protected Builder(T instance) { + super(instance); + } + + /** Build the registrar, nullifying empty fields. */ + @Override + public T build() { + checkNotNull(getInstance().registrarId, "Registrar ID cannot be null"); + checkValidEmail(getInstance().emailAddress); + // Check allowedToSetRegistryLockPassword here because if we want to allow the user to set + // a registry lock password, we must also set up the correct registry lock email concurrently + // or beforehand. + if (getInstance().allowedToSetRegistryLockPassword) { + checkArgument( + !isNullOrEmpty(getInstance().registryLockEmailAddress), + "Registry lock email must not be null if allowing registry lock access"); + } + return cloneEmptyToNull(super.build()); + } + + public B setName(String name) { + getInstance().name = name; + return thisCastToDerived(); + } + + public B setEmailAddress(String emailAddress) { + getInstance().emailAddress = emailAddress; + return thisCastToDerived(); + } + + public B setRegistryLockEmailAddress(@Nullable String registryLockEmailAddress) { + getInstance().registryLockEmailAddress = registryLockEmailAddress; + return thisCastToDerived(); + } + + public B setPhoneNumber(String phoneNumber) { + getInstance().phoneNumber = phoneNumber; + return thisCastToDerived(); + } + + public B setRegistrarId(String registrarId) { + getInstance().registrarId = registrarId; + return thisCastToDerived(); + } + + public B setRegistrar(Registrar registrar) { + getInstance().registrarId = registrar.getRegistrarId(); + return thisCastToDerived(); + } + + public B setFaxNumber(String faxNumber) { + getInstance().faxNumber = faxNumber; + return thisCastToDerived(); + } + + public B setTypes(Iterable types) { + getInstance().types = ImmutableSet.copyOf(types); + return thisCastToDerived(); + } + + public B setVisibleInWhoisAsAdmin(boolean visible) { + getInstance().visibleInWhoisAsAdmin = visible; + return thisCastToDerived(); + } + + public B setVisibleInWhoisAsTech(boolean visible) { + getInstance().visibleInWhoisAsTech = visible; + return thisCastToDerived(); + } + + public B setVisibleInDomainWhoisAsAbuse(boolean visible) { + getInstance().visibleInDomainWhoisAsAbuse = visible; + return thisCastToDerived(); + } + + public B setAllowedToSetRegistryLockPassword(boolean allowedToSetRegistryLockPassword) { + if (allowedToSetRegistryLockPassword) { + getInstance().registryLockPasswordSalt = null; + getInstance().registryLockPasswordHash = null; + } + getInstance().allowedToSetRegistryLockPassword = allowedToSetRegistryLockPassword; + return thisCastToDerived(); + } + + public B setRegistryLockPassword(String registryLockPassword) { + checkArgument( + getInstance().allowedToSetRegistryLockPassword, + "Not allowed to set registry lock password for this contact"); + checkArgument( + !isNullOrEmpty(registryLockPassword), "Registry lock password was null or empty"); + byte[] salt = SALT_SUPPLIER.get(); + getInstance().registryLockPasswordSalt = base64().encode(salt); + getInstance().registryLockPasswordHash = hashPassword(registryLockPassword, salt); + getInstance().allowedToSetRegistryLockPassword = false; + return thisCastToDerived(); + } } /** Class to represent the composite primary key for {@link RegistrarPoc} entity. */ @@ -90,13 +451,4 @@ public class RegistrarPoc extends RegistrarPocBase { return registrarId; } } - - public static class Builder extends RegistrarPocBase.Builder { - - public Builder() {} - - public Builder(RegistrarPoc registrarPoc) { - super(registrarPoc); - } - } } diff --git a/core/src/main/java/google/registry/model/registrar/RegistrarPocBase.java b/core/src/main/java/google/registry/model/registrar/RegistrarPocBase.java deleted file mode 100644 index f1e881134..000000000 --- a/core/src/main/java/google/registry/model/registrar/RegistrarPocBase.java +++ /dev/null @@ -1,425 +0,0 @@ -// 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.model.registrar; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Strings.isNullOrEmpty; -import static com.google.common.collect.ImmutableSet.toImmutableSet; -import static com.google.common.io.BaseEncoding.base64; -import static google.registry.model.registrar.RegistrarBase.checkValidEmail; -import static google.registry.persistence.transaction.TransactionManagerFactory.tm; -import static google.registry.util.CollectionUtils.nullToEmptyImmutableSortedCopy; -import static google.registry.util.PasswordUtils.SALT_SUPPLIER; -import static google.registry.util.PasswordUtils.hashPassword; -import static java.util.stream.Collectors.joining; - -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSortedSet; -import com.google.gson.annotations.Expose; -import google.registry.model.Buildable.GenericBuilder; -import google.registry.model.ImmutableObject; -import google.registry.model.JsonMapBuilder; -import google.registry.model.Jsonifiable; -import google.registry.model.UnsafeSerializable; -import google.registry.util.PasswordUtils; -import jakarta.persistence.Access; -import jakarta.persistence.AccessType; -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.Id; -import jakarta.persistence.MappedSuperclass; -import jakarta.persistence.Transient; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import javax.annotation.Nullable; - -/** - * A contact for a Registrar. Note, equality, hashCode and comparable have been overridden to only - * enable key equality. - * - *

IMPORTANT NOTE: Any time that you change, update, or delete RegistrarContact entities, you - * *MUST* also modify the persisted Registrar entity with {@link Registrar#contactsRequireSyncing} - * set to true. - * - *

This class deliberately does not include an {@link Id} so that any foreign-keyed fields can - * refer to the proper parent entity's ID, whether we're storing this in the DB itself or as part of - * another entity. - */ -@Access(AccessType.FIELD) -@Embeddable -@MappedSuperclass -public class RegistrarPocBase extends ImmutableObject implements Jsonifiable, UnsafeSerializable { - /** - * Registrar contacts types for partner communication tracking. - * - *

Note: These types only matter to the registry. They are not meant to be used for - * WHOIS or RDAP results. - */ - public enum Type { - ABUSE("abuse", true), - ADMIN("primary", true), - BILLING("billing", true), - LEGAL("legal", true), - MARKETING("marketing", false), - TECH("technical", true), - WHOIS("whois-inquiry", true); - - private final String displayName; - - private final boolean required; - - public String getDisplayName() { - return displayName; - } - - public boolean isRequired() { - return required; - } - - Type(String display, boolean required) { - displayName = display; - this.required = required; - } - } - - /** The name of the contact. */ - @Expose String name; - - /** The contact email address of the contact. */ - @Expose @Transient String emailAddress; - - @Expose @Transient public String registrarId; - - /** External email address of this contact used for registry lock confirmations. */ - String registryLockEmailAddress; - - /** The voice number of the contact. */ - @Expose String phoneNumber; - - /** The fax number of the contact. */ - @Expose String faxNumber; - - /** - * Multiple types are used to associate the registrar contact with various mailing groups. This - * data is internal to the registry. - */ - @Enumerated(EnumType.STRING) - @Expose - Set types; - - /** - * Whether this contact is publicly visible in WHOIS registrar query results as an Admin contact. - */ - @Column(nullable = false) - @Expose - boolean visibleInWhoisAsAdmin = false; - - /** - * Whether this contact is publicly visible in WHOIS registrar query results as a Technical - * contact. - */ - @Column(nullable = false) - @Expose - boolean visibleInWhoisAsTech = false; - - /** - * Whether this contact's phone number and email address is publicly visible in WHOIS domain query - * results as registrar abuse contact info. - */ - @Column(nullable = false) - @Expose - boolean visibleInDomainWhoisAsAbuse = false; - - /** - * Whether the contact is allowed to set their registry lock password through the registrar - * console. This will be set to false on contact creation and when the user sets a password. - */ - @Column(nullable = false) - boolean allowedToSetRegistryLockPassword = false; - - /** - * A hashed password that exists iff this contact is registry-lock-enabled. The hash is a base64 - * encoded SHA256 string. - */ - String registryLockPasswordHash; - - /** Randomly generated hash salt. */ - String registryLockPasswordSalt; - - /** - * Helper to update the contacts associated with a Registrar. This requires querying for the - * existing contacts, deleting existing contacts that are not part of the given {@code contacts} - * set, and then saving the given {@code contacts}. - * - *

IMPORTANT NOTE: If you call this method then it is your responsibility to also persist the - * relevant Registrar entity with the {@link Registrar#contactsRequireSyncing} field set to true. - */ - public static void updateContacts( - final Registrar registrar, final ImmutableSet contacts) { - ImmutableSet emailAddressesToKeep = - contacts.stream().map(RegistrarPoc::getEmailAddress).collect(toImmutableSet()); - tm().query( - "DELETE FROM RegistrarPoc WHERE registrarId = :registrarId AND " - + "emailAddress NOT IN :emailAddressesToKeep") - .setParameter("registrarId", registrar.getRegistrarId()) - .setParameter("emailAddressesToKeep", emailAddressesToKeep) - .executeUpdate(); - - tm().putAll(contacts); - } - - public String getName() { - return name; - } - - public String getEmailAddress() { - return emailAddress; - } - - public Optional getRegistryLockEmailAddress() { - return Optional.ofNullable(registryLockEmailAddress); - } - - public String getPhoneNumber() { - return phoneNumber; - } - - public String getFaxNumber() { - return faxNumber; - } - - public ImmutableSortedSet getTypes() { - return nullToEmptyImmutableSortedCopy(types); - } - - public boolean getVisibleInWhoisAsAdmin() { - return visibleInWhoisAsAdmin; - } - - public boolean getVisibleInWhoisAsTech() { - return visibleInWhoisAsTech; - } - - public boolean getVisibleInDomainWhoisAsAbuse() { - return visibleInDomainWhoisAsAbuse; - } - - public Builder asBuilder() { - return new Builder<>(clone(this)); - } - - public boolean isAllowedToSetRegistryLockPassword() { - return allowedToSetRegistryLockPassword; - } - - public boolean isRegistryLockAllowed() { - return !isNullOrEmpty(registryLockPasswordHash) && !isNullOrEmpty(registryLockPasswordSalt); - } - - public boolean verifyRegistryLockPassword(String registryLockPassword) { - if (isNullOrEmpty(registryLockPassword) - || isNullOrEmpty(registryLockPasswordSalt) - || isNullOrEmpty(registryLockPasswordHash)) { - return false; - } - return PasswordUtils.verifyPassword( - registryLockPassword, registryLockPasswordHash, registryLockPasswordSalt); - } - - /** - * Returns a string representation that's human friendly. - * - *

The output will look something like this: - * - *

{@code
-   * Some Person
-   * person@example.com
-   * Tel: +1.2125650666
-   * Types: [ADMIN, WHOIS]
-   * Visible in WHOIS as Admin contact: Yes
-   * Visible in WHOIS as Technical contact: No
-   * Registrar-Console access: Yes
-   * Login Email Address: person@registry.example
-   * }
- */ - public String toStringMultilinePlainText() { - StringBuilder result = new StringBuilder(256); - result.append(getName()).append('\n'); - result.append(getEmailAddress()).append('\n'); - if (phoneNumber != null) { - result.append("Tel: ").append(getPhoneNumber()).append('\n'); - } - if (faxNumber != null) { - result.append("Fax: ").append(getFaxNumber()).append('\n'); - } - result.append("Types: ").append(getTypes()).append('\n'); - result - .append("Visible in registrar WHOIS query as Admin contact: ") - .append(getVisibleInWhoisAsAdmin() ? "Yes" : "No") - .append('\n'); - result - .append("Visible in registrar WHOIS query as Technical contact: ") - .append(getVisibleInWhoisAsTech() ? "Yes" : "No") - .append('\n'); - result - .append( - "Phone number and email visible in domain WHOIS query as " - + "Registrar Abuse contact info: ") - .append(getVisibleInDomainWhoisAsAbuse() ? "Yes" : "No") - .append('\n'); - return result.toString(); - } - - @Override - public Map toJsonMap() { - return new JsonMapBuilder() - .put("name", name) - .put("emailAddress", emailAddress) - .put("registryLockEmailAddress", registryLockEmailAddress) - .put("phoneNumber", phoneNumber) - .put("faxNumber", faxNumber) - .put("types", getTypes().stream().map(Object::toString).collect(joining(","))) - .put("visibleInWhoisAsAdmin", visibleInWhoisAsAdmin) - .put("visibleInWhoisAsTech", visibleInWhoisAsTech) - .put("visibleInDomainWhoisAsAbuse", visibleInDomainWhoisAsAbuse) - .put("allowedToSetRegistryLockPassword", allowedToSetRegistryLockPassword) - .put("registryLockAllowed", isRegistryLockAllowed()) - .build(); - } - - /** - * These methods set the email address and registrar ID - * - *

This should only be used for restoring the fields of an object being loaded in a PostLoad - * method (effectively, when it is still under construction by Hibernate). In all other cases, the - * object should be regarded as immutable and changes should go through a Builder. - * - *

In addition to this special case use, this method must exist to satisfy Hibernate. - */ - public void setEmailAddress(String emailAddress) { - this.emailAddress = emailAddress; - } - - public void setRegistrarId(String registrarId) { - this.registrarId = registrarId; - } - - /** A builder for constructing a {@link RegistrarPoc}, since it is immutable. */ - public static class Builder> - extends GenericBuilder { - public Builder() {} - - protected Builder(T instance) { - super(instance); - } - - /** Build the registrar, nullifying empty fields. */ - @Override - public T build() { - checkNotNull(getInstance().registrarId, "Registrar ID cannot be null"); - checkValidEmail(getInstance().emailAddress); - // Check allowedToSetRegistryLockPassword here because if we want to allow the user to set - // a registry lock password, we must also set up the correct registry lock email concurrently - // or beforehand. - if (getInstance().allowedToSetRegistryLockPassword) { - checkArgument( - !isNullOrEmpty(getInstance().registryLockEmailAddress), - "Registry lock email must not be null if allowing registry lock access"); - } - return cloneEmptyToNull(super.build()); - } - - public B setName(String name) { - getInstance().name = name; - return thisCastToDerived(); - } - - public B setEmailAddress(String emailAddress) { - getInstance().emailAddress = emailAddress; - return thisCastToDerived(); - } - - public B setRegistryLockEmailAddress(@Nullable String registryLockEmailAddress) { - getInstance().registryLockEmailAddress = registryLockEmailAddress; - return thisCastToDerived(); - } - - public B setPhoneNumber(String phoneNumber) { - getInstance().phoneNumber = phoneNumber; - return thisCastToDerived(); - } - - public B setRegistrarId(String registrarId) { - getInstance().registrarId = registrarId; - return thisCastToDerived(); - } - - public B setRegistrar(Registrar registrar) { - getInstance().registrarId = registrar.getRegistrarId(); - return thisCastToDerived(); - } - - public B setFaxNumber(String faxNumber) { - getInstance().faxNumber = faxNumber; - return thisCastToDerived(); - } - - public B setTypes(Iterable types) { - getInstance().types = ImmutableSet.copyOf(types); - return thisCastToDerived(); - } - - public B setVisibleInWhoisAsAdmin(boolean visible) { - getInstance().visibleInWhoisAsAdmin = visible; - return thisCastToDerived(); - } - - public B setVisibleInWhoisAsTech(boolean visible) { - getInstance().visibleInWhoisAsTech = visible; - return thisCastToDerived(); - } - - public B setVisibleInDomainWhoisAsAbuse(boolean visible) { - getInstance().visibleInDomainWhoisAsAbuse = visible; - return thisCastToDerived(); - } - - public B setAllowedToSetRegistryLockPassword(boolean allowedToSetRegistryLockPassword) { - if (allowedToSetRegistryLockPassword) { - getInstance().registryLockPasswordSalt = null; - getInstance().registryLockPasswordHash = null; - } - getInstance().allowedToSetRegistryLockPassword = allowedToSetRegistryLockPassword; - return thisCastToDerived(); - } - - public B setRegistryLockPassword(String registryLockPassword) { - checkArgument( - getInstance().allowedToSetRegistryLockPassword, - "Not allowed to set registry lock password for this contact"); - checkArgument( - !isNullOrEmpty(registryLockPassword), "Registry lock password was null or empty"); - byte[] salt = SALT_SUPPLIER.get(); - getInstance().registryLockPasswordSalt = base64().encode(salt); - getInstance().registryLockPasswordHash = hashPassword(registryLockPassword, salt); - getInstance().allowedToSetRegistryLockPassword = false; - return thisCastToDerived(); - } - } -} diff --git a/core/src/main/java/google/registry/rde/RegistrarToXjcConverter.java b/core/src/main/java/google/registry/rde/RegistrarToXjcConverter.java index 16c22c328..f19104e10 100644 --- a/core/src/main/java/google/registry/rde/RegistrarToXjcConverter.java +++ b/core/src/main/java/google/registry/rde/RegistrarToXjcConverter.java @@ -19,9 +19,8 @@ import static com.google.common.base.Preconditions.checkState; import com.google.common.collect.ImmutableMap; import google.registry.model.registrar.Registrar; +import google.registry.model.registrar.Registrar.State; import google.registry.model.registrar.RegistrarAddress; -import google.registry.model.registrar.RegistrarBase; -import google.registry.model.registrar.RegistrarBase.State; import google.registry.xjc.contact.XjcContactE164Type; import google.registry.xjc.rderegistrar.XjcRdeRegistrar; import google.registry.xjc.rderegistrar.XjcRdeRegistrarAddrType; @@ -41,7 +40,7 @@ final class RegistrarToXjcConverter { private static final String UNKNOWN_CC = "US"; /** A conversion map between internal Registrar states and external RDE states. */ - private static final ImmutableMap + private static final ImmutableMap REGISTRAR_STATUS_CONVERSIONS = ImmutableMap.of( State.ACTIVE, XjcRdeRegistrarStatusType.OK, diff --git a/core/src/main/java/google/registry/request/auth/AuthenticatedRegistrarAccessor.java b/core/src/main/java/google/registry/request/auth/AuthenticatedRegistrarAccessor.java index e686aa05e..4b742d90f 100644 --- a/core/src/main/java/google/registry/request/auth/AuthenticatedRegistrarAccessor.java +++ b/core/src/main/java/google/registry/request/auth/AuthenticatedRegistrarAccessor.java @@ -27,7 +27,7 @@ import google.registry.config.RegistryConfig.Config; import google.registry.groups.GroupsConnection; import google.registry.model.console.User; import google.registry.model.registrar.Registrar; -import google.registry.model.registrar.RegistrarBase.State; +import google.registry.model.registrar.Registrar.State; import jakarta.inject.Inject; import java.util.Optional; import javax.annotation.concurrent.Immutable; diff --git a/core/src/main/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java b/core/src/main/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java index 5451c9103..db52912de 100644 --- a/core/src/main/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java +++ b/core/src/main/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java @@ -30,7 +30,6 @@ import com.google.common.collect.ImmutableSet; import google.registry.flows.certs.CertificateChecker; import google.registry.model.registrar.Registrar; import google.registry.model.registrar.RegistrarAddress; -import google.registry.model.registrar.RegistrarBase; import google.registry.tools.params.KeyValueMapParameter.CurrencyUnitToStringMap; import google.registry.tools.params.OptionalLongParameter; import google.registry.tools.params.OptionalPhoneNumberParameter; @@ -61,11 +60,11 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand { List mainParameters; @Parameter(names = "--registrar_type", description = "Type of the registrar") - RegistrarBase.Type registrarType; + Registrar.Type registrarType; @Nullable @Parameter(names = "--registrar_state", description = "Initial state of the registrar") - RegistrarBase.State registrarState; + Registrar.State registrarState; @Parameter( names = "--allowed_tlds", diff --git a/core/src/main/java/google/registry/tools/CreateRegistrarCommand.java b/core/src/main/java/google/registry/tools/CreateRegistrarCommand.java index c86ce867e..e5c1cf885 100644 --- a/core/src/main/java/google/registry/tools/CreateRegistrarCommand.java +++ b/core/src/main/java/google/registry/tools/CreateRegistrarCommand.java @@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Strings.emptyToNull; import static com.google.common.collect.Iterables.getOnlyElement; -import static google.registry.model.registrar.RegistrarBase.State.ACTIVE; +import static google.registry.model.registrar.Registrar.State.ACTIVE; import static google.registry.tools.RegistryToolEnvironment.PRODUCTION; import static google.registry.tools.RegistryToolEnvironment.SANDBOX; import static google.registry.tools.RegistryToolEnvironment.UNITTEST; diff --git a/core/src/main/java/google/registry/tools/RegistrarPocCommand.java b/core/src/main/java/google/registry/tools/RegistrarPocCommand.java index 37b7b5d60..f095523d8 100644 --- a/core/src/main/java/google/registry/tools/RegistrarPocCommand.java +++ b/core/src/main/java/google/registry/tools/RegistrarPocCommand.java @@ -29,7 +29,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import google.registry.model.registrar.Registrar; import google.registry.model.registrar.RegistrarPoc; -import google.registry.model.registrar.RegistrarPocBase; import google.registry.tools.params.OptionalPhoneNumberParameter; import google.registry.tools.params.PathParameter; import google.registry.tools.params.StringListParameter; @@ -153,7 +152,7 @@ final class RegistrarPocCommand extends MutatingCommand { private static final ImmutableSet MODES_REQUIRING_CONTACT_SYNC = ImmutableSet.of(Mode.CREATE, Mode.UPDATE, Mode.DELETE); - @Nullable private ImmutableSet contactTypes; + @Nullable private ImmutableSet contactTypes; @Override protected void init() throws Exception { @@ -169,7 +168,7 @@ final class RegistrarPocCommand extends MutatingCommand { } else { contactTypes = contactTypeNames.stream() - .map(Enums.stringConverter(RegistrarPocBase.Type.class)) + .map(Enums.stringConverter(RegistrarPoc.Type.class)) .collect(toImmutableSet()); } ImmutableSet contacts = registrar.getContacts(); diff --git a/core/src/main/java/google/registry/tools/server/CreateGroupsAction.java b/core/src/main/java/google/registry/tools/server/CreateGroupsAction.java index 865e7fc2d..0f4e27eed 100644 --- a/core/src/main/java/google/registry/tools/server/CreateGroupsAction.java +++ b/core/src/main/java/google/registry/tools/server/CreateGroupsAction.java @@ -24,7 +24,7 @@ import google.registry.config.RegistryConfig.Config; import google.registry.groups.GroupsConnection; import google.registry.groups.GroupsConnection.Role; import google.registry.model.registrar.Registrar; -import google.registry.model.registrar.RegistrarPocBase; +import google.registry.model.registrar.RegistrarPoc; import google.registry.request.Action; import google.registry.request.Action.GaeService; import google.registry.request.HttpException.BadRequestException; @@ -65,7 +65,7 @@ public class CreateGroupsAction implements Runnable { if (registrar == null) { return; } - List types = asList(RegistrarPocBase.Type.values()); + List types = asList(RegistrarPoc.Type.values()); // Concurrently create the groups for each RegistrarContact.Type, collecting the results from // each call (which are either an Exception if it failed, or absent() if it succeeded). List> results = diff --git a/core/src/main/java/google/registry/ui/server/RegistrarFormFields.java b/core/src/main/java/google/registry/ui/server/RegistrarFormFields.java index 5759d5c5f..15e292b60 100644 --- a/core/src/main/java/google/registry/ui/server/RegistrarFormFields.java +++ b/core/src/main/java/google/registry/ui/server/RegistrarFormFields.java @@ -29,7 +29,6 @@ import com.google.re2j.Pattern; import google.registry.model.registrar.Registrar; import google.registry.model.registrar.RegistrarAddress; import google.registry.model.registrar.RegistrarPoc; -import google.registry.model.registrar.RegistrarPocBase; import google.registry.ui.forms.FormException; import google.registry.ui.forms.FormField; import google.registry.ui.forms.FormFieldException; @@ -201,10 +200,10 @@ public final class RegistrarFormFields { public static final FormField CONTACT_REGISTRY_LOCK_PASSWORD_FIELD = FormFields.NAME.asBuilderNamed("registryLockPassword").build(); - public static final FormField> CONTACT_TYPES = + public static final FormField> CONTACT_TYPES = FormField.named("types") .uppercased() - .asEnum(RegistrarPocBase.Type.class) + .asEnum(RegistrarPoc.Type.class) .asSet(Splitter.on(',').omitEmptyStrings().trimResults()) .build(); diff --git a/core/src/main/java/google/registry/ui/server/console/ConsoleApiAction.java b/core/src/main/java/google/registry/ui/server/console/ConsoleApiAction.java index 969c8f9b4..c244adca4 100644 --- a/core/src/main/java/google/registry/ui/server/console/ConsoleApiAction.java +++ b/core/src/main/java/google/registry/ui/server/console/ConsoleApiAction.java @@ -37,11 +37,10 @@ import google.registry.batch.CloudTasksUtils; import google.registry.config.RegistryConfig; import google.registry.export.sheet.SyncRegistrarsSheetAction; import google.registry.model.console.ConsolePermission; -import google.registry.model.console.SimpleConsoleUpdateHistory; +import google.registry.model.console.ConsoleUpdateHistory; import google.registry.model.console.User; import google.registry.model.registrar.Registrar; import google.registry.model.registrar.RegistrarPoc; -import google.registry.model.registrar.RegistrarPocBase; import google.registry.request.HttpException; import google.registry.security.XsrfTokenManager; import google.registry.util.DiffUtils; @@ -218,7 +217,7 @@ public abstract class ConsoleApiAction implements Runnable { consoleApiParams.authResult().userIdForLogging(), DiffUtils.prettyPrintDiffedMap(diffs, null)), contacts.stream() - .filter(c -> c.getTypes().contains(RegistrarPocBase.Type.ADMIN)) + .filter(c -> c.getTypes().contains(RegistrarPoc.Type.ADMIN)) .map(RegistrarPoc::getEmailAddress) .collect(toImmutableList())); } @@ -262,7 +261,7 @@ public abstract class ConsoleApiAction implements Runnable { } } - protected void finishAndPersistConsoleUpdateHistory(SimpleConsoleUpdateHistory.Builder builder) { + protected void finishAndPersistConsoleUpdateHistory(ConsoleUpdateHistory.Builder builder) { builder.setActingUser(consoleApiParams.authResult().user().get()); builder.setUrl(consoleApiParams.request().getRequestURI()); builder.setMethod(consoleApiParams.request().getMethod()); diff --git a/core/src/main/java/google/registry/ui/server/console/ConsoleEppPasswordAction.java b/core/src/main/java/google/registry/ui/server/console/ConsoleEppPasswordAction.java index 9817886d5..bcdefd7e4 100644 --- a/core/src/main/java/google/registry/ui/server/console/ConsoleEppPasswordAction.java +++ b/core/src/main/java/google/registry/ui/server/console/ConsoleEppPasswordAction.java @@ -28,7 +28,6 @@ import com.google.gson.annotations.Expose; import google.registry.flows.EppException.AuthenticationErrorException; import google.registry.flows.PasswordOnlyTransportCredentials; import google.registry.model.console.ConsoleUpdateHistory; -import google.registry.model.console.SimpleConsoleUpdateHistory; import google.registry.model.console.User; import google.registry.model.registrar.Registrar; import google.registry.request.Action; @@ -108,7 +107,7 @@ public class ConsoleEppPasswordAction extends ConsoleApiAction { registrar.asBuilder().setPassword(eppRequestBody.newPassword()).build(); tm().put(updatedRegistrar); finishAndPersistConsoleUpdateHistory( - new SimpleConsoleUpdateHistory.Builder() + new ConsoleUpdateHistory.Builder() .setType(ConsoleUpdateHistory.Type.EPP_PASSWORD_UPDATE) .setDescription(registrar.getRegistrarId())); sendExternalUpdates( diff --git a/core/src/main/java/google/registry/ui/server/console/ConsoleOteAction.java b/core/src/main/java/google/registry/ui/server/console/ConsoleOteAction.java index ca1a2735b..057daf84e 100644 --- a/core/src/main/java/google/registry/ui/server/console/ConsoleOteAction.java +++ b/core/src/main/java/google/registry/ui/server/console/ConsoleOteAction.java @@ -35,7 +35,6 @@ import google.registry.model.OteStats.StatType; import google.registry.model.console.ConsolePermission; import google.registry.model.console.User; import google.registry.model.registrar.Registrar; -import google.registry.model.registrar.RegistrarBase; import google.registry.request.Action; import google.registry.request.Action.GkeService; import google.registry.request.Parameter; @@ -140,7 +139,7 @@ public class ConsoleOteAction extends ConsoleApiAction { SC_BAD_REQUEST); return; } - if (!RegistrarBase.Type.OTE.equals(registrar.get().getType())) { + if (!Registrar.Type.OTE.equals(registrar.get().getType())) { setFailedResponse( String.format("Registrar with ID %s is not an OT&E registrar", registrarId), SC_BAD_REQUEST); 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 476c9aa9c..413625d0d 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 @@ -24,7 +24,6 @@ import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; import google.registry.model.console.ConsolePermission; import google.registry.model.console.ConsoleUpdateHistory; -import google.registry.model.console.SimpleConsoleUpdateHistory; import google.registry.model.console.User; import google.registry.model.registrar.Registrar; import google.registry.request.Action; @@ -102,7 +101,7 @@ public class ConsoleUpdateRegistrarAction extends ConsoleApiAction { tm().put(updatedRegistrar); finishAndPersistConsoleUpdateHistory( - new SimpleConsoleUpdateHistory.Builder() + new ConsoleUpdateHistory.Builder() .setType(ConsoleUpdateHistory.Type.REGISTRAR_UPDATE) .setDescription(updatedRegistrar.getRegistrarId())); sendExternalUpdatesIfNecessary( diff --git a/core/src/main/java/google/registry/ui/server/console/RegistrarsAction.java b/core/src/main/java/google/registry/ui/server/console/RegistrarsAction.java index f0772dc2b..32d4b4e99 100644 --- a/core/src/main/java/google/registry/ui/server/console/RegistrarsAction.java +++ b/core/src/main/java/google/registry/ui/server/console/RegistrarsAction.java @@ -28,11 +28,9 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Streams; import google.registry.model.console.ConsolePermission; import google.registry.model.console.ConsoleUpdateHistory; -import google.registry.model.console.SimpleConsoleUpdateHistory; import google.registry.model.console.User; import google.registry.model.registrar.Registrar; -import google.registry.model.registrar.RegistrarBase; -import google.registry.model.registrar.RegistrarBase.State; +import google.registry.model.registrar.Registrar.State; import google.registry.model.registrar.RegistrarPoc; import google.registry.request.Action; import google.registry.request.Action.GaeService; @@ -55,8 +53,8 @@ import java.util.Optional; public class RegistrarsAction extends ConsoleApiAction { private static final int PASSWORD_LENGTH = 16; private static final int PASSCODE_LENGTH = 5; - private static final ImmutableList allowedRegistrarTypes = - ImmutableList.of(Registrar.Type.REAL, RegistrarBase.Type.OTE); + private static final ImmutableList allowedRegistrarTypes = + ImmutableList.of(Registrar.Type.REAL, Registrar.Type.OTE); private static final String SQL_TEMPLATE = """ SELECT * FROM "Registrar" @@ -174,7 +172,7 @@ public class RegistrarsAction extends ConsoleApiAction { registrar.getRegistrarId()); tm().putAll(registrar, contact); finishAndPersistConsoleUpdateHistory( - new SimpleConsoleUpdateHistory.Builder() + new ConsoleUpdateHistory.Builder() .setType(ConsoleUpdateHistory.Type.REGISTRAR_CREATE) .setDescription(registrar.getRegistrarId())); }); diff --git a/core/src/main/java/google/registry/ui/server/console/domains/ConsoleBulkDomainAction.java b/core/src/main/java/google/registry/ui/server/console/domains/ConsoleBulkDomainAction.java index 199918d96..b20f33f5f 100644 --- a/core/src/main/java/google/registry/ui/server/console/domains/ConsoleBulkDomainAction.java +++ b/core/src/main/java/google/registry/ui/server/console/domains/ConsoleBulkDomainAction.java @@ -26,7 +26,7 @@ import google.registry.flows.EppController; import google.registry.flows.EppRequestSource; import google.registry.flows.PasswordOnlyTransportCredentials; import google.registry.flows.StatelessRequestSessionMetadata; -import google.registry.model.console.SimpleConsoleUpdateHistory; +import google.registry.model.console.ConsoleUpdateHistory; import google.registry.model.console.User; import google.registry.model.eppcommon.ProtocolDefinition; import google.registry.model.eppoutput.EppOutput; @@ -113,7 +113,7 @@ public class ConsoleBulkDomainAction extends ConsoleApiAction { .forEach( e -> finishAndPersistConsoleUpdateHistory( - new SimpleConsoleUpdateHistory.Builder() + new ConsoleUpdateHistory.Builder() .setDescription(e.getKey()) .setType(actionType.getConsoleUpdateHistoryType())))); } diff --git a/core/src/main/java/google/registry/ui/server/console/settings/ContactAction.java b/core/src/main/java/google/registry/ui/server/console/settings/ContactAction.java index 1bc1ec8e3..83336f3bb 100644 --- a/core/src/main/java/google/registry/ui/server/console/settings/ContactAction.java +++ b/core/src/main/java/google/registry/ui/server/console/settings/ContactAction.java @@ -32,7 +32,7 @@ import google.registry.model.console.ConsolePermission; import google.registry.model.console.User; import google.registry.model.registrar.Registrar; import google.registry.model.registrar.RegistrarPoc; -import google.registry.model.registrar.RegistrarPocBase.Type; +import google.registry.model.registrar.RegistrarPoc.Type; import google.registry.persistence.transaction.QueryComposer.Comparator; import google.registry.request.Action; import google.registry.request.Action.GaeService; diff --git a/core/src/main/java/google/registry/ui/server/console/settings/RdapRegistrarFieldsAction.java b/core/src/main/java/google/registry/ui/server/console/settings/RdapRegistrarFieldsAction.java index d6a69cb4e..7a211624b 100644 --- a/core/src/main/java/google/registry/ui/server/console/settings/RdapRegistrarFieldsAction.java +++ b/core/src/main/java/google/registry/ui/server/console/settings/RdapRegistrarFieldsAction.java @@ -22,7 +22,6 @@ import static jakarta.servlet.http.HttpServletResponse.SC_OK; import google.registry.model.console.ConsolePermission; import google.registry.model.console.ConsoleUpdateHistory; -import google.registry.model.console.SimpleConsoleUpdateHistory; import google.registry.model.console.User; import google.registry.model.registrar.Registrar; import google.registry.request.Action; @@ -93,7 +92,7 @@ public class RdapRegistrarFieldsAction extends ConsoleApiAction { .build(); tm().put(newRegistrar); finishAndPersistConsoleUpdateHistory( - new SimpleConsoleUpdateHistory.Builder() + new ConsoleUpdateHistory.Builder() .setType(ConsoleUpdateHistory.Type.REGISTRAR_UPDATE) .setDescription(newRegistrar.getRegistrarId())); sendExternalUpdatesIfNecessary( diff --git a/core/src/main/java/google/registry/ui/server/console/settings/SecurityAction.java b/core/src/main/java/google/registry/ui/server/console/settings/SecurityAction.java index 6f23f7dc7..742314248 100644 --- a/core/src/main/java/google/registry/ui/server/console/settings/SecurityAction.java +++ b/core/src/main/java/google/registry/ui/server/console/settings/SecurityAction.java @@ -26,7 +26,6 @@ import google.registry.flows.certs.CertificateChecker; import google.registry.flows.certs.CertificateChecker.InsecureCertificateException; import google.registry.model.console.ConsolePermission; import google.registry.model.console.ConsoleUpdateHistory; -import google.registry.model.console.SimpleConsoleUpdateHistory; import google.registry.model.console.User; import google.registry.model.registrar.Registrar; import google.registry.request.Action; @@ -120,7 +119,7 @@ public class SecurityAction extends ConsoleApiAction { Registrar updatedRegistrar = updatedRegistrarBuilder.build(); tm().put(updatedRegistrar); finishAndPersistConsoleUpdateHistory( - new SimpleConsoleUpdateHistory.Builder() + new ConsoleUpdateHistory.Builder() .setType(ConsoleUpdateHistory.Type.REGISTRAR_SECURITY_UPDATE) .setDescription(registrarId)); diff --git a/core/src/main/resources/META-INF/persistence.xml b/core/src/main/resources/META-INF/persistence.xml index ee8777230..68bc8679d 100644 --- a/core/src/main/resources/META-INF/persistence.xml +++ b/core/src/main/resources/META-INF/persistence.xml @@ -47,12 +47,8 @@ google.registry.model.billing.BillingRecurrence google.registry.model.common.Cursor google.registry.model.common.DnsRefreshRequest - google.registry.model.console.ConsoleEppActionHistory - google.registry.model.console.RegistrarPocUpdateHistory - google.registry.model.console.RegistrarUpdateHistory - google.registry.model.console.SimpleConsoleUpdateHistory + google.registry.model.console.ConsoleUpdateHistory google.registry.model.console.User - google.registry.model.console.UserUpdateHistory google.registry.model.contact.ContactHistory google.registry.model.contact.Contact google.registry.model.domain.Domain diff --git a/core/src/test/java/google/registry/batch/SendExpiringCertificateNotificationEmailActionTest.java b/core/src/test/java/google/registry/batch/SendExpiringCertificateNotificationEmailActionTest.java index 1a0355be4..ef513a2f6 100644 --- a/core/src/test/java/google/registry/batch/SendExpiringCertificateNotificationEmailActionTest.java +++ b/core/src/test/java/google/registry/batch/SendExpiringCertificateNotificationEmailActionTest.java @@ -36,8 +36,7 @@ import google.registry.groups.GmailClient; import google.registry.model.registrar.Registrar; import google.registry.model.registrar.RegistrarAddress; import google.registry.model.registrar.RegistrarPoc; -import google.registry.model.registrar.RegistrarPocBase; -import google.registry.model.registrar.RegistrarPocBase.Type; +import google.registry.model.registrar.RegistrarPoc.Type; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension; import google.registry.testing.FakeClock; @@ -57,13 +56,13 @@ class SendExpiringCertificateNotificationEmailActionTest { private static final String EXPIRATION_WARNING_EMAIL_BODY_TEXT = """ - Dear %1$s, + Dear %1$s, - We would like to inform you that your %2$s certificate will expire at %3$s. - Kind update your account using the following steps: - 1. Navigate to support and login using your %4$s@registry.example credentials. - 2. Click Settings -> Privacy on the top left corner. - 3. Click Edit and enter certificate string. 3. Click SaveRegards,Example Registry"""; + We would like to inform you that your %2$s certificate will expire at %3$s. + Kind update your account using the following steps: + 1. Navigate to support and login using your %4$s@registry.example credentials. + 2. Click Settings -> Privacy on the top left corner. + 3. Click Edit and enter certificate string. 3. Click SaveRegards,Example Registry"""; private static final String EXPIRATION_WARNING_EMAIL_SUBJECT_TEXT = "Expiration Warning Email"; @@ -220,7 +219,7 @@ class SendExpiringCertificateNotificationEmailActionTest { .setEmailAddress("will@example-registrar.tld") .setPhoneNumber("+1.3105551213") .setFaxNumber("+1.3105551213") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.TECH)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.TECH)) .setVisibleInWhoisAsAdmin(true) .setVisibleInWhoisAsTech(false) .build()); @@ -510,7 +509,7 @@ class SendExpiringCertificateNotificationEmailActionTest { .setEmailAddress("jd@example-registrar.tld") .setPhoneNumber("+1.3105551213") .setFaxNumber("+1.3105551213") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.TECH)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.TECH)) .setVisibleInWhoisAsAdmin(true) .setVisibleInWhoisAsTech(false) .build(), @@ -520,7 +519,7 @@ class SendExpiringCertificateNotificationEmailActionTest { .setEmailAddress("js@example-registrar.tld") .setPhoneNumber("+1.1111111111") .setFaxNumber("+1.1111111111") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.TECH)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.TECH)) .build(), new RegistrarPoc.Builder() .setRegistrar(registrar) @@ -528,7 +527,7 @@ class SendExpiringCertificateNotificationEmailActionTest { .setEmailAddress("will@example-registrar.tld") .setPhoneNumber("+1.3105551213") .setFaxNumber("+1.3105551213") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.TECH)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.TECH)) .setVisibleInWhoisAsAdmin(true) .setVisibleInWhoisAsTech(false) .build(), @@ -538,7 +537,7 @@ class SendExpiringCertificateNotificationEmailActionTest { .setEmailAddress("mike@example-registrar.tld") .setPhoneNumber("+1.1111111111") .setFaxNumber("+1.1111111111") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.ADMIN)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.ADMIN)) .build(), new RegistrarPoc.Builder() .setRegistrar(registrar) @@ -546,7 +545,7 @@ class SendExpiringCertificateNotificationEmailActionTest { .setEmailAddress("john@example-registrar.tld") .setPhoneNumber("+1.3105551215") .setFaxNumber("+1.3105551216") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.ADMIN)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.ADMIN)) .setVisibleInWhoisAsTech(true) .build()); persistSimpleResources(contacts); @@ -700,7 +699,7 @@ class SendExpiringCertificateNotificationEmailActionTest { /** Returns persisted sample contacts with a customized contact email type. */ private static ImmutableList persistSampleContacts( - Registrar registrar, RegistrarPocBase.Type emailType) { + Registrar registrar, RegistrarPoc.Type emailType) { return persistSimpleResources( ImmutableList.of( new RegistrarPoc.Builder() diff --git a/core/src/test/java/google/registry/beam/rde/RdePipelineTest.java b/core/src/test/java/google/registry/beam/rde/RdePipelineTest.java index 1744a2005..44035a322 100644 --- a/core/src/test/java/google/registry/beam/rde/RdePipelineTest.java +++ b/core/src/test/java/google/registry/beam/rde/RdePipelineTest.java @@ -70,7 +70,7 @@ import google.registry.model.rde.RdeMode; import google.registry.model.rde.RdeRevision; import google.registry.model.rde.RdeRevision.RdeRevisionId; import google.registry.model.registrar.Registrar; -import google.registry.model.registrar.RegistrarBase.State; +import google.registry.model.registrar.Registrar.State; import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField; import google.registry.model.reporting.HistoryEntry; @@ -388,22 +388,22 @@ public class RdePipelineTest { assertThat(domainFrags.stream().findFirst().get().xml().strip()) .isEqualTo( """ - - cat.fun - 15-FUN - cat.fun - - contact456 - contact456 - - ns1.external.tld - ns1.hello.soy - - TheRegistrar - TheRegistrar - 1970-01-01T00:00:00Z - 294247-01-10T04:00:54Z - """); + + cat.fun + 15-FUN + cat.fun + + contact456 + contact456 + + ns1.external.tld + ns1.hello.soy + + TheRegistrar + TheRegistrar + 1970-01-01T00:00:00Z + 294247-01-10T04:00:54Z + """); } if (kv.getKey().mode().equals(FULL)) { // Contact fragments for hello.soy. @@ -425,23 +425,23 @@ public class RdePipelineTest { assertThat(domainFrags.stream().findFirst().get().xml().strip()) .isEqualTo( """ - - hello.soy - E-SOY - hello.soy - - contact1234 - contact789 - contact1234 - - ns1.external.tld - ns1.lol.cat - - TheRegistrar - TheRegistrar - 1970-01-01T00:00:00Z - 294247-01-10T04:00:54Z - """); + + hello.soy + E-SOY + hello.soy + + contact1234 + contact789 + contact1234 + + ns1.external.tld + ns1.lol.cat + + TheRegistrar + TheRegistrar + 1970-01-01T00:00:00Z + 294247-01-10T04:00:54Z + """); } else { // Contact fragments for cat.fun. assertThat( @@ -471,20 +471,20 @@ public class RdePipelineTest { assertThat(domainFrags.stream().findFirst().get().xml().strip()) .isEqualTo( """ - - hello.soy - E-SOY - hello.soy - - - ns1.external.tld - ns1.lol.cat - - TheRegistrar - TheRegistrar - 1970-01-01T00:00:00Z - 294247-01-10T04:00:54Z - """); + + hello.soy + E-SOY + hello.soy + + + ns1.external.tld + ns1.lol.cat + + TheRegistrar + TheRegistrar + 1970-01-01T00:00:00Z + 294247-01-10T04:00:54Z + """); } }); return null; diff --git a/core/src/test/java/google/registry/export/SyncGroupMembersActionTest.java b/core/src/test/java/google/registry/export/SyncGroupMembersActionTest.java index c262417ec..e03ccdfa7 100644 --- a/core/src/test/java/google/registry/export/SyncGroupMembersActionTest.java +++ b/core/src/test/java/google/registry/export/SyncGroupMembersActionTest.java @@ -16,9 +16,9 @@ package google.registry.export; import static com.google.common.truth.Truth.assertThat; import static google.registry.export.SyncGroupMembersAction.getGroupEmailAddressForContactType; -import static google.registry.model.registrar.RegistrarPocBase.Type.ADMIN; -import static google.registry.model.registrar.RegistrarPocBase.Type.MARKETING; -import static google.registry.model.registrar.RegistrarPocBase.Type.TECH; +import static google.registry.model.registrar.RegistrarPoc.Type.ADMIN; +import static google.registry.model.registrar.RegistrarPoc.Type.MARKETING; +import static google.registry.model.registrar.RegistrarPoc.Type.TECH; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.testing.DatabaseHelper.loadRegistrar; import static google.registry.testing.DatabaseHelper.persistResource; diff --git a/core/src/test/java/google/registry/export/sheet/SyncRegistrarsSheetTest.java b/core/src/test/java/google/registry/export/sheet/SyncRegistrarsSheetTest.java index f99ef2fcb..703d9054f 100644 --- a/core/src/test/java/google/registry/export/sheet/SyncRegistrarsSheetTest.java +++ b/core/src/test/java/google/registry/export/sheet/SyncRegistrarsSheetTest.java @@ -38,7 +38,6 @@ import google.registry.model.common.Cursor; import google.registry.model.registrar.Registrar; import google.registry.model.registrar.RegistrarAddress; import google.registry.model.registrar.RegistrarPoc; -import google.registry.model.registrar.RegistrarPocBase; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension; import google.registry.testing.DatabaseHelper; @@ -158,8 +157,7 @@ public class SyncRegistrarsSheetTest { .setName("Jane Doe") .setEmailAddress("contact@example.com") .setPhoneNumber("+1.1234567890") - .setTypes( - ImmutableSet.of(RegistrarPocBase.Type.ADMIN, RegistrarPocBase.Type.BILLING)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.ADMIN, RegistrarPoc.Type.BILLING)) .build(), new RegistrarPoc.Builder() .setRegistrar(registrar) @@ -167,7 +165,7 @@ public class SyncRegistrarsSheetTest { .setEmailAddress("john.doe@example.tld") .setPhoneNumber("+1.1234567890") .setFaxNumber("+1.1234567891") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.ADMIN)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.ADMIN)) // Purposely flip the internal/external admin/tech // distinction to make sure we're not relying on it. Sigh. .setVisibleInWhoisAsAdmin(false) @@ -177,7 +175,7 @@ public class SyncRegistrarsSheetTest { .setRegistrar(registrar) .setName("Jane Smith") .setEmailAddress("pride@example.net") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.TECH)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.TECH)) .build()); // Use registrar key for contacts' parent. DateTime registrarCreationTime = persistResource(registrar).getCreationTime(); @@ -199,37 +197,37 @@ public class SyncRegistrarsSheetTest { .containsEntry( "primaryContacts", """ - Jane Doe - contact@example.com - Tel: +1.1234567890 - Types: [ADMIN, BILLING] - Visible in registrar WHOIS query as Admin contact: No - Visible in registrar WHOIS query as Technical contact: No - Phone number and email visible in domain WHOIS query as Registrar Abuse contact\ - info: No + Jane Doe + contact@example.com + Tel: +1.1234567890 + Types: [ADMIN, BILLING] + Visible in registrar WHOIS query as Admin contact: No + Visible in registrar WHOIS query as Technical contact: No + Phone number and email visible in domain WHOIS query as Registrar Abuse contact\ + info: No - John Doe - john.doe@example.tld - Tel: +1.1234567890 - Fax: +1.1234567891 - Types: [ADMIN] - Visible in registrar WHOIS query as Admin contact: No - Visible in registrar WHOIS query as Technical contact: Yes - Phone number and email visible in domain WHOIS query as Registrar Abuse contact\ - info: No - """); + John Doe + john.doe@example.tld + Tel: +1.1234567890 + Fax: +1.1234567891 + Types: [ADMIN] + Visible in registrar WHOIS query as Admin contact: No + Visible in registrar WHOIS query as Technical contact: Yes + Phone number and email visible in domain WHOIS query as Registrar Abuse contact\ + info: No + """); assertThat(row) .containsEntry( "techContacts", """ - Jane Smith - pride@example.net - Types: [TECH] - Visible in registrar WHOIS query as Admin contact: No - Visible in registrar WHOIS query as Technical contact: No - Phone number and email visible in domain WHOIS query as Registrar Abuse contact\ - info: No - """); + Jane Smith + pride@example.net + Types: [TECH] + Visible in registrar WHOIS query as Admin contact: No + Visible in registrar WHOIS query as Technical contact: No + Phone number and email visible in domain WHOIS query as Registrar Abuse contact\ + info: No + """); assertThat(row).containsEntry("marketingContacts", ""); assertThat(row).containsEntry("abuseContacts", ""); assertThat(row).containsEntry("whoisInquiryContacts", ""); @@ -238,30 +236,30 @@ public class SyncRegistrarsSheetTest { .containsEntry( "billingContacts", """ - Jane Doe - contact@example.com - Tel: +1.1234567890 - Types: [ADMIN, BILLING] - Visible in registrar WHOIS query as Admin contact: No - Visible in registrar WHOIS query as Technical contact: No - Phone number and email visible in domain WHOIS query as Registrar Abuse contact\ - info: No - """); + Jane Doe + contact@example.com + Tel: +1.1234567890 + Types: [ADMIN, BILLING] + Visible in registrar WHOIS query as Admin contact: No + Visible in registrar WHOIS query as Technical contact: No + Phone number and email visible in domain WHOIS query as Registrar Abuse contact\ + info: No + """); assertThat(row).containsEntry("contactsMarkedAsWhoisAdmin", ""); assertThat(row) .containsEntry( "contactsMarkedAsWhoisTech", """ - John Doe - john.doe@example.tld - Tel: +1.1234567890 - Fax: +1.1234567891 - Types: [ADMIN] - Visible in registrar WHOIS query as Admin contact: No - Visible in registrar WHOIS query as Technical contact: Yes - Phone number and email visible in domain WHOIS query as Registrar Abuse contact\ - info: No - """); + John Doe + john.doe@example.tld + Tel: +1.1234567890 + Fax: +1.1234567891 + Types: [ADMIN] + Visible in registrar WHOIS query as Admin contact: No + Visible in registrar WHOIS query as Technical contact: Yes + Phone number and email visible in domain WHOIS query as Registrar Abuse contact\ + info: No + """); assertThat(row).containsEntry("emailAddress", "nowhere@example.org"); assertThat(row).containsEntry( "address.street", "I get fallen back upon since there's no l10n addr"); diff --git a/core/src/test/java/google/registry/flows/domain/DomainCreateFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainCreateFlowTest.java index 9b29f3b88..3b0f63e25 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainCreateFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainCreateFlowTest.java @@ -174,7 +174,7 @@ import google.registry.model.eppoutput.EppResponse; import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse; import google.registry.model.poll.PollMessage; import google.registry.model.registrar.Registrar; -import google.registry.model.registrar.RegistrarBase.State; +import google.registry.model.registrar.Registrar.State; import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField; import google.registry.model.reporting.HistoryEntry; diff --git a/core/src/test/java/google/registry/flows/domain/DomainRenewFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainRenewFlowTest.java index 395d21956..d82efbdd7 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainRenewFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainRenewFlowTest.java @@ -93,7 +93,7 @@ import google.registry.model.domain.token.AllocationToken.TokenStatus; import google.registry.model.eppcommon.StatusValue; import google.registry.model.poll.PollMessage; import google.registry.model.registrar.Registrar; -import google.registry.model.registrar.RegistrarBase.State; +import google.registry.model.registrar.Registrar.State; import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField; import google.registry.model.reporting.HistoryEntry; diff --git a/core/src/test/java/google/registry/flows/domain/DomainRestoreRequestFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainRestoreRequestFlowTest.java index 8e94d653e..1dceced56 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainRestoreRequestFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainRestoreRequestFlowTest.java @@ -69,7 +69,7 @@ import google.registry.model.domain.rgp.GracePeriodStatus; import google.registry.model.eppcommon.StatusValue; import google.registry.model.poll.PollMessage; import google.registry.model.registrar.Registrar; -import google.registry.model.registrar.RegistrarBase.State; +import google.registry.model.registrar.Registrar.State; import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField; import google.registry.model.reporting.HistoryEntry; diff --git a/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowTest.java index 72859ddd1..3a75233c5 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowTest.java @@ -114,7 +114,7 @@ import google.registry.model.eppcommon.Trid; import google.registry.model.poll.PendingActionNotificationResponse; import google.registry.model.poll.PollMessage; import google.registry.model.registrar.Registrar; -import google.registry.model.registrar.RegistrarBase.State; +import google.registry.model.registrar.Registrar.State; import google.registry.model.reporting.DomainTransactionRecord; import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry.HistoryEntryId; diff --git a/core/src/test/java/google/registry/flows/session/LoginFlowTestCase.java b/core/src/test/java/google/registry/flows/session/LoginFlowTestCase.java index 7fd7c50e3..fbb338bcc 100644 --- a/core/src/test/java/google/registry/flows/session/LoginFlowTestCase.java +++ b/core/src/test/java/google/registry/flows/session/LoginFlowTestCase.java @@ -36,7 +36,7 @@ import google.registry.flows.session.LoginFlow.TooManyFailedLoginsException; import google.registry.flows.session.LoginFlow.UnsupportedLanguageException; import google.registry.model.eppoutput.EppOutput; import google.registry.model.registrar.Registrar; -import google.registry.model.registrar.RegistrarBase.State; +import google.registry.model.registrar.Registrar.State; import google.registry.testing.DatabaseHelper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/core/src/test/java/google/registry/model/console/ConsoleEppActionHistoryTest.java b/core/src/test/java/google/registry/model/console/ConsoleEppActionHistoryTest.java deleted file mode 100644 index 59ce65c94..000000000 --- a/core/src/test/java/google/registry/model/console/ConsoleEppActionHistoryTest.java +++ /dev/null @@ -1,68 +0,0 @@ -// 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.model.console; - -import static com.google.common.collect.Iterables.getOnlyElement; -import static com.google.common.truth.Truth.assertThat; -import static google.registry.persistence.transaction.TransactionManagerFactory.tm; -import static google.registry.testing.DatabaseHelper.createTld; -import static google.registry.testing.DatabaseHelper.persistActiveContact; -import static google.registry.testing.DatabaseHelper.persistDomainWithDependentResources; - -import google.registry.model.EntityTestCase; -import google.registry.model.domain.DomainHistory; -import google.registry.testing.DatabaseHelper; -import google.registry.util.DateTimeUtils; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -/** Tests for {@link ConsoleEppActionHistory}. */ -public class ConsoleEppActionHistoryTest extends EntityTestCase { - - ConsoleEppActionHistoryTest() { - super(JpaEntityCoverageCheck.ENABLED); - } - - @BeforeEach - void beforeEach() { - createTld("tld"); - persistDomainWithDependentResources( - "example", - "tld", - persistActiveContact("contact1234"), - fakeClock.nowUtc(), - fakeClock.nowUtc(), - DateTimeUtils.END_OF_TIME); - } - - @Test - void testPersistence() { - User user = DatabaseHelper.createAdminUser("email@email.com"); - DomainHistory domainHistory = getOnlyElement(DatabaseHelper.loadAllOf(DomainHistory.class)); - ConsoleEppActionHistory history = - new ConsoleEppActionHistory.Builder() - .setType(ConsoleUpdateHistory.Type.DOMAIN_DELETE) - .setActingUser(user) - .setModificationTime(fakeClock.nowUtc()) - .setMethod("POST") - .setUrl("https://some/url/for/creating/a/domain") - .setHistoryEntryClass(DomainHistory.class) - .setHistoryEntryId(domainHistory.getHistoryEntryId()) - .build(); - tm().transact(() -> tm().put(history)); - assertThat(getOnlyElement(DatabaseHelper.loadAllOf(ConsoleEppActionHistory.class))) - .isEqualTo(history); - } -} diff --git a/core/src/test/java/google/registry/model/console/SimpleConsoleUpdateHistoryTest.java b/core/src/test/java/google/registry/model/console/ConsoleUpdateHistoryTest.java similarity index 91% rename from core/src/test/java/google/registry/model/console/SimpleConsoleUpdateHistoryTest.java rename to core/src/test/java/google/registry/model/console/ConsoleUpdateHistoryTest.java index 784d560f5..705757534 100644 --- a/core/src/test/java/google/registry/model/console/SimpleConsoleUpdateHistoryTest.java +++ b/core/src/test/java/google/registry/model/console/ConsoleUpdateHistoryTest.java @@ -27,8 +27,8 @@ import google.registry.util.DateTimeUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -public class SimpleConsoleUpdateHistoryTest extends EntityTestCase { - SimpleConsoleUpdateHistoryTest() { +public class ConsoleUpdateHistoryTest extends EntityTestCase { + ConsoleUpdateHistoryTest() { super(JpaEntityCoverageCheck.ENABLED); } @@ -47,8 +47,8 @@ public class SimpleConsoleUpdateHistoryTest extends EntityTestCase { @Test void testPersistence() { User user = persistResource(DatabaseHelper.createAdminUser("email@email.com")); - SimpleConsoleUpdateHistory history = - new SimpleConsoleUpdateHistory.Builder() + ConsoleUpdateHistory history = + new ConsoleUpdateHistory.Builder() .setType(ConsoleUpdateHistory.Type.DOMAIN_SUSPEND) .setActingUser(user) .setMethod("POST") diff --git a/core/src/test/java/google/registry/model/console/RegistrarPocUpdateHistoryTest.java b/core/src/test/java/google/registry/model/console/RegistrarPocUpdateHistoryTest.java deleted file mode 100644 index 91cb17162..000000000 --- a/core/src/test/java/google/registry/model/console/RegistrarPocUpdateHistoryTest.java +++ /dev/null @@ -1,61 +0,0 @@ -// 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.model.console; - -import static com.google.common.truth.Truth.assertThat; -import static google.registry.persistence.transaction.TransactionManagerFactory.tm; - -import com.google.common.collect.Iterables; -import google.registry.model.EntityTestCase; -import google.registry.model.registrar.RegistrarPoc; -import google.registry.model.registrar.RegistrarPoc.RegistrarPocId; -import google.registry.persistence.VKey; -import google.registry.testing.DatabaseHelper; -import org.junit.jupiter.api.Test; - -/** Tests for {@link RegistrarPocUpdateHistory}. */ -public class RegistrarPocUpdateHistoryTest extends EntityTestCase { - - RegistrarPocUpdateHistoryTest() { - super(JpaEntityCoverageCheck.ENABLED); - } - - @Test - void testPersistence() { - User user = DatabaseHelper.createAdminUser("email@email.com"); - RegistrarPoc registrarPoc = - DatabaseHelper.loadByKey( - VKey.create( - RegistrarPoc.class, - new RegistrarPocId("johndoe@theregistrar.com", "TheRegistrar"))); - RegistrarPocUpdateHistory history = - new RegistrarPocUpdateHistory.Builder() - .setRegistrarPoc(registrarPoc) - .setActingUser(user) - .setModificationTime(fakeClock.nowUtc()) - .setType(ConsoleUpdateHistory.Type.USER_UPDATE) - .setMethod("POST") - .setUrl("someUrl") - .build(); - tm().transact(() -> tm().put(history)); - - // Change the POC and make sure the history stays the same - tm().transact(() -> tm().put(registrarPoc.asBuilder().setName("Some Othername").build())); - - RegistrarPocUpdateHistory fromDb = - Iterables.getOnlyElement(DatabaseHelper.loadAllOf(RegistrarPocUpdateHistory.class)); - assertThat(fromDb.getRegistrarPoc().getName()).isEqualTo("John Doe"); - } -} diff --git a/core/src/test/java/google/registry/model/console/RegistrarUpdateHistoryTest.java b/core/src/test/java/google/registry/model/console/RegistrarUpdateHistoryTest.java deleted file mode 100644 index 46634605d..000000000 --- a/core/src/test/java/google/registry/model/console/RegistrarUpdateHistoryTest.java +++ /dev/null @@ -1,55 +0,0 @@ -// 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.model.console; - -import static com.google.common.truth.Truth.assertThat; -import static google.registry.persistence.transaction.TransactionManagerFactory.tm; - -import com.google.common.collect.Iterables; -import google.registry.model.EntityTestCase; -import google.registry.model.registrar.Registrar; -import google.registry.testing.DatabaseHelper; -import org.junit.jupiter.api.Test; - -/** Tests for {@link RegistrarUpdateHistory}. */ -public class RegistrarUpdateHistoryTest extends EntityTestCase { - - RegistrarUpdateHistoryTest() { - super(JpaEntityCoverageCheck.ENABLED); - } - - @Test - void testPersistence() { - User user = DatabaseHelper.createAdminUser("email@email.com"); - Registrar theRegistrar = DatabaseHelper.loadRegistrar("TheRegistrar"); - RegistrarUpdateHistory history = - new RegistrarUpdateHistory.Builder() - .setRegistrar(theRegistrar) - .setActingUser(user) - .setModificationTime(fakeClock.nowUtc()) - .setType(ConsoleUpdateHistory.Type.USER_UPDATE) - .setMethod("POST") - .setUrl("someUrl") - .build(); - tm().transact(() -> tm().put(history)); - - // Change the registrar and make sure the history stays the same - tm().transact(() -> tm().put(theRegistrar.asBuilder().setUrl("https://other.url").build())); - - RegistrarUpdateHistory fromDb = - Iterables.getOnlyElement(DatabaseHelper.loadAllOf(RegistrarUpdateHistory.class)); - assertThat(fromDb.getRegistrar().getUrl()).isEqualTo("http://my.fake.url"); - } -} diff --git a/core/src/test/java/google/registry/model/console/UserUpdateHistoryTest.java b/core/src/test/java/google/registry/model/console/UserUpdateHistoryTest.java deleted file mode 100644 index df9b3630b..000000000 --- a/core/src/test/java/google/registry/model/console/UserUpdateHistoryTest.java +++ /dev/null @@ -1,65 +0,0 @@ -// 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.model.console; - -import static com.google.common.truth.Truth.assertThat; -import static google.registry.persistence.transaction.TransactionManagerFactory.tm; -import static google.registry.testing.DatabaseHelper.loadAllOf; - -import com.google.common.collect.Iterables; -import google.registry.model.EntityTestCase; -import google.registry.testing.DatabaseHelper; -import org.junit.jupiter.api.Test; - -/** Tests for {@link UserUpdateHistory}. */ -public class UserUpdateHistoryTest extends EntityTestCase { - - UserUpdateHistoryTest() { - super(JpaEntityCoverageCheck.ENABLED); - } - - @Test - void testPersistence() { - User user = DatabaseHelper.createAdminUser("email@email.com"); - User otherUser = DatabaseHelper.createAdminUser("otherEmail@email.com"); - UserUpdateHistory history = - new UserUpdateHistory.Builder() - .setUser(otherUser) - .setActingUser(user) - .setModificationTime(fakeClock.nowUtc()) - .setType(ConsoleUpdateHistory.Type.USER_UPDATE) - .setMethod("POST") - .setUrl("someUrl") - .build(); - tm().transact(() -> tm().put(history)); - - // Change the acted-upon user and make sure that nothing changed in the history DB - tm().transact( - () -> - tm().put( - otherUser - .asBuilder() - .setUserRoles( - otherUser - .getUserRoles() - .asBuilder() - .setGlobalRole(GlobalRole.SUPPORT_LEAD) - .build()) - .build())); - - UserUpdateHistory fromDb = Iterables.getOnlyElement(loadAllOf(UserUpdateHistory.class)); - assertThat(fromDb.getUser().getUserRoles().getGlobalRole()).isEqualTo(GlobalRole.FTE); - } -} diff --git a/core/src/test/java/google/registry/model/registrar/RegistrarTest.java b/core/src/test/java/google/registry/model/registrar/RegistrarTest.java index e2fa9de08..ee4757b16 100644 --- a/core/src/test/java/google/registry/model/registrar/RegistrarTest.java +++ b/core/src/test/java/google/registry/model/registrar/RegistrarTest.java @@ -39,8 +39,8 @@ import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedSet; import google.registry.config.RegistryConfig; import google.registry.model.EntityTestCase; -import google.registry.model.registrar.RegistrarBase.State; -import google.registry.model.registrar.RegistrarBase.Type; +import google.registry.model.registrar.Registrar.State; +import google.registry.model.registrar.Registrar.Type; import google.registry.model.tld.Tld; import google.registry.model.tld.Tld.TldType; import google.registry.model.tld.Tlds; @@ -129,7 +129,7 @@ class RegistrarTest extends EntityTestCase { .setVisibleInWhoisAsTech(false) .setPhoneNumber("+1.2125551213") .setFaxNumber("+1.2125551213") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.ABUSE, RegistrarPocBase.Type.ADMIN)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.ABUSE, RegistrarPoc.Type.ADMIN)) .build(); persistSimpleResources( ImmutableList.of( @@ -140,8 +140,7 @@ class RegistrarTest extends EntityTestCase { .setEmailAddress("johndoe@example.com") .setPhoneNumber("+1.2125551213") .setFaxNumber("+1.2125551213") - .setTypes( - ImmutableSet.of(RegistrarPocBase.Type.LEGAL, RegistrarPocBase.Type.MARKETING)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.LEGAL, RegistrarPoc.Type.MARKETING)) .build())); } @@ -327,7 +326,7 @@ class RegistrarTest extends EntityTestCase { .setVisibleInWhoisAsTech(true) .setPhoneNumber("+1.2125551213") .setFaxNumber("+1.2125551213") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.TECH)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.TECH)) .build()); RegistrarPoc newTechAbuseContact = persistSimpleResource( @@ -339,13 +338,13 @@ class RegistrarTest extends EntityTestCase { .setVisibleInWhoisAsTech(true) .setPhoneNumber("+1.2125551213") .setFaxNumber("+1.2125551213") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.TECH, RegistrarPocBase.Type.ABUSE)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.TECH, RegistrarPoc.Type.ABUSE)) .build()); ImmutableSortedSet techContacts = - registrar.getContactsOfType(RegistrarPocBase.Type.TECH); + registrar.getContactsOfType(RegistrarPoc.Type.TECH); assertThat(techContacts).containsExactly(newTechContact, newTechAbuseContact).inOrder(); ImmutableSortedSet abuseContacts = - registrar.getContactsOfType(RegistrarPocBase.Type.ABUSE); + registrar.getContactsOfType(RegistrarPoc.Type.ABUSE); assertThat(abuseContacts).containsExactly(newTechAbuseContact, abuseAdminContact).inOrder(); } diff --git a/core/src/test/java/google/registry/persistence/converter/StringValueEnumeratedTest.java b/core/src/test/java/google/registry/persistence/converter/StringValueEnumeratedTest.java index 23b786ce8..9f1593bb5 100644 --- a/core/src/test/java/google/registry/persistence/converter/StringValueEnumeratedTest.java +++ b/core/src/test/java/google/registry/persistence/converter/StringValueEnumeratedTest.java @@ -19,7 +19,7 @@ import static google.registry.persistence.transaction.TransactionManagerFactory. import static google.registry.testing.DatabaseHelper.insertInDb; import google.registry.model.ImmutableObject; -import google.registry.model.registrar.RegistrarBase.State; +import google.registry.model.registrar.Registrar.State; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaUnitTestExtension; import jakarta.persistence.Entity; diff --git a/core/src/test/java/google/registry/persistence/transaction/JpaTransactionManagerExtension.java b/core/src/test/java/google/registry/persistence/transaction/JpaTransactionManagerExtension.java index fe6d0f09b..8150f02b2 100644 --- a/core/src/test/java/google/registry/persistence/transaction/JpaTransactionManagerExtension.java +++ b/core/src/test/java/google/registry/persistence/transaction/JpaTransactionManagerExtension.java @@ -30,10 +30,9 @@ import com.google.common.collect.Maps; import com.google.common.collect.Streams; import com.google.common.io.Resources; import google.registry.model.registrar.Registrar; +import google.registry.model.registrar.Registrar.State; import google.registry.model.registrar.RegistrarAddress; -import google.registry.model.registrar.RegistrarBase.State; import google.registry.model.registrar.RegistrarPoc; -import google.registry.model.registrar.RegistrarPocBase; import google.registry.persistence.HibernateSchemaExporter; import google.registry.persistence.NomulusPostgreSql; import google.registry.persistence.PersistenceModule; @@ -410,7 +409,7 @@ public abstract class JpaTransactionManagerExtension .setVisibleInWhoisAsTech(false) .setEmailAddress("janedoe@theregistrar.com") .setPhoneNumber("+1.1234567890") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.ADMIN)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.ADMIN)) .build(); } @@ -424,7 +423,7 @@ public abstract class JpaTransactionManagerExtension .setName("John Doe") .setEmailAddress("johndoe@theregistrar.com") .setPhoneNumber("+1.1234567890") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.ADMIN)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.ADMIN)) .build(); } @@ -435,7 +434,7 @@ public abstract class JpaTransactionManagerExtension .setEmailAddress("Marla.Singer@crr.com") .setRegistryLockEmailAddress("Marla.Singer.RegistryLock@crr.com") .setPhoneNumber("+1.2128675309") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.TECH)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.TECH)) .setAllowedToSetRegistryLockPassword(true) .setRegistryLockPassword("hi") .build(); diff --git a/core/src/test/java/google/registry/rdap/RdapJsonFormatterTest.java b/core/src/test/java/google/registry/rdap/RdapJsonFormatterTest.java index 559bf215f..b1fd2fc8e 100644 --- a/core/src/test/java/google/registry/rdap/RdapJsonFormatterTest.java +++ b/core/src/test/java/google/registry/rdap/RdapJsonFormatterTest.java @@ -39,7 +39,6 @@ import google.registry.model.eppcommon.StatusValue; import google.registry.model.host.Host; import google.registry.model.registrar.Registrar; import google.registry.model.registrar.RegistrarPoc; -import google.registry.model.registrar.RegistrarPocBase; import google.registry.model.reporting.HistoryEntry; import google.registry.model.transfer.DomainTransferData; import google.registry.model.transfer.TransferStatus; @@ -261,7 +260,7 @@ class RdapJsonFormatterTest { .setEmailAddress("babydoe@example.com") .setPhoneNumber("+1.2125551217") .setFaxNumber("+1.2125551218") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.ADMIN)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.ADMIN)) .setVisibleInWhoisAsAdmin(false) .setVisibleInWhoisAsTech(false) .build(), @@ -270,7 +269,7 @@ class RdapJsonFormatterTest { .setName("John Doe") .setEmailAddress("johndoe@example.com") .setFaxNumber("+1.2125551213") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.ADMIN)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.ADMIN)) .setVisibleInWhoisAsAdmin(false) .setVisibleInWhoisAsTech(true) .build(), @@ -279,7 +278,7 @@ class RdapJsonFormatterTest { .setName("Jane Doe") .setEmailAddress("janedoe@example.com") .setPhoneNumber("+1.2125551215") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.TECH, RegistrarPocBase.Type.ADMIN)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.TECH, RegistrarPoc.Type.ADMIN)) .setVisibleInWhoisAsAdmin(true) .setVisibleInWhoisAsTech(false) .build(), @@ -289,7 +288,7 @@ class RdapJsonFormatterTest { .setEmailAddress("playdoe@example.com") .setPhoneNumber("+1.2125551217") .setFaxNumber("+1.2125551218") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.BILLING)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.BILLING)) .setVisibleInWhoisAsAdmin(true) .setVisibleInWhoisAsTech(true) .build()); diff --git a/core/src/test/java/google/registry/rdap/UpdateRegistrarRdapBaseUrlsActionTest.java b/core/src/test/java/google/registry/rdap/UpdateRegistrarRdapBaseUrlsActionTest.java index b6843add5..9657043ed 100644 --- a/core/src/test/java/google/registry/rdap/UpdateRegistrarRdapBaseUrlsActionTest.java +++ b/core/src/test/java/google/registry/rdap/UpdateRegistrarRdapBaseUrlsActionTest.java @@ -30,7 +30,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import google.registry.model.registrar.Registrar; import google.registry.model.registrar.RegistrarAddress; -import google.registry.model.registrar.RegistrarBase; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension; import google.registry.request.HttpException.InternalServerErrorException; @@ -50,22 +49,22 @@ public final class UpdateRegistrarRdapBaseUrlsActionTest { // This reply simulates part of the actual IANA CSV reply private static final String CSV_REPLY = """ - "ID",Registrar Name,Status,RDAP Base URL - 1,Reserved,Reserved, - 81,Gandi SAS,Accredited,https://rdap.gandi.net/ - 100,Whois Corp.,Accredited,https://www.yesnic.com/rdap/ - 134,BB-Online UK Limited,Accredited,https://rdap.bb-online.com/ - 1316,"Xiamen 35.Com Technology Co., Ltd.",Accredited,https://rdap.35.com/rdap/ - 1448,Blacknight Internet Solutions Ltd.,Accredited,https://rdap.blacknight.com/ - 1463,"Global Domains International, Inc. DBA DomainCostClub.com",Accredited,\ - https://rdap.domaincostclub.com/ - 1556,"Chengdu West Dimension Digital Technology Co., Ltd.",Accredited,\ - https://rdap.west.cn/rdap/ - 2288,Metaregistrar BV,Accredited,https://rdap.metaregistrar.com/ - 4000,Gname 031 Inc,Accredited, - 9999,Reserved for non-billable transactions where Registry Operator acts as\ - Registrar,Reserved, - """; + "ID",Registrar Name,Status,RDAP Base URL + 1,Reserved,Reserved, + 81,Gandi SAS,Accredited,https://rdap.gandi.net/ + 100,Whois Corp.,Accredited,https://www.yesnic.com/rdap/ + 134,BB-Online UK Limited,Accredited,https://rdap.bb-online.com/ + 1316,"Xiamen 35.Com Technology Co., Ltd.",Accredited,https://rdap.35.com/rdap/ + 1448,Blacknight Internet Solutions Ltd.,Accredited,https://rdap.blacknight.com/ + 1463,"Global Domains International, Inc. DBA DomainCostClub.com",Accredited,\ + https://rdap.domaincostclub.com/ + 1556,"Chengdu West Dimension Digital Technology Co., Ltd.",Accredited,\ + https://rdap.west.cn/rdap/ + 2288,Metaregistrar BV,Accredited,https://rdap.metaregistrar.com/ + 4000,Gname 031 Inc,Accredited, + 9999,Reserved for non-billable transactions where Registry Operator acts as\ + Registrar,Reserved, + """; @RegisterExtension public JpaIntegrationTestExtension jpa = @@ -94,7 +93,7 @@ public final class UpdateRegistrarRdapBaseUrlsActionTest { } private static void persistRegistrar( - String registrarId, Long ianaId, RegistrarBase.Type type, String... rdapBaseUrls) { + String registrarId, Long ianaId, Registrar.Type type, String... rdapBaseUrls) { persistSimpleResource( new Registrar.Builder() .setRegistrarId(registrarId) diff --git a/core/src/test/java/google/registry/rde/RegistrarToXjcConverterTest.java b/core/src/test/java/google/registry/rde/RegistrarToXjcConverterTest.java index c3c1a1bd2..f31085ef7 100644 --- a/core/src/test/java/google/registry/rde/RegistrarToXjcConverterTest.java +++ b/core/src/test/java/google/registry/rde/RegistrarToXjcConverterTest.java @@ -22,8 +22,8 @@ import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.collect.ImmutableList; import google.registry.model.registrar.Registrar; +import google.registry.model.registrar.Registrar.State; import google.registry.model.registrar.RegistrarAddress; -import google.registry.model.registrar.RegistrarBase.State; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension; import google.registry.testing.FakeClock; diff --git a/core/src/test/java/google/registry/request/auth/AuthenticatedRegistrarAccessorTest.java b/core/src/test/java/google/registry/request/auth/AuthenticatedRegistrarAccessorTest.java index 153533def..d18d4a3b6 100644 --- a/core/src/test/java/google/registry/request/auth/AuthenticatedRegistrarAccessorTest.java +++ b/core/src/test/java/google/registry/request/auth/AuthenticatedRegistrarAccessorTest.java @@ -38,7 +38,7 @@ 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.State; +import google.registry.model.registrar.Registrar.State; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension; import google.registry.request.auth.AuthenticatedRegistrarAccessor.RegistrarAccessDeniedException; diff --git a/core/src/test/java/google/registry/schema/integration/SqlIntegrationTestSuite.java b/core/src/test/java/google/registry/schema/integration/SqlIntegrationTestSuite.java index 527be6b54..67eaa5b20 100644 --- a/core/src/test/java/google/registry/schema/integration/SqlIntegrationTestSuite.java +++ b/core/src/test/java/google/registry/schema/integration/SqlIntegrationTestSuite.java @@ -24,12 +24,8 @@ import google.registry.model.billing.BillingBaseTest; import google.registry.model.common.CursorTest; import google.registry.model.common.DnsRefreshRequestTest; import google.registry.model.common.FeatureFlagTest; -import google.registry.model.console.ConsoleEppActionHistoryTest; -import google.registry.model.console.RegistrarPocUpdateHistoryTest; -import google.registry.model.console.RegistrarUpdateHistoryTest; -import google.registry.model.console.SimpleConsoleUpdateHistoryTest; +import google.registry.model.console.ConsoleUpdateHistoryTest; import google.registry.model.console.UserTest; -import google.registry.model.console.UserUpdateHistoryTest; import google.registry.model.contact.ContactTest; import google.registry.model.domain.DomainSqlTest; import google.registry.model.domain.token.AllocationTokenTest; @@ -98,7 +94,7 @@ import org.junit.runner.RunWith; BsaUnblockableDomainTest.class, BulkPricingPackageTest.class, ClaimsListDaoTest.class, - ConsoleEppActionHistoryTest.class, + ConsoleUpdateHistoryTest.class, ContactHistoryTest.class, ContactTest.class, CursorTest.class, @@ -112,18 +108,14 @@ import org.junit.runner.RunWith; PremiumListDaoTest.class, RdeRevisionTest.class, RegistrarDaoTest.class, - RegistrarPocUpdateHistoryTest.class, - RegistrarUpdateHistoryTest.class, ReservedListDaoTest.class, RegistryLockDaoTest.class, ServerSecretTest.class, - SimpleConsoleUpdateHistoryTest.class, SignedMarkRevocationListDaoTest.class, Spec11ThreatMatchTest.class, TldTest.class, TmchCrlTest.class, UserTest.class, - UserUpdateHistoryTest.class, // AfterSuiteTest must be the last entry. See class javadoc for details. AfterSuiteTest.class }) diff --git a/core/src/test/java/google/registry/schema/registrar/RegistrarPocTest.java b/core/src/test/java/google/registry/schema/registrar/RegistrarPocTest.java index c0b424c2b..c62aabaa3 100644 --- a/core/src/test/java/google/registry/schema/registrar/RegistrarPocTest.java +++ b/core/src/test/java/google/registry/schema/registrar/RegistrarPocTest.java @@ -15,7 +15,7 @@ package google.registry.schema.registrar; import static com.google.common.truth.Truth.assertThat; -import static google.registry.model.registrar.RegistrarPocBase.Type.WHOIS; +import static google.registry.model.registrar.RegistrarPoc.Type.WHOIS; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.testing.DatabaseHelper.insertInDb; import static google.registry.testing.DatabaseHelper.loadByEntity; diff --git a/core/src/test/java/google/registry/testing/DatabaseHelper.java b/core/src/test/java/google/registry/testing/DatabaseHelper.java index 86025426d..07d880d7e 100644 --- a/core/src/test/java/google/registry/testing/DatabaseHelper.java +++ b/core/src/test/java/google/registry/testing/DatabaseHelper.java @@ -93,9 +93,8 @@ import google.registry.model.host.Host; import google.registry.model.poll.PollMessage; import google.registry.model.pricing.StaticPremiumListPricingEngine; import google.registry.model.registrar.Registrar; +import google.registry.model.registrar.Registrar.State; import google.registry.model.registrar.RegistrarAddress; -import google.registry.model.registrar.RegistrarBase; -import google.registry.model.registrar.RegistrarBase.State; import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntryDao; import google.registry.model.tld.Tld; @@ -761,7 +760,7 @@ public final class DatabaseHelper { public static Registrar persistNewRegistrar( String registrarId, String registrarName, - RegistrarBase.Type type, + Registrar.Type type, @Nullable Long ianaIdentifier) { return persistSimpleResource( new Registrar.Builder() diff --git a/core/src/test/java/google/registry/testing/FullFieldsTestEntityHelper.java b/core/src/test/java/google/registry/testing/FullFieldsTestEntityHelper.java index 973eb424d..79665863b 100644 --- a/core/src/test/java/google/registry/testing/FullFieldsTestEntityHelper.java +++ b/core/src/test/java/google/registry/testing/FullFieldsTestEntityHelper.java @@ -38,9 +38,7 @@ import google.registry.model.eppcommon.Trid; import google.registry.model.host.Host; import google.registry.model.registrar.Registrar; import google.registry.model.registrar.RegistrarAddress; -import google.registry.model.registrar.RegistrarBase; import google.registry.model.registrar.RegistrarPoc; -import google.registry.model.registrar.RegistrarPocBase; import google.registry.model.reporting.HistoryEntry; import google.registry.persistence.VKey; import google.registry.util.Idn; @@ -54,12 +52,12 @@ import org.joda.time.DateTime; public final class FullFieldsTestEntityHelper { public static Registrar makeRegistrar( - String registrarId, String registrarName, RegistrarBase.State state) { + String registrarId, String registrarName, Registrar.State state) { return makeRegistrar(registrarId, registrarName, state, 1L); } public static Registrar makeRegistrar( - String registrarId, String registrarName, RegistrarBase.State state, Long ianaIdentifier) { + String registrarId, String registrarName, Registrar.State state, Long ianaIdentifier) { return new Registrar.Builder() .setRegistrarId(registrarId) .setRegistrarName(registrarName) @@ -101,7 +99,7 @@ public final class FullFieldsTestEntityHelper { .setEmailAddress("johndoe@example.com") .setPhoneNumber("+1.2125551213") .setFaxNumber("+1.2125551213") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.ADMIN)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.ADMIN)) // Purposely flip the internal/external admin/tech // distinction to make sure we're not relying on it. Sigh. .setVisibleInWhoisAsAdmin(false) @@ -113,7 +111,7 @@ public final class FullFieldsTestEntityHelper { .setEmailAddress("janedoe@example.com") .setPhoneNumber("+1.2125551215") .setFaxNumber("+1.2125551216") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.TECH)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.TECH)) // Purposely flip the internal/external admin/tech // distinction to make sure we're not relying on it. Sigh. .setVisibleInWhoisAsAdmin(true) diff --git a/core/src/test/java/google/registry/tools/CheckDomainClaimsCommandTest.java b/core/src/test/java/google/registry/tools/CheckDomainClaimsCommandTest.java index 54b7c6ffc..a902ec46b 100644 --- a/core/src/test/java/google/registry/tools/CheckDomainClaimsCommandTest.java +++ b/core/src/test/java/google/registry/tools/CheckDomainClaimsCommandTest.java @@ -18,7 +18,7 @@ import static google.registry.testing.DatabaseHelper.persistNewRegistrar; import static org.junit.jupiter.api.Assertions.assertThrows; import com.beust.jcommander.ParameterException; -import google.registry.model.registrar.RegistrarBase.Type; +import google.registry.model.registrar.Registrar.Type; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/core/src/test/java/google/registry/tools/CheckDomainCommandTest.java b/core/src/test/java/google/registry/tools/CheckDomainCommandTest.java index 6dd9d9b6c..32cf4875f 100644 --- a/core/src/test/java/google/registry/tools/CheckDomainCommandTest.java +++ b/core/src/test/java/google/registry/tools/CheckDomainCommandTest.java @@ -18,7 +18,7 @@ import static google.registry.testing.DatabaseHelper.persistNewRegistrar; import static org.junit.jupiter.api.Assertions.assertThrows; import com.beust.jcommander.ParameterException; -import google.registry.model.registrar.RegistrarBase.Type; +import google.registry.model.registrar.Registrar.Type; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/core/src/test/java/google/registry/tools/LockDomainCommandTest.java b/core/src/test/java/google/registry/tools/LockDomainCommandTest.java index cf78df9dc..b017812f9 100644 --- a/core/src/test/java/google/registry/tools/LockDomainCommandTest.java +++ b/core/src/test/java/google/registry/tools/LockDomainCommandTest.java @@ -26,7 +26,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import com.google.common.collect.ImmutableList; import google.registry.model.domain.Domain; -import google.registry.model.registrar.RegistrarBase.Type; +import google.registry.model.registrar.Registrar.Type; import google.registry.testing.CloudTasksHelper; import google.registry.testing.DatabaseHelper; import google.registry.testing.DeterministicStringGenerator; diff --git a/core/src/test/java/google/registry/tools/RegistrarPocCommandTest.java b/core/src/test/java/google/registry/tools/RegistrarPocCommandTest.java index 989d4bb3c..2eab7be2d 100644 --- a/core/src/test/java/google/registry/tools/RegistrarPocCommandTest.java +++ b/core/src/test/java/google/registry/tools/RegistrarPocCommandTest.java @@ -15,10 +15,10 @@ package google.registry.tools; import static com.google.common.truth.Truth.assertThat; -import static google.registry.model.registrar.RegistrarPocBase.Type.ABUSE; -import static google.registry.model.registrar.RegistrarPocBase.Type.ADMIN; -import static google.registry.model.registrar.RegistrarPocBase.Type.TECH; -import static google.registry.model.registrar.RegistrarPocBase.Type.WHOIS; +import static google.registry.model.registrar.RegistrarPoc.Type.ABUSE; +import static google.registry.model.registrar.RegistrarPoc.Type.ADMIN; +import static google.registry.model.registrar.RegistrarPoc.Type.TECH; +import static google.registry.model.registrar.RegistrarPoc.Type.WHOIS; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.testing.DatabaseHelper.loadRegistrar; import static google.registry.testing.DatabaseHelper.persistResource; diff --git a/core/src/test/java/google/registry/tools/SetupOteCommandTest.java b/core/src/test/java/google/registry/tools/SetupOteCommandTest.java index 77cb25e7b..da0be1a55 100644 --- a/core/src/test/java/google/registry/tools/SetupOteCommandTest.java +++ b/core/src/test/java/google/registry/tools/SetupOteCommandTest.java @@ -17,7 +17,7 @@ package google.registry.tools; import static com.google.common.truth.Truth.assertThat; import static google.registry.model.OteAccountBuilderTest.verifyUser; import static google.registry.model.console.User.IAP_SECURED_WEB_APP_USER_ROLE; -import static google.registry.model.registrar.RegistrarBase.State.ACTIVE; +import static google.registry.model.registrar.Registrar.State.ACTIVE; import static google.registry.model.tld.Tld.TldState.GENERAL_AVAILABILITY; import static google.registry.model.tld.Tld.TldState.START_DATE_SUNRISE; import static google.registry.testing.CertificateSamples.SAMPLE_CERT_HASH; diff --git a/core/src/test/java/google/registry/tools/UnlockDomainCommandTest.java b/core/src/test/java/google/registry/tools/UnlockDomainCommandTest.java index 0fbddd48d..3e02681db 100644 --- a/core/src/test/java/google/registry/tools/UnlockDomainCommandTest.java +++ b/core/src/test/java/google/registry/tools/UnlockDomainCommandTest.java @@ -29,7 +29,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import google.registry.model.domain.Domain; import google.registry.model.domain.RegistryLock; -import google.registry.model.registrar.RegistrarBase.Type; +import google.registry.model.registrar.Registrar.Type; import google.registry.testing.CloudTasksHelper; import google.registry.testing.DatabaseHelper; import google.registry.testing.DeterministicStringGenerator; diff --git a/core/src/test/java/google/registry/tools/UpdateRegistrarCommandTest.java b/core/src/test/java/google/registry/tools/UpdateRegistrarCommandTest.java index d1b46a6df..fc4fa3f37 100644 --- a/core/src/test/java/google/registry/tools/UpdateRegistrarCommandTest.java +++ b/core/src/test/java/google/registry/tools/UpdateRegistrarCommandTest.java @@ -37,8 +37,8 @@ import com.google.common.collect.ImmutableSortedMap; import google.registry.flows.certs.CertificateChecker; import google.registry.flows.certs.CertificateChecker.InsecureCertificateException; import google.registry.model.registrar.Registrar; -import google.registry.model.registrar.RegistrarBase.State; -import google.registry.model.registrar.RegistrarBase.Type; +import google.registry.model.registrar.Registrar.State; +import google.registry.model.registrar.Registrar.Type; import google.registry.persistence.transaction.JpaTransactionManagerExtension; import google.registry.util.CidrAddressBlock; import java.math.BigDecimal; diff --git a/core/src/test/java/google/registry/tools/ValidateLoginCredentialsCommandTest.java b/core/src/test/java/google/registry/tools/ValidateLoginCredentialsCommandTest.java index 8e72db07d..a21e9dcf6 100644 --- a/core/src/test/java/google/registry/tools/ValidateLoginCredentialsCommandTest.java +++ b/core/src/test/java/google/registry/tools/ValidateLoginCredentialsCommandTest.java @@ -15,7 +15,7 @@ package google.registry.tools; import static com.google.common.truth.Truth.assertThat; -import static google.registry.model.registrar.RegistrarBase.State.ACTIVE; +import static google.registry.model.registrar.Registrar.State.ACTIVE; import static google.registry.testing.DatabaseHelper.createTld; import static google.registry.testing.DatabaseHelper.loadRegistrar; import static google.registry.testing.DatabaseHelper.persistResource; @@ -32,7 +32,7 @@ import google.registry.flows.EppException; import google.registry.flows.TransportCredentials.BadRegistrarPasswordException; import google.registry.flows.certs.CertificateChecker; import google.registry.model.registrar.Registrar; -import google.registry.model.registrar.RegistrarBase.State; +import google.registry.model.registrar.Registrar.State; import google.registry.testing.CertificateSamples; import google.registry.util.CidrAddressBlock; import java.nio.file.Files; diff --git a/core/src/test/java/google/registry/ui/server/console/ConsoleEppPasswordActionTest.java b/core/src/test/java/google/registry/ui/server/console/ConsoleEppPasswordActionTest.java index 49737f9ba..bf41066b8 100644 --- a/core/src/test/java/google/registry/ui/server/console/ConsoleEppPasswordActionTest.java +++ b/core/src/test/java/google/registry/ui/server/console/ConsoleEppPasswordActionTest.java @@ -34,7 +34,6 @@ import com.google.gson.Gson; import google.registry.flows.PasswordOnlyTransportCredentials; import google.registry.model.console.ConsoleUpdateHistory; import google.registry.model.console.GlobalRole; -import google.registry.model.console.SimpleConsoleUpdateHistory; import google.registry.model.console.User; import google.registry.model.console.UserRoles; import google.registry.model.registrar.Registrar; @@ -140,7 +139,7 @@ class ConsoleEppPasswordActionTest { action.run(); assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_OK); assertDoesNotThrow(() -> credentials.validate(loadRegistrar("TheRegistrar"), "randomPassword")); - SimpleConsoleUpdateHistory history = loadSingleton(SimpleConsoleUpdateHistory.class).get(); + ConsoleUpdateHistory history = loadSingleton(ConsoleUpdateHistory.class).get(); assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.EPP_PASSWORD_UPDATE); assertThat(history.getDescription()).hasValue("TheRegistrar"); } diff --git a/core/src/test/java/google/registry/ui/server/console/ConsoleUpdateRegistrarActionTest.java b/core/src/test/java/google/registry/ui/server/console/ConsoleUpdateRegistrarActionTest.java index e2361d955..c398ef964 100644 --- a/core/src/test/java/google/registry/ui/server/console/ConsoleUpdateRegistrarActionTest.java +++ b/core/src/test/java/google/registry/ui/server/console/ConsoleUpdateRegistrarActionTest.java @@ -15,7 +15,7 @@ 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.model.registrar.RegistrarPoc.Type.WHOIS; import static google.registry.testing.DatabaseHelper.createTlds; import static google.registry.testing.DatabaseHelper.loadSingleton; import static google.registry.testing.DatabaseHelper.persistResource; @@ -31,11 +31,9 @@ import com.google.common.collect.ImmutableSet; import com.google.gson.Gson; import google.registry.model.console.ConsoleUpdateHistory; import google.registry.model.console.GlobalRole; -import google.registry.model.console.SimpleConsoleUpdateHistory; 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; @@ -83,7 +81,7 @@ class ConsoleUpdateRegistrarActionTest { persistResource( registrar .asBuilder() - .setType(RegistrarBase.Type.REAL) + .setType(Registrar.Type.REAL) .setAllowedTlds(ImmutableSet.of()) .setRegistryLockAllowed(false) .build()); @@ -108,7 +106,7 @@ class ConsoleUpdateRegistrarActionTest { assertThat(newRegistrar.getAllowedTlds()).containsExactly("app", "dev"); assertThat(newRegistrar.isRegistryLockAllowed()).isFalse(); assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_OK); - SimpleConsoleUpdateHistory history = loadSingleton(SimpleConsoleUpdateHistory.class).get(); + ConsoleUpdateHistory history = loadSingleton(ConsoleUpdateHistory.class).get(); assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.REGISTRAR_UPDATE); assertThat(history.getDescription()).hasValue("TheRegistrar"); } diff --git a/core/src/test/java/google/registry/ui/server/console/RegistrarsActionTest.java b/core/src/test/java/google/registry/ui/server/console/RegistrarsActionTest.java index c6f040487..8ce0b3b66 100644 --- a/core/src/test/java/google/registry/ui/server/console/RegistrarsActionTest.java +++ b/core/src/test/java/google/registry/ui/server/console/RegistrarsActionTest.java @@ -32,7 +32,6 @@ import com.google.gson.Gson; import google.registry.model.console.ConsoleUpdateHistory; import google.registry.model.console.GlobalRole; import google.registry.model.console.RegistrarRole; -import google.registry.model.console.SimpleConsoleUpdateHistory; import google.registry.model.console.User; import google.registry.model.console.UserRoles; import google.registry.model.registrar.Registrar; @@ -186,7 +185,7 @@ class RegistrarsActionTest { .findAny() .isPresent()) .isTrue(); - SimpleConsoleUpdateHistory history = loadSingleton(SimpleConsoleUpdateHistory.class).get(); + ConsoleUpdateHistory history = loadSingleton(ConsoleUpdateHistory.class).get(); assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.REGISTRAR_CREATE); assertThat(history.getDescription()).hasValue("regIdTest"); } diff --git a/core/src/test/java/google/registry/ui/server/console/domains/ConsoleBulkDomainActionTest.java b/core/src/test/java/google/registry/ui/server/console/domains/ConsoleBulkDomainActionTest.java index 69df63afd..1012fb997 100644 --- a/core/src/test/java/google/registry/ui/server/console/domains/ConsoleBulkDomainActionTest.java +++ b/core/src/test/java/google/registry/ui/server/console/domains/ConsoleBulkDomainActionTest.java @@ -42,7 +42,6 @@ import google.registry.model.common.FeatureFlag; import google.registry.model.console.ConsoleUpdateHistory; import google.registry.model.console.GlobalRole; import google.registry.model.console.RegistrarRole; -import google.registry.model.console.SimpleConsoleUpdateHistory; import google.registry.model.console.User; import google.registry.model.console.UserRoles; import google.registry.model.domain.Domain; @@ -123,7 +122,7 @@ public class ConsoleBulkDomainActionTest { {"example.tld":{"message":"Command completed successfully; action pending",\ "responseCode":1001}}"""); assertThat(loadByEntity(domain).getDeletionTime()).isEqualTo(clock.nowUtc().plusDays(35)); - SimpleConsoleUpdateHistory history = loadSingleton(SimpleConsoleUpdateHistory.class).get(); + ConsoleUpdateHistory history = loadSingleton(ConsoleUpdateHistory.class).get(); assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.DOMAIN_DELETE); assertThat(history.getDescription()).hasValue("example.tld"); } @@ -151,7 +150,7 @@ public class ConsoleBulkDomainActionTest { {"example.tld":{"message":"Command completed successfully","responseCode":1000}}"""); assertThat(loadByEntity(domain).getStatusValues()) .containsAtLeastElementsIn(serverSuspensionStatuses); - SimpleConsoleUpdateHistory history = loadSingleton(SimpleConsoleUpdateHistory.class).get(); + ConsoleUpdateHistory history = loadSingleton(ConsoleUpdateHistory.class).get(); assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.DOMAIN_SUSPEND); assertThat(history.getDescription()).hasValue("example.tld"); } @@ -181,7 +180,7 @@ public class ConsoleBulkDomainActionTest { """ {"example.tld":{"message":"Command completed successfully","responseCode":1000}}"""); assertThat(loadByEntity(domain).getStatusValues()).containsNoneIn(serverSuspensionStatuses); - SimpleConsoleUpdateHistory history = loadSingleton(SimpleConsoleUpdateHistory.class).get(); + ConsoleUpdateHistory history = loadSingleton(ConsoleUpdateHistory.class).get(); assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.DOMAIN_UNSUSPEND); assertThat(history.getDescription()).hasValue("example.tld"); } @@ -206,7 +205,7 @@ public class ConsoleBulkDomainActionTest { "nonexistent.tld":{"message":"The domain with given ID (nonexistent.tld) doesn\\u0027t exist.",\ "responseCode":2303}}"""); assertThat(loadByEntity(domain).getDeletionTime()).isEqualTo(clock.nowUtc().plusDays(35)); - SimpleConsoleUpdateHistory history = loadSingleton(SimpleConsoleUpdateHistory.class).get(); + ConsoleUpdateHistory history = loadSingleton(ConsoleUpdateHistory.class).get(); assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.DOMAIN_DELETE); assertThat(history.getDescription()).hasValue("example.tld"); } diff --git a/core/src/test/java/google/registry/ui/server/console/settings/ContactActionTest.java b/core/src/test/java/google/registry/ui/server/console/settings/ContactActionTest.java index f49c77efe..378f5d521 100644 --- a/core/src/test/java/google/registry/ui/server/console/settings/ContactActionTest.java +++ b/core/src/test/java/google/registry/ui/server/console/settings/ContactActionTest.java @@ -16,9 +16,9 @@ package google.registry.ui.server.console.settings; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.truth.Truth.assertThat; -import static google.registry.model.registrar.RegistrarPocBase.Type.ABUSE; -import static google.registry.model.registrar.RegistrarPocBase.Type.ADMIN; -import static google.registry.model.registrar.RegistrarPocBase.Type.TECH; +import static google.registry.model.registrar.RegistrarPoc.Type.ABUSE; +import static google.registry.model.registrar.RegistrarPoc.Type.ADMIN; +import static google.registry.model.registrar.RegistrarPoc.Type.TECH; import static google.registry.testing.DatabaseHelper.createAdminUser; import static google.registry.testing.DatabaseHelper.insertInDb; import static google.registry.testing.DatabaseHelper.loadAllOf; diff --git a/core/src/test/java/google/registry/ui/server/console/settings/RdapRegistrarFieldsActionTest.java b/core/src/test/java/google/registry/ui/server/console/settings/RdapRegistrarFieldsActionTest.java index 0bdf9655a..eab21fc29 100644 --- a/core/src/test/java/google/registry/ui/server/console/settings/RdapRegistrarFieldsActionTest.java +++ b/core/src/test/java/google/registry/ui/server/console/settings/RdapRegistrarFieldsActionTest.java @@ -29,7 +29,6 @@ import com.google.common.collect.Maps; import com.google.gson.Gson; import google.registry.model.console.ConsoleUpdateHistory; import google.registry.model.console.RegistrarRole; -import google.registry.model.console.SimpleConsoleUpdateHistory; import google.registry.model.console.User; import google.registry.model.console.UserRoles; import google.registry.model.registrar.Registrar; @@ -123,7 +122,7 @@ public class RdapRegistrarFieldsActionTest { assertAboutImmutableObjects() .that(newRegistrar) .isEqualExceptFields(oldRegistrar, "localizedAddress", "phoneNumber", "faxNumber"); - SimpleConsoleUpdateHistory history = loadSingleton(SimpleConsoleUpdateHistory.class).get(); + ConsoleUpdateHistory history = loadSingleton(ConsoleUpdateHistory.class).get(); assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.REGISTRAR_UPDATE); assertThat(history.getDescription()).hasValue("TheRegistrar"); } diff --git a/core/src/test/java/google/registry/ui/server/console/settings/SecurityActionTest.java b/core/src/test/java/google/registry/ui/server/console/settings/SecurityActionTest.java index 11d827582..6b47bf3a1 100644 --- a/core/src/test/java/google/registry/ui/server/console/settings/SecurityActionTest.java +++ b/core/src/test/java/google/registry/ui/server/console/settings/SecurityActionTest.java @@ -30,7 +30,6 @@ import com.google.common.collect.ImmutableSortedMap; import com.google.gson.Gson; import google.registry.flows.certs.CertificateChecker; import google.registry.model.console.ConsoleUpdateHistory; -import google.registry.model.console.SimpleConsoleUpdateHistory; import google.registry.model.registrar.Registrar; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.request.Action; @@ -101,7 +100,7 @@ class SecurityActionTest { .isEqualTo("GNd6ZP8/n91t9UTnpxR8aH7aAW4+CpvufYx9ViGbcMY"); assertThat(r.getIpAddressAllowList().get(0).getIp()).isEqualTo("192.168.1.1"); assertThat(r.getIpAddressAllowList().get(0).getNetmask()).isEqualTo(32); - SimpleConsoleUpdateHistory history = loadSingleton(SimpleConsoleUpdateHistory.class).get(); + ConsoleUpdateHistory history = loadSingleton(ConsoleUpdateHistory.class).get(); assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.REGISTRAR_SECURITY_UPDATE); assertThat(history.getDescription()).hasValue("registrarId"); } diff --git a/core/src/test/java/google/registry/whois/RegistrarWhoisResponseTest.java b/core/src/test/java/google/registry/whois/RegistrarWhoisResponseTest.java index 92c98fd4f..2150e7efc 100644 --- a/core/src/test/java/google/registry/whois/RegistrarWhoisResponseTest.java +++ b/core/src/test/java/google/registry/whois/RegistrarWhoisResponseTest.java @@ -25,7 +25,6 @@ import com.google.common.collect.ImmutableSet; import google.registry.model.registrar.Registrar; import google.registry.model.registrar.RegistrarAddress; import google.registry.model.registrar.RegistrarPoc; -import google.registry.model.registrar.RegistrarPocBase; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension; import google.registry.testing.FakeClock; @@ -75,7 +74,7 @@ class RegistrarWhoisResponseTest { .setEmailAddress("joeregistrar@example-registrar.tld") .setPhoneNumber("+1.3105551213") .setFaxNumber("+1.3105551213") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.ADMIN)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.ADMIN)) .setVisibleInWhoisAsAdmin(true) .setVisibleInWhoisAsTech(false) .build(), @@ -85,7 +84,7 @@ class RegistrarWhoisResponseTest { .setEmailAddress("johndoe@example-registrar.tld") .setPhoneNumber("+1.1111111111") .setFaxNumber("+1.1111111111") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.ADMIN)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.ADMIN)) .build(), new RegistrarPoc.Builder() .setRegistrar(registrar) @@ -93,7 +92,7 @@ class RegistrarWhoisResponseTest { .setEmailAddress("janeregistrar@example-registrar.tld") .setPhoneNumber("+1.3105551214") .setFaxNumber("+1.3105551213") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.ADMIN)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.ADMIN)) .setVisibleInWhoisAsAdmin(true) .build(), new RegistrarPoc.Builder() @@ -102,7 +101,7 @@ class RegistrarWhoisResponseTest { .setEmailAddress("janedoe@example-registrar.tld") .setPhoneNumber("+1.1111111112") .setFaxNumber("+1.1111111112") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.TECH)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.TECH)) .build(), new RegistrarPoc.Builder() .setRegistrar(registrar) @@ -110,7 +109,7 @@ class RegistrarWhoisResponseTest { .setEmailAddress("johngeek@example-registrar.tld") .setPhoneNumber("+1.3105551215") .setFaxNumber("+1.3105551216") - .setTypes(ImmutableSet.of(RegistrarPocBase.Type.TECH)) + .setTypes(ImmutableSet.of(RegistrarPoc.Type.TECH)) .setVisibleInWhoisAsTech(true) .build()); persistResource(registrar); diff --git a/core/src/test/java/google/registry/whois/WhoisActionTest.java b/core/src/test/java/google/registry/whois/WhoisActionTest.java index 385f8dd8b..9974860d3 100644 --- a/core/src/test/java/google/registry/whois/WhoisActionTest.java +++ b/core/src/test/java/google/registry/whois/WhoisActionTest.java @@ -17,8 +17,8 @@ package google.registry.whois; import static com.google.common.truth.Truth.assertThat; import static google.registry.bsa.persistence.BsaTestingUtils.persistBsaLabel; import static google.registry.model.EppResourceUtils.loadByForeignKeyCached; -import static google.registry.model.registrar.RegistrarBase.State.ACTIVE; -import static google.registry.model.registrar.RegistrarBase.Type.PDT; +import static google.registry.model.registrar.Registrar.State.ACTIVE; +import static google.registry.model.registrar.Registrar.Type.PDT; import static google.registry.model.tld.Tlds.getTlds; import static google.registry.testing.DatabaseHelper.createTlds; import static google.registry.testing.DatabaseHelper.loadRegistrar; diff --git a/core/src/test/java/google/registry/whois/WhoisHttpActionTest.java b/core/src/test/java/google/registry/whois/WhoisHttpActionTest.java index 6fd570b59..0bfaeda21 100644 --- a/core/src/test/java/google/registry/whois/WhoisHttpActionTest.java +++ b/core/src/test/java/google/registry/whois/WhoisHttpActionTest.java @@ -17,7 +17,7 @@ package google.registry.whois; import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8; import static com.google.common.truth.Truth.assertThat; import static google.registry.bsa.persistence.BsaTestingUtils.persistBsaLabel; -import static google.registry.model.registrar.RegistrarBase.State.ACTIVE; +import static google.registry.model.registrar.Registrar.State.ACTIVE; import static google.registry.testing.DatabaseHelper.createTlds; import static google.registry.testing.DatabaseHelper.loadRegistrar; import static google.registry.testing.DatabaseHelper.persistResource; diff --git a/db/src/main/resources/sql/er_diagram/brief_er_diagram.html b/db/src/main/resources/sql/er_diagram/brief_er_diagram.html index 25a05d456..856111365 100644 --- a/db/src/main/resources/sql/er_diagram/brief_er_diagram.html +++ b/db/src/main/resources/sql/er_diagram/brief_er_diagram.html @@ -261,7 +261,7 @@ td.section { generated on - 2025-04-17 17:11:45 + 2025-04-18 20:02:20 last flyway file @@ -280,7 +280,7 @@ td.section { generated by SchemaCrawler 16.25.2 generated on - 2025-04-17 17:11:45 + 2025-04-18 20:02:20 @@ -2689,7 +2689,7 @@ td.section { <tr> <td class="spacer"></td> <td class="minwidth"></td> - <td class="minwidth">default '2021-06-01 00:00:00+00'::timestamp with time zone</td> + <td class="minwidth">default '2021-05-31 20:00:00-04'::timestamp with time zone</td> </tr> <tr> <td colspan="3"></td> diff --git a/db/src/main/resources/sql/er_diagram/full_er_diagram.html b/db/src/main/resources/sql/er_diagram/full_er_diagram.html index 0456f7c45..9799bd04c 100644 --- a/db/src/main/resources/sql/er_diagram/full_er_diagram.html +++ b/db/src/main/resources/sql/er_diagram/full_er_diagram.html @@ -261,7 +261,7 @@ td.section { </tr> <tr> <td class="property_name">generated on</td> - <td class="property_value">2025-04-17 17:11:38</td> + <td class="property_value">2025-04-18 20:02:17</td> </tr> <tr> <td class="property_name">last flyway file</td> @@ -280,7 +280,7 @@ td.section { <text text-anchor="start" x="5435" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">generated by</text> <text text-anchor="start" x="5518" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">SchemaCrawler 16.25.2</text> <text text-anchor="start" x="5434" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">generated on</text> - <text text-anchor="start" x="5518" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">2025-04-17 17:11:38</text> + <text text-anchor="start" x="5518" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">2025-04-18 20:02:17</text> <polygon fill="none" stroke="#888888" points="5431,-4 5431,-44 5667,-44 5667,-4 5431,-4" /> <!-- allocationtoken_a08ccbef --> <g id="node1" class="node"> <title> @@ -4770,7 +4770,7 @@ td.section { <tr> <td class="spacer"></td> <td class="minwidth"></td> - <td class="minwidth">default '2021-06-01 00:00:00+00'::timestamp with time zone</td> + <td class="minwidth">default '2021-05-31 20:00:00-04'::timestamp with time zone</td> </tr> <tr> <td colspan="3"></td> diff --git a/db/src/main/resources/sql/schema/db-schema.sql.generated b/db/src/main/resources/sql/schema/db-schema.sql.generated index 9b1706df8..5e9674828 100644 --- a/db/src/main/resources/sql/schema/db-schema.sql.generated +++ b/db/src/main/resources/sql/schema/db-schema.sql.generated @@ -134,20 +134,6 @@ primary key (revision_id) ); - create table "ConsoleEppActionHistory" ( - history_revision_id bigint not null, - history_method text not null, - history_modification_time timestamp(6) with time zone not null, - history_request_body text, - history_type text not null check (history_type in ('DOMAIN_DELETE','DOMAIN_SUSPEND','DOMAIN_UNSUSPEND','EPP_PASSWORD_UPDATE','REGISTRAR_CREATE','REGISTRAR_SECURITY_UPDATE','REGISTRAR_UPDATE','USER_CREATE','USER_DELETE','USER_UPDATE')), - history_url text not null, - history_entry_class text not null, - repo_id text not null, - revision_id bigint, - history_acting_user text not null, - primary key (history_revision_id) - ); - create table "ConsoleUpdateHistory" ( revision_id bigint not null, description text, @@ -699,87 +685,6 @@ primary key (email_address, registrar_id) ); - create table "RegistrarPocUpdateHistory" ( - history_revision_id bigint not null, - history_method text not null, - history_modification_time timestamp(6) with time zone not null, - history_request_body text, - history_type text not null check (history_type in ('DOMAIN_DELETE','DOMAIN_SUSPEND','DOMAIN_UNSUSPEND','EPP_PASSWORD_UPDATE','REGISTRAR_CREATE','REGISTRAR_SECURITY_UPDATE','REGISTRAR_UPDATE','USER_CREATE','USER_DELETE','USER_UPDATE')), - history_url text not null, - email_address text not null, - registrar_id text not null, - allowed_to_set_registry_lock_password boolean not null, - fax_number text, - name text, - phone_number text, - registry_lock_email_address text, - registry_lock_password_hash text, - registry_lock_password_salt text, - types text[], - visible_in_domain_whois_as_abuse boolean not null, - visible_in_whois_as_admin boolean not null, - visible_in_whois_as_tech boolean not null, - history_acting_user text not null, - primary key (history_revision_id) - ); - - create table "RegistrarUpdateHistory" ( - history_revision_id bigint not null, - history_method text not null, - history_modification_time timestamp(6) with time zone not null, - history_request_body text, - history_type text not null check (history_type in ('DOMAIN_DELETE','DOMAIN_SUSPEND','DOMAIN_UNSUSPEND','EPP_PASSWORD_UPDATE','REGISTRAR_CREATE','REGISTRAR_SECURITY_UPDATE','REGISTRAR_UPDATE','USER_CREATE','USER_DELETE','USER_UPDATE')), - history_url text not null, - allowed_tlds text[], - billing_account_map hstore, - block_premium_names boolean not null, - client_certificate text, - client_certificate_hash text, - contacts_require_syncing boolean not null, - creation_time timestamp(6) with time zone not null, - drive_folder_id text, - email_address text, - failover_client_certificate text, - failover_client_certificate_hash text, - fax_number text, - iana_identifier bigint, - icann_referral_email text, - i18n_address_city text, - i18n_address_country_code text, - i18n_address_state text, - i18n_address_street_line1 text, - i18n_address_street_line2 text, - i18n_address_street_line3 text, - i18n_address_zip text, - ip_address_allow_list text[], - last_certificate_update_time timestamp(6) with time zone, - last_expiring_cert_notification_sent_date timestamp(6) with time zone, - last_expiring_failover_cert_notification_sent_date timestamp(6) with time zone, - localized_address_city text, - localized_address_country_code text, - localized_address_state text, - localized_address_street_line1 text, - localized_address_street_line2 text, - localized_address_street_line3 text, - localized_address_zip text, - password_hash text, - phone_number text, - phone_passcode text, - po_number text, - rdap_base_urls text[], - registrar_name text not null, - registry_lock_allowed boolean not null, - password_salt text, - state text check (state in ('PENDING','ACTIVE','SUSPENDED','DISABLED')), - type text not null check (type in ('REAL','OTE','PDT','EXTERNAL_MONITORING','INTERNAL','MONITORING','TEST')), - url text, - whois_server text, - update_timestamp timestamp(6) with time zone, - registrar_id text not null, - history_acting_user text not null, - primary key (history_revision_id) - ); - create table "RegistryLock" ( revision_id bigint generated by default as identity, last_update_time timestamp(6) with time zone not null, @@ -912,25 +817,6 @@ primary key (email_address) ); - create table "UserUpdateHistory" ( - history_revision_id bigint not null, - history_method text not null, - history_modification_time timestamp(6) with time zone not null, - history_request_body text, - history_type text not null check (history_type in ('DOMAIN_DELETE','DOMAIN_SUSPEND','DOMAIN_UNSUSPEND','EPP_PASSWORD_UPDATE','REGISTRAR_CREATE','REGISTRAR_SECURITY_UPDATE','REGISTRAR_UPDATE','USER_CREATE','USER_DELETE','USER_UPDATE')), - history_url text not null, - email_address text not null, - registry_lock_email_address text, - registry_lock_password_hash text, - registry_lock_password_salt text, - global_role text not null check (global_role in ('NONE','SUPPORT_AGENT','SUPPORT_LEAD','FTE')), - is_admin boolean not null, - registrar_roles hstore, - update_timestamp timestamp(6) with time zone, - history_acting_user text not null, - primary key (history_revision_id) - ); - create index allocation_token_domain_name_idx on "AllocationToken" (domain_name); @@ -1012,15 +898,6 @@ create index IDXj874kw19bgdnkxo1rue45jwlw on "BsaDownload" (creation_time); - create index IDXcclyb3n5gbex8u8m9fjlujitw - on "ConsoleEppActionHistory" (history_acting_user); - - create index IDX6y67d6wsffmr6jcxax5ghwqhd - on "ConsoleEppActionHistory" (repo_id); - - create index IDXiahqo1d1fqdfknywmj2xbxl7t - on "ConsoleEppActionHistory" (revision_id); - create index idx_console_update_history_acting_user on "ConsoleUpdateHistory" (acting_user); @@ -1204,21 +1081,6 @@ create index registrar_iana_identifier_idx on "Registrar" (iana_identifier); - create index IDXrn6posxkx58de1cp09g5257cw - on "RegistrarPocUpdateHistory" (history_acting_user); - - create index IDXr1cxua6it0rxgt9tpyugspxk - on "RegistrarPocUpdateHistory" (email_address); - - create index IDXfr24wvpg8qalwqy4pni7evrpj - on "RegistrarPocUpdateHistory" (registrar_id); - - create index IDXm6k18dusy2lfi5y81k8g256sa - on "RegistrarUpdateHistory" (history_acting_user); - - create index IDX3d1mucv7axrhud8w8jl4vsu62 - on "RegistrarUpdateHistory" (registrar_id); - create index idx_registry_lock_verification_code on "RegistryLock" (verification_code); @@ -1237,17 +1099,6 @@ create index spec11threatmatch_check_date_idx on "Spec11ThreatMatch" (check_date); - create index IDXbjacjlm8ianc4kxxvamnu94k5 - on "UserUpdateHistory" (history_acting_user); - - create index IDX5yqacw829y5bm6f7eajsq1cts - on "UserUpdateHistory" (email_address); - - alter table if exists "ConsoleEppActionHistory" - add constraint FKb686b9os2nsjpv930npa4r3b4 - foreign key (history_acting_user) - references "User"; - alter table if exists "ConsoleUpdateHistory" add constraint FKnhl1eolgix64u90xv3pj6xa3x foreign key (acting_user) @@ -1288,16 +1139,6 @@ foreign key (domain_repo_id, domain_history_revision_id) references "DomainHistory"; - alter table if exists "RegistrarPocUpdateHistory" - add constraint FKftpbwctxtkc1i0njc0tdcaa2g - foreign key (history_acting_user) - references "User"; - - alter table if exists "RegistrarUpdateHistory" - add constraint FKsr7w342s7x5s5jvdti2axqeq8 - foreign key (history_acting_user) - references "User"; - alter table if exists "RegistryLock" add constraint FK2lhcwpxlnqijr96irylrh1707 foreign key (relock_revision_id) @@ -1307,8 +1148,3 @@ add constraint FK5ivlhvs3121yx2li5tqh54u4 foreign key (revision_id) references "SignedMarkRevocationList"; - - alter table if exists "UserUpdateHistory" - add constraint FK1s7bopbl3pwrhv3jkkofnv3o0 - foreign key (history_acting_user) - references "User";