1
0
mirror of https://github.com/google/nomulus synced 2026-05-17 13:21:48 +00:00

Compare commits

...

8 Commits

Author SHA1 Message Date
Pavlo Tkach
2d072c3844 Update jetty console static files cache policies (#2731) 2025-03-28 19:53:02 +00:00
Pavlo Tkach
c15dec4419 Downgrade node type for pubapi and console, enable bursting for frontend and backend (#2723) 2025-03-28 19:14:33 +00:00
gbrodman
8340125bf4 Remove user FKs from console history tables (#2729)
This, obviously, can mess up user deletion
2025-03-25 20:47:47 +00:00
Pavlo Tkach
98ba80d94e Remove console security settings timeout (#2728) 2025-03-25 19:36:52 +00:00
gbrodman
967d04efce Include TLD in reserved/registered lists too (#2725)
We already do this for premium terms, but it's nice to do it for the
other list types too

https://b.corp.google.com/issues/390053672
2025-03-24 15:52:12 +00:00
gbrodman
20fd944e83 Remove allocation token custom logic (#2727)
This was added back in early 2018 long ago to enable promotions, but
since then (and for many years) we've added the ability to run
promotions on the tokens themselves, rather than relying on custom Java
classes.

This will make the changes for b/315504612 much easier, as that will
split up token validation into "is this token valid in general?" and "is
this token valid for this domain/action?"
2025-03-21 20:48:54 +00:00
gbrodman
daa56e6d85 Bump the number of retries in transaction failures and add skew (#2699)
This can potentially help even more with serializable transaction
failures (optimistic locking exceptions, which are expected to occur
somewhat frequently).

With six attempts, we will sleep at most five times, for
100+200+400+800+1600 ms each, for a total of at most 3.1 seconds (much
less than the EPP maximum which I believe (?) to be 30 seconds.

In addition, we add a 20% skew in an attempt to spread out
possibly-conflicting transaction retries.
2025-03-21 19:47:55 +00:00
gbrodman
ed33c7424d Add and use new SimpleConsoleUpdateHistory table (#2712)
This changes the code to only save console histories of this type. We
keep the old Java code (and, necessarily, the corresponding SQL code)
for now because there's no harm in doing so and we want to avoid hastily
deleting too much.
2025-03-21 14:46:16 +00:00
56 changed files with 5007 additions and 5247 deletions

View File

@@ -25,7 +25,11 @@ import textwrap
import re
# We should never analyze any generated files
UNIVERSALLY_SKIPPED_PATTERNS = {"/build/", "cloudbuild-caches", "/out/", ".git/", ".gradle/", "/dist/", "/console-alpha/", "/console-crash/", "/console-qa", "/console-sandbox", "/console-production", "karma.conf.js", "polyfills.ts", "test.ts", "/docs/console-endpoints/"}
UNIVERSALLY_SKIPPED_PATTERNS = {"/build/", "cloudbuild-caches", "/out/", ".git/",
".gradle/", "/dist/", "/console-alpha/", "/console-crash/", "/console-qa",
"/console-sandbox", "/console-production", "karma.conf.js", "polyfills.ts",
"test.ts", "/docs/console-endpoints/", "/bin/generated-sources/",
"/bin/generated-test-sources/", "src/main/generated", "src/test/generated"}
# We can't rely on CI to have the Enum package installed so we do this instead.
FORBIDDEN = 1
REQUIRED = 2
@@ -87,11 +91,9 @@ PRESUBMITS = {
PresubmitCheck(
r".*Copyright 20\d{2} The Nomulus Authors\. All Rights Reserved\.",
("java", "js", "soy", "sql", "py", "sh", "gradle", "ts"), {
".git", "/build/", "/bin/generated-sources/", "/bin/generated-test-sources/",
"node_modules/", "LoggerConfig.java", "registrar_bin.",
".git", "/build/", "node_modules/", "LoggerConfig.java", "registrar_bin.",
"registrar_dbg.", "google-java-format-diff.py",
"nomulus.golden.sql", "soyutils_usegoog.js", "javascript/checks.js",
"/src/main/generated", "/src/test/generated"
"nomulus.golden.sql", "soyutils_usegoog.js", "javascript/checks.js"
}, REQUIRED):
"File did not include the license header.",

View File

@@ -13,7 +13,7 @@
// limitations under the License.
import { Injectable } from '@angular/core';
import { switchMap, timeout } from 'rxjs';
import { switchMap } from 'rxjs';
import {
IpAllowListItem,
RegistrarService,
@@ -69,7 +69,6 @@ export class SecurityService {
uiToApiConverter(newSecuritySettings)
)
.pipe(
timeout(2000),
switchMap(() => {
return this.registrarService.loadRegistrars();
})

View File

@@ -1111,12 +1111,6 @@ public final class RegistryConfig {
return config.registryPolicy.whoisCommandFactoryClass;
}
@Provides
@Config("allocationTokenCustomLogicClass")
public static String provideAllocationTokenCustomLogicClass(RegistryConfigSettings config) {
return config.registryPolicy.allocationTokenCustomLogicClass;
}
@Provides
@Config("dnsCountQueryCoordinatorClass")
public static String dnsCountQueryCoordinatorClass(RegistryConfigSettings config) {

View File

@@ -91,7 +91,6 @@ public class RegistryConfigSettings {
public String productName;
public String customLogicFactoryClass;
public String whoisCommandFactoryClass;
public String allocationTokenCustomLogicClass;
public String dnsCountQueryCoordinatorClass;
public int contactAutomaticTransferDays;
public String greetingServerId;

View File

@@ -69,10 +69,6 @@ registryPolicy:
# See whois/WhoisCommandFactory.java
whoisCommandFactoryClass: google.registry.whois.WhoisCommandFactory
# Custom logic class for handling allocation tokens.
# See flows/domain/token/AllocationTokenCustomLogic.java
allocationTokenCustomLogicClass: google.registry.flows.domain.token.AllocationTokenCustomLogic
# Custom logic class for handling DNS query count reporting for ICANN.
# See reporting/icann/DnsCountQueryCoordinator.java
dnsCountQueryCoordinatorClass: google.registry.reporting.icann.DummyDnsCountQueryCoordinator

View File

@@ -78,7 +78,7 @@ public class ExportDomainListsAction implements Runnable {
ORDER BY d.domain_name""";
// This may be a CSV, but it is uses a .txt file extension for back-compatibility
static final String REGISTERED_DOMAINS_FILENAME = "registered_domains.txt";
static final String REGISTERED_DOMAINS_FILENAME_FORMAT = "registered_domains_%s.txt";
@Inject Clock clock;
@Inject DriveConnection driveConnection;
@@ -146,7 +146,7 @@ public class ExportDomainListsAction implements Runnable {
} else {
String resultMsg =
driveConnection.createOrUpdateFile(
REGISTERED_DOMAINS_FILENAME,
String.format(REGISTERED_DOMAINS_FILENAME_FORMAT, tldStr),
MediaType.PLAIN_TEXT_UTF_8,
tld.getDriveFolderId(),
domains.getBytes(UTF_8));

View File

@@ -43,7 +43,7 @@ public class ExportReservedTermsAction implements Runnable {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
static final MediaType EXPORT_MIME_TYPE = MediaType.PLAIN_TEXT_UTF_8;
static final String RESERVED_TERMS_FILENAME = "reserved_terms.txt";
static final String RESERVED_TERMS_FILENAME_FORMAT = "reserved_terms_%s.txt";
@Inject DriveConnection driveConnection;
@Inject ExportUtils exportUtils;
@@ -79,7 +79,7 @@ public class ExportReservedTermsAction implements Runnable {
} else {
resultMsg =
driveConnection.createOrUpdateFile(
RESERVED_TERMS_FILENAME,
String.format(RESERVED_TERMS_FILENAME_FORMAT, tldStr),
EXPORT_MIME_TYPE,
tld.getDriveFolderId(),
exportUtils.exportReservedTerms(tld).getBytes(UTF_8));

View File

@@ -43,7 +43,6 @@ import google.registry.flows.domain.DomainTransferQueryFlow;
import google.registry.flows.domain.DomainTransferRejectFlow;
import google.registry.flows.domain.DomainTransferRequestFlow;
import google.registry.flows.domain.DomainUpdateFlow;
import google.registry.flows.domain.token.AllocationTokenModule;
import google.registry.flows.host.HostCheckFlow;
import google.registry.flows.host.HostCreateFlow;
import google.registry.flows.host.HostDeleteFlow;
@@ -59,7 +58,6 @@ import google.registry.model.eppcommon.Trid;
/** Dagger component for flow classes. */
@FlowScope
@Subcomponent(modules = {
AllocationTokenModule.class,
BatchModule.class,
CustomLogicModule.class,
DnsModule.class,

View File

@@ -1,64 +0,0 @@
// Copyright 2017 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.flows.domain.token;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.net.InternetDomainName;
import google.registry.flows.EppException;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainCommand;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.tld.Tld;
import org.joda.time.DateTime;
/**
* A no-op base class for allocation token custom logic.
*
* <p>Extend this class and override the hook(s) to perform custom logic.
*/
public class AllocationTokenCustomLogic {
/** Performs additional custom logic for validating a token on a domain create. */
public AllocationToken validateToken(
DomainCommand.Create command,
AllocationToken token,
Tld tld,
String registrarId,
DateTime now)
throws EppException {
// Do nothing.
return token;
}
/** Performs additional custom logic for validating a token on an existing domain. */
public AllocationToken validateToken(
Domain domain, AllocationToken token, Tld tld, String registrarId, DateTime now)
throws EppException {
// Do nothing.
return token;
}
/** Performs additional custom logic for performing domain checks using a token. */
public ImmutableMap<InternetDomainName, String> checkDomainsWithToken(
ImmutableList<InternetDomainName> domainNames,
AllocationToken token,
String registrarId,
DateTime now) {
// Do nothing.
return Maps.toMap(domainNames, k -> "");
}
}

View File

@@ -19,7 +19,6 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import static google.registry.pricing.PricingEngineProxy.isDomainPremium;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.net.InternetDomainName;
@@ -48,12 +47,8 @@ import org.joda.time.DateTime;
/** Utility functions for dealing with {@link AllocationToken}s in domain flows. */
public class AllocationTokenFlowUtils {
private final AllocationTokenCustomLogic tokenCustomLogic;
@Inject
AllocationTokenFlowUtils(AllocationTokenCustomLogic tokenCustomLogic) {
this.tokenCustomLogic = tokenCustomLogic;
}
public AllocationTokenFlowUtils() {}
/**
* Checks if the allocation token applies to the given domain names, used for domain checks.
@@ -75,7 +70,6 @@ public class AllocationTokenFlowUtils {
// If the token is only invalid for some domain names (e.g. an invalid TLD), include those error
// results for only those domain names
ImmutableList.Builder<InternetDomainName> validDomainNames = new ImmutableList.Builder<>();
ImmutableMap.Builder<InternetDomainName, String> resultsBuilder = new ImmutableMap.Builder<>();
for (InternetDomainName domainName : domainNames) {
try {
@@ -86,16 +80,11 @@ public class AllocationTokenFlowUtils {
registrarId,
isDomainPremium(domainName.toString(), now),
now);
validDomainNames.add(domainName);
resultsBuilder.put(domainName, "");
} catch (EppException e) {
resultsBuilder.put(domainName, e.getMessage());
}
}
// For all valid domain names, run the custom logic and include the results
resultsBuilder.putAll(
tokenCustomLogic.checkDomainsWithToken(
validDomainNames.build(), tokenEntity, registrarId, now));
return new AllocationTokenDomainCheckResults(Optional.of(tokenEntity), resultsBuilder.build());
}
@@ -209,7 +198,7 @@ public class AllocationTokenFlowUtils {
registrarId,
isDomainPremium(command.getDomainName(), now),
now);
return Optional.of(tokenCustomLogic.validateToken(command, tokenEntity, tld, registrarId, now));
return Optional.of(tokenEntity);
}
/** Verifies and returns the allocation token if one is specified, otherwise does nothing. */
@@ -232,8 +221,7 @@ public class AllocationTokenFlowUtils {
registrarId,
isDomainPremium(existingDomain.getDomainName(), now),
now);
return Optional.of(
tokenCustomLogic.validateToken(existingDomain, tokenEntity, tld, registrarId, now));
return Optional.of(tokenEntity);
}
public static void verifyTokenAllowedOnDomain(

View File

@@ -1,33 +0,0 @@
// Copyright 2017 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.flows.domain.token;
import static google.registry.util.TypeUtils.getClassFromString;
import static google.registry.util.TypeUtils.instantiate;
import dagger.Module;
import dagger.Provides;
import google.registry.config.RegistryConfig.Config;
/** Dagger module for allocation token classes. */
@Module
public class AllocationTokenModule {
@Provides
static AllocationTokenCustomLogic provideAllocationTokenCustomLogic(
@Config("allocationTokenCustomLogicClass") String customClass) {
return instantiate(getClassFromString(customClass, AllocationTokenCustomLogic.class));
}
}

View File

@@ -41,10 +41,12 @@ import org.joda.time.DateTime;
public abstract class ConsoleUpdateHistory extends ImmutableObject implements Buildable {
public enum Type {
EPP_ACTION,
POC_CREATE,
POC_UPDATE,
POC_DELETE,
DOMAIN_DELETE,
DOMAIN_SUSPEND,
DOMAIN_UNSUSPEND,
EPP_PASSWORD_UPDATE,
REGISTRAR_CREATE,
REGISTRAR_SECURITY_UPDATE,
REGISTRAR_UPDATE,
USER_CREATE,
USER_DELETE,

View File

@@ -0,0 +1,153 @@
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.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<String> 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<SimpleConsoleUpdateHistory> {
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;
}
}
}

View File

@@ -86,7 +86,7 @@ import org.joda.time.DateTime;
public class JpaTransactionManagerImpl implements JpaTransactionManager {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final Retrier retrier = new Retrier(new SystemSleeper(), 3);
private static final Retrier retrier = new Retrier(new SystemSleeper(), 6);
private static final String NESTED_TRANSACTION_MESSAGE =
"Nested transaction detected. Try refactoring to avoid nested transactions. If unachievable,"
+ " use reTransact() in nested transactions";

View File

@@ -37,7 +37,7 @@ 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.ConsoleUpdateHistory;
import google.registry.model.console.SimpleConsoleUpdateHistory;
import google.registry.model.console.User;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarPoc;
@@ -262,7 +262,7 @@ public abstract class ConsoleApiAction implements Runnable {
}
}
protected void finishAndPersistConsoleUpdateHistory(ConsoleUpdateHistory.Builder<?, ?> builder) {
protected void finishAndPersistConsoleUpdateHistory(SimpleConsoleUpdateHistory.Builder builder) {
builder.setActingUser(consoleApiParams.authResult().user().get());
builder.setUrl(consoleApiParams.request().getRequestURI());
builder.setMethod(consoleApiParams.request().getMethod());

View File

@@ -28,7 +28,7 @@ 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.RegistrarUpdateHistory;
import google.registry.model.console.SimpleConsoleUpdateHistory;
import google.registry.model.console.User;
import google.registry.model.registrar.Registrar;
import google.registry.request.Action;
@@ -107,14 +107,10 @@ public class ConsoleEppPasswordAction extends ConsoleApiAction {
Registrar updatedRegistrar =
registrar.asBuilder().setPassword(eppRequestBody.newPassword()).build();
tm().put(updatedRegistrar);
EppPasswordData sanitizedData =
new EppPasswordData(
eppRequestBody.registrarId, "********", "••••••••", "••••••••");
finishAndPersistConsoleUpdateHistory(
new RegistrarUpdateHistory.Builder()
.setType(ConsoleUpdateHistory.Type.REGISTRAR_UPDATE)
.setRegistrar(updatedRegistrar)
.setRequestBody(consoleApiParams.gson().toJson(sanitizedData)));
new SimpleConsoleUpdateHistory.Builder()
.setType(ConsoleUpdateHistory.Type.EPP_PASSWORD_UPDATE)
.setDescription(registrar.getRegistrarId()));
sendExternalUpdates(
ImmutableMap.of("password", new DiffUtils.DiffPair("********", "••••••••")),
registrar,

View File

@@ -24,7 +24,7 @@ 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.RegistrarUpdateHistory;
import google.registry.model.console.SimpleConsoleUpdateHistory;
import google.registry.model.console.User;
import google.registry.model.registrar.Registrar;
import google.registry.request.Action;
@@ -102,10 +102,9 @@ public class ConsoleUpdateRegistrarAction extends ConsoleApiAction {
tm().put(updatedRegistrar);
finishAndPersistConsoleUpdateHistory(
new RegistrarUpdateHistory.Builder()
new SimpleConsoleUpdateHistory.Builder()
.setType(ConsoleUpdateHistory.Type.REGISTRAR_UPDATE)
.setRegistrar(updatedRegistrar)
.setRequestBody(consoleApiParams.gson().toJson(registrarParam)));
.setDescription(updatedRegistrar.getRegistrarId()));
sendExternalUpdatesIfNecessary(
EmailInfo.create(
existingRegistrar.get(),

View File

@@ -28,7 +28,7 @@ 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.RegistrarUpdateHistory;
import google.registry.model.console.SimpleConsoleUpdateHistory;
import google.registry.model.console.User;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarBase;
@@ -174,10 +174,9 @@ public class RegistrarsAction extends ConsoleApiAction {
registrar.getRegistrarId());
tm().putAll(registrar, contact);
finishAndPersistConsoleUpdateHistory(
new RegistrarUpdateHistory.Builder()
.setType(ConsoleUpdateHistory.Type.REGISTRAR_UPDATE)
.setRegistrar(registrar)
.setRequestBody(consoleApiParams.gson().toJson(registrar)));
new SimpleConsoleUpdateHistory.Builder()
.setType(ConsoleUpdateHistory.Type.REGISTRAR_CREATE)
.setDescription(registrar.getRegistrarId()));
});
}

View File

@@ -15,6 +15,7 @@
package google.registry.ui.server.console.domains;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -25,6 +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.User;
import google.registry.model.eppcommon.ProtocolDefinition;
import google.registry.model.eppoutput.EppOutput;
@@ -93,11 +95,29 @@ public class ConsoleBulkDomainAction extends ConsoleApiAction {
domainList.domainList.stream()
.collect(
toImmutableMap(d -> d, d -> executeEpp(actionType.getXmlContentsToRun(d), user)));
handleHistoryAdditions(result, actionType);
// Front end should parse situations where only some commands worked
consoleApiParams.response().setPayload(consoleApiParams.gson().toJson(result));
consoleApiParams.response().setStatus(SC_OK);
}
private void handleHistoryAdditions(
ImmutableMap<String, ConsoleEppOutput> result, ConsoleDomainActionType actionType) {
if (result.values().stream().noneMatch(ConsoleEppOutput::isSuccess)) {
return;
}
tm().transact(
() ->
result.entrySet().stream()
.filter(e -> e.getValue().isSuccess())
.forEach(
e ->
finishAndPersistConsoleUpdateHistory(
new SimpleConsoleUpdateHistory.Builder()
.setDescription(e.getKey())
.setType(actionType.getConsoleUpdateHistoryType()))));
}
private ConsoleEppOutput executeEpp(String xml, User user) {
return ConsoleEppOutput.fromEppOutput(
eppController.handleEppCommand(
@@ -115,5 +135,9 @@ public class ConsoleBulkDomainAction extends ConsoleApiAction {
Result result = eppOutput.getResponse().getResult();
return new ConsoleEppOutput(result.getMsg(), result.getCode().code);
}
boolean isSuccess() {
return responseCode < 2000;
}
}
}

View File

@@ -16,6 +16,7 @@ package google.registry.ui.server.console.domains;
import com.google.gson.JsonElement;
import google.registry.model.console.ConsolePermission;
import google.registry.model.console.ConsoleUpdateHistory;
/** An action that will run a delete EPP command on the given domain. */
public class ConsoleBulkDomainDeleteActionType extends ConsoleDomainActionType {
@@ -54,4 +55,9 @@ public class ConsoleBulkDomainDeleteActionType extends ConsoleDomainActionType {
public ConsolePermission getNecessaryPermission() {
return ConsolePermission.EXECUTE_EPP_COMMANDS;
}
@Override
public ConsoleUpdateHistory.Type getConsoleUpdateHistoryType() {
return ConsoleUpdateHistory.Type.DOMAIN_DELETE;
}
}

View File

@@ -16,6 +16,7 @@ package google.registry.ui.server.console.domains;
import com.google.gson.JsonElement;
import google.registry.model.console.ConsolePermission;
import google.registry.model.console.ConsoleUpdateHistory;
/** An action that will suspend the given domain, assigning all 5 server*Prohibited statuses. */
public class ConsoleBulkDomainSuspendActionType extends ConsoleDomainActionType {
@@ -64,4 +65,9 @@ public class ConsoleBulkDomainSuspendActionType extends ConsoleDomainActionType
public ConsolePermission getNecessaryPermission() {
return ConsolePermission.SUSPEND_DOMAIN;
}
@Override
public ConsoleUpdateHistory.Type getConsoleUpdateHistoryType() {
return ConsoleUpdateHistory.Type.DOMAIN_SUSPEND;
}
}

View File

@@ -16,6 +16,7 @@ package google.registry.ui.server.console.domains;
import com.google.gson.JsonElement;
import google.registry.model.console.ConsolePermission;
import google.registry.model.console.ConsoleUpdateHistory;
/** An action that will unsuspend the given domain, removing all 5 server*Prohibited statuses. */
public class ConsoleBulkDomainUnsuspendActionType extends ConsoleDomainActionType {
@@ -64,4 +65,9 @@ public class ConsoleBulkDomainUnsuspendActionType extends ConsoleDomainActionTyp
public ConsolePermission getNecessaryPermission() {
return ConsolePermission.SUSPEND_DOMAIN;
}
@Override
public ConsoleUpdateHistory.Type getConsoleUpdateHistoryType() {
return ConsoleUpdateHistory.Type.DOMAIN_UNSUSPEND;
}
}

View File

@@ -18,6 +18,7 @@ import com.google.common.escape.Escaper;
import com.google.common.xml.XmlEscapers;
import com.google.gson.JsonElement;
import google.registry.model.console.ConsolePermission;
import google.registry.model.console.ConsoleUpdateHistory;
/**
* A type of EPP action to perform on domain(s), run by the {@link ConsoleBulkDomainAction}.
@@ -68,6 +69,9 @@ public abstract class ConsoleDomainActionType {
/** Returns the permission necessary to successfully perform this action. */
public abstract ConsolePermission getNecessaryPermission();
/** Returns the type of history / audit logging object to save. */
public abstract ConsoleUpdateHistory.Type getConsoleUpdateHistoryType();
/** Returns the XML template contents for this action. */
protected abstract String getXmlTemplate();

View File

@@ -26,7 +26,7 @@ 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.RegistrarUpdateHistory;
import google.registry.model.console.SimpleConsoleUpdateHistory;
import google.registry.model.console.User;
import google.registry.model.registrar.Registrar;
import google.registry.request.Action;
@@ -120,10 +120,9 @@ public class SecurityAction extends ConsoleApiAction {
Registrar updatedRegistrar = updatedRegistrarBuilder.build();
tm().put(updatedRegistrar);
finishAndPersistConsoleUpdateHistory(
new RegistrarUpdateHistory.Builder()
.setType(ConsoleUpdateHistory.Type.REGISTRAR_UPDATE)
.setRegistrar(updatedRegistrar)
.setRequestBody(consoleApiParams.gson().toJson(registrar.get())));
new SimpleConsoleUpdateHistory.Builder()
.setType(ConsoleUpdateHistory.Type.REGISTRAR_SECURITY_UPDATE)
.setDescription(registrarId));
sendExternalUpdatesIfNecessary(
EmailInfo.create(savedRegistrar, updatedRegistrar, ImmutableSet.of(), ImmutableSet.of()));

View File

@@ -23,7 +23,7 @@ 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.RegistrarUpdateHistory;
import google.registry.model.console.SimpleConsoleUpdateHistory;
import google.registry.model.console.User;
import google.registry.model.registrar.Registrar;
import google.registry.request.Action;
@@ -107,10 +107,9 @@ public class WhoisRegistrarFieldsAction extends ConsoleApiAction {
.build();
tm().put(newRegistrar);
finishAndPersistConsoleUpdateHistory(
new RegistrarUpdateHistory.Builder()
new SimpleConsoleUpdateHistory.Builder()
.setType(ConsoleUpdateHistory.Type.REGISTRAR_UPDATE)
.setRegistrar(newRegistrar)
.setRequestBody(consoleApiParams.gson().toJson(registrar.get())));
.setDescription(newRegistrar.getRegistrarId()));
sendExternalUpdatesIfNecessary(
EmailInfo.create(
savedRegistrar,

View File

@@ -50,6 +50,7 @@
<class>google.registry.model.console.ConsoleEppActionHistory</class>
<class>google.registry.model.console.RegistrarPocUpdateHistory</class>
<class>google.registry.model.console.RegistrarUpdateHistory</class>
<class>google.registry.model.console.SimpleConsoleUpdateHistory</class>
<class>google.registry.model.console.User</class>
<class>google.registry.model.console.UserUpdateHistory</class>
<class>google.registry.model.contact.ContactHistory</class>

View File

@@ -15,7 +15,6 @@
package google.registry.export;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.export.ExportDomainListsAction.REGISTERED_DOMAINS_FILENAME;
import static google.registry.model.common.FeatureFlag.FeatureName.INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS;
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
@@ -83,10 +82,11 @@ class ExportDomainListsActionTest {
persistFeatureFlag(INACTIVE);
}
private void verifyExportedToDrive(String folderId, String domains) throws Exception {
private void verifyExportedToDrive(String folderId, String filename, String domains)
throws Exception {
verify(driveConnection)
.createOrUpdateFile(
eq(REGISTERED_DOMAINS_FILENAME),
eq(filename),
eq(MediaType.PLAIN_TEXT_UTF_8),
eq(folderId),
bytesExportedToDrive.capture());
@@ -103,7 +103,7 @@ class ExportDomainListsActionTest {
String tlds = new String(gcsUtils.readBytesFrom(existingFile), UTF_8);
// Check that it only contains the active domains, not the dead one.
assertThat(tlds).isEqualTo("onetwo.tld\nrudnitzky.tld");
verifyExportedToDrive("brouhaha", "onetwo.tld\nrudnitzky.tld");
verifyExportedToDrive("brouhaha", "registered_domains_tld.txt", "onetwo.tld\nrudnitzky.tld");
verifyNoMoreInteractions(driveConnection);
}
@@ -118,7 +118,7 @@ class ExportDomainListsActionTest {
String tlds = new String(gcsUtils.readBytesFrom(existingFile), UTF_8);
// Check that it only contains the active domains, not the dead one.
assertThat(tlds).isEqualTo("onetwo.tld,\nrudnitzky.tld,");
verifyExportedToDrive("brouhaha", "onetwo.tld,\nrudnitzky.tld,");
verifyExportedToDrive("brouhaha", "registered_domains_tld.txt", "onetwo.tld,\nrudnitzky.tld,");
verifyNoMoreInteractions(driveConnection);
}
@@ -137,7 +137,7 @@ class ExportDomainListsActionTest {
assertThrows(StorageException.class, () -> gcsUtils.readBytesFrom(nonexistentFile));
ImmutableList<String> ls = gcsUtils.listFolderObjects("outputbucket", "");
assertThat(ls).containsExactly("tld.txt");
verifyExportedToDrive("brouhaha", "onetwo.tld\nrudnitzky.tld");
verifyExportedToDrive("brouhaha", "registered_domains_tld.txt", "onetwo.tld\nrudnitzky.tld");
verifyNoMoreInteractions(driveConnection);
}
@@ -157,7 +157,7 @@ class ExportDomainListsActionTest {
assertThrows(StorageException.class, () -> gcsUtils.readBytesFrom(nonexistentFile));
ImmutableList<String> ls = gcsUtils.listFolderObjects("outputbucket", "");
assertThat(ls).containsExactly("tld.txt");
verifyExportedToDrive("brouhaha", "onetwo.tld,\nrudnitzky.tld,");
verifyExportedToDrive("brouhaha", "registered_domains_tld.txt", "onetwo.tld,\nrudnitzky.tld,");
verifyNoMoreInteractions(driveConnection);
}
@@ -189,7 +189,9 @@ class ExportDomainListsActionTest {
action.run();
verifyExportedToDrive(
"brouhaha", "active.tld,\npendingdelete.tld,2020-02-05T02:02:02.000Z\nredemption.tld,");
"brouhaha",
"registered_domains_tld.txt",
"active.tld,\npendingdelete.tld,2020-02-05T02:02:02.000Z\nredemption.tld,");
}
@Test
@@ -215,8 +217,9 @@ class ExportDomainListsActionTest {
BlobId thirdTldFile = BlobId.of("outputbucket", "tldthree.txt");
String evenMoreTlds = new String(gcsUtils.readBytesFrom(thirdTldFile), UTF_8).trim();
assertThat(evenMoreTlds).isEqualTo("cupid.tldthree");
verifyExportedToDrive("brouhaha", "dasher.tld\nprancer.tld");
verifyExportedToDrive("hooray", "buddy.tldtwo\nrudolph.tldtwo\nsanta.tldtwo");
verifyExportedToDrive("brouhaha", "registered_domains_tld.txt", "dasher.tld\nprancer.tld");
verifyExportedToDrive(
"hooray", "registered_domains_tldtwo.txt", "buddy.tldtwo\nrudolph.tldtwo\nsanta.tldtwo");
// tldthree does not have a drive id, so no export to drive is performed.
verifyNoMoreInteractions(driveConnection);
}
@@ -245,8 +248,9 @@ class ExportDomainListsActionTest {
BlobId thirdTldFile = BlobId.of("outputbucket", "tldthree.txt");
String evenMoreTlds = new String(gcsUtils.readBytesFrom(thirdTldFile), UTF_8).trim();
assertThat(evenMoreTlds).isEqualTo("cupid.tldthree,");
verifyExportedToDrive("brouhaha", "dasher.tld,\nprancer.tld,");
verifyExportedToDrive("hooray", "buddy.tldtwo,\nrudolph.tldtwo,\nsanta.tldtwo,");
verifyExportedToDrive("brouhaha", "registered_domains_tld.txt", "dasher.tld,\nprancer.tld,");
verifyExportedToDrive(
"hooray", "registered_domains_tldtwo.txt", "buddy.tldtwo,\nrudolph.tldtwo,\nsanta.tldtwo,");
// tldthree does not have a drive id, so no export to drive is performed.
verifyNoMoreInteractions(driveConnection);
}

View File

@@ -16,7 +16,6 @@ package google.registry.export;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.export.ExportReservedTermsAction.EXPORT_MIME_TYPE;
import static google.registry.export.ExportReservedTermsAction.RESERVED_TERMS_FILENAME;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistReservedList;
import static google.registry.testing.DatabaseHelper.persistResource;
@@ -77,7 +76,7 @@ public class ExportReservedTermsActionTest {
runAction("tld");
byte[] expected = "# This is a disclaimer.\ncat\nlol\n".getBytes(UTF_8);
verify(driveConnection)
.createOrUpdateFile(RESERVED_TERMS_FILENAME, EXPORT_MIME_TYPE, "brouhaha", expected);
.createOrUpdateFile("reserved_terms_tld.txt", EXPORT_MIME_TYPE, "brouhaha", expected);
assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getPayload()).isEqualTo("1001");
}

View File

@@ -35,7 +35,6 @@ 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.Maps;
import com.google.common.net.InternetDomainName;
import google.registry.flows.EppException;
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException;
@@ -63,8 +62,7 @@ import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link AllocationTokenFlowUtils}. */
class AllocationTokenFlowUtilsTest {
private final AllocationTokenFlowUtils flowUtils =
new AllocationTokenFlowUtils(new AllocationTokenCustomLogic());
private final AllocationTokenFlowUtils flowUtils = new AllocationTokenFlowUtils();
@RegisterExtension
final JpaIntegrationTestExtension jpa =
@@ -185,47 +183,6 @@ class AllocationTokenFlowUtilsTest {
.marshalsToXml();
}
@Test
void test_validateTokenCreate_callsCustomLogic() {
AllocationTokenFlowUtils failingFlowUtils =
new AllocationTokenFlowUtils(new FailingAllocationTokenCustomLogic());
persistResource(
new AllocationToken.Builder().setToken("tokeN").setTokenType(SINGLE_USE).build());
when(allocationTokenExtension.getAllocationToken()).thenReturn("tokeN");
Exception thrown =
assertThrows(
IllegalStateException.class,
() ->
failingFlowUtils.verifyAllocationTokenCreateIfPresent(
createCommand("blah.tld"),
Tld.get("tld"),
"TheRegistrar",
DateTime.now(UTC),
Optional.of(allocationTokenExtension)));
assertThat(thrown).hasMessageThat().isEqualTo("failed for tests");
}
@Test
void test_validateTokenExistingDomain_callsCustomLogic() {
AllocationTokenFlowUtils failingFlowUtils =
new AllocationTokenFlowUtils(new FailingAllocationTokenCustomLogic());
persistResource(
new AllocationToken.Builder().setToken("tokeN").setTokenType(SINGLE_USE).build());
when(allocationTokenExtension.getAllocationToken()).thenReturn("tokeN");
Exception thrown =
assertThrows(
IllegalStateException.class,
() ->
failingFlowUtils.verifyAllocationTokenIfPresent(
DatabaseHelper.newDomain("blah.tld"),
Tld.get("tld"),
"TheRegistrar",
DateTime.now(UTC),
CommandName.RENEW,
Optional.of(allocationTokenExtension)));
assertThat(thrown).hasMessageThat().isEqualTo("failed for tests");
}
@Test
void test_validateTokenCreate_invalidForClientId() {
persistResource(
@@ -383,49 +340,6 @@ class AllocationTokenFlowUtilsTest {
.inOrder();
}
@Test
void test_checkDomainsWithToken_callsCustomLogic() {
persistResource(
new AllocationToken.Builder().setToken("tokeN").setTokenType(SINGLE_USE).build());
AllocationTokenFlowUtils failingFlowUtils =
new AllocationTokenFlowUtils(new FailingAllocationTokenCustomLogic());
Exception thrown =
assertThrows(
IllegalStateException.class,
() ->
failingFlowUtils.checkDomainsWithToken(
ImmutableList.of(
InternetDomainName.from("blah.tld"), InternetDomainName.from("blah2.tld")),
"tokeN",
"TheRegistrar",
DateTime.now(UTC)));
assertThat(thrown).hasMessageThat().isEqualTo("failed for tests");
}
@Test
void test_checkDomainsWithToken_resultsFromCustomLogicAreIntegrated() {
persistResource(
new AllocationToken.Builder().setToken("tokeN").setTokenType(SINGLE_USE).build());
AllocationTokenFlowUtils customResultFlowUtils =
new AllocationTokenFlowUtils(new CustomResultAllocationTokenCustomLogic());
assertThat(
customResultFlowUtils
.checkDomainsWithToken(
ImmutableList.of(
InternetDomainName.from("blah.tld"), InternetDomainName.from("bunny.tld")),
"tokeN",
"TheRegistrar",
DateTime.now(UTC))
.domainCheckResults())
.containsExactlyEntriesIn(
ImmutableMap.of(
InternetDomainName.from("blah.tld"),
"",
InternetDomainName.from("bunny.tld"),
"fufu"))
.inOrder();
}
private void assertValidateCreateThrowsEppException(Class<? extends EppException> clazz) {
assertAboutEppExceptions()
.that(
@@ -475,46 +389,4 @@ class AllocationTokenFlowUtilsTest {
.put(promoStart.plusMonths(1), ENDED)
.build());
}
/** An {@link AllocationTokenCustomLogic} class that throws exceptions on every method. */
private static class FailingAllocationTokenCustomLogic extends AllocationTokenCustomLogic {
@Override
public AllocationToken validateToken(
DomainCommand.Create command,
AllocationToken token,
Tld tld,
String registrarId,
DateTime now) {
throw new IllegalStateException("failed for tests");
}
@Override
public AllocationToken validateToken(
Domain domain, AllocationToken token, Tld tld, String registrarId, DateTime now) {
throw new IllegalStateException("failed for tests");
}
@Override
public ImmutableMap<InternetDomainName, String> checkDomainsWithToken(
ImmutableList<InternetDomainName> domainNames,
AllocationToken tokenEntity,
String registrarId,
DateTime now) {
throw new IllegalStateException("failed for tests");
}
}
/** An {@link AllocationTokenCustomLogic} class that returns custom check results for bunnies. */
private static class CustomResultAllocationTokenCustomLogic extends AllocationTokenCustomLogic {
@Override
public ImmutableMap<InternetDomainName, String> checkDomainsWithToken(
ImmutableList<InternetDomainName> domainNames,
AllocationToken tokenEntity,
String registrarId,
DateTime now) {
return Maps.toMap(domainNames, domain -> domain.toString().contains("bunny") ? "fufu" : "");
}
}
}

View File

@@ -53,7 +53,7 @@ public class ConsoleEppActionHistoryTest extends EntityTestCase {
DomainHistory domainHistory = getOnlyElement(DatabaseHelper.loadAllOf(DomainHistory.class));
ConsoleEppActionHistory history =
new ConsoleEppActionHistory.Builder()
.setType(ConsoleUpdateHistory.Type.EPP_ACTION)
.setType(ConsoleUpdateHistory.Type.DOMAIN_DELETE)
.setActingUser(user)
.setModificationTime(fakeClock.nowUtc())
.setMethod("POST")

View File

@@ -0,0 +1,62 @@
// 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 com.google.common.truth.Truth.assertThat;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.loadByEntity;
import static google.registry.testing.DatabaseHelper.persistActiveContact;
import static google.registry.testing.DatabaseHelper.persistDomainWithDependentResources;
import static google.registry.testing.DatabaseHelper.persistResource;
import google.registry.model.EntityTestCase;
import google.registry.testing.DatabaseHelper;
import google.registry.util.DateTimeUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class SimpleConsoleUpdateHistoryTest extends EntityTestCase {
SimpleConsoleUpdateHistoryTest() {
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 = persistResource(DatabaseHelper.createAdminUser("email@email.com"));
SimpleConsoleUpdateHistory history =
new SimpleConsoleUpdateHistory.Builder()
.setType(ConsoleUpdateHistory.Type.DOMAIN_SUSPEND)
.setActingUser(user)
.setMethod("POST")
.setUrl("/console-api/bulk-domain")
.setDescription("example.tld")
.setModificationTime(fakeClock.nowUtc())
.build();
persistResource(history);
assertThat(loadByEntity(history)).isEqualTo(history);
}
}

View File

@@ -293,11 +293,11 @@ class JpaTransactionManagerImplTest {
assertThrows(
OptimisticLockException.class,
() -> spyJpaTm.transact(() -> spyJpaTm.delete(theEntityKey)));
verify(spyJpaTm, times(3)).delete(theEntityKey);
verify(spyJpaTm, times(6)).delete(theEntityKey);
assertThrows(
OptimisticLockException.class,
() -> spyJpaTm.transact(() -> spyJpaTm.delete(theEntityKey)));
verify(spyJpaTm, times(6)).delete(theEntityKey);
verify(spyJpaTm, times(12)).delete(theEntityKey);
}
@Test
@@ -355,10 +355,10 @@ class JpaTransactionManagerImplTest {
spyJpaTm.transact(() -> spyJpaTm.insert(theEntity));
assertThrows(
RuntimeException.class, () -> spyJpaTm.transact(() -> spyJpaTm.delete(theEntityKey)));
verify(spyJpaTm, times(3)).delete(theEntityKey);
verify(spyJpaTm, times(6)).delete(theEntityKey);
assertThrows(
RuntimeException.class, () -> spyJpaTm.transact(() -> spyJpaTm.delete(theEntityKey)));
verify(spyJpaTm, times(6)).delete(theEntityKey);
verify(spyJpaTm, times(12)).delete(theEntityKey);
}
@Test
@@ -759,11 +759,11 @@ class JpaTransactionManagerImplTest {
spyJpaTm.transact(
() -> {
spyJpaTm.exists(theEntity);
spyJpaTm.transact(() -> spyJpaTm.delete(theEntityKey));
spyJpaTm.delete(theEntityKey);
}));
verify(spyJpaTm, times(3)).exists(theEntity);
verify(spyJpaTm, times(3)).delete(theEntityKey);
verify(spyJpaTm, times(6)).exists(theEntity);
verify(spyJpaTm, times(6)).delete(theEntityKey);
}
private static void insertPerson(int age) {

View File

@@ -27,6 +27,7 @@ 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.UserTest;
import google.registry.model.console.UserUpdateHistoryTest;
import google.registry.model.contact.ContactTest;
@@ -113,12 +114,13 @@ import org.junit.runner.RunWith;
RegistrarDaoTest.class,
RegistrarPocUpdateHistoryTest.class,
RegistrarUpdateHistoryTest.class,
TldTest.class,
ReservedListDaoTest.class,
RegistryLockDaoTest.class,
ServerSecretTest.class,
SimpleConsoleUpdateHistoryTest.class,
SignedMarkRevocationListDaoTest.class,
Spec11ThreatMatchTest.class,
TldTest.class,
TmchCrlTest.class,
UserTest.class,
UserUpdateHistoryTest.class,

View File

@@ -32,8 +32,9 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSetMultimap;
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.RegistrarUpdateHistory;
import google.registry.model.console.SimpleConsoleUpdateHistory;
import google.registry.model.console.User;
import google.registry.model.console.UserRoles;
import google.registry.model.registrar.Registrar;
@@ -138,14 +139,10 @@ class ConsoleEppPasswordActionTest {
createAction("TheRegistrar", "foobar", "randomPassword", "randomPassword");
action.run();
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_OK);
assertDoesNotThrow(
() -> {
credentials.validate(loadRegistrar("TheRegistrar"), "randomPassword");
});
assertThat(loadSingleton(RegistrarUpdateHistory.class).get().getRequestBody())
.isEqualTo(
"{\"registrarId\":\"TheRegistrar\",\"oldPassword\":\"********\",\"newPassword\":"
+ "\"••••••••\",\"newPasswordRepeat\":\"••••••••\"}");
assertDoesNotThrow(() -> credentials.validate(loadRegistrar("TheRegistrar"), "randomPassword"));
SimpleConsoleUpdateHistory history = loadSingleton(SimpleConsoleUpdateHistory.class).get();
assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.EPP_PASSWORD_UPDATE);
assertThat(history.getDescription()).hasValue("TheRegistrar");
}
private ConsoleEppPasswordAction createAction(

View File

@@ -15,7 +15,6 @@
package google.registry.ui.server.console;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
import static google.registry.model.registrar.RegistrarPocBase.Type.WHOIS;
import static google.registry.testing.DatabaseHelper.createTlds;
import static google.registry.testing.DatabaseHelper.loadSingleton;
@@ -30,8 +29,9 @@ import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableList;
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.RegistrarUpdateHistory;
import google.registry.model.console.SimpleConsoleUpdateHistory;
import google.registry.model.console.User;
import google.registry.model.console.UserRoles;
import google.registry.model.registrar.Registrar;
@@ -108,9 +108,9 @@ class ConsoleUpdateRegistrarActionTest {
assertThat(newRegistrar.getAllowedTlds()).containsExactly("app", "dev");
assertThat(newRegistrar.isRegistryLockAllowed()).isFalse();
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_OK);
assertAboutImmutableObjects()
.that(newRegistrar)
.hasFieldsEqualTo(loadSingleton(RegistrarUpdateHistory.class).get().getRegistrar());
SimpleConsoleUpdateHistory history = loadSingleton(SimpleConsoleUpdateHistory.class).get();
assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.REGISTRAR_UPDATE);
assertThat(history.getDescription()).hasValue("TheRegistrar");
}
@Test

View File

@@ -15,7 +15,6 @@
package google.registry.ui.server.console;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
import static google.registry.testing.DatabaseHelper.loadAllOf;
import static google.registry.testing.DatabaseHelper.loadRegistrar;
import static google.registry.testing.DatabaseHelper.loadSingleton;
@@ -30,9 +29,10 @@ import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import google.registry.model.console.ConsoleUpdateHistory;
import google.registry.model.console.GlobalRole;
import google.registry.model.console.RegistrarRole;
import google.registry.model.console.RegistrarUpdateHistory;
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,9 +186,9 @@ class RegistrarsActionTest {
.findAny()
.isPresent())
.isTrue();
assertAboutImmutableObjects()
.that(r)
.isEqualExceptFields(loadSingleton(RegistrarUpdateHistory.class).get().getRegistrar());
SimpleConsoleUpdateHistory history = loadSingleton(SimpleConsoleUpdateHistory.class).get();
assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.REGISTRAR_CREATE);
assertThat(history.getDescription()).hasValue("regIdTest");
}
@Test

View File

@@ -19,6 +19,7 @@ import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATAS
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.loadByEntity;
import static google.registry.testing.DatabaseHelper.loadSingleton;
import static google.registry.testing.DatabaseHelper.persistActiveContact;
import static google.registry.testing.DatabaseHelper.persistDomainWithDependentResources;
import static google.registry.testing.DatabaseHelper.persistResource;
@@ -38,8 +39,10 @@ import google.registry.flows.DaggerEppTestComponent;
import google.registry.flows.EppController;
import google.registry.flows.EppTestComponent;
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;
@@ -120,6 +123,9 @@ 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();
assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.DOMAIN_DELETE);
assertThat(history.getDescription()).hasValue("example.tld");
}
@Test
@@ -145,6 +151,9 @@ public class ConsoleBulkDomainActionTest {
{"example.tld":{"message":"Command completed successfully","responseCode":1000}}""");
assertThat(loadByEntity(domain).getStatusValues())
.containsAtLeastElementsIn(serverSuspensionStatuses);
SimpleConsoleUpdateHistory history = loadSingleton(SimpleConsoleUpdateHistory.class).get();
assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.DOMAIN_SUSPEND);
assertThat(history.getDescription()).hasValue("example.tld");
}
@Test
@@ -172,6 +181,9 @@ public class ConsoleBulkDomainActionTest {
"""
{"example.tld":{"message":"Command completed successfully","responseCode":1000}}""");
assertThat(loadByEntity(domain).getStatusValues()).containsNoneIn(serverSuspensionStatuses);
SimpleConsoleUpdateHistory history = loadSingleton(SimpleConsoleUpdateHistory.class).get();
assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.DOMAIN_UNSUSPEND);
assertThat(history.getDescription()).hasValue("example.tld");
}
@Test
@@ -194,6 +206,9 @@ 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();
assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.DOMAIN_DELETE);
assertThat(history.getDescription()).hasValue("example.tld");
}
@Test

View File

@@ -15,7 +15,6 @@
package google.registry.ui.server.console.settings;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
import static google.registry.testing.CertificateSamples.SAMPLE_CERT2;
import static google.registry.testing.DatabaseHelper.loadRegistrar;
import static google.registry.testing.DatabaseHelper.loadSingleton;
@@ -30,7 +29,8 @@ import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.ImmutableSortedMap;
import com.google.gson.Gson;
import google.registry.flows.certs.CertificateChecker;
import google.registry.model.console.RegistrarUpdateHistory;
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,9 +101,9 @@ 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);
assertAboutImmutableObjects()
.that(loadSingleton(RegistrarUpdateHistory.class).get().getRegistrar())
.hasFieldsEqualTo(r);
SimpleConsoleUpdateHistory history = loadSingleton(SimpleConsoleUpdateHistory.class).get();
assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.REGISTRAR_SECURITY_UPDATE);
assertThat(history.getDescription()).hasValue("registrarId");
}
private SecurityAction createAction(AuthResult authResult, String registrarId)

View File

@@ -27,8 +27,9 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSetMultimap;
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.RegistrarUpdateHistory;
import google.registry.model.console.SimpleConsoleUpdateHistory;
import google.registry.model.console.User;
import google.registry.model.console.UserRoles;
import google.registry.model.registrar.Registrar;
@@ -131,9 +132,9 @@ public class WhoisRegistrarFieldsActionTest {
.that(newRegistrar)
.isEqualExceptFields(
oldRegistrar, "whoisServer", "url", "localizedAddress", "phoneNumber", "faxNumber");
assertAboutImmutableObjects()
.that(loadSingleton(RegistrarUpdateHistory.class).get().getRegistrar())
.hasFieldsEqualTo(newRegistrar);
SimpleConsoleUpdateHistory history = loadSingleton(SimpleConsoleUpdateHistory.class).get();
assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.REGISTRAR_UPDATE);
assertThat(history.getDescription()).hasValue("TheRegistrar");
}
@Test

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -185,3 +185,7 @@ V184__remove_fk_pollmessage_domainhistory.sql
V185__remove_fk_domaintransactionrecord_domainhistory.sql
V186__remove_fk_domaindsdatahistory_domainhistory.sql
V187__console_update_history.sql
V188__remove_fk_userupdatehistory.sql
V189__remove_fk_consoleeppactionhistory.sql
V190__remove_fk_registrarupdatehistory.sql
V191__remove_fk_registrarpocupdatehistory.sql

View File

@@ -0,0 +1,16 @@
-- 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.
ALTER TABLE "UserUpdateHistory" DROP CONSTRAINT fk1s7bopbl3pwrhv3jkkofnv3o0;
ALTER TABLE "UserUpdateHistory" DROP CONSTRAINT fkuserupdatehistoryemailaddress;

View File

@@ -0,0 +1,15 @@
-- 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.
ALTER TABLE "ConsoleEppActionHistory" DROP CONSTRAINT fkb686b9os2nsjpv930npa4r3b4;

View File

@@ -0,0 +1,15 @@
-- 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.
ALTER TABLE "RegistrarUpdateHistory" DROP CONSTRAINT fksr7w342s7x5s5jvdti2axqeq8;

View File

@@ -0,0 +1,15 @@
-- 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.
ALTER TABLE "RegistrarPocUpdateHistory" DROP CONSTRAINT fkftpbwctxtkc1i0njc0tdcaa2g;

View File

@@ -139,7 +139,7 @@
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 ('EPP_ACTION','POC_CREATE','POC_UPDATE','POC_DELETE','REGISTRAR_UPDATE','USER_CREATE','USER_DELETE','USER_UPDATE')),
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,
@@ -148,6 +148,17 @@
primary key (history_revision_id)
);
create table "ConsoleUpdateHistory" (
revision_id bigint not null,
description text,
method text not null,
modification_time timestamp(6) with time zone not null,
type text not null check (type in ('DOMAIN_DELETE','DOMAIN_SUSPEND','DOMAIN_UNSUSPEND','EPP_PASSWORD_UPDATE','REGISTRAR_CREATE','REGISTRAR_SECURITY_UPDATE','REGISTRAR_UPDATE','USER_CREATE','USER_DELETE','USER_UPDATE')),
url text not null,
acting_user text not null,
primary key (revision_id)
);
create table "Contact" (
repo_id text not null,
update_timestamp timestamp(6) with time zone,
@@ -693,7 +704,7 @@
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 ('EPP_ACTION','POC_CREATE','POC_UPDATE','POC_DELETE','REGISTRAR_UPDATE','USER_CREATE','USER_DELETE','USER_UPDATE')),
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,
@@ -717,7 +728,7 @@
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 ('EPP_ACTION','POC_CREATE','POC_UPDATE','POC_DELETE','REGISTRAR_UPDATE','USER_CREATE','USER_DELETE','USER_UPDATE')),
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,
@@ -906,7 +917,7 @@
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 ('EPP_ACTION','POC_CREATE','POC_UPDATE','POC_DELETE','REGISTRAR_UPDATE','USER_CREATE','USER_DELETE','USER_UPDATE')),
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,
@@ -1010,6 +1021,15 @@
create index IDXiahqo1d1fqdfknywmj2xbxl7t
on "ConsoleEppActionHistory" (revision_id);
create index idx_console_update_history_acting_user
on "ConsoleUpdateHistory" (acting_user);
create index idx_console_update_history_type
on "ConsoleUpdateHistory" (type);
create index idx_console_update_history_modification_time
on "ConsoleUpdateHistory" (modification_time);
create index IDX3y752kr9uh4kh6uig54vemx0l
on "Contact" (creation_time);
@@ -1228,6 +1248,11 @@
foreign key (history_acting_user)
references "User";
alter table if exists "ConsoleUpdateHistory"
add constraint FKnhl1eolgix64u90xv3pj6xa3x
foreign key (acting_user)
references "User";
alter table if exists "DelegationSignerData"
add constraint FKtr24j9v14ph2mfuw2gsmt12kq
foreign key (domain_repo_id)

