1
0
mirror of https://github.com/google/nomulus synced 2026-05-21 23:31:51 +00:00

Compare commits

...

3 Commits

Author SHA1 Message Date
gbrodman
8547ad7941 Remove the concept of a GAE service endpoint (#2869)
We don't need to support the mix of GAE and GKE any more so we can get
rid of the GaeService bits and unify everything under one constant
service. This also allows us to reduce the number of services down to
four (FE, BE, PUBAPI, console) which is nice.
2025-11-18 19:31:40 +00:00
gbrodman
b1266c95e8 Add and default to Argon2 hashing (#2877)
We've previously been using Scrypt since PR #2191 which, while being a
memory-hard slow function, isn't the optimal solution according to the
OWASP recommendations. While we could get away with increasing the
parallelization parameter to 3, it's better to just switch to the
most-recommended solution if we're switching things up anyway.

For the transition, we do something similar to PR #2191 where if the
previous-algorithm's hash is successful, we re-hash with Argo2id and
store that version. By doing this, we should not need any intervention
for registrars who log in at any point during the transition period.

Much of this PR, especially the parts where we re-hash the passwords in
Argon2 instead of Scrypt upon login, is based on the code that was
eventually removed in #2310.
2025-11-17 20:11:22 +00:00
Weimin Yu
bc9aab6790 Reformat Fee extension v1.0 schema (#2888)
Reformat the current schema file for RFC 8748 final version. This was
adapted from v0.12 is not fully consistent with the final schema

This helps highlight the differences we missed in PR 2855 when we check
in the official schema.
2025-11-17 15:58:56 +00:00
120 changed files with 503 additions and 633 deletions

View File

@@ -20,7 +20,6 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.flogger.FluentLogger;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.UrlConnectionService;
@@ -43,7 +42,7 @@ import javax.net.ssl.HttpsURLConnection;
* --service BACKEND -X POST -u '/_dr/task/executeCannedScript}'}
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = "/_dr/task/executeCannedScript",
method = {POST, GET},
automaticallyPrintOk = true,

View File

@@ -27,7 +27,6 @@ import google.registry.model.domain.token.AllocationToken;
import google.registry.model.domain.token.BulkPricingPackage;
import google.registry.model.registrar.Registrar;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.auth.Auth;
import google.registry.ui.server.SendEmailUtils;
import google.registry.util.Clock;
@@ -39,7 +38,10 @@ import org.joda.time.Days;
* An action that checks all {@link BulkPricingPackage} objects for compliance with their max create
* limit.
*/
@Action(service = GaeService.BACKEND, path = CheckBulkComplianceAction.PATH, auth = Auth.AUTH_ADMIN)
@Action(
service = Action.Service.BACKEND,
path = CheckBulkComplianceAction.PATH,
auth = Auth.AUTH_ADMIN)
public class CheckBulkComplianceAction implements Runnable {
public static final String PATH = "/_dr/task/checkBulkCompliance";

View File

@@ -43,7 +43,6 @@ import google.registry.config.CredentialModule.ApplicationDefaultCredential;
import google.registry.config.RegistryConfig.Config;
import google.registry.request.Action;
import google.registry.request.Action.Method;
import google.registry.request.Action.Service;
import google.registry.util.Clock;
import google.registry.util.CollectionUtils;
import google.registry.util.GoogleCredentialsBundle;
@@ -175,7 +174,7 @@ public class CloudTasksUtils implements Serializable {
* the worker service</a>
*/
protected Task createTask(
String path, Method method, Service service, Multimap<String, String> params) {
String path, Method method, Action.Service service, Multimap<String, String> params) {
checkArgument(
path != null && !path.isEmpty() && path.charAt(0) == '/',
"The path must start with a '/'.");
@@ -231,9 +230,7 @@ public class CloudTasksUtils implements Serializable {
method,
actionClazz.getSimpleName(),
allowedMethods);
Service service =
RegistryEnvironment.isOnJetty() ? Action.ServiceGetter.get(action) : action.service();
return createTask(path, method, service, params);
return createTask(path, method, action.service(), params);
}
/**
@@ -256,7 +253,7 @@ public class CloudTasksUtils implements Serializable {
public Task createTaskWithJitter(
String path,
Method method,
Service service,
Action.Service service,
Multimap<String, String> params,
Optional<Integer> jitterSeconds) {
if (jitterSeconds.isEmpty() || jitterSeconds.get() <= 0) {
@@ -297,9 +294,7 @@ public class CloudTasksUtils implements Serializable {
"Action class %s is not annotated with @Action",
actionClazz.getSimpleName());
String path = action.path();
Service service =
RegistryEnvironment.isOnJetty() ? Action.ServiceGetter.get(action) : action.service();
return createTaskWithJitter(path, method, service, params, jitterSeconds);
return createTaskWithJitter(path, method, action.service(), params, jitterSeconds);
}
/**
@@ -319,7 +314,7 @@ public class CloudTasksUtils implements Serializable {
private Task createTaskWithDelay(
String path,
Method method,
Service service,
Action.Service service,
Multimap<String, String> params,
Duration delay) {
if (delay.isEqual(Duration.ZERO)) {
@@ -354,9 +349,7 @@ public class CloudTasksUtils implements Serializable {
Duration delay) {
Action action = getAction(actionClazz);
String path = action.path();
Service service =
RegistryEnvironment.isOnJetty() ? Action.ServiceGetter.get(action) : action.service();
return createTaskWithDelay(path, method, service, params, delay);
return createTaskWithDelay(path, method, action.service(), params, delay);
}
private static Action getAction(Class<? extends Runnable> actionClazz) {

View File

@@ -37,7 +37,6 @@ import google.registry.model.eppcommon.ProtocolDefinition;
import google.registry.model.eppoutput.EppOutput;
import google.registry.persistence.transaction.QueryComposer.Comparator;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.request.lock.LockHandler;
@@ -68,7 +67,7 @@ import org.joda.time.Duration;
* this action runs, thus alerting us that human action is needed to correctly process the delete.
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = DeleteExpiredDomainsAction.PATH,
auth = Auth.AUTH_ADMIN)
public class DeleteExpiredDomainsAction implements Runnable {

View File

@@ -37,7 +37,6 @@ import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.HistoryEntryDao;
import google.registry.persistence.VKey;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
@@ -55,7 +54,7 @@ import jakarta.inject.Inject;
* production.
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = "/_dr/task/deleteLoadTestData",
method = POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -42,7 +42,6 @@ import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.tld.Tld.TldType;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.util.RegistryEnvironment;
@@ -59,7 +58,7 @@ import org.joda.time.Duration;
* billing events, along with their ForeignKeyDomainIndex entities.
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = "/_dr/task/deleteProberData",
method = POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -35,7 +35,6 @@ import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingRecurrence;
import google.registry.model.common.Cursor;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
@@ -51,7 +50,7 @@ import org.joda.time.DateTime;
* BillingRecurrence} billing events into synthetic {@link BillingEvent} events.
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = "/_dr/task/expandBillingRecurrences",
auth = Auth.AUTH_ADMIN)
public class ExpandBillingRecurrencesAction implements Runnable {

View File

@@ -32,7 +32,6 @@ import google.registry.model.eppcommon.StatusValue;
import google.registry.model.tld.RegistryLockDao;
import google.registry.persistence.VKey;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
@@ -47,7 +46,7 @@ import org.joda.time.Duration;
/** Task that re-locks a previously-Registry-Locked domain after a predetermined period of time. */
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = RelockDomainAction.PATH,
method = POST,
automaticallyPrintOk = true,

View File

@@ -44,7 +44,6 @@ import google.registry.model.eppcommon.ProtocolDefinition;
import google.registry.model.eppoutput.EppOutput;
import google.registry.persistence.VKey;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.request.lock.LockHandler;
@@ -67,7 +66,7 @@ import org.joda.time.Duration;
* leaving behind a record recording that update.
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = RemoveAllDomainContactsAction.PATH,
method = Action.Method.POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -28,7 +28,6 @@ import com.google.common.flogger.FluentLogger;
import com.google.common.net.MediaType;
import google.registry.config.RegistryConfig.Config;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
@@ -54,7 +53,7 @@ import jakarta.inject.Inject;
* <p>This runs the {@link google.registry.beam.resave.ResaveAllEppResourcesPipeline}.
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = ResaveAllEppResourcesPipelineAction.PATH,
auth = Auth.AUTH_ADMIN)
public class ResaveAllEppResourcesPipelineAction implements Runnable {

View File

@@ -25,7 +25,6 @@ import com.google.common.flogger.FluentLogger;
import google.registry.model.EppResource;
import google.registry.persistence.VKey;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Action.Method;
import google.registry.request.Parameter;
import google.registry.request.Response;
@@ -40,7 +39,7 @@ import org.joda.time.DateTime;
* <p>{@link EppResource}s will be projected forward to the current time.
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = ResaveEntityAction.PATH,
auth = Auth.AUTH_ADMIN,
method = Method.POST)

View File

@@ -35,7 +35,6 @@ import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarPoc;
import google.registry.model.registrar.RegistrarPoc.Type;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.EmailMessage;
@@ -50,7 +49,7 @@ import org.joda.time.format.DateTimeFormatter;
/** An action that sends notification emails to registrars whose certificates are expiring soon. */
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = SendExpiringCertificateNotificationEmailAction.PATH,
auth = Auth.AUTH_ADMIN)
public class SendExpiringCertificateNotificationEmailAction implements Runnable {

View File

@@ -30,7 +30,6 @@ import google.registry.beam.wipeout.WipeOutContactHistoryPiiPipeline;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.contact.ContactHistory;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
@@ -49,7 +48,7 @@ import org.joda.time.DateTime;
* time.
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = WipeOutContactHistoryPiiAction.PATH,
auth = Auth.AUTH_ADMIN)
public class WipeOutContactHistoryPiiAction implements Runnable {

View File

@@ -41,7 +41,6 @@ import google.registry.bsa.persistence.DownloadScheduler;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.tld.Tlds;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
@@ -51,7 +50,7 @@ import java.util.Optional;
import java.util.stream.Stream;
@Action(
service = GaeService.BSA,
service = Action.Service.BACKEND,
path = BsaDownloadAction.PATH,
method = {GET, POST},
auth = Auth.AUTH_ADMIN)

View File

@@ -31,7 +31,6 @@ import google.registry.bsa.persistence.RefreshScheduler;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.tld.Tlds;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.BatchedStreams;
@@ -42,7 +41,7 @@ import java.util.stream.Stream;
import org.joda.time.Duration;
@Action(
service = GaeService.BSA,
service = Action.Service.BACKEND,
path = BsaRefreshAction.PATH,
method = {GET, POST},
auth = Auth.AUTH_ADMIN)

View File

@@ -53,7 +53,6 @@ import google.registry.model.domain.Domain;
import google.registry.model.tld.Tld;
import google.registry.model.tld.Tld.TldType;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
@@ -66,7 +65,7 @@ import org.joda.time.Duration;
/** Validates the BSA data in the database against the most recent block lists. */
@Action(
service = GaeService.BSA,
service = Action.Service.BACKEND,
path = BsaValidateAction.PATH,
method = {GET, POST},
auth = Auth.AUTH_ADMIN)

View File

@@ -42,7 +42,6 @@ import google.registry.model.tld.Tld;
import google.registry.model.tld.Tld.TldType;
import google.registry.model.tld.label.ReservedList;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
import jakarta.inject.Inject;
@@ -78,7 +77,7 @@ import org.joda.time.DateTime;
* <p>The file is also uploaded to GCS to preserve it as a record for ourselves.
*/
@Action(
service = GaeService.BSA,
service = Action.Service.BACKEND,
path = "/_dr/task/uploadBsaUnavailableNames",
method = {GET, POST},
auth = Auth.AUTH_ADMIN)

View File

@@ -37,7 +37,7 @@ import google.registry.bsa.UploadBsaUnavailableDomainsAction;
import google.registry.dns.ReadDnsRefreshRequestsAction;
import google.registry.model.common.DnsRefreshRequest;
import google.registry.persistence.transaction.JpaTransactionManager;
import google.registry.request.Action.GkeService;
import google.registry.request.Action.Service;
import google.registry.util.RegistryEnvironment;
import google.registry.util.YamlUtils;
import jakarta.inject.Named;
@@ -1444,7 +1444,7 @@ public final class RegistryConfig {
return CONFIG_SETTINGS.get().gcpProject.baseDomain;
}
public static URL getServiceUrl(GkeService service) {
public static URL getServiceUrl(Service service) {
return makeUrl(String.format("https://%s.%s", service.getServiceId(), getBaseDomain()));
}

View File

@@ -40,14 +40,12 @@ import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import google.registry.batch.CloudTasksUtils;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Action.GkeService;
import google.registry.request.Action.Service;
import google.registry.request.Parameter;
import google.registry.request.ParameterMap;
import google.registry.request.RequestParameters;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.RegistryEnvironment;
import jakarta.inject.Inject;
import java.util.Optional;
import java.util.stream.Stream;
@@ -80,7 +78,7 @@ import java.util.stream.Stream;
* </ul>
*/
@Action(
service = GaeService.BACKEND,
service = Service.BACKEND,
path = "/_dr/cron/fanout",
automaticallyPrintOk = true,
auth = Auth.AUTH_ADMIN)
@@ -160,10 +158,6 @@ public final class TldFanoutAction implements Runnable {
params.put(RequestParameters.PARAM_TLD, tld);
}
return cloudTasksUtils.createTaskWithJitter(
endpoint,
Action.Method.POST,
RegistryEnvironment.isOnJetty() ? GkeService.BACKEND : GaeService.BACKEND,
params,
jitterSeconds);
endpoint, Action.Method.POST, Service.BACKEND, params, jitterSeconds);
}
}

View File

@@ -52,7 +52,6 @@ import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarPoc;
import google.registry.model.tld.Tld;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Header;
import google.registry.request.HttpException.ServiceUnavailableException;
import google.registry.request.Parameter;
@@ -72,7 +71,7 @@ import org.joda.time.Duration;
/** Task that sends domain and host updates to the DNS server. */
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = PublishDnsUpdatesAction.PATH,
method = POST,
automaticallyPrintOk = true,

View File

@@ -45,7 +45,6 @@ import google.registry.dns.DnsUtils.TargetType;
import google.registry.model.common.DnsRefreshRequest;
import google.registry.model.tld.Tld;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
@@ -60,7 +59,7 @@ import org.joda.time.Duration;
* table.
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = "/_dr/task/readDnsRefreshRequests",
automaticallyPrintOk = true,
method = POST,

View File

@@ -26,7 +26,6 @@ import google.registry.model.annotations.ExternalMessagingName;
import google.registry.model.domain.Domain;
import google.registry.model.host.Host;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.HttpException.NotFoundException;
import google.registry.request.Parameter;
@@ -36,7 +35,7 @@ import jakarta.inject.Inject;
/** Action that manually triggers refresh of DNS information. */
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = "/_dr/task/dnsRefresh",
automaticallyPrintOk = true,
auth = Auth.AUTH_ADMIN)