View File

@@ -2614,14 +2614,6 @@ CREATE INDEX spec11threatmatch_registrar_id_idx ON public."Spec11ThreatMatch" US
CREATE INDEX spec11threatmatch_tld_idx ON public."Spec11ThreatMatch" USING btree (tld);
--
-- Name: UserUpdateHistory fk1s7bopbl3pwrhv3jkkofnv3o0; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."UserUpdateHistory"
ADD CONSTRAINT fk1s7bopbl3pwrhv3jkkofnv3o0 FOREIGN KEY (history_acting_user) REFERENCES public."User"(email_address);
--
-- Name: Contact fk1sfyj7o7954prbn1exk7lpnoe; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@@ -3070,14 +3062,6 @@ ALTER TABLE ONLY public."DomainHistoryHost"
ADD CONSTRAINT fka9woh3hu8gx5x0vly6bai327n FOREIGN KEY (domain_history_domain_repo_id, domain_history_history_revision_id) REFERENCES public."DomainHistory"(domain_repo_id, history_revision_id) DEFERRABLE INITIALLY DEFERRED;
--
-- Name: ConsoleEppActionHistory fkb686b9os2nsjpv930npa4r3b4; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."ConsoleEppActionHistory"
ADD CONSTRAINT fkb686b9os2nsjpv930npa4r3b4 FOREIGN KEY (history_acting_user) REFERENCES public."User"(email_address);
--
-- Name: BsaUnblockableDomain fkbsaunblockabledomainlabel; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@@ -3094,14 +3078,6 @@ ALTER TABLE ONLY public."DomainHost"
ADD CONSTRAINT fkfmi7bdink53swivs390m2btxg FOREIGN KEY (domain_repo_id) REFERENCES public."Domain"(repo_id) DEFERRABLE INITIALLY DEFERRED;
--
-- Name: RegistrarPocUpdateHistory fkftpbwctxtkc1i0njc0tdcaa2g; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."RegistrarPocUpdateHistory"
ADD CONSTRAINT fkftpbwctxtkc1i0njc0tdcaa2g FOREIGN KEY (history_acting_user) REFERENCES public."User"(email_address);
--
-- Name: ReservedEntry fkgq03rk0bt1hb915dnyvd3vnfc; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@@ -3150,14 +3126,6 @@ ALTER TABLE ONLY public."RegistrarUpdateHistory"
ADD CONSTRAINT fkregistrarupdatehistoryregistrarid FOREIGN KEY (registrar_id) REFERENCES public."Registrar"(registrar_id);
--
-- Name: RegistrarUpdateHistory fksr7w342s7x5s5jvdti2axqeq8; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."RegistrarUpdateHistory"
ADD CONSTRAINT fksr7w342s7x5s5jvdti2axqeq8 FOREIGN KEY (history_acting_user) REFERENCES public."User"(email_address);
--
-- Name: DelegationSignerData fktr24j9v14ph2mfuw2gsmt12kq; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@@ -3166,14 +3134,6 @@ ALTER TABLE ONLY public."DelegationSignerData"
ADD CONSTRAINT fktr24j9v14ph2mfuw2gsmt12kq FOREIGN KEY (domain_repo_id) REFERENCES public."Domain"(repo_id) DEFERRABLE INITIALLY DEFERRED;
--
-- Name: UserUpdateHistory fkuserupdatehistoryemailaddress; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."UserUpdateHistory"
ADD CONSTRAINT fkuserupdatehistoryemailaddress FOREIGN KEY (email_address) REFERENCES public."User"(email_address);
--
-- PostgreSQL database dump complete
--