View File

@@ -26,7 +26,6 @@ import google.registry.model.domain.Domain;
import google.registry.model.host.Host;
import google.registry.persistence.VKey;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
@@ -34,7 +33,7 @@ import jakarta.inject.Inject;
import org.joda.time.DateTime;
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = PATH,
method = Action.Method.POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -36,7 +36,6 @@ import google.registry.model.eppcommon.StatusValue;
import google.registry.model.tld.Tld;
import google.registry.model.tld.Tld.TldType;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.auth.Auth;
import google.registry.storage.drive.DriveConnection;
import google.registry.util.Clock;
@@ -58,7 +57,7 @@ import org.joda.time.DateTimeZone;
* name TLD.txt into the domain-lists bucket. Note that this overwrites the files in place.
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = "/_dr/task/exportDomainLists",
method = POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -33,7 +33,6 @@ import google.registry.model.tld.Tld;
import google.registry.model.tld.label.PremiumList.PremiumEntry;
import google.registry.model.tld.label.PremiumListDao;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.RequestParameters;
import google.registry.request.Response;
@@ -46,7 +45,7 @@ import java.util.SortedSet;
/** Action that exports the premium terms list for a TLD to Google Drive. */
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = "/_dr/task/exportPremiumTerms",
method = POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -25,7 +25,6 @@ import com.google.common.flogger.FluentLogger;
import com.google.common.net.MediaType;
import google.registry.model.tld.Tld;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.RequestParameters;
import google.registry.request.Response;
@@ -35,7 +34,7 @@ import jakarta.inject.Inject;
/** Action that exports the publicly viewable reserved terms list for a TLD to Google Drive. */
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = "/_dr/task/exportReservedTerms",
method = POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -34,7 +34,6 @@ import google.registry.groups.GroupsConnection.Role;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarPoc;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.Retrier;
@@ -53,7 +52,7 @@ import javax.annotation.Nullable;
* <p>This uses the <a href="https://developers.google.com/admin-sdk/directory/">Directory API</a>.
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = "/_dr/task/syncGroupMembers",
method = POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -24,7 +24,6 @@ import static jakarta.servlet.http.HttpServletResponse.SC_OK;
import com.google.common.flogger.FluentLogger;
import google.registry.config.RegistryConfig.Config;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
@@ -55,7 +54,7 @@ import org.joda.time.Duration;
* @see SyncRegistrarsSheet
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = SyncRegistrarsSheetAction.PATH,
method = POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -54,7 +54,6 @@ import google.registry.model.tld.label.ReservationType;
import google.registry.monitoring.whitebox.CheckApiMetric;
import google.registry.monitoring.whitebox.CheckApiMetric.Availability;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.RequestParameters;
import google.registry.request.Response;
@@ -72,7 +71,7 @@ import org.joda.time.DateTime;
* user controlled, lest it open an XSS vector. Do not modify this to return the domain name in the
* response.
*/
@Action(service = GaeService.PUBAPI, path = "/check", auth = Auth.AUTH_PUBLIC)
@Action(service = Action.Service.PUBAPI, path = "/check", auth = Auth.AUTH_PUBLIC)
public class CheckApiAction implements Runnable {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();

View File

@@ -15,7 +15,6 @@
package google.registry.flows;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Action.Method;
import google.registry.request.Payload;
import google.registry.request.auth.Auth;
@@ -27,7 +26,7 @@ import jakarta.servlet.http.HttpServletRequest;
* to RFC 5730. Commands must be requested via POST.
*/
@Action(
service = GaeService.DEFAULT,
service = Action.Service.FRONTEND,
path = "/_dr/epp",
method = Method.POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -22,7 +22,6 @@ import dagger.Module;
import dagger.Provides;
import google.registry.model.eppcommon.ProtocolDefinition;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Action.Method;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
@@ -31,7 +30,7 @@ import jakarta.servlet.http.HttpServletRequest;
/** Runs EPP commands directly without logging in, verifying an XSRF token from the tool. */
@Action(
service = GaeService.TOOLS,
service = Action.Service.BACKEND,
path = EppToolAction.PATH,
method = Method.POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -47,6 +47,7 @@ import google.registry.model.eppinput.EppInput.Options;
import google.registry.model.eppinput.EppInput.Services;
import google.registry.model.eppoutput.EppResponse;
import google.registry.model.registrar.Registrar;
import google.registry.util.PasswordUtils;
import google.registry.util.StopwatchLogger;
import jakarta.inject.Inject;
import java.util.Optional;
@@ -150,8 +151,19 @@ public class LoginFlow implements MutatingFlow {
throw new RegistrarAccountNotActiveException();
}
if (login.getNewPassword().isPresent()) {
String newPassword = login.getNewPassword().get();
// TODO(b/458423787): Remove this circa March 2026 after enough time has passed for the logins
// to have transitioned to Argon2 hashing.
if (login.getNewPassword().isPresent()
|| registrar.get().getCurrentHashAlgorithm(login.getPassword()).orElse(null)
!= PasswordUtils.HashAlgorithm.ARGON_2_ID) {
String newPassword =
login
.getNewPassword()
.orElseGet(
() -> {
logger.atInfo().log("Rehashing existing registrar password with ARGON_2_ID");
return login.getPassword();
});
// Load fresh from database (bypassing the cache) to ensure we don't save stale data.
Optional<Registrar> freshRegistrar = Registrar.loadByRegistrarId(login.getClientId());
stopwatch.tick("LoginFlow reload freshRegistrar");

View File

@@ -28,7 +28,6 @@ import com.google.protobuf.Timestamp;
import google.registry.batch.CloudTasksUtils;
import google.registry.flows.EppToolAction;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
@@ -52,7 +51,7 @@ import org.joda.time.DateTime;
* least one must be specified in order for load testing to do anything.
*/
@Action(
service = GaeService.TOOLS,
service = Action.Service.BACKEND,
path = LoadTestAction.PATH,
method = Action.Method.POST,
automaticallyPrintOk = true,

View File

@@ -37,6 +37,7 @@ import google.registry.tools.IamClient;
import google.registry.tools.ServiceConnection;
import google.registry.tools.server.UpdateUserGroupAction;
import google.registry.util.PasswordUtils;
import google.registry.util.PasswordUtils.HashAlgorithm;
import google.registry.util.RegistryEnvironment;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
@@ -229,6 +230,10 @@ public class User extends UpdateAutoTimestampEntity implements Buildable {
|| isNullOrEmpty(registryLockPasswordHash)) {
return false;
}
return getCurrentHashAlgorithm(registryLockPassword).isPresent();
}
public Optional<HashAlgorithm> getCurrentHashAlgorithm(String registryLockPassword) {
return PasswordUtils.verifyPassword(
registryLockPassword, registryLockPasswordHash, registryLockPasswordSalt);
}

View File

@@ -67,6 +67,7 @@ import google.registry.persistence.converter.CurrencyToStringMapUserType;
import google.registry.persistence.transaction.TransactionManager;
import google.registry.util.CidrAddressBlock;
import google.registry.util.PasswordUtils;
import google.registry.util.PasswordUtils.HashAlgorithm;
import jakarta.mail.internet.AddressException;
import jakarta.mail.internet.InternetAddress;
import jakarta.persistence.AttributeOverride;
@@ -672,6 +673,10 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
}
public boolean verifyPassword(String password) {
return getCurrentHashAlgorithm(password).isPresent();
}
public Optional<HashAlgorithm> getCurrentHashAlgorithm(String password) {
return PasswordUtils.verifyPassword(password, passwordHash, salt);
}

View File

@@ -19,8 +19,7 @@ import static jakarta.servlet.http.HttpServletResponse.SC_OK;
import com.google.common.flogger.FluentLogger;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Action.GkeService;
import google.registry.request.Action.Service;
import google.registry.request.auth.Auth;
import jakarta.inject.Inject;
import jakarta.servlet.http.HttpServletResponse;
@@ -53,8 +52,7 @@ public class ReadinessProbeAction implements Runnable {
}
@Action(
service = GaeService.DEFAULT,
gkeService = GkeService.CONSOLE,
service = Service.CONSOLE,
path = ReadinessProbeConsoleAction.PATH,
auth = Auth.AUTH_PUBLIC)
public static class ReadinessProbeConsoleAction extends ReadinessProbeAction {
@@ -66,11 +64,7 @@ public class ReadinessProbeAction implements Runnable {
}
}
@Action(
service = GaeService.PUBAPI,
gkeService = GkeService.PUBAPI,
path = ReadinessProbeActionPubApi.PATH,
auth = Auth.AUTH_PUBLIC)
@Action(service = Service.PUBAPI, path = ReadinessProbeActionPubApi.PATH, auth = Auth.AUTH_PUBLIC)
public static class ReadinessProbeActionPubApi extends ReadinessProbeAction {
public static final String PATH = "/ready/pubapi";
@@ -81,8 +75,7 @@ public class ReadinessProbeAction implements Runnable {
}
@Action(
service = GaeService.DEFAULT,
gkeService = GkeService.FRONTEND,
service = Service.FRONTEND,
path = ReadinessProbeActionFrontend.PATH,
auth = Auth.AUTH_PUBLIC)
public static final class ReadinessProbeActionFrontend extends ReadinessProbeAction {

View File

@@ -16,7 +16,6 @@ package google.registry.monitoring.whitebox;
import com.google.api.services.monitoring.v3.Monitoring;
import com.google.api.services.monitoring.v3.model.MonitoredResource;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.monitoring.metrics.MetricReporter;
import com.google.monitoring.metrics.MetricWriter;
@@ -29,7 +28,6 @@ import google.registry.config.RegistryConfig.Config;
import google.registry.util.Clock;
import google.registry.util.GoogleCredentialsBundle;
import google.registry.util.MetricParameters;
import google.registry.util.RegistryEnvironment;
import jakarta.inject.Named;
import jakarta.inject.Singleton;
import org.joda.time.Duration;
@@ -40,13 +38,9 @@ public final class StackdriverModule {
private StackdriverModule() {}
// We need a fake GCE zone to appease Stackdriver's resource model.
// TODO(b/265973059): Switch to resource type "gke_container".
private static final String SPOOFED_GCE_ZONE = "us-central1-f";
// We cannot use a static fake intance ID which is shared by all instances, because metrics might
// be flushed to stackdriver with delays, which lead to time inversion erros when another instance
// has already written a data point at a later time.
// We cannot use a static fake instance ID which is shared by all instances, because metrics might
// be flushed to stackdriver with delays, which lead to time inversion errors when another
// instance has already written a data point at a later time.
@Singleton
@Provides
@Named("spoofedGceInstanceId")
@@ -72,23 +66,11 @@ public final class StackdriverModule {
Lazy<MetricParameters> gkeParameters,
@Config("projectId") String projectId,
@Config("stackdriverMaxQps") int maxQps,
@Config("stackdriverMaxPointsPerRequest") int maxPointsPerRequest,
@Named("spoofedGceInstanceId") String instanceId) {
@Config("stackdriverMaxPointsPerRequest") int maxPointsPerRequest) {
MonitoredResource resource =
RegistryEnvironment.isOnJetty()
? new MonitoredResource()
.setType("gke_container")
.setLabels(gkeParameters.get().makeLabelsMap())
:
// The MonitoredResource for GAE apps is not writable (and missing fields anyway) so we
// just use the gce_instance resource type instead.
new MonitoredResource()
.setType("gce_instance")
.setLabels(
ImmutableMap.of(
// The "zone" field MUST be a valid GCE zone, so we fake one.
"zone", SPOOFED_GCE_ZONE, "instance_id", instanceId));
new MonitoredResource()
.setType("gke_container")
.setLabels(gkeParameters.get().makeLabelsMap());
return new StackdriverWriter(
monitoringClient, projectId, resource, maxQps, maxPointsPerRequest);
}

View File

@@ -20,7 +20,6 @@ import static google.registry.request.Action.Method.HEAD;
import google.registry.rdap.RdapMetrics.EndpointType;
import google.registry.rdap.RdapObjectClasses.ReplyPayloadBase;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.HttpException.NotImplementedException;
import google.registry.request.auth.Auth;
import jakarta.inject.Inject;
@@ -32,7 +31,7 @@ import jakarta.inject.Inject;
* ARIN, not domain registries.
*/
@Action(
service = GaeService.PUBAPI,
service = Action.Service.PUBAPI,
path = "/rdap/autnum/",
method = {GET, HEAD},
isPrefix = true,

View File

@@ -29,7 +29,6 @@ import google.registry.rdap.RdapJsonFormatter.OutputDataType;
import google.registry.rdap.RdapMetrics.EndpointType;
import google.registry.rdap.RdapObjectClasses.RdapDomain;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.HttpException.NotFoundException;
import google.registry.request.auth.Auth;
@@ -38,7 +37,7 @@ import java.util.Optional;
/** RDAP action for domain requests. */
@Action(
service = GaeService.PUBAPI,
service = Action.Service.PUBAPI,
path = "/rdap/domain/",
method = {GET, HEAD},
isPrefix = true,

View File

@@ -42,7 +42,6 @@ import google.registry.rdap.RdapMetrics.WildcardType;
import google.registry.rdap.RdapSearchResults.DomainSearchResponse;
import google.registry.rdap.RdapSearchResults.IncompletenessWarningType;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.HttpException.NotFoundException;
import google.registry.request.HttpException.UnprocessableEntityException;
@@ -71,7 +70,7 @@ import org.joda.time.DateTime;
* Data Access Protocol (RDAP)</a>
*/
@Action(
service = GaeService.PUBAPI,
service = Action.Service.PUBAPI,
path = "/rdap/domains",
method = {GET, HEAD},
auth = Auth.AUTH_PUBLIC)

View File

@@ -30,7 +30,7 @@ import java.io.IOException;
* them the help response.
*/
@Action(
service = Action.GaeService.PUBAPI,
service = Action.Service.PUBAPI,
path = "/rdap/",
method = {GET, HEAD},
auth = Auth.AUTH_PUBLIC)

View File

@@ -25,7 +25,6 @@ import google.registry.rdap.RdapJsonFormatter.OutputDataType;
import google.registry.rdap.RdapMetrics.EndpointType;
import google.registry.rdap.RdapObjectClasses.RdapEntity;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.HttpException.NotFoundException;
import google.registry.request.auth.Auth;
import jakarta.inject.Inject;
@@ -40,7 +39,7 @@ import java.util.Optional;
* the handle of the entity with the registrar role is be [sic] equal to the IANA Registrar ID.
*/
@Action(
service = GaeService.PUBAPI,
service = Action.Service.PUBAPI,
path = "/rdap/entity/",
method = {GET, HEAD},
isPrefix = true,

View File

@@ -31,7 +31,6 @@ import google.registry.rdap.RdapMetrics.SearchType;
import google.registry.rdap.RdapSearchResults.EntitySearchResponse;
import google.registry.rdap.RdapSearchResults.IncompletenessWarningType;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.HttpException.NotFoundException;
import google.registry.request.HttpException.UnprocessableEntityException;
@@ -56,7 +55,7 @@ import java.util.Optional;
* Data Access Protocol (RDAP)</a>
*/
@Action(
service = GaeService.PUBAPI,
service = Action.Service.PUBAPI,
path = "/rdap/entities",
method = {GET, HEAD},
auth = Auth.AUTH_PUBLIC)

View File

@@ -22,7 +22,6 @@ import google.registry.rdap.RdapDataStructures.Notice;
import google.registry.rdap.RdapMetrics.EndpointType;
import google.registry.rdap.RdapObjectClasses.HelpResponse;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.HttpException.NotFoundException;
import google.registry.request.auth.Auth;
import jakarta.inject.Inject;
@@ -30,7 +29,7 @@ import java.util.Optional;
/** RDAP action for help requests. */
@Action(
service = GaeService.PUBAPI,
service = Action.Service.PUBAPI,
path = RdapHelpAction.PATH,
method = {GET, HEAD},
isPrefix = true,

View File

@@ -20,7 +20,6 @@ import static google.registry.request.Action.Method.HEAD;
import google.registry.rdap.RdapMetrics.EndpointType;
import google.registry.rdap.RdapObjectClasses.ReplyPayloadBase;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.HttpException.NotImplementedException;
import google.registry.request.auth.Auth;
import jakarta.inject.Inject;
@@ -32,7 +31,7 @@ import jakarta.inject.Inject;
* ARIN, not domain registries.
*/
@Action(
service = GaeService.PUBAPI,
service = Action.Service.PUBAPI,
path = "/rdap/ip/",
method = {GET, HEAD},
isPrefix = true,

View File

@@ -26,7 +26,6 @@ import google.registry.rdap.RdapJsonFormatter.OutputDataType;
import google.registry.rdap.RdapMetrics.EndpointType;
import google.registry.rdap.RdapObjectClasses.RdapNameserver;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.HttpException.NotFoundException;
import google.registry.request.auth.Auth;
@@ -35,7 +34,7 @@ import java.util.Optional;
/** RDAP action for nameserver requests. */
@Action(
service = GaeService.PUBAPI,
service = Action.Service.PUBAPI,
path = "/rdap/nameserver/",
method = {GET, HEAD},
isPrefix = true,

View File

@@ -34,7 +34,6 @@ import google.registry.rdap.RdapMetrics.SearchType;
import google.registry.rdap.RdapSearchResults.IncompletenessWarningType;
import google.registry.rdap.RdapSearchResults.NameserverSearchResponse;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.HttpException.NotFoundException;
import google.registry.request.HttpException.UnprocessableEntityException;
@@ -57,7 +56,7 @@ import java.util.Optional;
* Data Access Protocol (RDAP)</a>
*/
@Action(
service = GaeService.PUBAPI,
service = Action.Service.PUBAPI,
path = "/rdap/nameservers",
method = {GET, HEAD},
auth = Auth.AUTH_PUBLIC)

View File

@@ -27,7 +27,6 @@ import com.google.common.flogger.FluentLogger;
import google.registry.model.registrar.Registrar;
import google.registry.persistence.PersistenceModule;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.HttpException.InternalServerErrorException;
import google.registry.request.UrlConnectionService;
import google.registry.request.UrlConnectionUtils;
@@ -54,7 +53,7 @@ import org.apache.commons.csv.CSVRecord;
* CSV endpoint requires no authentication.
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = "/_dr/task/updateRegistrarRdapBaseUrls",
automaticallyPrintOk = true,
auth = Auth.AUTH_ADMIN)

View File

@@ -32,7 +32,6 @@ import google.registry.model.rde.RdeNamingUtils;
import google.registry.model.rde.RdeRevision;
import google.registry.model.tld.Tld;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.HttpException.NoContentException;
import google.registry.request.Parameter;
import google.registry.request.RequestParameters;
@@ -63,7 +62,7 @@ import org.joda.time.DateTime;
* Agreement</a>
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = BrdaCopyAction.PATH,
method = POST,
automaticallyPrintOk = true,

View File

@@ -36,7 +36,6 @@ import google.registry.model.rde.RdeRevision;
import google.registry.model.tld.Tld;
import google.registry.rde.EscrowTaskRunner.EscrowTask;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.HttpException.NoContentException;
import google.registry.request.Parameter;
import google.registry.request.RequestParameters;
@@ -54,7 +53,7 @@ import org.joda.time.Duration;
* Action that uploads a small XML RDE report to ICANN after {@link RdeUploadAction} has finished.
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = RdeReportAction.PATH,
method = POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -50,7 +50,6 @@ import google.registry.model.host.Host;
import google.registry.model.rde.RdeMode;
import google.registry.model.registrar.Registrar;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.Parameter;
import google.registry.request.RequestParameters;
@@ -205,7 +204,7 @@ import org.joda.time.Duration;
* Name Registration Data Objects Mapping</a>
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = RdeStagingAction.PATH,
method = {GET, POST},
auth = Auth.AUTH_ADMIN)

View File

@@ -50,7 +50,6 @@ import google.registry.model.tld.Tld;
import google.registry.rde.EscrowTaskRunner.EscrowTask;
import google.registry.rde.JSchSshSession.JSchSshSessionFactory;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.HttpException.NoContentException;
import google.registry.request.Parameter;
import google.registry.request.RequestParameters;
@@ -84,7 +83,7 @@ import org.joda.time.Duration;
* RdeReportAction}.
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = RdeUploadAction.PATH,
method = POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -33,7 +33,6 @@ import google.registry.gcs.GcsUtils;
import google.registry.model.registrar.Registrar;
import google.registry.reporting.billing.BillingModule.InvoiceDirectoryPrefix;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.storage.drive.DriveConnection;
@@ -45,7 +44,7 @@ import java.util.Optional;
/** Copy all registrar detail reports in a given bucket's subdirectory from GCS to Drive. */
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = CopyDetailReportsAction.PATH,
method = POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -32,7 +32,6 @@ import google.registry.config.RegistryConfig.Config;
import google.registry.persistence.PersistenceModule;
import google.registry.reporting.ReportingModule;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
@@ -51,7 +50,7 @@ import org.joda.time.YearMonth;
* template. The pipeline then generates invoices for the month and stores them on GCS.
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = GenerateInvoicesAction.PATH,
method = POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -30,7 +30,6 @@ import google.registry.batch.CloudTasksUtils;
import google.registry.config.RegistryConfig.Config;
import google.registry.reporting.ReportingModule;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
@@ -49,7 +48,7 @@ import org.joda.time.YearMonth;
* Job States</a>
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = PublishInvoicesAction.PATH,
method = POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -34,7 +34,6 @@ import google.registry.groups.GmailClient;
import google.registry.reporting.ReportingModule;
import google.registry.reporting.icann.IcannReportingModule.ReportType;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
@@ -66,7 +65,7 @@ import org.joda.time.format.DateTimeFormat;
* 'transactions'. If none specified - defaults to generating both.
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = IcannReportingStagingAction.PATH,
method = POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -35,7 +35,6 @@ import google.registry.model.tld.Tld.TldType;
import google.registry.model.tld.Tlds;
import google.registry.persistence.VKey;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.HttpException.ServiceUnavailableException;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
@@ -68,7 +67,7 @@ import org.joda.time.Duration;
* Defaults to "icann/monthly/[last month in yyyy-MM format]".
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = IcannReportingUploadAction.PATH,
method = POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -32,7 +32,6 @@ import google.registry.config.RegistryConfig.Config;
import google.registry.keyring.api.KeyModule.Key;
import google.registry.reporting.ReportingModule;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
@@ -50,7 +49,7 @@ import org.joda.time.LocalDate;
* generates the specified month's Spec11 report and stores it on GCS.
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = GenerateSpec11ReportAction.PATH,
method = POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -38,7 +38,6 @@ import google.registry.config.RegistryConfig.Config;
import google.registry.reporting.ReportingModule;
import google.registry.reporting.spec11.soy.Spec11EmailSoyInfo;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
@@ -57,7 +56,7 @@ import org.json.JSONException;
* ImmutableSet)} on success or {@link Spec11EmailUtils#sendAlertEmail(String, String)} on failure.
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = PublishSpec11ReportAction.PATH,
method = POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -14,7 +14,6 @@
package google.registry.request;
import static com.google.common.base.Preconditions.checkState;
import google.registry.config.RegistryConfig;
import google.registry.request.auth.Auth;
@@ -38,46 +37,7 @@ public @interface Action {
DELETE
}
interface Service {
String getServiceId();
URL getServiceUrl();
}
enum GaeService implements Service {
BSA("bsa"),
DEFAULT("default"),
TOOLS("tools"),
BACKEND("backend"),
PUBAPI("pubapi");
private final String serviceId;
GaeService(String serviceId) {
this.serviceId = serviceId;
}
@Override
public String getServiceId() {
return serviceId;
}
@Override
public URL getServiceUrl() {
return switch (this) {
case DEFAULT -> RegistryConfig.getDefaultServer();
case TOOLS -> RegistryConfig.getToolsServer();
case BACKEND -> RegistryConfig.getBackendServer();
case BSA -> RegistryConfig.getBsaServer();
case PUBAPI -> RegistryConfig.getPubapiServer();
};
}
}
enum GkeService implements Service {
// This designation means that it defers to the GAE service, so we don't have to annotate EVERY
// action during the GKE migration.
SAME_AS_GAE("same_as_gae"),
enum Service {
FRONTEND("frontend"),
BACKEND("backend"),
PUBAPI("pubapi"),
@@ -85,27 +45,21 @@ public @interface Action {
private final String serviceId;
GkeService(String serviceId) {
Service(String serviceId) {
this.serviceId = serviceId;
}
@Override
public String getServiceId() {
checkState(this != SAME_AS_GAE, "Cannot get service Id for SAME_AS_GAE");
return serviceId;
}
@Override
public URL getServiceUrl() {
return RegistryConfig.getServiceUrl(this);
}
}
/** Which App Engine service this action lives on. */
GaeService service();
/** Which GKE service this action lives on. */
GkeService gkeService() default GkeService.SAME_AS_GAE;
Service service();
/** HTTP path to serve the action from. The path components must be percent-escaped. */
String path();
@@ -127,22 +81,4 @@ public @interface Action {
/** Authentication settings. */
Auth auth();
// TODO(jianglai): Use Action.gkeService() directly once we are off GAE.
class ServiceGetter {
public static GkeService get(Action action) {
GkeService service = action.gkeService();
if (service != GkeService.SAME_AS_GAE) {
return service;
}
GaeService gaeService = action.service();
return switch (gaeService) {
case DEFAULT -> GkeService.FRONTEND;
case BACKEND -> GkeService.BACKEND;
case TOOLS -> GkeService.BACKEND;
case BSA -> GkeService.BACKEND;
case PUBAPI -> GkeService.PUBAPI;
};
}
}
}

View File

@@ -23,7 +23,7 @@ import static jakarta.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import com.google.common.flogger.FluentLogger;
import google.registry.config.RegistryConfig;
import google.registry.request.Action.GkeService;
import google.registry.request.Action.Service;
import google.registry.request.auth.AuthResult;
import google.registry.request.auth.RequestAuthenticator;
import google.registry.util.NonFinalForTesting;
@@ -139,21 +139,20 @@ public class RequestHandler<C> {
rsp.sendError(SC_NOT_FOUND);
return;
}
if (RegistryEnvironment.isOnJetty()) {
GkeService service = Action.ServiceGetter.get(route.get().action());
String expectedDomain = RegistryConfig.getServiceUrl(service).getHost();
String actualDomain = req.getServerName();
// If the request doesn't come from GKE readiness prober
String maybeUserAgent = Optional.ofNullable(req.getHeader("User-Agent")).orElse("");
if (!maybeUserAgent.startsWith("kube-probe")
// If the hostname is "localhost", it must have come from the sidecar proxy.
&& !Objects.equals("localhost", actualDomain)
&& !Objects.equals(actualDomain, expectedDomain)) {
logger.atWarning().log(
"Actual domain %s does not match expected domain %s", actualDomain, expectedDomain);
rsp.sendError(SC_NOT_FOUND);
return;
}
Service service = route.get().action().service();
String expectedDomain = RegistryConfig.getServiceUrl(service).getHost();
String actualDomain = req.getServerName();
// If the request doesn't come from GKE readiness prober
String maybeUserAgent = Optional.ofNullable(req.getHeader("User-Agent")).orElse("");
if (!maybeUserAgent.startsWith("kube-probe")
// If the hostname is "localhost", it must have come from the sidecar proxy.
&& !Objects.equals("localhost", actualDomain)
&& !Objects.equals(actualDomain, expectedDomain)
&& !RegistryEnvironment.isInTestServer()) {
logger.atWarning().log(
"Actual domain %s does not match expected domain %s", actualDomain, expectedDomain);
rsp.sendError(SC_NOT_FOUND);
return;
}
if (!route.get().isMethodAllowed(method)) {
logger.atWarning().log("Method %s not allowed for: %s", method, path);

View File

@@ -94,7 +94,7 @@ public class RouterDisplayHelper {
private static String routeToString(Route route, String formatString) {
return String.format(
formatString,
Action.ServiceGetter.get(route.action()).name(),
route.action().service().name(),
route.action().isPrefix() ? (route.action().path() + "(*)") : route.action().path(),
route.actionClass().getSimpleName(),
Joiner.on(",").join(route.action().method()),
@@ -112,7 +112,7 @@ public class RouterDisplayHelper {
int methodsWidth = 7;
int minLevelWidth = 3;
for (Route route : routes) {
int len = Action.ServiceGetter.get(route.action()).name().length();
int len = route.action().service().name().length();
if (len > serviceWidth) {
serviceWidth = len;
}
@@ -148,9 +148,7 @@ public class RouterDisplayHelper {
return headerToString(formatString)
+ String.format("%n")
+ Streams.stream(routes)
.sorted(
Comparator.comparing(
(Route route) -> Action.ServiceGetter.get(route.action()).ordinal()))
.sorted(Comparator.comparing((Route route) -> route.action().service().ordinal()))
.map(route -> routeToString(route, formatString))
.collect(joining(String.format("%n")));
}

View File

@@ -54,10 +54,7 @@ public class AuthModule {
// See https://cloud.google.com/iap/docs/signed-headers-howto#securing_iap_headers.
public static final String IAP_HEADER_NAME = "X-Goog-IAP-JWT-Assertion";
public static final String BEARER_PREFIX = "Bearer ";
// TODO (jianglai): Only use GKE audience once we are fully migrated to GKE.
// See: https://cloud.google.com/iap/docs/signed-headers-howto#verifying_the_jwt_payload
private static final String IAP_GAE_AUDIENCE_FORMAT = "/projects/%d/apps/%s";
private static final String IAP_GKE_AUDIENCE_FORMAT = "/projects/%d/global/backendServices/%d";
private static final String IAP_AUDIENCE_FORMAT = "/projects/%d/global/backendServices/%d";
private static final String IAP_ISSUER_URL = "https://cloud.google.com/iap";
private static final String REGULAR_ISSUER_URL = "https://accounts.google.com";
// The backend service IDs created when setting up GKE routes. They will be included in the
@@ -89,24 +86,18 @@ public class AuthModule {
@IapOidc
@Singleton
TokenVerifier provideIapTokenVerifier(
@Config("projectId") String projectId,
@Config("projectIdNumber") long projectIdNumber,
@Named("backendServiceIdMap") Supplier<ImmutableMap<String, Long>> backendServiceIdMap) {
com.google.auth.oauth2.TokenVerifier.Builder tokenVerifierBuilder =
com.google.auth.oauth2.TokenVerifier.newBuilder().setIssuer(IAP_ISSUER_URL);
return (String service, String token) -> {
String audience;
if (RegistryEnvironment.isOnJetty()) {
Long backendServiceId = backendServiceIdMap.get().get(service);
checkNotNull(
backendServiceId,
"Backend service ID not found for service: %s, available IDs are %s",
service,
backendServiceIdMap);
audience = String.format(IAP_GKE_AUDIENCE_FORMAT, projectIdNumber, backendServiceId);
} else {
audience = String.format(IAP_GAE_AUDIENCE_FORMAT, projectIdNumber, projectId);
}
Long backendServiceId = backendServiceIdMap.get().get(service);
checkNotNull(
backendServiceId,
"Backend service ID not found for service: %s, available IDs are %s",
service,
backendServiceIdMap);
String audience = String.format(IAP_AUDIENCE_FORMAT, projectIdNumber, backendServiceId);
return tokenVerifierBuilder.setAudience(audience).build().verify(token);
};
}

View File

@@ -86,13 +86,10 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
}
JsonWebSignature token = null;
try {
String service = null;
if (RegistryEnvironment.isOnJetty()) {
String hostname = request.getServerName();
service = Splitter.on('.').split(hostname).iterator().next();
if (request.getHeader("canary") != null) {
service += "-canary";
}
String hostname = request.getServerName();
String service = Splitter.on('.').split(hostname).iterator().next();
if (request.getHeader("canary") != null) {
service += "-canary";
}
token = tokenVerifier.verify(service, rawIdToken);
} catch (Exception e) {

View File

@@ -37,7 +37,6 @@ import google.registry.batch.CloudTasksUtils;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.domain.Domain;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.RequestParameters;
import google.registry.request.UrlConnectionService;
@@ -66,7 +65,7 @@ import org.joda.time.Duration;
* @see NordnVerifyAction
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = NordnUploadAction.PATH,
method = Action.Method.POST,
automaticallyPrintOk = true,

View File

@@ -23,7 +23,6 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.flogger.FluentLogger;
import com.google.common.io.ByteSource;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.HttpException.ConflictException;
import google.registry.request.Parameter;
import google.registry.request.RequestParameters;
@@ -51,7 +50,7 @@ import java.util.Map.Entry;
* http://tools.ietf.org/html/draft-lozano-tmch-func-spec-08#section-5.2.3.3</a>
*/
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = NordnVerifyAction.PATH,
method = Action.Method.POST,
automaticallyPrintOk = true,

View File

@@ -19,7 +19,6 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import google.registry.config.RegistryConfig.Config;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.auth.Auth;
import jakarta.inject.Inject;
import java.io.IOException;
@@ -29,7 +28,7 @@ import java.util.Optional;
/** Action to download the latest ICANN TMCH CRL from MarksDB. */
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = "/_dr/task/tmchCrl",
method = POST,
automaticallyPrintOk = true,

View File

@@ -22,7 +22,6 @@ import google.registry.keyring.api.KeyModule.Key;
import google.registry.model.tmch.ClaimsList;
import google.registry.model.tmch.ClaimsListDao;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.auth.Auth;
import jakarta.inject.Inject;
import java.io.IOException;
@@ -32,7 +31,7 @@ import org.bouncycastle.openpgp.PGPException;
/** Action to download the latest domain name list (aka claims list) from MarksDB. */
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = "/_dr/task/tmchDnl",
method = POST,
automaticallyPrintOk = true,

View File

@@ -22,7 +22,6 @@ import google.registry.keyring.api.KeyModule.Key;
import google.registry.model.smd.SignedMarkRevocationList;
import google.registry.model.smd.SignedMarkRevocationListDao;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.auth.Auth;
import jakarta.inject.Inject;
import java.io.IOException;
@@ -32,7 +31,7 @@ import org.bouncycastle.openpgp.PGPException;
/** Action to download the latest signed mark revocation list from MarksDB. */
@Action(
service = GaeService.BACKEND,
service = Action.Service.BACKEND,
path = "/_dr/task/tmchSmdrl",
method = POST,
automaticallyPrintOk = true,

View File

@@ -28,8 +28,6 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.net.MediaType;
import google.registry.config.RegistryConfig.Config;
import google.registry.request.Action.GaeService;
import google.registry.request.Action.GkeService;
import google.registry.request.Action.Service;
import jakarta.inject.Inject;
import java.util.List;
@@ -80,10 +78,6 @@ class CurlCommand implements CommandWithConnection {
required = true)
private String serviceName;
@Inject
@Config("useGke")
boolean useGke;
@Inject
@Config("useCanary")
boolean useCanary;
@@ -103,11 +97,7 @@ class CurlCommand implements CommandWithConnection {
throw new IllegalArgumentException("You may not specify a body for a get method.");
}
Service service =
useGke
? GkeService.valueOf(Ascii.toUpperCase(serviceName))
: GaeService.valueOf(Ascii.toUpperCase(serviceName));
Service service = Service.valueOf(Ascii.toUpperCase(serviceName));
ServiceConnection connectionToService = connection.withService(service, useCanary);
String response =
(method == Method.GET)

View File

@@ -19,7 +19,6 @@ import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.base.Verify.verify;
import static com.google.common.net.HttpHeaders.X_REQUESTED_WITH;
import static com.google.common.net.MediaType.JSON_UTF_8;
import static google.registry.config.ConfigUtils.makeUrl;
import static google.registry.config.RegistryConfig.CANARY_HEADER;
import static google.registry.security.JsonHttp.JSON_SAFETY_PREFIX;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -36,8 +35,6 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.io.CharStreams;
import com.google.common.net.MediaType;
import google.registry.config.RegistryConfig.Config;
import google.registry.request.Action.GaeService;
import google.registry.request.Action.GkeService;
import google.registry.request.Action.Service;
import jakarta.inject.Inject;
import java.io.IOException;
@@ -61,10 +58,9 @@ public class ServiceConnection {
@Inject
ServiceConnection(
@Config("useGke") boolean useGke,
@Config("useCanary") boolean useCanary,
HttpRequestFactory requestFactory) {
this(useGke ? GkeService.BACKEND : GaeService.TOOLS, requestFactory, useCanary);
this(Service.BACKEND, requestFactory, useCanary);
}
private ServiceConnection(Service service, HttpRequestFactory requestFactory, boolean useCanary) {
@@ -75,14 +71,6 @@ public class ServiceConnection {
/** Returns a copy of this connection that talks to a different service endpoint. */
public ServiceConnection withService(Service service, boolean useCanary) {
Class<? extends Service> oldServiceClazz = this.service.getClass();
Class<? extends Service> newServiceClazz = service.getClass();
if (oldServiceClazz != newServiceClazz) {
throw new IllegalArgumentException(
String.format(
"Cannot switch from %s to %s",
oldServiceClazz.getSimpleName(), newServiceClazz.getSimpleName()));
}
return new ServiceConnection(service, requestFactory, useCanary);
}
@@ -136,12 +124,6 @@ public class ServiceConnection {
URL getServer() {
URL url = service.getServiceUrl();
verify(!isNullOrEmpty(url.getHost()), "Null host in url");
if (useCanary && service instanceof GaeService) {
url =
makeUrl(
String.format(
"%s://nomulus-dot-%s%s", url.getProtocol(), url.getHost(), url.getFile()));
}
return url;
}

View File

@@ -26,7 +26,6 @@ import google.registry.groups.GroupsConnection.Role;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarPoc;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.HttpException.InternalServerErrorException;
import google.registry.request.Parameter;
@@ -41,7 +40,7 @@ import java.util.Optional;
/** Action that creates Google Groups for a registrar's mailing lists. */
@Action(
service = GaeService.TOOLS,
service = Action.Service.BACKEND,
path = CreateGroupsAction.PATH,
method = POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -33,7 +33,6 @@ import google.registry.model.domain.secdns.DomainDsData;
import google.registry.model.host.Host;
import google.registry.model.tld.Tld;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.JsonActionRunner;
import google.registry.request.auth.Auth;
@@ -63,7 +62,7 @@ import org.joda.time.Duration;
* days in the past, and must be at midnight UTC.
*/
@Action(
service = GaeService.TOOLS,
service = Action.Service.BACKEND,
path = GenerateZoneFilesAction.PATH,
method = POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -28,7 +28,6 @@ import com.google.common.collect.ImmutableSet;
import google.registry.model.EppResourceUtils;
import google.registry.model.domain.Domain;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
@@ -37,7 +36,7 @@ import jakarta.inject.Inject;
/** An action that lists domains, for use by the {@code nomulus list_domains} command. */
@Action(
service = GaeService.TOOLS,
service = Action.Service.BACKEND,
path = ListDomainsAction.PATH,
method = {GET, POST},
auth = Auth.AUTH_ADMIN)

View File

@@ -25,7 +25,6 @@ import com.google.common.collect.ImmutableSet;
import google.registry.model.EppResourceUtils;
import google.registry.model.host.Host;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
import jakarta.inject.Inject;
@@ -33,7 +32,7 @@ import org.joda.time.DateTime;
/** An action that lists hosts, for use by the {@code nomulus list_hosts} command. */
@Action(
service = GaeService.TOOLS,
service = Action.Service.BACKEND,
path = ListHostsAction.PATH,
method = {GET, POST},
auth = Auth.AUTH_ADMIN)

View File

@@ -23,7 +23,6 @@ import com.google.common.collect.ImmutableSet;
import google.registry.model.tld.label.PremiumList;
import google.registry.model.tld.label.PremiumListDao;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.auth.Auth;
import jakarta.inject.Inject;
import java.util.Comparator;
@@ -33,7 +32,7 @@ import java.util.Optional;
* An action that lists premium lists, for use by the {@code nomulus list_premium_lists} command.
*/
@Action(
service = GaeService.TOOLS,
service = Action.Service.BACKEND,
path = ListPremiumListsAction.PATH,
method = {GET, POST},
auth = Auth.AUTH_ADMIN)

View File

@@ -22,13 +22,12 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import google.registry.model.registrar.Registrar;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.auth.Auth;
import jakarta.inject.Inject;
/** An action that lists registrars, for use by the {@code nomulus list_registrars} command. */
@Action(
service = GaeService.TOOLS,
service = Action.Service.BACKEND,
path = ListRegistrarsAction.PATH,
method = {GET, POST},
auth = Auth.AUTH_ADMIN)

View File

@@ -23,7 +23,6 @@ import com.google.common.collect.ImmutableSet;
import google.registry.model.tld.label.ReservedList;
import google.registry.model.tld.label.ReservedListDao;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.auth.Auth;
import jakarta.inject.Inject;
import java.util.Comparator;
@@ -31,7 +30,7 @@ import java.util.Optional;
/** A that lists reserved lists, for use by the {@code nomulus list_reserved_lists} command. */
@Action(
service = GaeService.TOOLS,
service = Action.Service.BACKEND,
path = ListReservedListsAction.PATH,
method = {GET, POST},
auth = Auth.AUTH_ADMIN)

View File

@@ -24,7 +24,6 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import google.registry.model.tld.Tld;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
import jakarta.inject.Inject;
@@ -32,7 +31,7 @@ import org.joda.time.DateTime;
/** An action that lists top-level domains, for use by the {@code nomulus list_tlds} command. */
@Action(
service = GaeService.TOOLS,
service = Action.Service.BACKEND,
path = ListTldsAction.PATH,
method = {GET, POST},
auth = Auth.AUTH_ADMIN)

View File

@@ -29,7 +29,6 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
@@ -58,7 +57,7 @@ import org.joda.time.Duration;
* domains that have since been deleted.
*/
@Action(
service = GaeService.TOOLS,
service = Action.Service.BACKEND,
path = "/_dr/task/refreshDnsForAllDomains",
auth = Auth.AUTH_ADMIN)
public class RefreshDnsForAllDomainsAction implements Runnable {

View File

@@ -20,7 +20,6 @@ import com.google.common.flogger.FluentLogger;
import google.registry.groups.GroupsConnection;
import google.registry.groups.GroupsConnection.Role;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
@@ -28,7 +27,7 @@ import jakarta.inject.Inject;
/** Action that adds or deletes a console user to/from the group that has IAP permissions. */
@Action(
service = GaeService.TOOLS,
service = Action.Service.BACKEND,
path = UpdateUserGroupAction.PATH,
method = POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -21,7 +21,6 @@ import com.google.common.collect.Maps;
import google.registry.model.OteStats;
import google.registry.model.OteStats.StatType;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.JsonActionRunner;
import google.registry.request.JsonActionRunner.JsonAction;
import google.registry.request.auth.Auth;
@@ -35,7 +34,7 @@ import java.util.stream.Collectors;
* OT&amp;E commands that have been run just previously to verification may not be picked up yet.
*/
@Action(
service = GaeService.TOOLS,
service = Action.Service.BACKEND,
path = VerifyOteAction.PATH,
method = Action.Method.POST,
auth = Auth.AUTH_ADMIN)

View File

@@ -23,8 +23,7 @@ import google.registry.model.console.ConsolePermission;
import google.registry.model.console.User;
import google.registry.model.domain.Domain;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Action.GkeService;
import google.registry.request.Action.Service;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import jakarta.inject.Inject;
@@ -32,8 +31,7 @@ import java.util.Optional;
/** Returns a JSON representation of a domain to the registrar console. */
@Action(
service = GaeService.DEFAULT,
gkeService = GkeService.CONSOLE,
service = Service.CONSOLE,
path = ConsoleDomainGetAction.PATH,
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
public class ConsoleDomainGetAction extends ConsoleApiAction {

View File

@@ -26,8 +26,7 @@ import google.registry.model.CreateAutoTimestamp;
import google.registry.model.console.User;
import google.registry.model.domain.Domain;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Action.GkeService;
import google.registry.request.Action.Service;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import jakarta.inject.Inject;
@@ -38,8 +37,7 @@ import org.joda.time.DateTime;
/** Returns a (paginated) list of domains for a particular registrar. */
@Action(
service = GaeService.DEFAULT,
gkeService = GkeService.CONSOLE,
service = Service.CONSOLE,
path = ConsoleDomainListAction.PATH,
method = Action.Method.GET,
auth = Auth.AUTH_PUBLIC_LOGGED_IN)

View File

@@ -26,8 +26,7 @@ import google.registry.model.console.ConsolePermission;
import google.registry.model.console.ConsoleUpdateHistory;
import google.registry.model.console.User;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Action.GkeService;
import google.registry.request.Action.Service;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
@@ -39,8 +38,7 @@ import org.apache.commons.csv.CSVPrinter;
import org.joda.time.DateTime;
@Action(
service = GaeService.DEFAULT,
gkeService = GkeService.CONSOLE,
service = Service.CONSOLE,
path = ConsoleDumDownloadAction.PATH,
method = {GET},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)

View File

@@ -31,8 +31,7 @@ import google.registry.model.console.ConsoleUpdateHistory;
import google.registry.model.console.User;
import google.registry.model.registrar.Registrar;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Action.GkeService;
import google.registry.request.Action.Service;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
@@ -42,8 +41,7 @@ import jakarta.inject.Inject;
import java.util.Optional;
@Action(
service = GaeService.DEFAULT,
gkeService = GkeService.CONSOLE,
service = Service.CONSOLE,
path = ConsoleEppPasswordAction.PATH,
method = {POST},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)

View File

@@ -29,8 +29,7 @@ import google.registry.model.console.GlobalRole;
import google.registry.model.console.User;
import google.registry.model.console.UserRoles;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Action.GkeService;
import google.registry.request.Action.Service;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import jakarta.inject.Inject;
@@ -38,8 +37,7 @@ import java.util.List;
import java.util.Optional;
@Action(
service = GaeService.DEFAULT,
gkeService = GkeService.CONSOLE,
service = Service.CONSOLE,
path = ConsoleHistoryDataAction.PATH,
method = {GET},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)

View File

@@ -36,7 +36,7 @@ import google.registry.model.console.ConsolePermission;
import google.registry.model.console.User;
import google.registry.model.registrar.Registrar;
import google.registry.request.Action;
import google.registry.request.Action.GkeService;
import google.registry.request.Action.Service;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.tools.IamClient;
@@ -48,8 +48,7 @@ import java.util.Map;
import java.util.Optional;
@Action(
service = Action.GaeService.DEFAULT,
gkeService = GkeService.CONSOLE,
service = Service.CONSOLE,
path = ConsoleOteAction.PATH,
method = {GET, POST},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)

View File

@@ -23,6 +23,7 @@ import static jakarta.servlet.http.HttpServletResponse.SC_OK;
import static jakarta.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
import com.google.gson.annotations.Expose;
import google.registry.flows.EppException;
import google.registry.flows.domain.DomainFlowUtils;
@@ -33,13 +34,13 @@ import google.registry.model.domain.RegistryLock;
import google.registry.model.registrar.Registrar;
import google.registry.model.tld.RegistryLockDao;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Action.GkeService;
import google.registry.request.Action.Service;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.tools.DomainLockUtils;
import google.registry.util.EmailMessage;
import google.registry.util.PasswordUtils.HashAlgorithm;
import jakarta.inject.Inject;
import jakarta.mail.internet.AddressException;
import jakarta.mail.internet.InternetAddress;
@@ -54,20 +55,22 @@ import org.joda.time.Duration;
* action).
*/
@Action(
service = GaeService.DEFAULT,
gkeService = GkeService.CONSOLE,
service = Service.CONSOLE,
path = ConsoleRegistryLockAction.PATH,
method = {GET, POST},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
public class ConsoleRegistryLockAction extends ConsoleApiAction {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
static final String PATH = "/console-api/registry-lock";
static final String VERIFICATION_EMAIL_TEMPLATE =
"""
Please click the link below to perform the lock / unlock action on domain %s. Note: this\
code will expire in one hour.
%s""";
%s\
""";
private final DomainLockUtils domainLockUtils;
private final GmailClient gmailClient;
@@ -114,7 +117,6 @@ public class ConsoleRegistryLockAction extends ConsoleApiAction {
optionalPostInput.orElseThrow(() -> new IllegalArgumentException("No POST input provided"));
String domainName = postInput.domainName();
boolean isLock = postInput.isLock();
Optional<String> maybePassword = Optional.ofNullable(postInput.password());
Optional<Long> relockDurationMillis = Optional.ofNullable(postInput.relockDurationMillis());
try {
@@ -126,11 +128,25 @@ public class ConsoleRegistryLockAction extends ConsoleApiAction {
// Passwords aren't required for admin users, otherwise we need to validate it
boolean isAdmin = user.getUserRoles().isAdmin();
if (!isAdmin) {
checkArgument(maybePassword.isPresent(), "No password provided");
if (!user.verifyRegistryLockPassword(maybePassword.get())) {
checkArgument(postInput.password != null, "No password provided");
Optional<HashAlgorithm> hashAlgorithm = user.getCurrentHashAlgorithm(postInput.password);
if (hashAlgorithm.isEmpty()) {
setFailedResponse("Incorrect registry lock password", SC_UNAUTHORIZED);
return;
}
// TODO(b/458423787): Remove this circa March 2026 after enough time has passed for the logins
// to have transitioned to Argon2 hashing.
if (hashAlgorithm.get() != HashAlgorithm.ARGON_2_ID) {
logger.atInfo().log("Rehashing existing registry lock password with ARGON_2_ID.");
tm().transact(
() ->
tm().update(
tm().loadByEntity(user)
.asBuilder()
.removeRegistryLockPassword()
.setRegistryLockPassword(postInput.password)
.build()));
}
}
String registryLockEmail =

View File

@@ -23,8 +23,7 @@ import google.registry.model.console.ConsoleUpdateHistory;
import google.registry.model.console.User;
import google.registry.model.domain.RegistryLock;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Action.GkeService;
import google.registry.request.Action.Service;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.tools.DomainLockUtils;
@@ -33,8 +32,7 @@ import jakarta.servlet.http.HttpServletResponse;
/** Handler for verifying registry lock requests, a form of 2FA. */
@Action(
service = GaeService.DEFAULT,
gkeService = GkeService.CONSOLE,
service = Service.CONSOLE,
path = ConsoleRegistryLockVerifyAction.PATH,
method = {GET},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)

View File

@@ -29,8 +29,7 @@ import google.registry.model.console.ConsoleUpdateHistory;
import google.registry.model.console.User;
import google.registry.model.registrar.Registrar;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Action.GkeService;
import google.registry.request.Action.Service;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
@@ -42,8 +41,7 @@ import java.util.stream.Collectors;
import org.joda.time.DateTime;
@Action(
service = GaeService.DEFAULT,
gkeService = GkeService.CONSOLE,
service = Service.CONSOLE,
path = ConsoleUpdateRegistrarAction.PATH,
method = {POST},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)

View File

@@ -21,8 +21,7 @@ import com.google.common.collect.ImmutableMap;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.console.User;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Action.GkeService;
import google.registry.request.Action.Service;
import google.registry.request.auth.Auth;
import google.registry.security.XsrfTokenManager;
import jakarta.inject.Inject;
@@ -30,8 +29,7 @@ import jakarta.servlet.http.Cookie;
import org.json.JSONObject;
@Action(
service = GaeService.DEFAULT,
gkeService = GkeService.CONSOLE,
service = Service.CONSOLE,
path = ConsoleUserDataAction.PATH,
method = {GET},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)

View File

@@ -43,7 +43,7 @@ import google.registry.model.console.UserRoles;
import google.registry.model.registrar.Registrar;
import google.registry.persistence.VKey;
import google.registry.request.Action;
import google.registry.request.Action.GkeService;
import google.registry.request.Action.Service;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
@@ -61,8 +61,7 @@ import java.util.stream.Collectors;
import javax.annotation.Nullable;
@Action(
service = Action.GaeService.DEFAULT,
gkeService = GkeService.CONSOLE,
service = Service.CONSOLE,
path = ConsoleUsersAction.PATH,
method = {GET, POST, DELETE, PUT},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)

View File

@@ -34,8 +34,7 @@ import jakarta.servlet.http.HttpServletResponse;
import javax.annotation.Nullable;
@Action(
service = Action.GaeService.DEFAULT,
gkeService = Action.GkeService.CONSOLE,
service = Action.Service.CONSOLE,
path = PasswordResetRequestAction.PATH,
method = Action.Method.POST,
auth = Auth.AUTH_PUBLIC_LOGGED_IN)

View File

@@ -36,8 +36,7 @@ import java.util.Optional;
import org.joda.time.Duration;
@Action(
service = Action.GaeService.DEFAULT,
gkeService = Action.GkeService.CONSOLE,
service = Action.Service.CONSOLE,
path = PasswordResetVerifyAction.PATH,
method = {GET, POST},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)

View File

@@ -33,8 +33,7 @@ import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.Registrar.State;
import google.registry.model.registrar.RegistrarPoc;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Action.GkeService;
import google.registry.request.Action.Service;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.util.StringGenerator;
@@ -45,8 +44,7 @@ import java.util.Map;
import java.util.Optional;
@Action(
service = GaeService.DEFAULT,
gkeService = GkeService.CONSOLE,
service = Service.CONSOLE,
path = RegistrarsAction.PATH,
method = {GET, POST},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)

View File

@@ -49,8 +49,7 @@ import java.util.Optional;
* request body depend on the type of action -- some requests may require more data than others.
*/
@Action(
service = Action.GaeService.DEFAULT,
gkeService = Action.GkeService.CONSOLE,
service = Action.Service.CONSOLE,
path = ConsoleBulkDomainAction.PATH,
method = Action.Method.POST,
auth = Auth.AUTH_PUBLIC_LOGGED_IN)

View File

@@ -36,8 +36,7 @@ import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarPoc;
import google.registry.model.registrar.RegistrarPoc.Type;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Action.GkeService;
import google.registry.request.Action.Service;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.ui.server.console.ConsoleApiAction;
@@ -49,8 +48,7 @@ import java.util.Set;
import java.util.function.BiFunction;
@Action(
service = GaeService.DEFAULT,
gkeService = GkeService.CONSOLE,
service = Service.CONSOLE,
path = ContactAction.PATH,
method = {GET, POST, DELETE, PUT},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)

View File

@@ -25,8 +25,7 @@ import google.registry.model.console.ConsoleUpdateHistory;
import google.registry.model.console.User;
import google.registry.model.registrar.Registrar;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Action.GkeService;
import google.registry.request.Action.Service;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
@@ -44,8 +43,7 @@ import java.util.StringJoiner;
* and don't have any other obvious means of edit.
*/
@Action(
service = GaeService.DEFAULT,
gkeService = GkeService.CONSOLE,
service = Service.CONSOLE,
path = RdapRegistrarFieldsAction.PATH,
method = {POST},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)

View File

@@ -29,8 +29,7 @@ import google.registry.model.console.ConsoleUpdateHistory;
import google.registry.model.console.User;
import google.registry.model.registrar.Registrar;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Action.GkeService;
import google.registry.request.Action.Service;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
@@ -42,8 +41,7 @@ import java.util.Optional;
import java.util.StringJoiner;
@Action(
service = GaeService.DEFAULT,
gkeService = GkeService.CONSOLE,
service = Service.CONSOLE,
path = SecurityAction.PATH,
method = {POST},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)

Some files were not shown because too many files have changed in this diff Show More