View File

@@ -38,7 +38,7 @@ dependencies {
tasks.register('copyConsole', Copy) {
from("${rootDir}/console-webapp/staged/") {
include "console-*/*"
include "console-*/", "console-*/**"
}
into layout.buildDirectory.dir('jetty-base/webapps/')
dependsOn(':console-webapp:buildConsoleForAll')

View File

@@ -16,6 +16,7 @@ spec:
serviceAccountName: nomulus
nodeSelector:
cloud.google.com/compute-class: "Performance"
cloud.google.com/machine-family: c4
containers:
- name: backend
image: gcr.io/GCP_PROJECT/nomulus

View File

@@ -14,8 +14,6 @@ spec:
service: console
spec:
serviceAccountName: nomulus
nodeSelector:
cloud.google.com/compute-class: "Performance"
containers:
- name: console
image: gcr.io/GCP_PROJECT/nomulus
@@ -24,8 +22,17 @@ spec:
name: http
resources:
requests:
# explicit pod-slots 0 is required in order to downgrade node
# class from performance, which has implicit pod-slots 1
cloud.google.com/pod-slots: 0
cpu: "100m"
memory: "512Mi"
limits:
# explicit pod-slots 0 is required in order to downgrade node
# class from performance, which has implicit pod-slots 1
cloud.google.com/pod-slots: 0
cpu: "500m"
memory: "2Gi"
args: [ENVIRONMENT]
env:
- name: POD_ID

View File

@@ -16,6 +16,7 @@ spec:
serviceAccountName: nomulus
nodeSelector:
cloud.google.com/compute-class: "Performance"
cloud.google.com/machine-family: c4
containers:
- name: frontend
image: gcr.io/GCP_PROJECT/nomulus

View File

@@ -14,8 +14,6 @@ spec:
service: pubapi
spec:
serviceAccountName: nomulus
nodeSelector:
cloud.google.com/compute-class: "Performance"
containers:
- name: pubapi
image: gcr.io/GCP_PROJECT/nomulus
@@ -24,8 +22,17 @@ spec:
name: http
resources:
requests:
# explicit pod-slots 0 is required in order to downgrade node
# class from performance, which has implicit pod-slots 1
cloud.google.com/pod-slots: 0
cpu: "100m"
memory: "512Mi"
limits:
# explicit pod-slots 0 is required in order to downgrade node
# class from performance, which has implicit pod-slots 1
cloud.google.com/pod-slots: 0
cpu: "500m"
memory: "2Gi"
args: [ENVIRONMENT]
env:
- name: POD_ID

View File

@@ -0,0 +1,54 @@
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<servlet>
<servlet-name>default-no-cache</servlet-name>
<servlet-class>org.eclipse.jetty.ee10.servlet.DefaultServlet</servlet-class>
<init-param>
<param-name>cacheControl</param-name>
<param-value>no-cache, no-store, must-revalidate</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>default-cache-static</servlet-name>
<servlet-class>org.eclipse.jetty.ee10.servlet.DefaultServlet</servlet-class>
<init-param>
<param-name>cacheControl</param-name>
<param-value>public, max-age=604800</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>default-no-cache</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default-no-cache</servlet-name>
<url-pattern>main.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default-no-cache</servlet-name>
<url-pattern>styles.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default-cache-static</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default-cache-static</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default-cache-static</servlet-name>
<url-pattern>*.png</url-pattern>
</servlet-mapping>
</web-app>

View File

@@ -15,7 +15,11 @@
env=${1:-"alpha"}
cd /jetty-base
cp -rf webapps/console-${env} webapps/console
cp -rf webapps/console-${env}/. webapps/console/
cd webapps
# Remove all environment builds not used in the deployment
find . -maxdepth 1 -type d -name "console-*" -exec rm -rf {} +
cd /jetty-base
echo "Running ${env}"
java -Dgoogle.registry.environment=${env} \
-Djava.util.logging.config.file=/logging.properties \

View File

@@ -24,6 +24,7 @@ import com.google.common.flogger.FluentLogger;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import java.io.Serializable;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.function.Predicate;
@@ -34,6 +35,8 @@ public class Retrier implements Serializable {
private static final long serialVersionUID = 1167386907195735483L;
private static final Random randomForSkew = new Random();
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final Sleeper sleeper;
@@ -157,9 +160,11 @@ public class Retrier implements Serializable {
throw new RuntimeException(e);
}
failureReporter.beforeRetry(e, failures, attempts);
// Wait (skewed) 100ms on the first attempt, doubling on each subsequent attempt.
long backoffMillis = pow(2, failures) * 100L;
long sleepDurationMillis = Math.round(randomForSkew.nextDouble(0.8, 1.2) * backoffMillis);
try {
// Wait 100ms on the first attempt, doubling on each subsequent attempt.
sleeper.sleep(Duration.millis(pow(2, failures) * 100L));
sleeper.sleep(Duration.millis(sleepDurationMillis));
} catch (InterruptedException e2) {
// Since we're not rethrowing InterruptedException, set the interrupt state on the thread
// so the next blocking operation will know to abort the thread.