1
0
mirror of https://github.com/google/nomulus synced 2026-07-18 22:12:24 +00:00

Compare commits

...

73 Commits

Author SHA1 Message Date
Weimin Yu 7e0489e5f9 Sanitize and parse logged TLDs using JSON arrays (#3164)
In FlowReporter, we extract TLDs from EPP domain commands and log them under 'tld' and 'tlds' metadata fields to generate ICANN activity reports. Previously, invalid or extremely long TLD names (such as email addresses or long domain labels in non-validated XML payloads) could break downstream log parsing.

To prevent this issue, this change does the following:

1. Java Sanitization:
   Introduces `toLogSafeLabel(...)` in `DomainFlowUtils`, which converts ASCII to lowercase, replaces any character outside of `[a-z0-9.-]` with `-`, and limits the length to 63 characters (appending '...' if truncated). FlowReporter now uses this method on guessed TLDs before logging them.

2. SQL Modernization:
   Upgrades `epp_metrics.sql` and `epp_metrics_test.sql` to use BigQuery's native `JSON_EXTRACT_STRING_ARRAY(json, '$.tlds')` instead of the legacy `SPLIT(REGEXP_EXTRACT(JSON_EXTRACT(...)))` workaround. Because the native function extracts clean string elements, it removes the need to strip quotation marks with additional regexes and prevents parsing failures on commas, spaces, or nested structures.

SQL change tested in BigQuery.

BUG=http://b/535230985
2026-07-17 19:42:00 +00:00
gbrodman d29a98bdd3 Handle RegistryLock unlock edge case with doubly-applied lock (#3159)
We allow admin locks to overwrite already-applied locks. In the case
where that happens in between an unlock request and an unlock
completion, we shouldn't allow the unlock completion to go through.
2026-07-17 17:36:26 +00:00
gbrodman d6dd95b052 Only delete Workspace accounts if we created them (#3161)
Just in case, we should check to make sure that we created the
account-with-no-registrars before we delete it.
2026-07-17 16:10:34 +00:00
gbrodman 6b74924067 Handle too-large requests/responses (#3147)
100 MB is kind of arbitrary, but it's as good as any other limit.

D.1 numbers 1, 8, 9
2026-07-17 16:10:20 +00:00
gbrodman 71e9bc95a7 Use cache for RDAP searches for host by superord domain (#3145)
this allows us to only do one query instead of looping over the hosts
and doing queries one by one, while still leveraging the cache.
2026-07-17 14:33:48 +00:00
gbrodman bf54a0d0c4 Add index to rlock email address (#3150)
We sometimes query by this. The table is very small but eh, just in case
2026-07-17 03:47:53 +00:00
gbrodman 2a04b9be9b Reload SINGLE_USE ATs to avoid cache race conditions (#3157) 2026-07-17 03:47:48 +00:00
gbrodman 675354ae65 Add cloud scheduler task to sync remote caches (#3158) 2026-07-16 19:14:26 +00:00
gbrodman 21421726e0 Update security checks around registry lock verification (#3137)
1. make it a POST instead of a GET
2. pass the user into DomainLockUtils so we can check permissions to
   make sure they have permissions on the registrar
3. update all the tests
2026-07-16 17:12:34 +00:00
Ben McIlwain 192a7e6c4e Implement Expiry Access Period flows and billing (#3131)
This commit implements the second stage (Java ORM mappings and EPP flow
enforcement) of the Expiry Access Period (XAP) launch and opt-in mechanism,
completing the Two-PR deployment split mandated by db/README.md after the
Stage 1 database migrations (PR #3134) are live.

During XAP, a TLD charges a fee for domain registration during a timed period
after deletion. To prevent accidental charges, registrars must explicitly
opt in to participate in XAP registrations.

Specifically, this commit:
- Adds expiryAccessPeriodTransitions (a TimedTransitionProperty of
  ExpiryAccessPeriodMode) to Tld.java and ExpiryAccessPeriodModeTransitionUserType
  for mapping XAP mode schedules to PostgreSQL hstore columns.
- Adds expiryAccessPeriodEnabled to Registrar.java with getter, builder
  setter, and JSON map serialization, and regenerates db-schema.sql.generated.
- Enforces registrar opt-in in DomainCheckFlow: when an unallocated domain is
  in XAP and the querying registrar has not opted in, domain:check returns
  avail="0" with reason "Reserved".
- Enforces registrar opt-in in DomainCreateFlow: when attempting to register
  a domain in XAP without registrar opt-in, throws DomainReservedException
  (EPP error 2304 "Object status prohibits operation").
- Enforces explicit fee acknowledgment during XAP domain creation when the XAP
  fee is non-zero, throwing FeesRequiredDuringExpiryAccessPeriodException in
  DomainFlowUtils if omitted.
- Creates a one-time BillingEvent with Reason.FEE_EXPIRY_ACCESS when a domain
  is created during XAP with a non-zero fee, and adds getXapCost() to
  FeesAndCredits.
- Updates all test TLD YAML configurations and adds comprehensive unit and
  integration tests across DomainCheckFlowTest, DomainCreateFlowTest,
  DomainPricingLogicTest, and RegistrarTest.

TAG=agy
CONV=f2488c74-8b4a-43f1-9c22-d1dddbdbb4e0
BUG=http://b/437398822
2026-07-15 21:25:40 +00:00
Weimin Yu 542084eb70 Avoid outputting garbage sequences in Nomulus CLI (#3156)
Lazy-initialize the terminal and LineReader in ShellCommand to prevent JLine from eagerly probing terminal capabilities during JCommander command-line startup.

During CLI startup, JCommander instantiates every command (including ShellCommand) to build the CLI's command map. Eager instantiation of the LineReader inside the ShellCommand constructor causes JLine to query the terminal cursor via Device Status Report (DSR) escape sequences. Standard commands (e.g. list_tlds) do not consume stdin, leaving these probing responses (such as ^[[71;1R and 1;1R) in the buffer to be leaked as garbage characters to standard output.

This change defers terminal and LineReader initialization until the shell's run() method is actually executed, while preserving the original constructor for unit tests.

BUG=http://b/534855218
2026-07-15 16:42:18 +00:00
Ben McIlwain 54e605fa27 Add Git boundary and PR sync rules to GEMINI.md (#3151)
An AI coding agent (Jetski) repeatedly violated user instructions by executing
unsolicited remote updates (git push --force-with-lease origin ...) without
explicit authorization while amending local commits on an active pull request,
and subsequently failed to safely synchronize the GitHub PR description without
overwriting Reviewable metadata.

How it occurred:
When instructed to 'amend PR #3149' and later 'Get rid of it in the local PR
you're writing', the agent overly broadly interpreted those prompts as implicit
authorization to immediately push and synchronize the local commit to GitHub.
Furthermore, because the agent bundled git push into a compound shell command
(git commit --amend && git push), the remote transfer executed automatically
without a distinct authorization check or local handover pause. Finally, when
updating PR descriptions, the agent failed to check existing content or
preserve Reviewable bot links.

How this commit structurally prevents recurrence:
1. Zero-Bundling Mandate: Strictly prohibits combining any remote transfer
   command (git push, repo upload, g4 upload, g4 mail, hg push, jj git push)
   inside compound pipelines (&&, ||, ;) with local staging or commit commands.
   Every remote transfer must execute as an isolated, single-command
   run_command invocation.
2. Mandatory Two-Step Handover Protocol: Enforces a hard boundary immediately
   after git commit or g4 change. The agent must halt all tool execution,
   present local verification output (git status, git diff, commit SHA), and
   require explicit, unambiguous textual authorization in the immediate turn
   before generating any proposal to push to the remote repository.
3. Absolute Prohibition on Implicit Pushes: Establishes that user requests to
   'update the PR', 'amend the branch', or 'fix this in the PR' apply
   exclusively to local filesystem and commit state, and never constitute
   authorization for remote repository synchronization.
4. Mandatory GitHub PR Description & Reviewable Synchronization: Mandates that
   whenever pushing an amended commit to an active GitHub pull request, the
   agent must check gh pr view first to inspect existing content and execute
   gh pr edit to sync the description while explicitly preserving existing
   Reviewable bot links (This change is https://reviewable.io/reviews/...).
2026-07-15 14:45:09 +00:00
gbrodman 188670e156 Add distributionSha256Sum to Gradle wrapper (#3155)
one can double check the hash of 9.4.1-all on https://gradle.org/release-checksums/

this just verifies the integrity of the downloaded Gradle
2026-07-14 21:15:55 +00:00
gbrodman 2fd609ed03 Error out if Gradle hashes don't match (#3154)
this seems like it's probably safer
2026-07-14 20:52:22 +00:00
gbrodman fc805ba4d9 Move testcontainers out of prod deps (#3152)
this reduces prod bundle size a bit and cleans things up

by including testcontainers in the nonprod deps of core, we can still
run things like the schema-generating commands
2026-07-14 19:10:21 +00:00
Ben McIlwain 21976bee24 Support dry-run and build-environment flags in Nomulus creation commands (#3149)
When checking in a brand new premium list (production/*.txt) or reserved list (reserved/production/*.txt), presubmit testing commands in gradle-runner.sh fall back from update_premium_list / update_reserved_list (which require existing database entities) to create_premium_list / create_reserved_list.

However, because --dry_run (-d) and --build_environment flags were previously only defined in UpdatePremiumListCommand and UpdateReservedListCommand, passing them during non-interactive presubmit creation validation resulted in ParameterException (Was passed main parameter '-d' but no main parameter was defined...) or NullPointerException when ConfirmingCommand attempted to prompt on a null system console.

This change moves --dry_run and --build_environment parameters and dontRunCommand() behavior up into CreateOrUpdatePremiumListCommand and CreateOrUpdateReservedListCommand, enabling safe dry-run presubmit validation for newly created lists in CI environments.

BUG= http://b/534511633, http://b/529397845
2026-07-14 17:52:04 +00:00
gbrodman c2bd11c528 Tweak a few console UI fields (#3146)
- treat password fields as passwords
- add Validators to a few places
- remove empty fields from billing maps if they exist
2026-07-13 16:10:44 +00:00
gbrodman 653da19e53 Implement CSP for the registrar console (#3129)
Implement a hybrid Content Security Policy (CSP) for the Registrar Console
to protect against XSS

- The CspFilter injects the proper headers on the Java backend endpoints
- Uinsg Jetty's HeaderFilter to inject the header for statically-served
  frontend assets

We need to add the ee10-servlets.ini file for Jetty to have acess to the
HeaderFilter class
2026-07-10 18:03:36 +00:00
gbrodman 4df734da2a Project domains and hosts on cache retrieval (#3143)
It's not perfect because we're running outside of a transaction, but we
should make a best-effort attempt to project domains/hosts to the
current time.
2026-07-10 17:15:59 +00:00
gbrodman b8a51cacc4 Enforce nonnegative costs for TLDs (#3139)
Note: we remove the checks in the setters because we check them all in
the build method instead (and the setters are only called in tests).
2026-07-10 16:49:17 +00:00
gbrodman 5067b3d339 Use secure processing in XmlTransformer's TransformerFactory (#3144)
C.1 number 9
2026-07-10 15:16:44 +00:00
gbrodman 93ec04018f Change registry lock input to password-type (#3125) 2026-07-10 15:12:39 +00:00
gbrodman ae310ea141 Enforce non-negative SPECIFIED prices (#3140)
This applies to both allocation tokens and billing recurrences
2026-07-09 19:13:14 +00:00
gbrodman c785365590 Remove reconstructed XSRF token from log (#3141)
A.3 #13
2026-07-09 15:49:48 +00:00
gbrodman 6ad85a2d21 Make small OIDC token javadoc update (#3142) 2026-07-08 18:40:11 +00:00
gbrodman 2e43295d63 Sanitize a few log instances (#3135)
Just remove a few possibly-insecure logs
2026-07-08 17:35:35 +00:00
gbrodman a7118dfbba Add small perms (etc) fixes to actions (#3138)
- nicer error in Registrar builder instead of an NPE
- nicer error if hosts were deleted in GenerateZoneFilesAction
- extra permissions checks in ConsoleUsersAction
- using "replace" (text) instead of "replaceAll" (regex) in
  BulkDomainAction
2026-07-08 17:35:32 +00:00
gbrodman 0bf0b4fc66 Require EPP validation for complex OT&E stats (#3107)
Previously, if there were no XML bytes somehow, it'd increment a bunch
of counts for requirements that shouldn't be incremented. We should only
increment these values if we have the data to do so.

Note: this doesn't happen and shouldn't happen, so there's no issues
currently occurring, but this code is more correct anyway.
2026-07-07 20:05:01 +00:00
Ben McIlwain 2ce91e3477 Add loadAllOfSorted to TransactionManager (#3136)
During our implementation of Expiry Access Period (XAP), adding a field to the
Registrar entity altered database heap scan order when loadAllOf() was
called without an explicit ORDER BY clause. This caused non-deterministic
row shifting in the Angular console registrar table (/console/registrars),
breaking golden image comparisons in ConsoleScreenshotTest.

To permanently fix this root cause and guarantee deterministic UI rendering
and test stability across schema evolutions, this commit introduces sorted
database loading across our persistence layer.

Specifically, this commit:
- Adds loadAllOfSorted and loadAllOfSortedStream to TransactionManager,
  JpaTransactionManagerImpl, and DelegatingReplicaJpaTransactionManager.
- Enforces strict allowlist regex validation (^[a-zA-Z0-9_.]+$) on property
  names in JpaTransactionManagerImpl before appending dynamic ORDER BY
  clauses, preventing JPQL/SQL injection since bind parameters cannot be
  used for schema identifiers.
- Adds Registrar.loadAllSorted and updates RegistrarsAction to explicitly
  sort registrars alphabetically by registrarName and registrarId.
- Updates golden screenshot
  ConsoleScreenshotTest_globalRole_registrars_registrarsPage.png to
  reflect the newly enforced deterministic alphabetical registrar ordering.
- Adds comprehensive unit test coverage in JpaTransactionManagerImplTest
  verifying exact sorted entity retrieval across multiple fields and regex
  rejection of invalid field names.

TAG=agy
CONV=f2488c74-8b4a-43f1-9c22-d1dddbdbb4e0
BUG=http://b/531889856
2026-07-07 19:44:56 +00:00
gbrodman 4d6b5a82df Forbid negative premium prices (#3108)
This hasn't been an issue, but it seems like a good idea. Note that I'm
allowing prices of 0 here just in case there's some shenanigans at some
point in time where we want a domain to technically be premium without
having a cost.
2026-07-07 19:33:10 +00:00
gbrodman 1fe1043306 Enforce GCS Public Access Prevention (#3133)
Implement programmatic verification of Public Access Prevention (PAP) on all GCS buckets before performing write operations via GcsUtils.

- Add verifyPublicAccessPrevention(String bucketName) to GcsUtils.java, querying the bucket's IAM configuration and asserting that PAP is ENFORCED.

- Call verifyPublicAccessPrevention in all output stream and byte creation write paths in GcsUtils.
2026-07-07 18:37:58 +00:00
gbrodman b1e42cfd5e Verify billing recurrence expansion is caught up before invoicing (#3121) 2026-07-07 18:05:48 +00:00
gbrodman 0fa82e30bb Fix typo in .gitignore (#3130) 2026-07-07 16:08:26 +00:00
Ben McIlwain 6608ee282d Add XAP registrar opt-in database schema (#3134)
This commit implements the first stage of the Two-PR database schema
deployment for registrar opt-in to the Expiry Access Period (XAP), along
with updating GEMINI.md to mandate strict adherence to db/README.md for all
future database schema modifications.

Specifically, it:
- Adds Flyway migration
  V224__add_registrar_expiry_access_period_enabled.sql, which adds the
  expiry_access_period_enabled boolean column (DEFAULT false NOT NULL) to
  the Registrar table.
- Updates flyway.txt, nomulus.golden.sql, and ER diagrams to reflect the
  schema changes.
- Updates GEMINI.md with explicit instructions for AI agents to always
  consult db/README.md, follow the mandatory Two-PR deployment split when
  altering database schemas, and run both :db:test and
  :core:sqlIntegrationTest whenever database schema or ORM mappings are
  touched.
- Updates golden screenshot
  ConsoleScreenshotTest_globalRole_registrars_registrarsPage.png to reflect
  the new default database ordering after adding the column. (Note: A
  separate pull request, PR #3136 / http://go/r3pr/3136, permanently fixes
  this underlying sort non-determinism across our persistence layer by
  enforcing deterministic alphabetical ordering in Registrar.loadAllSorted).

TAG=agy
CONV=f2488c74-8b4a-43f1-9c22-d1dddbdbb4e0
BUG=http://b/437398822
2026-07-07 03:44:37 +00:00
gbrodman 22c867f3f2 Add TLD verification to GenerateZoneFilesAction (#3132)
1. this is a good idea in general
2. it can prevent GCS path traversal if someone somehow bypasses auth to
   add a weird path
2026-07-06 19:26:42 +00:00
gbrodman 160abef731 Throw on invalid responses from SafeBrowsing (#3111)
this allows us to
1. use the built-in retrier to retry
2. fail loudly if it keeps actually throwing an exception if we're doing
   something invalid
2026-07-06 18:52:13 +00:00
gbrodman a6e4017971 Join cancellations on billing time as well (#3124)
This is a small edge case where a cancellation for year 2 could cancel
out events in year 1 if we re-ran billing for year 1 for some reason. I
don't think we would ever re-run one year later so this is kind of moot,
but it's probably still a good thing to do.
2026-07-06 18:51:51 +00:00
gbrodman d9a857133a Remove class name option in GetRoutingMapCommand (#3101)
we only have the one component now, and in general it's a good idea to
remove class loading based on user input, even if that user is one of us
2026-07-01 18:55:26 +00:00
gbrodman c7a27061d8 Remove entries from token store atomically (#3112)
just in case some other thread comes in while we're iterating
2026-07-01 18:42:59 +00:00
Weimin Yu 7766db36a7 Support custom npm location in local environment (#3127)
Make sure Gradle can find the npm executable at custom locations,
e.g., when installed by nvm.
2026-07-01 18:39:29 +00:00
Ben McIlwain eda0f7ad7c Harden EppXmlSanitizer against XXE and entity expansion (#3117)
Configure a throwing XMLResolver on XMLInputFactory in XmlTransformer
to act as an additional defense-in-depth security layer against XML
External Entity (XXE) and entity expansion/resolution attacks.
This prevents resolution of external and internal entities in StAX
parsing pipelines, ensuring that malformed payloads containing entity
definitions are safely blocked and rejected.

Also add a unit test to EppXmlSanitizerTest verifying that EPP
messages containing DTD and external entities fail parsing and fall
back safely to their Base64 representation.

TAG=agy
CONV=610c2358-a99f-4605-94cd-ff0d4ee08176

BUG= http://b/529387728
2026-06-30 18:19:50 +00:00
gbrodman 67527f1560 Invalidate TLD cache on any type of update (#3123) 2026-06-30 18:15:45 +00:00
Ben McIlwain 4aeba6e3f7 Validate non-empty tlds in refresh DNS action (#3114)
Add validation to RefreshDnsForAllDomainsAction to ensure the tlds parameter is not empty when invoked. Previously, invoking the action without specifying tlds would result in a no-op that logged an empty list and exited without error. This change makes missing tlds an explicit error, throwing an IllegalArgumentException so users running the command via nomulus curl receive clear feedback that the parameter is required.
2026-06-30 16:34:15 +00:00
Ben McIlwain d6f1f5894b Restore RFC schemas, prober XML, and OT&E route (#3120)
In commit 17b851de42 (#2979), <element name="contact"> and related types were incorrectly removed from core/src/main/java/google/registry/xml/xsd/domain.xsd and rde-domain.xsd. Because those XSD schemas are published standards (RFC 5731 and RFC 5831), altering them caused JAXB unmarshaling to reject historical database records (HistoryEntry) containing <domain:contact> elements during OT&E status checks.

Restore <element name="contact" minOccurs="0" maxOccurs="unbounded"/> to domain.xsd and rde-domain.xsd so all XSD schemas conform strictly to published EPP RFCs and historical contact elements unmarshal cleanly without throwing cvc-complex-type.2.4.a schema validation errors.

Remove deprecated <domain:contact> elements from prober creation payload (prober/.../create.xml) to match current flow rules where contact associations are forbidden.

Remove trailing slash from ote-status route parameter in RegistrarDetailsComponent to prevent double-slash (/ote-status//<id>) URL generation when checking OT&E status.

BUG= http://b/529391412
2026-06-30 16:07:31 +00:00
gbrodman 47ad569cb0 Verify that marksdb verify URL starts with ry.marksdb.org (#3115) 2026-06-30 16:02:00 +00:00
gbrodman 06934daf94 Don't allow registrar-based ops on global users (#3100) 2026-06-29 19:13:20 +00:00
gbrodman 9a032e4bb9 Add extra perm check for EPP password changes (#3099) 2026-06-29 18:30:52 +00:00
gbrodman 0ab612ab23 Forbid mismatched inner element in XML flow inputs (#3104) 2026-06-26 19:04:12 +00:00
gbrodman 403c7ad275 Add test enforcing host references in redemption domains (#3109)
Domains in the redemption grace period can still be restored, so hosts
referencing them cannot be deleted. Fortunately this is already the
case. This just adds a test.
2026-06-26 19:04:09 +00:00
gbrodman 11d625b837 Enforce additional pw reset permissions (#3098)
This should not have been an issue in practice due to defense in depth,
but we should check these just in case.
2026-06-25 20:45:58 +00:00
gbrodman 6a47287da7 Forbid non-routable IPs for host glue records (#3105)
Reject loopback, link-local, site-local, wildcard, and multicast IP
addresses during host creation and update flows.

Glue records (A/AAAA records published in the parent zone for subordinate
name servers) must point to globally routable, public IP addresses to
ensure that recursive DNS resolvers on the public internet can reach the
authoritative name servers.

Using non-public or non-routable IP addresses in glue records is invalid
for the following reasons:
- Loopback (127.0.0.1, ::1) and Any-Local (0.0.0.0, ::) addresses point
  back to the client or are unspecified, causing resolvers to query
  themselves and fail.
- Private/Site-Local (e.g., 10.0.0.0/8, 192.168.0.0/16) and Link-Local
  (169.254.0.0/16) addresses are not routable on the public internet,
  rendering the delegated domain completely unreachable to external clients.
- Multicast addresses are designed for one-to-many delivery and cannot
  be used for standard unicast DNS queries to a specific name server.

Rename LoopbackIpNotValidForHostException to IpAddressNotRoutableException
to reflect the broader set of forbidden non-routable IP addresses.
2026-06-25 18:06:33 +00:00
gbrodman cdc0ffe831 Use filename regex in CopyDetailReportsAction (#3102)
No registrars have an underscore in their name, but as far as I'm aware
there's nothing explicitly preventing that.
2026-06-25 16:58:52 +00:00
Ben McIlwain 7c23413d83 Fix Console API and Angular XSS security flaws (#3076)
This commit addresses the following security vulnerabilities identified in the recent audit of the Console App and Backend APIs:

1. Angular XSS: Removed unsafe [innerHTML] bindings across all console-webapp templates (Contact, Registrars, Registrar Details, Users List) in favor of standard Angular interpolation.
2. Broken Access Control (IDOR): PasswordResetRequestAction and PasswordResetVerifyAction now explicitly verify that the target user's email belongs to the authorized registrarId.
3. Missing Permission Check: ConsoleEppPasswordAction now explicitly checks for CONFIGURE_EPP_CONNECTION permission before updating the EPP password.
4. Denial of Service (DoS): ConsoleBulkDomainAction now strictly limits the size of bulk domain lists (configurable, default 500) to prevent thread exhaustion.
5. Denial of Service (OOM): ConsoleHistoryDataAction now uses .setMaxResults() (configurable, default 500) on JPA native queries to prevent eager loading of the entire database into memory.

Makes the history query limit and bulk domain action limit configurable via RegistryConfig, allowing smaller limits to be used in tests to avoid heavy resource persistence.

Also removes an outdated Joda-Time migration reference from GEMINI.md.
2026-06-24 20:39:42 +00:00
gbrodman fe222bbdcf Fix a couple edge cases with token pricing (#3103)
It's unlikely we'd run into this because our tokens are generally just
valid for one year, but we should fix these regardless
2026-06-24 15:55:01 +00:00
Ben McIlwain f770f6a46d Improve db/README.md with refactoring guide (#3096)
This commit improves the database documentation in db/README.md by adding comprehensive guidelines for refactoring column types and managing two-PR schema deployments.

Key additions:
- Added a section on the "Expand and Contract" pattern for refactoring column types, explaining when it is safe to drop columns immediately vs. when a three-step release process is required.
- Added a section on writing safe NOT NULL migrations for timed transition properties, explaining the "Temporary Database Default" pattern to maintain backward compatibility with running servers during Two-PR deployments, and demonstrating the required explicit PostgreSQL `::hstore` casting syntax.
- Added a step-by-step "Recommended Git Workflow" section to help developers cleanly split their database and Java changes into chained PRs using Git.

TAG=agy
CONV=88271e71-e272-40e0-85f8-a075a423b7c2
2026-06-23 21:32:22 +00:00
Ben McIlwain e071f5579c Implement database schema for scheduled XAP launch (#3095)
This commit implements the database schema changes for the Expiry Access Period (XAP) launch configuration on TLDs. It represents the first step of a Two-PR deployment strategy, deploying the database schema changes in advance of the server logic.

Specifically, it replaces the `expiry_access_period_enabled` boolean column (originally introduced in PR #2804) with a new `expiry_access_period_transitions` hstore column.

Why we are making this change:
A basic boolean flag only allows an immediate, manual on/off toggle. To launch XAP on a TLD, registry operators would have to manually flip the flag at the exact launch time, which is operationally fragile and cannot be planned in advance. Refactoring this to an hstore-backed timed transition map (mapping Instant to ExpiryAccessPeriodMode) allows operators to schedule the XAP launch in advance via TLD YAML configurations. The registry will automatically transition the TLD to the ENABLED mode at the scheduled timestamp, aligning with how other scheduled TLD changes (like TLD states and EAP fee schedules) are managed.

Since the original boolean column was never mapped in Java (PR #2804 only added the database column), it is completely safe to drop it immediately in this migration.

To ensure backward compatibility with running servers (which are still executing the old Java code during the deployment transition), the new column is added as `NOT NULL` with a temporary `DEFAULT` constraint. This prevents constraint violations on inserts from old servers. A TODO has been left in the SQL migration to drop this default in a subsequent schema release once the Java changes have been deployed.

TAG=agy
CONV=88271e71-e272-40e0-85f8-a075a423b7c2
2026-06-23 19:46:17 +00:00
Ben McIlwain 6080cd2f7a Fix flaky RdapDomainSearchActionTest (#3097)
Refactor RdapDomainSearchActionTest to dynamically resolve all domain
and host Repository IDs (ROIDs) instead of asserting on hardcoded,
sequence-generated strings (like "2E-LOL" or "6-LOL").

When tests are executed in parallel (as is common in CI environments like
Kokoro), multiple test threads concurrently reset and allocate from the
shared database sequence 'project_wide_unique_id_seq'. This interleaves
ID allocations non-deterministically, causing any test asserting on
exact, hardcoded sequence values to flake.

To fix this, createManyDomainsAndHosts was updated to return the list of
persisted domains, allowing tests to dynamically resolve their ROIDs.
All other test cases were refactored to dynamically fetch the ROIDs of
pre-created domains and hosts (stored in fields or in hostNameToHostMap,
using punycode keys for IDN hosts) for their JSON assertions, rendering
the entire suite robust against sequence shifts.
2026-06-23 16:30:04 +00:00
gbrodman 90f583910e Add max retry duration for cloud tasks that don't have it (#3094) 2026-06-22 19:56:45 +00:00
gbrodman a85bf5c30a Use a (small) map to cache token verifiers (#3088)
we shouldn't have to rebuild it each time we get a request to a
different service or really ever at all -- we might get a tiny bit of
cache benefit here
2026-06-22 18:26:02 +00:00
Ben McIlwain dcfe939c38 Update docs for Java 25 and GKE migration (#3089)
Summarize all documentation updates across the repository to align with modern GKE, Cloud SQL Proxy v2, standard EPP fee v1.0, and Postgres database environments.

Key Updates:
- Prerequisites: Bump Java requirement to Java 25.
- Architecture & Scaling: Document GKE workloads, Cloud Tasks queues, and scheduled tasks. Replace App Engine references with GKE deployment restart commands (kubectl rollout restart).
- Configuration: Update Cloud SQL Proxy instructions to v2, fix keyring verification commands, and document IAP configuration.
- Escrow (RDE/BRDA): Fix manual generation and download procedures to match the Dataflow job ID folder structure, and correct deposit encryption/verification command parameters.
- Monitoring: Correct metric names and expand the documented metrics list with caching, locking, and reserved list metrics.
- Fixes: Standardize lists formatting across markdown files, fix broken webdriver links, and resolve various typos.
- Cleanup: Remove leftover cloud scheduler configurations for the deleted wipeOutContactHistoryPii task, and update ICANN reporting documentation to reflect open-sourced DNS query coordinator.

TAG=agy
CONV=88271e71-e272-40e0-85f8-a075a423b7c2
2026-06-22 18:20:00 +00:00
gbrodman ae61922318 Use existing pw reset code if creating from an existing instance (#3091)
this means that modifying requests changes them in place in the db
2026-06-22 17:33:17 +00:00
gbrodman 84e97aa2db Use time-constant comparison in password testing (#3090)
Normal comparison exits out once a difference is found so theoretically
a time variance can leak information. This isn't really a huge deal but
is probably still worth doing.
2026-06-17 21:00:13 +00:00
Ben McIlwain 4df4bf1489 Add FeatureFlag helper methods to DatabaseHelper (#3087)
Introduced overloaded helper methods `persistFeatureFlag` in `DatabaseHelper` to simplify the creation and persistence of `FeatureFlag` entities in test setups.

1. `persistFeatureFlag(FeatureName, FeatureStatus)`: Persists a feature flag with a single status starting from the Unix Epoch (`START_INSTANT`).
2. `persistFeatureFlag(FeatureName, FeatureStatus, Instant, FeatureStatus)`: Persists a feature flag with an initial status at `START_INSTANT` and a subsequent transition at a specified time.

Refactored 11 occurrences of manual 1-transition flag creation and 5 occurrences of 2-transition flag creation across the test suite to use these new helpers, significantly reducing boilerplate and improving test readability.

TAG=agy
CONV=583b8a23-9fe5-476d-ac35-aeba7b218eb0
2026-06-16 21:24:01 +00:00
gbrodman 9faabb0f1d Forbid deprecated algorithms in DNSSEC data (#3078)
This is similar to PR #3069 but for the algorithms themselves rather
than the digest data. This forbids algorithms, that, according to RFC
9904, should not be used.
2026-06-16 19:09:05 +00:00
gbrodman ea9d717378 Use custom Protostuff delegate for InetAddresses (#3079)
We need this to serialize/deserialize hosts, because we're not allowed
to reflectively access InetAddress.
2026-06-08 19:16:35 +00:00
Juan Celhay d1769b29ef fix relative path of manifests (#3084) 2026-06-08 15:37:17 +00:00
gbrodman 14376953e5 Skip EPP params for BRDA (#3083)
this is an extra field that shouldn't be included in this output
apparently
2026-06-05 20:52:54 +00:00
Juan Celhay c13c9b53e3 Add sandbox to the Cloud Deploy delivery pipeline (#3080)
* add sandbox target to the delivery pipeline

* add sandbox target
2026-06-05 19:32:28 +00:00
Ben McIlwain 1cd6026151 Fix npx build overriding Angular output paths (#3082)
This commit reverts changes from PR #3068 that swapped 'npm run build' for 'npx ng build' while attempting to dynamically set the '--output-path' via the CLI.

Passing '--output-path' on the command line overrides the entire 'outputPath' configuration object in angular.json. Because the new Angular 18 Application Builder (esbuild) nests outputs inside a 'browser/' directory by default, overriding the configuration bypassed the 'browser: ""' flattening property, causing all client assets to be nested deeper than expected.

This resulted in empty deployments because downstream tasks (like Jetty's copyConsole and the deployment tar scripts) expected the assets to be completely flat. By removing the '--output-path' override from the 'npx ng build' calls, the Angular CLI once again respects angular.json, flattens the output into 'staged/dist/', and the restored 'doLast' block successfully copies the artifacts where they belong.
2026-06-05 16:31:14 +00:00
Ben McIlwain 75524fd403 Restore default builds and fix Kokoro tests (#3081)
This commit reverts changes from 5599a0eb3d and most of 5286b1a0dc (PR #3068) that stripped essential dependencies (buildConsoleForAll, buildNomulusImage, buildToolImage, fragileTest) from the default './gradlew build' target, which broke downstream deployment pipelines. It restores the default build to correctly generate all necessary production artifacts and Docker images.

It introduces a new 'fastBuild' target designed explicitly for local developers and CI checks. This lightweight target disables the execution of heavy Docker image builds, Angular compilations, and fragile tests to provide rapid feedback. Sequential execution constraints for parallel Angular builds are maintained to prevent cache corruption.

It updates the ':core:generateSqlSchema' task to execute using the 'unittest' environment instead of 'alpha'. The 'alpha' configuration is a private, internal environment config that is not distributed in the open-source repository, which caused the task to fail for public contributors. By switching to 'unittest', the generator can successfully run using the public test configuration. With this fixed, it also includes the newly generated 'db-schema.sql.generated' file, which now correctly tracks the 'FORBID_INSECURE_ALGORITHMS_RFC_9904' feature flag that was recently added.

Finally, it implements a split-runner execution strategy for the 'sqlIntegrationTest' task to permanently resolve 'failed to discover tests' and 'NoSuchMethodError' exceptions on Kokoro. Because Kokoro tests cross-version compatibility against both legacy deployed artifacts (compiled with JUnit 4 @RunWith wrappers) and modern artifacts (compiled with JUnit 5 @Suite annotations), we cannot statically configure a single test runner. We now dynamically run both the legacy 'useJUnit()' and modern 'useJUnitPlatform()' runners sequentially with 'failOnNoDiscoveredTests' disabled, allowing the appropriate engine to discover and execute the suite without causing classpath collisions.
2026-06-04 15:38:03 +00:00
gbrodman a5b280838c Remove usages of json-simple (#3060)
We should use gson wherever possible. There's no point in having
unnecessary dependencies (we'll need to keep around jackson for YAML
parsing).
2026-06-01 20:16:32 +00:00
Ben McIlwain 5599a0eb3d Add buildAll task and fix fragile builds (#3077)
This commit adds the buildAll task to restore the existence of a target that builds everything, which was unintentionally removed when the default build was stripped down in PR #3068. It also introduces necessary sequential constraints to the console-webapp build tasks to prevent parallel execution from corrupting the Angular CLI cache. Finally, it addresses paths for the newer Angular esbuild output and hardens the style injection in ConsoleScreenshotTest to prevent fragile test failures.
2026-06-01 19:11:05 +00:00
gbrodman dde41078cd Forbid SHA-1 digests as part of RFC 9904 changes (#3069)
We can't change digest types that are already in the database but that's
fine (since we just store them as integers). But we forbid them as part
of domain creates/updates.
2026-06-01 17:59:19 +00:00
323 changed files with 7518 additions and 2585 deletions
+17 -1
View File
@@ -44,6 +44,11 @@ This document outlines foundational mandates, architectural patterns, and projec
- **Test Helpers & Timestamps:** If a static test helper method (like in `DatabaseHelper`) needs the database transaction time but might be called from outside a transaction, using `tm().reTransact(tm()::getTxTime)` is acceptable. However, NEVER wrap it redundantly like `tm().transact(() -> tm().reTransact(tm()::getTxTime))`. If you are just setting an arbitrary timestamp in a test where the exact DB transaction time isn't strictly required, prefer `Instant.now()` or `clock.now()` to avoid creating unnecessary database transactions.
- **Production Code:** In production code, if a flow fails because it is calling `getTxTime()` outside of a transaction, you must wrap the *caller* in a transaction instead of adding an unnecessary `reTransact()` around `getTxTime()`.
- **Transactional Time:** Ensure code that relies on `tm().getTransactionTime()` (or `tm().getTxTime()`) is executed within a transaction context.
- **Database Schema Migrations & 2-PR Split Mandate:**
- **Mandatory Consultation of `db/README.md`:** Before planning, drafting, or executing any database schema modifications (e.g., adding/altering columns, creating Flyway `.sql` migration scripts, or modifying JPA entity `@Column` mappings), you **MUST read and strictly adhere to `db/README.md`**.
- **Strict 2-PR Deployment Split:** Never propose or submit combining Flyway SQL scripts (`db/src/main/resources/sql/flyway/V*.sql`) and Java ORM changes (`.java` entity files + `db-schema.sql.generated`) into a single PR for submission to `master`. Because live servers during a rolling deployment will fail if Java code attempts to access unmigrated database columns/constraints, all schema additions must be split into two sequential PRs per `db/README.md`:
1. **PR #1 (Database Schema Only):** Contains *only* the new Flyway `.sql` script, the `flyway.txt` index update (`:db:generateFlywayIndex`), the `nomulus.golden.sql` dump (`:nom:generate_golden_file`), and ER diagrams (`er_diagram/`). Must contain **zero `.java` files or `db-schema.sql.generated` changes**.
2. **PR #2 (Java ORM, EPP Flows & Generated Schema Map):** Submitted *only after* PR #1 is deployed to production. Contains all `.java` entity/flow modifications, tests, and the regenerated `db-schema.sql.generated` (`generateSqlSchema`).
### 5. Testing Best Practices
- **Mandatory Proactive Testing:** You MUST automatically write and update tests alongside your code changes WITHOUT waiting for the user to prompt you. If you add a new feature, fix a bug, or change core logic, you are explicitly required to identify the corresponding `*Test.java` file and implement comprehensive test coverage for your changes.
@@ -51,6 +56,7 @@ This document outlines foundational mandates, architectural patterns, and projec
- **Empirical Reproduction:** Before fixing a bug, always create a test case that reproduces the failure.
- **Base Classes:** Leverage `CommandTestCase`, `EppToolCommandTestCase`, etc., to reduce boilerplate and ensure consistent setup (e.g., clock initialization).
- **Gradle Test Patterns:** When running tests to investigate fixes in the "core" directory, try to first use the "standardTest" Gradle task. It is faster than the "test" task, which includes the "fragileTest" task. Only run the full "test" task after "standardTest" succeeds.
- **Mandatory SQL Integration Verification:** Whenever you modify any database schema, Flyway script (`.sql`), or JPA entity class, you MUST explicitly run both `./gradlew :db:test` and `./gradlew :core:sqlIntegrationTest` **in addition to** the standard test suites (`./gradlew standardTest` or `./gradlew test`) before finalizing the task or declaring completion. Do not rely solely on `standardTest` when database schemas or ORM mappings are touched; all three test suites (`standardTest`, `:db:test`, and `:core:sqlIntegrationTest`) are mandatory.
### 6. Project Dependencies
- **Common Module:** When using `Clock` or other core utilities in a new or separate module (like `load-testing`), ensure `implementation project(':common')` is added to the module's `build.gradle`.
@@ -66,10 +72,19 @@ This document outlines foundational mandates, architectural patterns, and projec
- **No CodeSearch:** This project is hosted on GitHub, not Google3. Do NOT use `mcp_Coding_search_for_files_codesearch` or other internal Google3 search tools.
- **Local Grep:** Use local shell commands like `git grep` or `grep` via `run_shell_command` to search the codebase.
### 8. Git Operation Boundaries & Remote Push Prohibition
- **Zero-Bundling Rule for Remote Transfers:** NEVER bundle remote transfer commands (`git push`, `repo upload`, `g4 upload`, `g4 mail`, `hg push`, `jj git push`) inside compound bash pipelines (`&&`, `||`, `;`) with local staging or commit commands (e.g., `git commit --amend && git push ...`). Every remote transfer command MUST be executed as a standalone, single-command `run_command` invocation.
- **Mandatory Two-Step Handover Protocol:** When working on pull requests, changelists, or branches, strictly halt right at the local repository boundary (`git commit` / `g4 change`). Report the local commit SHA, `git status`, and `git diff` verification to the user, stop calling tools, and explicitly prompt: *"Local commit `[SHA]` is verified. Do you authorize running `git push origin [branch]` (or `--force-with-lease` when force-pushing an amended branch) to update the remote repository?"*
- **Absolute Prohibition on Unsolicited Pushes:** NEVER propose or run any remote synchronization or push command (`git push`, `repo upload`, `g4 upload`) on your own volition without explicit, unambiguous textual authorization in the immediate turn from the user.
- **Mandatory GitHub PR Description & Reviewable Synchronization:** Whenever pushing an amended commit to a branch associated with an active pull request, the agent MUST immediately synchronize the PR description on GitHub (`gh pr edit`). To do this safely:
1. Check `gh pr view --json body` first to inspect existing content.
2. Explicitly preserve any existing Reviewable bot links (`This change is https://reviewable.io/reviews/...`) when updating the body rather than blindly overwriting the entire description.
## Performance and Efficiency
- **Turn Minimization:** Aim for "perfect" code in the first iteration. Iterative fixes for checkstyle or compilation errors consume significant context and time.
- **Context Management:** Use sub-agents for batch refactoring or high-volume output tasks to keep the main session history lean and efficient.
- **Code Formatting:** Do not write custom Python scripts or manual regex replacements to fix code formatting issues (e.g., unused imports, import ordering, line length). Instead, use the project's built-in formatting tools: run `./gradlew spotlessApply` to fix unused/unordered imports and `./gradlew javaIncrementalFormatApply` (or `google-java-format --replace <files>`) to automatically fix Java formatting and indentation errors.
- **Programmatic Measurement for Markdown:** When writing Markdown files (`.md`)—and especially when formatting or line-wrapping them—you **MUST** explicitly use programmatic functions (such as `awk '{print length($0)}'` or Python `len(line)`) to measure the exact string length of every line from column 1, rather than making assumptions based on what appears in your context window.
## General Code Review Lessons & Avoidable Mistakes
Based on historical PR reviews, avoid the following common mistakes:
@@ -94,7 +109,7 @@ Do not wait for the user to tell you to improve the skills; it is your responsib
# Gemini Engineering Guide: Nomulus Codebase
This document captures high-level architectural patterns, lessons learned from large-scale refactorings (like the Joda-Time to `java.time` migration), and specific instructions to avoid common pitfalls in this environment.
This document captures high-level architectural patterns, lessons learned from large-scale refactorings, and specific instructions to avoid common pitfalls in this environment.
## 🏛 Architecture Overview
@@ -171,3 +186,4 @@ This protocol defines the standard for interacting with GitHub repositories and
- **One Commit Per PR:** Ensure all changes are squashed into a single, clean commit. Use `git commit --amend --no-edit` for follow-up fixes.
- **Clean Workspace:** Always run `git status` and verify the repository state before declaring a task complete.
- **Package Lock:** The Gradle build automatically modifies `console-webapp/package-lock.json` via the `npmInstallDeps` task. ALWAYS revert this file (`git checkout console-webapp/package-lock.json`) before staging changes or finalizing a commit unless you explicitly modified NPM dependencies.
- **PR Description Synchronization:** Whenever you amend or update a commit's description, you **MUST** check whether the corresponding GitHub PR description (if one exists) matches the previous commit description. If the PR description was not manually customized (i.e., it simply reflects the older commit description), you must automatically update it via `gh pr edit` to keep it in sync with your newly updated commit description—**strictly after the updated commit has been pushed to the remote GitHub PR branch**. Do not update the remote PR description when changes are only committed locally. **CRITICAL:** When updating the PR description, you must strictly preserve any automated review links or footer blocks (such as Reviewable link blocks: `<!-- Reviewable:start -->...<!-- Reviewable:end -->`) at the bottom of the description.
+2 -2
View File
@@ -12,7 +12,7 @@ Nomulus is an open source, scalable, cloud-based service for operating
[top-level domains](https://en.wikipedia.org/wiki/Top-level_domain) (TLDs). It
is the authoritative source for the TLDs that it runs, meaning that it is
responsible for tracking domain name ownership and handling registrations,
renewals, availability checks, and WHOIS requests. End-user registrants (i.e.,
renewals, availability checks, and RDAP requests. End-user registrants (i.e.,
people or companies that want to register a domain name) use an intermediate
domain name registrar acting on their behalf to interact with the registry.
@@ -97,7 +97,7 @@ Nomulus has the following capabilities:
for details), and an implementation based on
[Google Cloud Secret Manager](https://cloud.google.com/security/products/secret-manager) is
available.
* **TPC Proxy**: Nomulus is built on top of the [Jetty](https://jetty.org/)
* **TCP Proxy**: Nomulus is built on top of the [Jetty](https://jetty.org/)
container that implements the [Jakarta Servlet](https://jakarta.ee/specifications/servlet/)
specification and only serves HTTP/S traffic. A proxy to translate raw TCP traffic (e.g., EPP)
to and from HTTP is provided.
+21 -1
View File
@@ -395,7 +395,7 @@ subprojects {
// expose to users.
if (project.name != 'jetty' && !services.contains(project.path)) {
javadocSource << project.sourceSets.main.allJava
javadocClasspath << { project.sourceSets.main.runtimeClasspath.files }
javadocClasspath << { project.sourceSets.main.compileClasspath.files }
javadocClasspath << "${buildDir}/generated/sources/annotationProcessor/java/main"
if (project.tasks.findByName('compileJava')) {
javadocDependentTasks << project.tasks.compileJava
@@ -609,3 +609,23 @@ gradle.taskGraph.whenReady { graph ->
}
}
}
task fastBuild {
group = 'build'
description = 'A lightweight build for local dev. Compiles Java, runs standard tests, and checks formatting, but skips Docker images, fragile tests, and the massive Angular console builds. (Do not use this target to verify console changes.)'
dependsOn build
// Remove the heavy default dependencies specifically for fastBuild
gradle.taskGraph.whenReady { graph ->
if (graph.hasTask(fastBuild)) {
project(':console-webapp').tasks.named('buildConsoleForAll').get().enabled = false
project(':jetty').tasks.named('buildNomulusImage').get().enabled = false
project(':core').tasks.named('buildToolImage').get().enabled = false
project(':core').tasks.named('fragileTest').get().enabled = false
project(':jetty').tasks.named('stage').get().enabled = false
if (project.tasks.findByName('stage') != null) {
project.tasks.named('stage').get().enabled = false
}
}
}
}
+6 -6
View File
@@ -68,12 +68,12 @@ org.jacoco:org.jacoco.core:0.8.14=jacocoAnt
org.jacoco:org.jacoco.report:0.8.14=jacocoAnt
org.javassist:javassist:3.28.0-GA=checkstyle
org.jspecify:jspecify:1.0.0=annotationProcessor,checkstyle,compileClasspath,deploy_jar,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath,testing,testingAnnotationProcessor,testingCompileClasspath
org.junit.jupiter:junit-jupiter-api:6.1.0=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-engine:6.1.0=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-commons:6.1.0=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-engine:6.1.0=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-launcher:6.1.0=testCompileClasspath,testRuntimeClasspath
org.junit:junit-bom:6.1.0=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-api:6.1.2=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-engine:6.1.2=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-commons:6.1.2=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-engine:6.1.2=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-launcher:6.1.2=testCompileClasspath,testRuntimeClasspath
org.junit:junit-bom:6.1.2=testCompileClasspath,testRuntimeClasspath
org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
org.ow2.asm:asm-commons:9.9=jacocoAnt
org.ow2.asm:asm-tree:9.9=jacocoAnt
+4 -5
View File
@@ -9,15 +9,14 @@ expected to change.
## Deployment
The webapp is deployed with the nomulus default service war to GKE.
During nomulus default service war build task, gradle script triggers the
following:
The webapp is deployed as part of the default Nomulus GKE service image.
During the image build task, the Gradle script triggers the following:
1) Console webapp build script `buildConsoleWebapp`, which installs
dependencies, assembles a compiled ts -> js, minified, optimized static
artifact (html, css, js)
2) Artifact assembled in step 1 then gets copied to core project web artifact
location, so that it can be deployed with the rest of the core webapp
2) Artifact assembled in step 1 then gets copied to the jetty webapp resource
location, so that it can be staged inside the default GKE service container.
## Development server
+23 -14
View File
@@ -21,27 +21,23 @@ clean {
task npmInstallDeps(type: Exec) {
workingDir "${consoleDir}/"
executable 'npm'
args 'i', '--no-audit', '--no-fund', '--loglevel=error'
commandLine 'sh', '-c', 'npm i --no-audit --no-fund --loglevel=error'
}
task runConsoleWebappLocally(type: Exec) {
workingDir "${consoleDir}/"
executable 'npm'
args 'run', 'start:dev'
commandLine 'sh', '-c', 'npm run start:dev'
}
task runConsoleWebappUnitTests(type: Exec) {
workingDir "${consoleDir}/"
executable 'npm'
args 'run', 'test'
commandLine 'sh', '-c', 'npm run test'
}
task buildConsoleWebapp(type: Exec) {
workingDir "${consoleDir}/"
executable 'npx'
def configuration = project.getProperty('configuration')
args 'ng', 'build', '--base-href=/console/', "--configuration=${configuration}", "--output-path=staged/dist"
commandLine 'sh', '-c', "npx ng build --base-href=/console/ --configuration=${configuration} --output-path=staged/dist"
doFirst {
println "Building console for environment: ${configuration}"
}
@@ -52,11 +48,17 @@ task buildConsoleForAll() {}
def createConsoleTask = { env ->
project.tasks.register("buildConsoleFor${env.capitalize()}", Exec) {
workingDir "${consoleDir}/"
executable 'npx'
args 'ng', 'build', '--base-href=/console/', "--configuration=${env}", "--output-path=staged/console-${env}"
commandLine 'sh', '-c', "npx ng build --base-href=/console/ --configuration=${env}"
doFirst {
println "Building console for environment: ${env}"
}
doLast {
copy {
from "${consoleDir}/staged/dist/"
into "${consoleDir}/staged/console-${env}"
}
delete "${consoleDir}/staged/dist"
}
dependsOn(tasks.npmInstallDeps)
}
project.tasks.register("deleteConsoleFor${env.capitalize()}", Delete) {
@@ -74,16 +76,22 @@ def createConsoleTask = { env ->
createConsoleTask(env)
}
// Force an order so we don't run these tasks in parallel.
tasks.buildConsoleForCrash.mustRunAfter(tasks.buildConsoleForAlpha)
tasks.buildConsoleForQa.mustRunAfter(tasks.buildConsoleForCrash)
tasks.buildConsoleForSandbox.mustRunAfter(tasks.buildConsoleForQa)
tasks.buildConsoleForProduction.mustRunAfter(tasks.buildConsoleForSandbox)
// This task must run last, otherwise the previous tasks will have deleted the "dist" folder.
tasks.buildConsoleWebapp.mustRunAfter(tasks.buildConsoleForProduction)
task applyFormatting(type: Exec) {
workingDir "${consoleDir}/"
executable 'npm'
args 'run', 'prettify'
commandLine 'sh', '-c', 'npm run prettify'
}
task checkFormatting(type: Exec) {
workingDir "${consoleDir}/"
executable 'npm'
args 'run', 'prettify:check'
commandLine 'sh', '-c', 'npm run prettify:check'
}
tasks.buildConsoleWebapp.dependsOn(tasks.npmInstallDeps)
@@ -92,3 +100,4 @@ tasks.applyFormatting.dependsOn(tasks.npmInstallDeps)
tasks.checkFormatting.dependsOn(tasks.npmInstallDeps)
tasks.build.dependsOn(tasks.checkFormatting)
tasks.build.dependsOn(tasks.runConsoleWebappUnitTests)
tasks.build.dependsOn(tasks.buildConsoleForAll)
@@ -48,7 +48,11 @@ interface DomainData {
selector: 'app-response-dialog',
template: `
<h2 mat-dialog-title>{{ data.title }}</h2>
<mat-dialog-content [innerHTML]="data.content" />
<mat-dialog-content>
@for (line of data.content; track line) {
<div>{{ line }}</div>
}
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button (click)="onClose()">Close</button>
</mat-dialog-actions>
@@ -59,7 +63,7 @@ export class ResponseDialogComponent {
constructor(
public dialogRef: MatDialogRef<ReasonDialogComponent>,
@Inject(MAT_DIALOG_DATA)
public data: { title: string; content: string }
public data: { title: string; content: string[] }
) {}
onClose(): void {
@@ -312,11 +316,13 @@ export class DomainListComponent {
this.dialog.open(ResponseDialogComponent, {
data: {
title: 'Domain Deletion Results',
content: `Successfully deleted - ${successCount} domain(s)<br/>Failed to delete - ${failureCount} domain(s)<br/>${
content: [
`Successfully deleted - ${successCount} domain(s)`,
`Failed to delete - ${failureCount} domain(s)`,
failureCount
? 'Some domains could not be deleted due to ongoing processes or server errors. '
: ''
}Please check the table for more information.`,
? 'Some domains could not be deleted due to ongoing processes or server errors. Please check the table for more information.'
: 'Please check the table for more information.',
],
},
});
this.selection.clear();
@@ -20,7 +20,7 @@
<p>
<mat-label for="password">Password: </mat-label>
<mat-form-field name="password" appearance="outline">
<input matInput type="text" formControlName="password" required />
<input matInput type="password" formControlName="password" required />
</mat-form-field>
</p>
<p>
@@ -60,7 +60,7 @@
<p>
<mat-label for="password">Password: </mat-label>
<mat-form-field name="password" appearance="outline">
<input matInput type="text" formControlName="password" required />
<input matInput type="password" formControlName="password" required />
</mat-form-field>
</p>
@@ -14,7 +14,7 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Component, computed } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { MatSnackBar } from '@angular/material/snack-bar';
import { RegistrarService } from '../registrar/registrar.service';
import { UserDataService } from '../shared/services/userData.service';
@@ -42,11 +42,11 @@ export class RegistryLockComponent {
];
lockDomain = new FormGroup({
password: new FormControl(''),
password: new FormControl('', [Validators.required]),
});
unlockDomain = new FormGroup({
password: new FormControl(''),
password: new FormControl('', [Validators.required]),
relockTime: new FormControl(undefined),
});
@@ -50,7 +50,10 @@ export class NewOteComponent {
createOte = new FormGroup({
registrarId: new FormControl('', [Validators.required]),
registrarEmail: new FormControl('', [Validators.required]),
registrarEmail: new FormControl('', [
Validators.required,
Validators.email,
]),
});
constructor(
@@ -67,8 +67,10 @@ export default class NewRegistrarComponent {
onBillingAccountMapChange(val: String) {
const billingAccountMap: { [key: string]: string } = {};
this.newRegistrar.billingAccountMap = val.split('\n').reduce((acc, val) => {
const [currency, billingCode] = val.split('=');
acc[currency] = billingCode;
const [currency, billingCode] = val.split('=').map((s) => s?.trim());
if (currency && billingCode) {
acc[currency] = billingCode;
}
return acc;
}, billingAccountMap);
}
@@ -97,10 +97,9 @@
@for (column of columns; track column.columnDef) {
<mat-list-item role="listitem">
<span class="console-app__list-key">{{ column.header }} </span>
<span
class="console-app__list-value"
[innerHTML]="column.cell(registrarInEdit).replace('<br/>', ' ')"
></span>
<span class="console-app__list-value">{{
column.cell(registrarInEdit)
}}</span>
</mat-list-item>
<mat-divider></mat-divider>
}
@@ -75,7 +75,7 @@ export class RegistrarDetailsComponent implements OnInit {
}
checkOteStatus() {
this.router.navigate(['ote-status/', this.registrarInEdit.registrarId], {
this.router.navigate(['ote-status', this.registrarInEdit.registrarId], {
queryParamsHandling: 'merge',
});
}
@@ -49,10 +49,9 @@
<mat-header-cell *matHeaderCellDef>
{{ column.header }}
</mat-header-cell>
<mat-cell
*matCellDef="let row"
[innerHTML]="column.cell(row)"
></mat-cell>
<mat-cell *matCellDef="let row" style="white-space: pre-wrap">{{
column.cell(row)
}}</mat-cell>
</ng-container>
}
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
@@ -56,7 +56,7 @@ export const columns = [
cell: (record: Registrar) =>
`${Object.entries(record.billingAccountMap || {}).reduce(
(acc, [key, val]) => {
return `${acc}${key}=${val}<br/>`;
return `${acc}${key}=${val}\n`;
},
''
)}`,
@@ -25,7 +25,18 @@
@for (column of columns; track column) {
<ng-container [matColumnDef]="column.columnDef">
<mat-header-cell *matHeaderCellDef> {{ column.header }} </mat-header-cell>
<mat-cell *matCellDef="let row" [innerHTML]="column.cell(row)"></mat-cell>
<mat-cell *matCellDef="let row">
@if (column.columnDef === 'name') {
<div class="contact__name-column">
<div class="contact__name-column-title">{{ row.name }}</div>
<div class="contact__name-column-roles">
{{ row.userFriendlyTypes.join(" • ") }}
</div>
</div>
} @else {
{{ column.cell(row) }}
}
</mat-cell>
</ng-container>
}
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
@@ -34,14 +34,7 @@ export default class ContactComponent {
{
columnDef: 'name',
header: 'Name',
cell: (contact: ViewReadyContact) => `
<div class="contact__name-column">
<div class="contact__name-column-title">${contact.name}</div>
<div class="contact__name-column-roles">${contact.userFriendlyTypes.join(
' • '
)}</div>
</div>
`,
cell: (contact: ViewReadyContact) => `${contact.name}`,
},
{
columnDef: 'emailAddress',
@@ -9,7 +9,7 @@
<mat-label>Old password: </mat-label>
<input
matInput
type="text"
type="password"
formControlName="oldPassword"
required
autocomplete="current-password"
@@ -25,7 +25,7 @@
<mat-label>New password: </mat-label>
<input
matInput
type="text"
type="password"
formControlName="newPassword"
required
autocomplete="new-password"
@@ -40,7 +40,7 @@
<mat-label>Confirm new password: </mat-label>
<input
matInput
type="text"
type="password"
formControlName="newPasswordRepeat"
required
autocomplete="new-password"
@@ -288,8 +288,9 @@ export class BackendService {
verifyRegistryLockRequest(
lockVerificationCode: string
): Observable<RegistryLockVerificationResponse> {
return this.http.get<RegistryLockVerificationResponse>(
`/console-api/registry-lock-verify?lockVerificationCode=${lockVerificationCode}`
return this.http.post<RegistryLockVerificationResponse>(
`/console-api/registry-lock-verify?lockVerificationCode=${lockVerificationCode}`,
{}
);
}
@@ -10,7 +10,7 @@
<mat-header-cell *matHeaderCellDef>
{{ column.header }}
</mat-header-cell>
<mat-cell *matCellDef="let row" [innerHTML]="column.cell(row)"></mat-cell>
<mat-cell *matCellDef="let row">{{ column.cell(row) }}</mat-cell>
</ng-container>
}
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
+6 -7
View File
@@ -170,7 +170,6 @@ dependencies {
implementation deps['com.google.oauth-client:google-oauth-client-servlet']
implementation deps['com.google.re2j:re2j']
implementation deps['org.freemarker:freemarker']
implementation deps['com.googlecode.json-simple:json-simple']
implementation deps['com.github.mwiede:jsch']
implementation deps['com.zaxxer:HikariCP']
implementation deps['com.squareup.okhttp3:okhttp']
@@ -230,9 +229,9 @@ dependencies {
runtimeOnly deps['org.slf4j:slf4j-jdk14']
testImplementation deps['org.testcontainers:jdbc']
testImplementation deps['org.testcontainers:junit-jupiter']
implementation deps['org.testcontainers:postgresql']
nonprodImplementation deps['org.testcontainers:postgresql']
testImplementation deps['org.testcontainers:selenium']
testImplementation deps['org.testcontainers:testcontainers']
nonprodImplementation deps['org.testcontainers:testcontainers']
implementation deps['redis.clients:jedis']
implementation deps['us.fatehi:schemacrawler']
implementation deps['us.fatehi:schemacrawler-api']
@@ -443,7 +442,7 @@ project.tasks.create('generateSqlSchema', JavaExec) {
mainClass = 'google.registry.tools.DevTool'
jvmArgs "--sun-misc-unsafe-memory-access=allow"
args = [
'-e', 'alpha',
'-e', 'unittest',
'generate_sql_schema', '--start_postgresql', '-o',
"${rootProject.projectRootDir}/db/src/main/resources/sql/schema/" +
"db-schema.sql.generated"
@@ -742,9 +741,9 @@ test {
// Don't run any tests from this task, all testing gets done in the
// FilteringTest tasks.
exclude "**"
}.dependsOn(standardTest, registryToolIntegrationTest, sqlIntegrationTest)
}.dependsOn(standardTest, registryToolIntegrationTest, sqlIntegrationTest, fragileTest)
// When we override tests, we also break the cleanTest command.
cleanTest.dependsOn(cleanStandardTest, cleanRegistryToolIntegrationTest, cleanSqlIntegrationTest)
cleanTest.dependsOn(cleanStandardTest, cleanRegistryToolIntegrationTest, cleanSqlIntegrationTest, cleanFragileTest)
project.build.dependsOn devtool
project.build.dependsOn devtool, buildToolImage
+257 -256
View File
@@ -3,20 +3,20 @@
# This file is expected to be part of source control.
args4j:args4j:2.33=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.charleskorn.kaml:kaml:0.20.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-annotations:2.21=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-core:2.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-databind:2.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.datatype:jackson-datatype-joda:2.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson:jackson-bom:2.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-annotations:2.22=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-core:2.22.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-databind:2.22.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.22.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.22.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.datatype:jackson-datatype-joda:2.22.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.22.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson:jackson-bom:2.22.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.woodstox:woodstox-core:7.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.ben-manes.caffeine:caffeine:3.0.5=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
com.github.ben-manes.caffeine:caffeine:3.2.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.docker-java:docker-java-api:3.4.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.docker-java:docker-java-transport-zerodep:3.4.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.docker-java:docker-java-transport:3.4.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.docker-java:docker-java-api:3.4.2=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.docker-java:docker-java-transport-zerodep:3.4.2=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.docker-java:docker-java-transport:3.4.2=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.jnr:jffi:1.3.15=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.jnr:jnr-a64asm:1.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.jnr:jnr-constants:0.10.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -26,71 +26,70 @@ com.github.jnr:jnr-posix:3.1.22=compileClasspath,deploy_jar,nonprodCompileClassp
com.github.jnr:jnr-unixsocket:0.38.25=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.jnr:jnr-x86asm:1.0.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.kevinstern:software-and-algorithms:1.0=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
com.github.mwiede:jsch:2.28.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.mwiede:jsch:2.28.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.android:annotations:4.1.1.4=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api-client:google-api-client-jackson2:2.0.1=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api-client:google-api-client-jackson2:2.7.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api-client:google-api-client-gson:2.9.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api-client:google-api-client-java6:2.1.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api-client:google-api-client-servlet:2.7.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api-client:google-api-client-servlet:2.9.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api-client:google-api-client:2.9.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:gapic-google-cloud-storage-v2:2.64.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:gapic-google-cloud-storage-v2:2.68.0=testCompileClasspath
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1:3.24.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.196.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta2:0.196.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-bigtable-v2:2.73.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-pubsub-v1:1.132.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-spanner-admin-database-v1:6.113.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-spanner-admin-instance-v1:6.113.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-spanner-v1:6.113.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-storage-control-v2:2.44.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-storage-v2:2.64.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-storage-v2:2.68.0=testCompileClasspath
com.google.api.grpc:grpc-google-common-protos:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1:3.24.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1alpha:3.24.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta1:0.196.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta2:0.196.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta:3.24.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigtable-admin-v2:2.73.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigtable-v2:2.75.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-compute-v1:1.102.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-datastore-v1:0.128.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-firestore-v1:3.39.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-logging-v2:0.118.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-monitoring-v3:3.85.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-pubsub-v1:1.132.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:gapic-google-cloud-storage-v2:2.68.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:gapic-google-cloud-storage-v2:2.70.0=testCompileClasspath
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1:3.28.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.200.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta2:0.200.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-bigtable-v2:2.78.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-pubsub-v1:1.132.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-spanner-admin-database-v1:6.117.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-spanner-admin-instance-v1:6.117.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-spanner-v1:6.117.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-storage-control-v2:2.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-storage-v2:2.68.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-storage-v2:2.70.0=testCompileClasspath
com.google.api.grpc:grpc-google-common-protos:2.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1:3.28.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1alpha:3.28.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta1:0.200.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta2:0.200.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta:3.28.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigtable-admin-v2:2.78.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigtable-v2:2.78.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-compute-v1:1.104.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-datastore-v1:0.133.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-firestore-v1:3.42.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-logging-v2:0.122.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-monitoring-v3:3.93.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-pubsub-v1:1.132.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-secretmanager-v1:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-secretmanager-v1:2.92.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:2.92.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-secretmanager-v1:2.94.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:2.94.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta2:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta2:2.92.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:6.113.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:6.113.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-spanner-v1:6.113.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-storage-control-v2:2.44.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-storage-v2:2.64.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.api.grpc:proto-google-cloud-storage-v2:2.68.0=testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta2:2.94.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:6.117.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:6.117.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-spanner-v1:6.117.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-storage-control-v2:2.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-storage-v2:2.68.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.api.grpc:proto-google-cloud-storage-v2:2.70.0=testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2:2.92.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2:2.94.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.141.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.182.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:2.94.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.141.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.182.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-common-protos:2.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-iam-v1:1.62.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
com.google.api.grpc:proto-google-iam-v1:1.66.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api:api-common:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api:gax-grpc:2.80.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api:gax-httpjson:2.80.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api:gax:2.80.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-admin-directory:directory_v1-rev20260522-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:2.94.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-common-protos:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-iam-v1:1.66.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
com.google.api.grpc:proto-google-iam-v1:1.68.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api:api-common:2.65.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api:gax-grpc:2.82.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api:gax-httpjson:2.82.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api:gax:2.82.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-admin-directory:directory_v1-rev20260531-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-bigquery:v2-rev20251012-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-cloudresourcemanager:v1-rev20250606-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dataflow:v1b3-rev20260503-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dns:v1-rev20260421-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-drive:v3-rev20260428-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dataflow:v1b3-rev20260615-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dns:v1-rev20260616-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-drive:v3-rev20260624-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-gmail:v1-rev20260525-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-groupssettings:v1-rev20220614-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-healthcare:v1-rev20240130-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -98,11 +97,12 @@ com.google.apis:google-api-services-iam:v2-rev20250502-2.0.0=compileClasspath,de
com.google.apis:google-api-services-iamcredentials:v1-rev20211203-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-monitoring:v3-rev20260129-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-pubsub:v1-rev20220904-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-sheets:v4-rev20260213-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-sqladmin:v1beta4-rev20260510-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-storage:v1-rev20260204-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.auth:google-auth-library-credentials:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.auth:google-auth-library-oauth2-http:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-sheets:v4-rev20260610-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-sqladmin:v1beta4-rev20260529-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-storage:v1-rev20260204-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.apis:google-api-services-storage:v1-rev20260524-2.0.0=testCompileClasspath,testRuntimeClasspath
com.google.auth:google-auth-library-credentials:1.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.auth:google-auth-library-oauth2-http:1.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.auto.service:auto-service-annotations:1.0.1=nonprodAnnotationProcessor,testAnnotationProcessor
com.google.auto.service:auto-service-annotations:1.1.1=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.auto.service:auto-service:1.1.1=annotationProcessor
@@ -110,47 +110,48 @@ com.google.auto.value:auto-value-annotations:1.11.0=compileClasspath,deploy_jar,
com.google.auto.value:auto-value-annotations:1.9=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
com.google.auto.value:auto-value:1.11.1=annotationProcessor,deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testRuntimeClasspath
com.google.auto:auto-common:1.2.2=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
com.google.cloud.bigdataoss:gcsio:2.2.26=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.bigdataoss:util:2.2.26=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.bigdataoss:gcsio:3.1.16=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.bigdataoss:util:3.1.16=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.bigtable:bigtable-client-core-config:1.28.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.datastore:datastore-v1-proto-client:2.37.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.datastore:datastore-v1-proto-client:3.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.opentelemetry:detector-resources-support:0.33.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.opentelemetry:exporter-metrics:0.33.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.opentelemetry:shared-resourcemapping:0.33.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud.sql:jdbc-socket-factory-core:1.28.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.sql:postgres-socket-factory:1.28.4=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-bigquerystorage:3.24.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-bigtable:2.73.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-compute:1.102.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core-grpc:2.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.cloud:google-cloud-core-grpc:2.70.0=testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core-http:2.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core-http:2.70.0=testCompileClasspath
com.google.cloud:google-cloud-core:2.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.cloud:google-cloud-core:2.70.0=testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-firestore:3.39.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-logging:3.29.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-monitoring:3.85.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.sql:jdbc-socket-factory-core:1.28.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.sql:postgres-socket-factory:1.28.6=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud.sql:schemas:1.28.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-bigquerystorage:3.28.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-bigtable:2.78.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-compute:1.104.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core-grpc:2.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.cloud:google-cloud-core-grpc:2.72.0=testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core-http:2.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core-http:2.72.0=testCompileClasspath
com.google.cloud:google-cloud-core:2.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.cloud:google-cloud-core:2.72.0=testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-firestore:3.42.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-logging:3.33.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-monitoring:3.93.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-nio:0.127.24=testRuntimeClasspath
com.google.cloud:google-cloud-nio:0.132.0=testCompileClasspath
com.google.cloud:google-cloud-pubsub:1.150.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-nio:0.134.0=testCompileClasspath
com.google.cloud:google-cloud-pubsub:1.150.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-secretmanager:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-secretmanager:2.92.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.cloud:google-cloud-spanner:6.113.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-storage-control:2.44.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-storage:2.64.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-storage:2.68.0=testCompileClasspath
com.google.cloud:google-cloud-secretmanager:2.94.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.cloud:google-cloud-spanner:6.117.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-storage-control:2.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-storage:2.68.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-storage:2.70.0=testCompileClasspath
com.google.cloud:google-cloud-tasks:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-tasks:2.92.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.cloud:grpc-gcp:1.9.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-tasks:2.94.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.cloud:grpc-gcp:1.10.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:libraries-bom:26.48.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:proto-google-cloud-firestore-bundle-v1:3.39.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:proto-google-cloud-firestore-bundle-v1:3.42.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
com.google.code.gson:gson:2.14.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.dagger:dagger-compiler:2.59.2=annotationProcessor,testAnnotationProcessor
com.google.dagger:dagger-spi:2.59.2=annotationProcessor,testAnnotationProcessor
com.google.dagger:dagger:2.59.2=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
com.google.devtools.ksp:symbol-processing-api:2.2.20-2.0.3=annotationProcessor,testAnnotationProcessor
com.google.dagger:dagger-compiler:2.60.1=annotationProcessor,testAnnotationProcessor
com.google.dagger:dagger-spi:2.60.1=annotationProcessor,testAnnotationProcessor
com.google.dagger:dagger:2.60.1=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
com.google.devtools.ksp:symbol-processing-api:2.3.7=annotationProcessor,testAnnotationProcessor
com.google.errorprone:error_prone_annotation:2.48.0=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
com.google.errorprone:error_prone_annotations:2.36.0=checkstyle
com.google.errorprone:error_prone_annotations:2.48.0=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
@@ -160,21 +161,24 @@ com.google.errorprone:error_prone_core:2.48.0=annotationProcessor,nonprodAnnotat
com.google.flatbuffers:flatbuffers-java:24.3.25=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.flogger:flogger-system-backend:0.7.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.flogger:flogger:0.7.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.flogger:google-extensions:0.7.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.flogger:google-extensions:0.7.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.googlejavaformat:google-java-format:1.34.1=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
com.google.guava:failureaccess:1.0.3=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
com.google.guava:guava-testlib:33.3.0-jre=testRuntimeClasspath
com.google.guava:guava-testlib:33.6.0-jre=testCompileClasspath
com.google.guava:guava:33.4.8-jre=checkstyle
com.google.guava:guava:33.5.0-jre=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
com.google.guava:guava:33.6.0-jre=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.guava:guava:33.5.0-jre=nonprodAnnotationProcessor
com.google.guava:guava:33.6.0-jre=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-apache-v2:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-appengine:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-gson:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-jackson2:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-apache-v2:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.http-client:google-http-client-apache-v2:2.1.1=testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-appengine:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-appengine:2.1.1=testCompileClasspath
com.google.http-client:google-http-client-gson:2.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-jackson2:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-jackson2:2.1.1=testCompileClasspath
com.google.http-client:google-http-client-protobuf:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client:2.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.j2objc:j2objc-annotations:3.0.0=checkstyle
com.google.j2objc:j2objc-annotations:3.1=annotationProcessor,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
com.google.monitoring-client:contrib:1.0.7=testCompileClasspath,testRuntimeClasspath
@@ -189,20 +193,19 @@ com.google.oauth-client:google-oauth-client-servlet:1.39.0=compileClasspath,nonp
com.google.oauth-client:google-oauth-client:1.39.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.protobuf:protobuf-java-util:4.33.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.protobuf:protobuf-java:4.33.2=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
com.google.protobuf:protobuf-java:4.35.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.protobuf:protobuf-java:4.35.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.re2j:re2j:1.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.truth:truth:1.4.5=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.googlecode.json-simple:json-simple:1.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.ibm.icu:icu4j:73.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.lmax:disruptor:3.4.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.puppycrawl.tools:checkstyle:10.24.0=checkstyle
com.squareup.okhttp3:okhttp-jvm:5.3.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.squareup.okhttp3:okhttp:5.3.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.squareup.okhttp3:okhttp-jvm:5.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.squareup.okhttp3:okhttp:5.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.squareup.okio:okio-bom:3.0.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.squareup.okio:okio-fakefilesystem-jvm:3.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.squareup.okio:okio-fakefilesystem:3.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.squareup.okio:okio-jvm:3.16.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.squareup.okio:okio:3.16.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.squareup.okio:okio-jvm:3.17.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.squareup.okio:okio:3.17.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.squareup.wire:wire-compiler:4.5.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.squareup.wire:wire-grpc-server-generator:4.5.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.squareup.wire:wire-grpc-server:4.5.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -222,7 +225,8 @@ com.sun.istack:istack-commons-tools:4.1.2=jaxb
com.sun.xml.bind.external:relaxng-datatype:4.0.9=jaxb
com.sun.xml.bind.external:rngom:4.0.9=jaxb
com.sun.xml.dtd-parser:dtd-parser:1.5.1=jaxb
com.zaxxer:HikariCP:7.0.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.zaxxer:HikariCP:7.0.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.zaxxer:HikariCP:7.1.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
commons-beanutils:commons-beanutils:1.10.1=checkstyle
commons-codec:commons-codec:1.15=checkstyle
commons-codec:commons-codec:1.19.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -247,79 +251,74 @@ io.github.ss-bhatt:testcontainers-valkey:1.0.0=testCompileClasspath,testRuntimeC
io.grpc:grpc-alts:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-api:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-auth:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-census:1.76.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-census:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-context:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-core:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-googleapis:1.81.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-grpclb:1.76.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.grpc:grpc-grpclb:1.81.0=testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-grpclb:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-inprocess:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-netty-shaded:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-netty:1.76.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-opentelemetry:1.76.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.grpc:grpc-opentelemetry:1.81.0=testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-netty:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-opentelemetry:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-protobuf-lite:1.81.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-protobuf:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-rls:1.76.3=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
io.grpc:grpc-rls:1.81.0=testRuntimeClasspath
io.grpc:grpc-services:1.76.3=compileClasspath,nonprodCompileClasspath,testCompileClasspath
io.grpc:grpc-services:1.81.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-rls:1.81.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-services:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-stub:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-util:1.76.3=compileClasspath,nonprodCompileClasspath,testCompileClasspath
io.grpc:grpc-util:1.81.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-xds:1.76.3=compileClasspath,nonprodCompileClasspath,testCompileClasspath
io.grpc:grpc-xds:1.81.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.netty:netty-buffer:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-codec-http2:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-codec-http:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-codec-socks:4.1.124.Final=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.netty:netty-codec:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-common:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-handler-proxy:4.1.124.Final=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.netty:netty-handler:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-resolver:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-testing:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-util:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-xds:1.81.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-buffer:4.1.132.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-codec-http2:4.1.132.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-codec-http:4.1.132.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-codec-socks:4.1.132.Final=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.netty:netty-codec:4.1.132.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-common:4.1.132.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-handler-proxy:4.1.132.Final=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.netty:netty-handler:4.1.132.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-resolver:4.1.132.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-tcnative-boringssl-static:2.0.52.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-tcnative-classes:2.0.52.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-transport-native-unix-common:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-transport:4.1.124.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-transport-native-unix-common:4.1.132.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-transport:4.1.132.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opencensus:opencensus-api:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opencensus:opencensus-contrib-exemplar-util:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opencensus:opencensus-contrib-grpc-metrics:0.31.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
io.opencensus:opencensus-contrib-grpc-metrics:0.31.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.opencensus:opencensus-contrib-exemplar-util:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opencensus:opencensus-contrib-grpc-metrics:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opencensus:opencensus-contrib-grpc-util:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opencensus:opencensus-contrib-http-util:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opencensus:opencensus-contrib-resource-util:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opencensus:opencensus-exporter-metrics-util:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opencensus:opencensus-exporter-stats-stackdriver:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opencensus:opencensus-impl-core:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opencensus:opencensus-impl:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opencensus:opencensus-contrib-resource-util:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opencensus:opencensus-exporter-metrics-util:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opencensus:opencensus-exporter-stats-stackdriver:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opencensus:opencensus-impl-core:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opencensus:opencensus-impl:0.31.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opentelemetry.contrib:opentelemetry-gcp-resources:1.37.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.opentelemetry.contrib:opentelemetry-gcp-resources:1.45.0-alpha=testCompileClasspath
io.opentelemetry.instrumentation:opentelemetry-grpc-1.6:2.1.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator:2.1.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opentelemetry.semconv:opentelemetry-semconv:1.29.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-api:1.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-api:1.62.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-api:1.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-api:1.64.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-bom:1.42.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-common:1.62.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-context:1.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-context:1.62.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-exporter-logging:1.62.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-common:1.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-common:1.64.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-context:1.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-context:1.64.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-exporter-logging:1.64.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-extension-incubator:1.35.0-alpha=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-common:1.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-common:1.62.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.62.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.62.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-logs:1.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-logs:1.62.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-metrics:1.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-metrics:1.62.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-trace:1.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-trace:1.62.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk:1.51.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk:1.62.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-common:1.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-common:1.64.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.64.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.64.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-logs:1.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-logs:1.64.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-metrics:1.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-metrics:1.64.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-trace:1.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-trace:1.64.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk:1.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk:1.64.0=testCompileClasspath,testRuntimeClasspath
io.outfoxx:swiftpoet:1.3.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.perfmark:perfmark-api:0.27.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.protostuff:protostuff-api:1.8.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -347,9 +346,9 @@ junit:junit:4.13.2=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileCl
net.arnx:nashorn-promise:0.1.1=testRuntimeClasspath
net.bytebuddy:byte-buddy-agent:1.17.7=testCompileClasspath,testRuntimeClasspath
net.bytebuddy:byte-buddy:1.17.7=compileClasspath,nonprodCompileClasspath
net.bytebuddy:byte-buddy:1.18.8=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
net.bytebuddy:byte-buddy:1.18.8-jdk5=testCompileClasspath
net.java.dev.jna:jna:5.13.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
net.bytebuddy:byte-buddy:1.18.11=testCompileClasspath,testRuntimeClasspath
net.bytebuddy:byte-buddy:1.18.8=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
net.java.dev.jna:jna:5.13.0=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
net.ltgt.gradle.incap:incap:0.2=annotationProcessor,testAnnotationProcessor
net.sf.saxon:Saxon-HE:12.5=checkstyle
org.abego.treelayout:org.abego.treelayout.core:1.0.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -360,25 +359,25 @@ org.antlr:antlr4:4.13.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonp
org.apache.arrow:arrow-format:17.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.arrow:arrow-memory-core:17.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.arrow:arrow-vector:17.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.avro:avro:1.11.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-model-fn-execution:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-model-job-management:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-model-pipeline:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.avro:avro:1.12.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-model-fn-execution:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-model-job-management:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-model-pipeline:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-runners-core-construction-java:2.54.0=testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-runners-core-java:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-runners-direct-java:2.73.0=testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-runners-google-cloud-dataflow-java:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-runners-java-fn-execution:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-core:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-expansion-service:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-extensions-arrow:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-extensions-avro:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-extensions-google-cloud-platform-core:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-extensions-protobuf:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-runners-core-java:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-runners-direct-java:2.75.0=testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-runners-google-cloud-dataflow-java:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-runners-java-fn-execution:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-core:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-expansion-service:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-extensions-arrow:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-extensions-avro:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-extensions-google-cloud-platform-core:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-extensions-protobuf:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-fn-execution:2.54.0=testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-harness:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-io-google-cloud-platform:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-transform-service-launcher:2.73.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-harness:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-io-google-cloud-platform:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-transform-service-launcher:2.75.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-vendor-grpc-1_60_1:0.1=testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-vendor-grpc-1_69_0:0.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-vendor-guava-32_1_2-jre:0.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -405,22 +404,23 @@ org.apache.maven.doxia:doxia-logging-api:1.12.0=checkstyle
org.apache.maven.doxia:doxia-module-xdoc:1.12.0=checkstyle
org.apache.maven.doxia:doxia-sink-api:1.12.0=checkstyle
org.apache.mina:mina-core:2.2.4=testCompileClasspath,testRuntimeClasspath
org.apache.sshd:sshd-common:3.0.0-M4=testCompileClasspath,testRuntimeClasspath
org.apache.sshd:sshd-core:3.0.0-M4=testCompileClasspath,testRuntimeClasspath
org.apache.sshd:sshd-scp:3.0.0-M4=testCompileClasspath,testRuntimeClasspath
org.apache.sshd:sshd-sftp:3.0.0-M4=testCompileClasspath,testRuntimeClasspath
org.apache.tomcat:tomcat-annotations-api:11.0.22=testCompileClasspath,testRuntimeClasspath
org.apache.sshd:sshd-common:3.0.0-M5=testCompileClasspath,testRuntimeClasspath
org.apache.sshd:sshd-core:3.0.0-M5=testCompileClasspath,testRuntimeClasspath
org.apache.sshd:sshd-scp:3.0.0-M5=testCompileClasspath,testRuntimeClasspath
org.apache.sshd:sshd-sftp:3.0.0-M5=testCompileClasspath,testRuntimeClasspath
org.apache.tomcat:tomcat-annotations-api:11.0.24=testCompileClasspath,testRuntimeClasspath
org.apache.xbean:xbean-reflect:3.7=checkstyle
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
org.bouncycastle:bcpg-jdk18on:1.84=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcpkix-jdk18on:1.84=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcprov-jdk18on:1.84=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcutil-jdk18on:1.84=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcpg-jdk18on:1.85=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcpkix-jdk18on:1.85=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcprov-jdk18on:1.85=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcutil-jdk18on:1.85=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.checkerframework:checker-compat-qual:2.5.3=annotationProcessor,compileClasspath,nonprodCompileClasspath,testAnnotationProcessor,testCompileClasspath
org.checkerframework:checker-compat-qual:2.5.6=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.checkerframework:checker-qual:3.19.0=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
org.checkerframework:checker-qual:3.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.checkerframework:checker-qual:3.49.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
org.checkerframework:checker-qual:3.49.3=checkstyle
org.checkerframework:checker-qual:3.55.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.codehaus.mojo:animal-sniffer-annotations:1.27=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.codehaus.plexus:plexus-classworlds:2.6.0=checkstyle
org.codehaus.plexus:plexus-component-annotations:2.1.0=checkstyle
@@ -431,18 +431,18 @@ org.conscrypt:conscrypt-openjdk-uber:2.5.2=compileClasspath,deploy_jar,nonprodCo
org.eclipse.angus:angus-activation:2.0.3=jaxb
org.eclipse.angus:angus-activation:2.1.0-M1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.eclipse.angus:jakarta.mail:2.1.0-M1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.eclipse.jetty.ee10:jetty-ee10-servlet:12.1.9=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty.ee10:jetty-ee10-webapp:12.1.9=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty.ee:jetty-ee-webapp:12.1.9=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-http:12.1.9=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-io:12.1.9=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-security:12.1.9=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-server:12.1.9=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-session:12.1.9=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-util:12.1.9=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-xml:12.1.9=testCompileClasspath,testRuntimeClasspath
org.flywaydb:flyway-core:12.7.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.flywaydb:flyway-database-postgresql:12.7.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty.ee10:jetty-ee10-servlet:12.1.11=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty.ee10:jetty-ee10-webapp:12.1.11=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty.ee:jetty-ee-webapp:12.1.11=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-http:12.1.11=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-io:12.1.11=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-security:12.1.11=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-server:12.1.11=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-session:12.1.11=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-util:12.1.11=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-xml:12.1.11=testCompileClasspath,testRuntimeClasspath
org.flywaydb:flyway-core:12.11.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.flywaydb:flyway-database-postgresql:12.11.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.freemarker:freemarker:2.3.34=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.glassfish.jaxb:codemodel:4.0.9=jaxb
org.glassfish.jaxb:jaxb-core:4.0.6=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -470,7 +470,7 @@ org.javassist:javassist:3.28.0-GA=checkstyle
org.jboss.logging:jboss-logging:3.6.3.Final=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.jcommander:jcommander:3.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-bom:1.4.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-metadata-jvm:2.2.20=annotationProcessor,testAnnotationProcessor
org.jetbrains.kotlin:kotlin-metadata-jvm:2.3.21=annotationProcessor,testAnnotationProcessor
org.jetbrains.kotlin:kotlin-reflect:1.6.10=annotationProcessor,testAnnotationProcessor
org.jetbrains.kotlin:kotlin-reflect:1.9.20=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-stdlib-common:2.2.21=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -478,35 +478,35 @@ org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0=annotationProcessor,testAnnotation
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0=annotationProcessor,testAnnotationProcessor
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-stdlib:2.2.20=annotationProcessor,testAnnotationProcessor
org.jetbrains.kotlin:kotlin-stdlib:2.2.21=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-stdlib:2.3.21=annotationProcessor,testAnnotationProcessor
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-datetime:0.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.0.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.jetbrains:annotations:13.0=annotationProcessor,testAnnotationProcessor
org.jetbrains:annotations:17.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jline:jline:4.1.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jetbrains:annotations:13.0=annotationProcessor,compileClasspath,deploy_jar,runtimeClasspath,testAnnotationProcessor
org.jetbrains:annotations:17.0.0=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jline:jline:4.3.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.joda:joda-money:2.0.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.json:json:20260522=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jsoup:jsoup:1.22.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jspecify:jspecify:1.0.0=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
org.junit-pioneer:junit-pioneer:2.3.0=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-api:6.1.0=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-engine:6.1.0=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-migrationsupport:6.1.0=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-params:6.1.0=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-commons:6.1.0=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-engine:6.1.0=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-launcher:6.1.0=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-api:6.1.2=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-engine:6.1.2=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-migrationsupport:6.1.2=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-params:6.1.2=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-commons:6.1.2=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-engine:6.1.2=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-launcher:6.1.2=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-runner:1.14.4=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-suite-api:6.1.0=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-suite-api:6.1.2=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-suite-commons:1.14.4=testRuntimeClasspath
org.junit.platform:junit-platform-suite-engine:6.1.0=testRuntimeClasspath
org.junit.platform:junit-platform-suite:6.1.0=testCompileClasspath,testRuntimeClasspath
org.junit:junit-bom:6.1.0=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-suite-engine:6.1.2=testRuntimeClasspath
org.junit.platform:junit-platform-suite:6.1.2=testCompileClasspath,testRuntimeClasspath
org.junit:junit-bom:6.1.2=testCompileClasspath,testRuntimeClasspath
org.mockito:mockito-core:5.23.0=testCompileClasspath,testRuntimeClasspath
org.mockito:mockito-junit-jupiter:5.23.0=testCompileClasspath,testRuntimeClasspath
org.objenesis:objenesis:3.3=testRuntimeClasspath
@@ -522,37 +522,38 @@ org.ow2.asm:asm:9.7.1=compileClasspath,nonprodCompileClasspath
org.ow2.asm:asm:9.8=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.ow2.asm:asm:9.9=jacocoAnt
org.pcollections:pcollections:4.0.1=annotationProcessor,nonprodAnnotationProcessor,testAnnotationProcessor
org.postgresql:postgresql:42.7.11=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.postgresql:postgresql:42.7.13=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.reflections:reflections:0.10.2=checkstyle
org.rnorth.duct-tape:duct-tape:1.0.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-api:4.44.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-chrome-driver:4.44.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-chromium-driver:4.44.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-devtools-v146:4.44.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-devtools-v147:4.44.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-devtools-v148:4.44.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-edge-driver:4.44.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-firefox-driver:4.44.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-http:4.44.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-ie-driver:4.44.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-java:4.44.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-json:4.44.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-manager:4.44.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-os:4.44.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-remote-driver:4.44.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-safari-driver:4.44.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-support:4.44.0=testCompileClasspath,testRuntimeClasspath
org.rnorth.duct-tape:duct-tape:1.0.8=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-api:4.46.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-chrome-driver:4.46.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-chromium-driver:4.46.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-devtools-latest:4.46.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-devtools-v148:4.46.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-devtools-v149:4.46.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-devtools-v150:4.46.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-edge-driver:4.46.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-firefox-driver:4.46.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-http:4.46.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-ie-driver:4.46.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-java:4.46.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-json:4.46.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-manager:4.46.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-os:4.46.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-remote-driver:4.46.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-safari-driver:4.46.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-support:4.46.0=testCompileClasspath,testRuntimeClasspath
org.slf4j:jcl-over-slf4j:1.7.36=testCompileClasspath,testRuntimeClasspath
org.slf4j:jul-to-slf4j:1.7.30=testRuntimeClasspath
org.slf4j:slf4j-api:2.0.18=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.slf4j:slf4j-jdk14:2.0.17=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.snakeyaml:snakeyaml-engine:2.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.testcontainers:database-commons:1.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.testcontainers:jdbc:1.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.testcontainers:database-commons:1.21.4=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.testcontainers:jdbc:1.21.4=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.testcontainers:junit-jupiter:1.21.4=testCompileClasspath,testRuntimeClasspath
org.testcontainers:postgresql:1.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.testcontainers:postgresql:1.21.4=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.testcontainers:selenium:1.21.4=testCompileClasspath,testRuntimeClasspath
org.testcontainers:testcontainers:1.21.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.testcontainers:testcontainers:1.21.4=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.threeten:threetenbp:1.7.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.webjars.npm:viz.js-graphviz-java:2.1.3=testRuntimeClasspath
org.xerial.snappy:snappy-java:1.1.10.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -560,16 +561,16 @@ org.xmlresolver:xmlresolver:5.2.2=checkstyle
org.yaml:snakeyaml:2.5=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
redis.clients.authentication:redis-authx-core:0.1.1-beta2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
redis.clients:jedis:8.0.0-beta1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
tools.jackson.core:jackson-core:3.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
tools.jackson.core:jackson-databind:3.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
tools.jackson:jackson-bom:3.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
tools.jackson.core:jackson-core:3.1.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
tools.jackson.core:jackson-databind:3.1.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
tools.jackson:jackson-bom:3.1.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-api:17.1.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-diagram:17.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-operations:17.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-postgresql:17.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-text:17.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-diagram:17.12.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-operations:17.12.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-postgresql:17.12.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-text:17.12.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-tools:17.1.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-utility:17.1.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler:17.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler:17.12.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
xerces:xmlParserAPIs:2.6.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
empty=devtool,shadow
@@ -212,17 +212,16 @@ public class RdeIO {
}
}
// Don't write the IDN elements for BRDA.
// Don't write the IDN elements or EPP params for BRDA.
if (mode == RdeMode.FULL) {
for (IdnTableEnum idn : IdnTableEnum.values()) {
output.write(marshaller.marshalIdn(idn.getTable()));
counter.increment(RdeResourceType.IDN);
}
output.write(marshaller.marshalRdeEppParams());
counter.increment(RdeResourceType.EPP_PARAMS);
}
output.write(marshaller.marshalRdeEppParams());
counter.increment(RdeResourceType.EPP_PARAMS);
// Output XML that says how many resources were emitted.
header = counter.makeHeader(tld, mode);
output.write(marshaller.marshalOrDie(new XjcRdeHeaderElement(header)));
@@ -372,7 +372,7 @@ public class RdePipeline implements Serializable {
* <p>The (repoId, pendingDeposit) pairs denote hosts that are referenced from a domain, that are
* to be included in the corresponding pending deposit.
*
* <p>The (repoId, revisionId) paris come from the most recent history entry query, which can be
* <p>The (repoId, revisionId) pairs come from the most recent history entry query, which can be
* used to load the embedded resources themselves.
*
* @return a pair of (repoId, ([pendingDeposit], [revisionId])) where neither the pendingDeposit
@@ -226,18 +226,17 @@ public class SafeBrowsingTransforms {
private void processResponse(
CloseableHttpResponse response,
ImmutableSet.Builder<KV<DomainNameInfo, ThreatMatch>> resultBuilder)
throws JSONException, IOException {
throws IOException {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != SC_OK) {
logger.atWarning().log("Got unexpected status code %s from response.", statusCode);
} else {
// Unpack the response body
JSONObject responseBody =
new JSONObject(
CharStreams.toString(
new InputStreamReader(response.getEntity().getContent(), UTF_8)));
logger.atInfo().log("Got response: %s", responseBody);
if (responseBody.length() == 0) {
throw new IOException(
String.format("Got unexpected status code %s from response.", statusCode));
}
// Unpack the response body
try (InputStreamReader reader =
new InputStreamReader(response.getEntity().getContent(), UTF_8)) {
JSONObject responseBody = new JSONObject(CharStreams.toString(reader));
if (responseBody.isEmpty()) {
logger.atInfo().log("Response was empty, no threats detected.");
} else {
// Emit all DomainNameInfos with their API results.
+9 -4
View File
@@ -38,6 +38,7 @@ import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.Optional;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
@@ -106,12 +107,16 @@ public final class CacheModule {
@Provides
@Singleton
public static HostCache provideHostCache(
Optional<SimplifiedJedisClient> jedisClient, CacheMetrics cacheMetrics) {
Optional<SimplifiedJedisClient> jedisClient, Clock clock, CacheMetrics cacheMetrics) {
if (jedisClient.isEmpty()) {
return repoId ->
Optional.ofNullable(EppResource.loadByCache(VKey.create(Host.class, repoId)));
return repoId -> {
Instant now = clock.now();
return Optional.ofNullable(EppResource.loadByCache(VKey.create(Host.class, repoId)))
.filter(host -> now.isBefore(host.getDeletionTime()))
.map(host -> (Host) host.cloneProjectedAtTime(now));
};
}
return new MultilayerHostCache(jedisClient.get(), cacheMetrics);
return new MultilayerHostCache(jedisClient.get(), clock, cacheMetrics);
}
private static SSLSocketFactory createValkeySslSocketFactory(String valkeyCertificateAuthority) {
@@ -19,7 +19,6 @@ import google.registry.model.ForeignKeyUtils;
import google.registry.model.domain.Domain;
import google.registry.model.tld.Tld;
import google.registry.util.Clock;
import java.time.Instant;
import java.util.Optional;
/**
@@ -30,12 +29,9 @@ import java.util.Optional;
public class MultilayerDomainCache extends MultilayerEppResourceCache<Domain>
implements DomainCache {
private final Clock clock;
public MultilayerDomainCache(
SimplifiedJedisClient jedisClient, Clock clock, CacheMetrics cacheMetrics) {
super(jedisClient, cacheMetrics);
this.clock = clock;
super(jedisClient, clock, cacheMetrics);
}
@Override
@@ -46,15 +42,10 @@ public class MultilayerDomainCache extends MultilayerEppResourceCache<Domain>
@Override
protected Optional<Domain> loadFromDatabase(String domainName) {
// Don't use the cache (avoid caching the same domain twice). Do use the replica SQL instance.
Optional<Domain> possibleDomain =
Optional.ofNullable(
ForeignKeyUtils.loadMostRecentResourceObjects(
Domain.class, ImmutableList.of(domainName), true)
.get(domainName));
Instant now = clock.now();
return possibleDomain
.filter(domain -> now.isBefore(domain.getDeletionTime()))
.map(domain -> domain.cloneProjectedAtTime(now));
return Optional.ofNullable(
ForeignKeyUtils.loadMostRecentResourceObjects(
Domain.class, ImmutableList.of(domainName), true)
.get(domainName));
}
@Override
@@ -18,7 +18,9 @@ import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import google.registry.config.RegistryConfig;
import google.registry.model.EppResource;
import google.registry.util.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.Optional;
/**
@@ -36,11 +38,13 @@ public abstract class MultilayerEppResourceCache<V extends EppResource> {
.build();
private final SimplifiedJedisClient jedisClient;
private final Clock clock;
private final CacheMetrics cacheMetrics;
protected MultilayerEppResourceCache(
SimplifiedJedisClient jedisClient, CacheMetrics cacheMetrics) {
SimplifiedJedisClient jedisClient, Clock clock, CacheMetrics cacheMetrics) {
this.jedisClient = jedisClient;
this.clock = clock;
this.cacheMetrics = cacheMetrics;
}
@@ -50,7 +54,16 @@ public abstract class MultilayerEppResourceCache<V extends EppResource> {
return true;
}
@SuppressWarnings("unchecked")
protected Optional<V> loadFromCaches(Class<V> clazz, String key) {
Instant now = clock.now();
return (Optional<V>)
loadFromCachesInternal(clazz, key)
.filter(v -> now.isBefore(v.getDeletionTime()))
.map(v -> v.cloneProjectedAtTime(now));
}
private Optional<V> loadFromCachesInternal(Class<V> clazz, String key) {
// hopefully the resource is in the local cache
Optional<V> possibleValue = Optional.ofNullable(localCache.getIfPresent(key));
if (possibleValue.isPresent()) {
@@ -18,6 +18,7 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import google.registry.model.host.Host;
import google.registry.persistence.VKey;
import google.registry.util.Clock;
import java.util.Optional;
/**
@@ -27,8 +28,9 @@ import java.util.Optional;
*/
public class MultilayerHostCache extends MultilayerEppResourceCache<Host> implements HostCache {
public MultilayerHostCache(SimplifiedJedisClient jedisClient, CacheMetrics cacheMetrics) {
super(jedisClient, cacheMetrics);
public MultilayerHostCache(
SimplifiedJedisClient jedisClient, Clock clock, CacheMetrics cacheMetrics) {
super(jedisClient, clock, cacheMetrics);
}
@Override
@@ -25,10 +25,20 @@ import com.google.common.flogger.FluentLogger;
import google.registry.model.EppResource;
import google.registry.model.domain.Domain;
import google.registry.model.host.Host;
import io.protostuff.Input;
import io.protostuff.LinkedBuffer;
import io.protostuff.Output;
import io.protostuff.Pipe;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.WireFormat;
import io.protostuff.runtime.DefaultIdStrategy;
import io.protostuff.runtime.Delegate;
import io.protostuff.runtime.RuntimeSchema;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import redis.clients.jedis.AbstractPipeline;
@@ -52,11 +62,20 @@ public class SimplifiedJedisClient {
Domain.class, "d_",
Host.class, "h_");
/** We need to inform Protostuff of the custom {@link InetAddress} delegates. */
private static DefaultIdStrategy createIdStrategy() {
DefaultIdStrategy strategy = new DefaultIdStrategy();
strategy.registerDelegate(new GenericInetAddressDelegate<>(InetAddress.class));
strategy.registerDelegate(new GenericInetAddressDelegate<>(Inet4Address.class));
strategy.registerDelegate(new GenericInetAddressDelegate<>(Inet6Address.class));
return strategy;
}
private static final ImmutableMap<Class<? extends EppResource>, Schema<? extends EppResource>>
VALUE_SCHEMAS =
ImmutableMap.of(
Domain.class, RuntimeSchema.getSchema(Domain.class),
Host.class, RuntimeSchema.getSchema(Host.class));
Host.class, RuntimeSchema.getSchema(Host.class, createIdStrategy()));
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -151,4 +170,46 @@ public class SimplifiedJedisClient {
checkArgument(VALUE_SCHEMAS.containsKey(clazz), "Unknown class type %s", clazz);
return (Schema<V>) VALUE_SCHEMAS.get(clazz);
}
/**
* A custom Protostuff {@link Delegate} for {@link InetAddress} and its subclasses.
*
* <p>This is required in Java 17+ because Protostuff's default runtime schema serialization
* relies on reflection. Since {@link InetAddress} is part of the encapsulated {@code java.base}
* module, reflective access is restricted and throws {@link
* java.lang.reflect.InaccessibleObjectException}.
*
* <p>This delegate serializes the IP address as a raw byte array using {@link
* InetAddress#getAddress()} and reconstructs it using {@link InetAddress#getByAddress(byte[])}
*/
private record GenericInetAddressDelegate<T extends InetAddress>(Class<T> clazz)
implements Delegate<T> {
@Override
public WireFormat.FieldType getFieldType() {
return WireFormat.FieldType.BYTES;
}
@Override
public Class<T> typeClass() {
return clazz;
}
@SuppressWarnings("unchecked")
@Override
public T readFrom(Input input) throws IOException {
return (T) InetAddress.getByAddress(input.readByteArray());
}
@Override
public void writeTo(Output output, int number, T value, boolean repeated) throws IOException {
output.writeByteArray(number, value.getAddress(), repeated);
}
@Override
public void transfer(Pipe pipe, Input input, Output output, int number, boolean repeated)
throws IOException {
output.writeByteArray(number, input.readByteArray(), repeated);
}
}
}
@@ -16,6 +16,7 @@ package google.registry.config;
import static com.google.common.base.Suppliers.memoize;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.collect.ImmutableSortedMap.toImmutableSortedMap;
import static google.registry.config.ConfigUtils.makeUrl;
import static google.registry.util.DateTimeUtils.START_INSTANT;
@@ -38,6 +39,7 @@ import google.registry.dns.ReadDnsRefreshRequestsAction;
import google.registry.model.common.DnsRefreshRequest;
import google.registry.mosapi.MosApiClient;
import google.registry.persistence.transaction.JpaTransactionManager;
import google.registry.rde.RdeUploadUrl;
import google.registry.request.Action.Service;
import google.registry.util.RegistryEnvironment;
import google.registry.util.YamlUtils;
@@ -48,6 +50,7 @@ import jakarta.mail.internet.AddressException;
import jakarta.mail.internet.InternetAddress;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.math.BigDecimal;
import java.net.URI;
import java.net.URL;
import java.time.DayOfWeek;
@@ -58,6 +61,7 @@ import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.joda.money.CurrencyUnit;
/**
* Central clearing-house for all configuration.
@@ -173,6 +177,18 @@ public final class RegistryConfig {
return config.registrarConsole.supportEmailAddress;
}
@Provides
@Config("consoleHistoryQueryLimit")
public static int provideConsoleHistoryQueryLimit(RegistryConfigSettings config) {
return config.registrarConsole.historyQueryLimit;
}
@Provides
@Config("consoleBulkDomainActionLimit")
public static int provideConsoleBulkDomainActionLimit(RegistryConfigSettings config) {
return config.registrarConsole.bulkDomainActionLimit;
}
/**
* The DUM file name, used as a file name base for DUM csv file
*
@@ -830,8 +846,8 @@ public final class RegistryConfig {
*/
@Provides
@Config("rdeUploadUrl")
public static URI provideRdeUploadUrl(RegistryConfigSettings config) {
return URI.create(config.rde.uploadUrl);
public static RdeUploadUrl provideRdeUploadUrl(RegistryConfigSettings config) {
return RdeUploadUrl.create(URI.create(config.rde.uploadUrl));
}
/**
@@ -1097,6 +1113,40 @@ public final class RegistryConfig {
return config.registryPolicy.registryAdminClientId;
}
/** Returns the total length of the Expiry Access Period. */
@Provides
@Config("domainExpiryAccessPeriodTotalLength")
public static Duration provideDomainExpiryAccessPeriodTotalLength(
RegistryConfigSettings config) {
return Duration.ofSeconds(config.registryPolicy.domainExpiryAccessPeriod.totalLengthSeconds);
}
/** Returns the length of each tier of the Expiry Access Period. */
@Provides
@Config("domainExpiryAccessPeriodTierLength")
public static Duration provideDomainExpiryAccessPeriodTierLength(
RegistryConfigSettings config) {
return Duration.ofSeconds(config.registryPolicy.domainExpiryAccessPeriod.tierLengthSeconds);
}
/** Returns a map of the fee for the first tier of the Expiry Access Period, by currency. */
@Provides
@Config("domainExpiryAccessPeriodInitialFee")
public static ImmutableMap<CurrencyUnit, BigDecimal> provideDomainExpiryAccessPeriodInitialFee(
RegistryConfigSettings config) {
return config.registryPolicy.domainExpiryAccessPeriod.initialFee.entrySet().stream()
.collect(toImmutableMap(entry -> CurrencyUnit.of(entry.getKey()), Entry::getValue));
}
/** Returns a map of the fee for the last tier of the Expiry Access Period, by currency. */
@Provides
@Config("domainExpiryAccessPeriodFinalFee")
public static ImmutableMap<CurrencyUnit, BigDecimal> provideDomainExpiryAccessPeriodFinalFee(
RegistryConfigSettings config) {
return config.registryPolicy.domainExpiryAccessPeriod.finalFee.entrySet().stream()
.collect(toImmutableMap(entry -> CurrencyUnit.of(entry.getKey()), Entry::getValue));
}
@Singleton
@Provides
static RegistryConfigSettings provideRegistryConfigSettings() {
@@ -14,6 +14,7 @@
package google.registry.config;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -94,6 +95,7 @@ public class RegistryConfigSettings {
public String tmchCrlUrl;
public String tmchMarksDbUrl;
public String registryAdminClientId;
public DomainExpiryAccessPeriod domainExpiryAccessPeriod;
public String premiumTermsExportDisclaimer;
public String reservedTermsExportDisclaimer;
public String rdapTos;
@@ -106,6 +108,13 @@ public class RegistryConfigSettings {
public Set<String> noPollMessageOnDeletionRegistrarIds;
}
public static class DomainExpiryAccessPeriod {
public long totalLengthSeconds;
public long tierLengthSeconds;
public Map<String, BigDecimal> initialFee;
public Map<String, BigDecimal> finalFee;
}
/** Configuration for Hibernate. */
public static class Hibernate {
public boolean allowNestedTransactions;
@@ -181,6 +190,8 @@ public class RegistryConfigSettings {
public String supportPhoneNumber;
public String supportEmailAddress;
public String technicalDocsUrl;
public int historyQueryLimit;
public int bulkDomainActionLimit;
}
/** Configuration for monitoring. */
@@ -19,6 +19,7 @@
<queue>
<name>dns-refresh</name>
<max-dispatches-per-second>100</max-dispatches-per-second>
<max-retry-duration>82800s</max-retry-duration>
</queue>
<!-- Queue for publishing DNS updates in batches. -->
@@ -29,6 +30,7 @@
<min-backoff>30s</min-backoff>
<max-backoff>1800s</max-backoff>
<max-doublings>0</max-doublings>
<max-retry-duration>82800s</max-retry-duration>
</queue>
<!-- Queue for uploading RDE deposits to the escrow provider. -->
@@ -59,6 +61,7 @@
<queue>
<name>async-host-rename</name>
<max-dispatches-per-second>1</max-dispatches-per-second>
<max-retry-duration>82800s</max-retry-duration>
</queue>
<!-- Queue for tasks that wait for a Beam pipeline to complete (i.e. Spec11 and invoicing). -->
@@ -110,11 +113,12 @@
<max-attempts>3</max-attempts>
</queue>
<!-- &lt;!&ndash; Queue for async actions that should be run at some point in the future. &ndash;&gt;-->
<!-- Queue for async actions that should be run at some point in the future.-->
<queue>
<name>async-actions</name>
<max-dispatches-per-second>1</max-dispatches-per-second>
<max-concurrent-dispatches>5</max-concurrent-dispatches>
<max-retry-duration>82800s</max-retry-duration>
</queue>
</entries>
@@ -88,6 +88,28 @@ registryPolicy:
# registrar
registryAdminClientId: TheRegistrar
# Configuration for the Expiry Access Period (XAP) that occurs immediately
# following completion of a domain's deletion. This needs to be enabled on a
# per-TLD basis as well.
domainExpiryAccessPeriod:
# The length of the total expiry access period; defaults to 10 days.
totalLengthSeconds: 864000
# The length of each tier during the expiry access period; defaults to 1
# hour. The XAP fee remains constant for the duration of each tier. The
# total length must be evenly divisible by the tier length, i.e. there
# should be a whole number of tiers all the same length.
tierLengthSeconds: 3600
# The initial fee for the first tier when the expiry access period starts,
# specified for each currency this registry platform uses.
initialFee:
USD: 100000
JPY: 10000000
# The final fee for the last tier when the expiry access period ends,
# specified for each currency this registry platform uses.
finalFee:
USD: 10
JPY: 1000
# Disclaimer at the top of the exported premium terms list.
premiumTermsExportDisclaimer: |
This list contains domains for the TLD offered at a premium price. This
@@ -380,6 +402,12 @@ registrarConsole:
# URL linking to directory of technical support docs on the registry.
technicalDocsUrl: http://example.com/your_support_docs/
# Maximum number of history records returned in a single query.
historyQueryLimit: 500
# Maximum number of domains allowed in a single bulk action.
bulkDomainActionLimit: 500
monitoring:
# Max queries per second for the Google Cloud Monitoring V3 (aka Stackdriver)
# API. The limit can be adjusted by contacting Cloud Support.
@@ -266,16 +266,6 @@
<schedule>0 15 * * *</schedule>
</task>
<task>
<url><![CDATA[/_dr/task/wipeOutContactHistoryPii]]></url>
<name>wipeOutContactHistoryPii</name>
<description>
This job runs weekly to wipe out PII fields of ContactHistory entities
that have been in the database for a certain period of time.
</description>
<schedule>0 15 * * 1</schedule>
</task>
<task>
<url><![CDATA[/_dr/task/bsaDownload]]></url>
<name>bsaDownload</name>
@@ -333,4 +323,14 @@
<schedule>*/5 * * * *</schedule>
</task>
<task>
<url><![CDATA[/_dr/task/syncRemoteCache]]></url>
<name>syncRemoteCache</name>
<description>
Syncs remote (Valkey/Redis) EPP resource caches with changes made recently.
</description>
<!-- Runs every 5 minutes. RDAP needs to be updated within 60 minutes of changes. -->
<schedule>*/5 * * * *</schedule>
</task>
</entries>
@@ -155,16 +155,6 @@
<schedule>*/1 * * * *</schedule>
</task>
<task>
<url><![CDATA[/_dr/task/wipeOutContactHistoryPii]]></url>
<name>wipeOutContactHistoryPii</name>
<description>
This job runs weekly to wipe out PII fields of ContactHistory entities
that have been in the database for a certain period of time.
</description>
<schedule>0 15 * * 1</schedule>
</task>
<task>
<url><![CDATA[/_dr/task/bsaDownload]]></url>
<name>bsaDownload</name>
@@ -36,13 +36,13 @@ import static google.registry.persistence.PersistenceModule.TransactionIsolation
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
import static google.registry.pricing.PricingEngineProxy.isDomainPremium;
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import static org.json.simple.JSONValue.toJSONString;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import com.google.common.net.InternetDomainName;
import com.google.common.net.MediaType;
import com.google.gson.Gson;
import dagger.Module;
import dagger.Provides;
import google.registry.flows.domain.DomainFlowUtils.BadCommandForRegistryPhaseException;
@@ -83,6 +83,7 @@ public class CheckApiAction implements Runnable {
@Inject Response response;
@Inject CheckApiMetric.Builder metricBuilder;
@Inject CheckApiMetrics checkApiMetrics;
@Inject Gson gson;
@Inject
CheckApiAction() {}
@@ -94,7 +95,7 @@ public class CheckApiAction implements Runnable {
response.setHeader("X-Content-Type-Options", "nosniff");
response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
response.setContentType(MediaType.JSON_UTF_8);
response.setPayload(toJSONString(doCheck()));
response.setPayload(gson.toJson(doCheck()));
} finally {
CheckApiMetric metric = metricBuilder.build();
checkApiMetrics.incrementCheckApiRequest(metric);
@@ -19,7 +19,6 @@ import static java.nio.charset.StandardCharsets.US_ASCII;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import com.google.common.io.BaseEncoding;
import google.registry.request.Response;
import jakarta.servlet.http.HttpServletRequest;
@@ -55,7 +54,6 @@ public class CookieSessionMetadata extends SessionMetadata {
Pattern.compile("serviceExtensionUris=([^,\\s}]+)?");
private static final Pattern FAILED_LOGIN_ATTEMPTS_PATTERN =
Pattern.compile("failedLoginAttempts=([^,\\s]+)?");
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final Map<String, String> data = new HashMap<>();
@@ -66,7 +64,6 @@ public class CookieSessionMetadata extends SessionMetadata {
Matcher matcher = COOKIE_PATTERN.matcher(cookie);
if (matcher.find()) {
String sessionInfo = decode(matcher.group(1));
logger.atInfo().log("SESSION INFO: %s", sessionInfo);
matcher = REGISTRAR_ID_PATTERN.matcher(sessionInfo);
if (matcher.find()) {
String registrarId = matcher.group(1);
@@ -24,6 +24,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import com.google.common.flogger.FluentLogger;
import com.google.gson.Gson;
import google.registry.flows.FlowModule.EppExceptionInProviderException;
import google.registry.model.eppcommon.Trid;
import google.registry.model.eppinput.EppInput;
@@ -34,7 +35,6 @@ import google.registry.model.eppoutput.Result.Code;
import google.registry.monitoring.whitebox.EppMetric;
import jakarta.inject.Inject;
import java.util.Optional;
import org.json.simple.JSONValue;
/**
* An implementation of the EPP command/response protocol.
@@ -50,6 +50,8 @@ public final class EppController {
@Inject EppMetric.Builder eppMetricBuilder;
@Inject EppMetrics eppMetrics;
@Inject ServerTridProvider serverTridProvider;
@Inject Gson gson;
@Inject EppController() {}
/** Reads EPP XML, executes the matching flow, and returns an {@link EppOutput}. */
@@ -66,13 +68,16 @@ public final class EppController {
try {
eppInput = unmarshalEpp(EppInput.class, inputXmlBytes);
} catch (EppException e) {
// Log the unmarshalling error, with the raw bytes (in base64) to help with debugging.
// Log the unmarshalling error, with the sanitized bytes (in base64) to help with debugging.
Optional<String> sanitizedXml = EppXmlSanitizer.sanitizeEppXmlIfValid(inputXmlBytes);
String xmlBytesToLog =
base64().encode(sanitizedXml.map(xml -> xml.getBytes(UTF_8)).orElse(inputXmlBytes));
logger.atInfo().withCause(e).log(
"EPP request XML unmarshalling failed - \"%s\":\n%s\n%s\n%s\n%s",
"EPP request XML unmarshalling failed - \"%s\":\n%s\n%s",
e.getMessage(),
lazy(
() ->
JSONValue.toJSONString(
gson.toJson(
ImmutableMap.<String, Object>of(
"clientId",
nullToEmpty(sessionMetadata.getRegistrarId()),
@@ -81,12 +86,7 @@ public final class EppController {
"resultMessage",
e.getResult().getCode().msg,
"xmlBytes",
base64().encode(inputXmlBytes)))),
LOG_SEPARATOR,
lazy(
() ->
new String(inputXmlBytes, UTF_8)
.trim()), // Charset decoding failures are swallowed.
xmlBytesToLog))),
LOG_SEPARATOR);
// Return early by sending an error message, with no clTRID since we couldn't unmarshal it.
eppMetricBuilder.setStatus(e.getResult().getCode());
@@ -87,16 +87,22 @@ public class EppXmlSanitizer {
*
* <p>Also, an empty element will be formatted as {@code <tag></tag>} instead of {@code <tag/>}.
*/
public static String sanitizeEppXml(byte[] inputXmlBytes) {
public static Optional<String> sanitizeEppXmlIfValid(byte[] inputXmlBytes) {
try {
// Keep exactly one newline at end of sanitized string.
return CharMatcher.whitespace().trimTrailingFrom(sanitizeAndEncode(inputXmlBytes)) + "\n";
return Optional.of(
CharMatcher.whitespace().trimTrailingFrom(sanitizeAndEncode(inputXmlBytes)) + "\n");
} catch (XMLStreamException | UnsupportedEncodingException e) {
logger.atWarning().withCause(e).log("Failed to sanitize EPP XML message.");
return Base64.getMimeEncoder().encodeToString(inputXmlBytes);
return Optional.empty();
}
}
public static String sanitizeEppXml(byte[] inputXmlBytes) {
return sanitizeEppXmlIfValid(inputXmlBytes)
.orElseGet(() -> Base64.getMimeEncoder().encodeToString(inputXmlBytes));
}
private static String sanitizeAndEncode(byte[] inputXmlBytes)
throws XMLStreamException, UnsupportedEncodingException {
XMLEventReader xmlEventReader =
@@ -15,14 +15,15 @@
package google.registry.flows;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.flows.domain.DomainFlowUtils.toLogSafeLabel;
import static java.util.Collections.EMPTY_LIST;
import com.google.common.base.Ascii;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import com.google.gson.Gson;
import google.registry.flows.FlowModule.InputXml;
import google.registry.flows.FlowModule.RegistrarId;
import google.registry.flows.annotations.ReportingSpec;
@@ -30,7 +31,6 @@ import google.registry.model.eppcommon.Trid;
import google.registry.model.eppinput.EppInput;
import jakarta.inject.Inject;
import java.util.Optional;
import org.json.simple.JSONValue;
/** Reporter used by {@link FlowRunner} to record flow execution data for reporting. */
public class FlowReporter {
@@ -49,6 +49,8 @@ public class FlowReporter {
@Inject @InputXml byte[] inputXmlBytes;
@Inject EppInput eppInput;
@Inject Class<? extends Flow> flowClass;
@Inject Gson gson;
@Inject FlowReporter() {}
/** Records information about the current flow execution in the request logs. */
@@ -61,7 +63,7 @@ public class FlowReporter {
logger.atInfo().log(
"%s: %s",
METADATA_LOG_SIGNATURE,
JSONValue.toJSONString(
gson.toJson(
new ImmutableMap.Builder<String, Object>()
.put("serverTrid", trid.getServerTransactionId())
.put("clientId", registrarId)
@@ -88,9 +90,7 @@ public class FlowReporter {
*/
private static Optional<String> extractTld(String domainName) {
int index = domainName.indexOf('.');
return index == -1
? Optional.empty()
: Optional.of(Ascii.toLowerCase(domainName.substring(index + 1)));
return index == -1 ? Optional.empty() : toLogSafeLabel(domainName.substring(index + 1));
}
/**
@@ -44,6 +44,9 @@ import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.Host;
import google.registry.model.transfer.TransferStatus;
import google.registry.persistence.VKey;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.Objects;
@@ -98,8 +101,30 @@ public final class ResourceFlowUtils {
public static <R extends EppResource> void verifyResourceDoesNotExist(
Class<R> clazz, String targetId, Instant now, String registrarId) throws EppException {
Optional<R> resource = ForeignKeyUtils.loadResource(clazz, targetId, now);
verifyResourceDoesNotExist(clazz, targetId, now, Duration.ZERO, registrarId);
}
/**
* Verifies that a resource with the specified type and id did not exist at the specified time.
*
* <p>This will throw an exception if the resource exists with a deletionTime greater than the
* specified time (i.e. a registrar is attempting to create a duplicate domain, host, or contact).
* If the resource had existed within the specified lookback window, but is not active now, i.e.
* it is recently deleted, then don't throw an exception, but do return the recently deleted
* resource for further processing (this is used by the Expiry Access Period).
*
* <p>If there is no need to specially handle the situation of recently deleted resources, then
* simply pass a lookback duration of {@link Duration#ZERO}.
*/
public static <R extends EppResource> Optional<R> verifyResourceDoesNotExist(
Class<R> clazz, String targetId, Instant now, Duration lookback, String registrarId)
throws EppException {
Instant startOfWindow = now.minus(lookback);
Optional<R> resource = ForeignKeyUtils.loadResource(clazz, targetId, startOfWindow);
if (resource.isPresent()) {
if (resource.get().getDeletionTime().isBefore(now)) {
return resource;
}
// These are similar exceptions, but we can track them internally as log-based metrics.
if (Objects.equals(registrarId, resource.get().getPersistedCurrentSponsorRegistrarId())) {
throw new ResourceAlreadyExistsForThisClientException(targetId);
@@ -107,6 +132,7 @@ public final class ResourceFlowUtils {
throw new ResourceCreateContentionException(targetId);
}
}
return Optional.empty();
}
/** Check that the given AuthInfo is present for a resource being transferred. */
@@ -134,7 +160,9 @@ public final class ResourceFlowUtils {
}
String authPassword = authInfo.getPw().getValue();
String domainPassword = domain.getAuthInfo().getPw().getValue();
if (!domainPassword.equals(authPassword)) {
if (!MessageDigest.isEqual(
authPassword.getBytes(StandardCharsets.UTF_8),
domainPassword.getBytes(StandardCharsets.UTF_8))) {
throw new BadAuthInfoForResourceException();
}
}
@@ -25,6 +25,7 @@ import static google.registry.flows.domain.DomainFlowUtils.checkHasBillingAccoun
import static google.registry.flows.domain.DomainFlowUtils.getReservationTypes;
import static google.registry.flows.domain.DomainFlowUtils.handleFeeRequest;
import static google.registry.flows.domain.DomainFlowUtils.isAnchorTenant;
import static google.registry.flows.domain.DomainFlowUtils.isDomainEligibleForXap;
import static google.registry.flows.domain.DomainFlowUtils.isRegisterBsaCreate;
import static google.registry.flows.domain.DomainFlowUtils.isReserved;
import static google.registry.flows.domain.DomainFlowUtils.isValidReservedCreate;
@@ -75,6 +76,7 @@ import google.registry.model.eppoutput.CheckData.DomainCheck;
import google.registry.model.eppoutput.CheckData.DomainCheckData;
import google.registry.model.eppoutput.EppResponse;
import google.registry.model.eppoutput.EppResponse.ResponseExtension;
import google.registry.model.registrar.Registrar;
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
import google.registry.model.tld.Tld;
import google.registry.model.tld.Tld.TldState;
@@ -82,6 +84,7 @@ import google.registry.model.tld.label.ReservationType;
import google.registry.persistence.VKey;
import google.registry.util.Clock;
import jakarta.inject.Inject;
import java.time.Duration;
import java.time.Instant;
import java.util.Collection;
import java.util.HashSet;
@@ -138,6 +141,10 @@ public final class DomainCheckFlow implements TransactionalFlow {
@Config("maxChecks")
int maxChecks;
@Inject
@Config("domainExpiryAccessPeriodTotalLength")
Duration domainExpiryAccessPeriodTotalLength;
@Inject @Superuser boolean isSuperuser;
@Inject Clock clock;
@Inject EppResponse.Builder responseBuilder;
@@ -249,7 +256,14 @@ public final class DomainCheckFlow implements TransactionalFlow {
return Optional.of(e.getMessage());
}
return getMessageForCheckWithToken(
idn, existingDomains, bsaBlockedDomainNames, tldStates, token);
idn, existingDomains, bsaBlockedDomainNames, tldStates, token, now);
}
private static Optional<Domain> loadDomainIfInXap(
String domainName, Instant now, Duration domainExpiryAccessPeriodTotalLength) {
return ForeignKeyUtils.loadResource(
Domain.class, domainName, now.minus(domainExpiryAccessPeriodTotalLength))
.filter(domain -> isDomainEligibleForXap(domain, Tld.get(domain.getTld()), now));
}
private Optional<String> getMessageForCheckWithToken(
@@ -257,10 +271,18 @@ public final class DomainCheckFlow implements TransactionalFlow {
ImmutableMap<String, VKey<Domain>> existingDomains,
ImmutableSet<InternetDomainName> bsaBlockedDomains,
ImmutableMap<String, TldState> tldStates,
Optional<AllocationToken> allocationToken) {
Optional<AllocationToken> allocationToken,
Instant now) {
if (existingDomains.containsKey(domainName.toString())) {
return Optional.of("In use");
}
Tld tld = Tld.get(domainName.parent().toString());
if (tld.getExpiryAccessPeriodModeAt(now) == Tld.ExpiryAccessPeriodMode.ENABLED
&& !Registrar.loadByRegistrarIdCached(registrarId).get().getExpiryAccessPeriodEnabled()
&& loadDomainIfInXap(domainName.toString(), now, domainExpiryAccessPeriodTotalLength)
.isPresent()) {
return Optional.of("Reserved");
}
TldState tldState = tldStates.get(domainName.parent().toString());
if (isReserved(domainName, START_DATE_SUNRISE.equals(tldState))) {
if (!isValidReservedCreate(domainName, allocationToken)
@@ -339,16 +361,27 @@ public final class DomainCheckFlow implements TransactionalFlow {
.build());
continue;
}
Optional<Domain> domainForFee = domain;
boolean isAvailable = availableDomains.contains(domainName);
if (isAvailable
&& feeCheckItem.getCommandName().equals(FeeQueryCommandExtensionItem.CommandName.CREATE)
&& tld.getExpiryAccessPeriodModeAt(now) == Tld.ExpiryAccessPeriodMode.ENABLED) {
Optional<Domain> recentlyDeletedDomain =
loadDomainIfInXap(domainName, now, domainExpiryAccessPeriodTotalLength);
if (recentlyDeletedDomain.isPresent()) {
domainForFee = recentlyDeletedDomain;
}
}
handleFeeRequest(
feeCheckItem,
builder,
domainNames.get(domainName),
domain,
domainForFee,
feeCheck.getCurrency(),
now,
pricingLogic,
token,
availableDomains.contains(domainName),
isAvailable,
recurrences.getOrDefault(domainName, null));
// In the case of a registrar that is running a tiered pricing promotion, we issue two
// responses for the CREATE fee check command: one (the default response) with the
@@ -18,6 +18,7 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.dns.DnsUtils.requestDomainDnsRefresh;
import static google.registry.flows.FlowUtils.persistEntityChanges;
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.verifyResourceDoesNotExist;
import static google.registry.flows.domain.DomainFlowUtils.COLLISION_MESSAGE;
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
import static google.registry.flows.domain.DomainFlowUtils.checkHasBillingAccount;
@@ -25,6 +26,7 @@ import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReference
import static google.registry.flows.domain.DomainFlowUtils.createFeeCreateResponse;
import static google.registry.flows.domain.DomainFlowUtils.getReservationTypes;
import static google.registry.flows.domain.DomainFlowUtils.isAnchorTenant;
import static google.registry.flows.domain.DomainFlowUtils.isDomainEligibleForXap;
import static google.registry.flows.domain.DomainFlowUtils.isReserved;
import static google.registry.flows.domain.DomainFlowUtils.isValidReservedCreate;
import static google.registry.flows.domain.DomainFlowUtils.validateCreateCommandContactsAndNameservers;
@@ -58,6 +60,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.net.InternetDomainName;
import google.registry.config.RegistryConfig;
import google.registry.config.RegistryConfig.Config;
import google.registry.flows.EppException;
import google.registry.flows.EppException.CommandUseErrorException;
import google.registry.flows.EppException.ParameterValuePolicyErrorException;
@@ -71,6 +74,7 @@ import google.registry.flows.custom.DomainCreateFlowCustomLogic;
import google.registry.flows.custom.DomainCreateFlowCustomLogic.BeforeResponseParameters;
import google.registry.flows.custom.DomainCreateFlowCustomLogic.BeforeResponseReturnData;
import google.registry.flows.custom.EntityChanges;
import google.registry.flows.domain.DomainFlowUtils.DomainReservedException;
import google.registry.flows.domain.DomainFlowUtils.RegistrantProhibitedException;
import google.registry.flows.domain.token.AllocationTokenFlowUtils;
import google.registry.flows.exceptions.ContactsProhibitedException;
@@ -107,6 +111,7 @@ import google.registry.model.eppoutput.EppResponse;
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
import google.registry.model.poll.PollMessage;
import google.registry.model.poll.PollMessage.Autorenew;
import google.registry.model.registrar.Registrar;
import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
import google.registry.model.reporting.HistoryEntry;
@@ -123,6 +128,7 @@ import jakarta.inject.Inject;
import java.time.Duration;
import java.time.Instant;
import java.util.Optional;
import org.joda.money.Money;
/**
* An EPP flow that creates a new domain resource.
@@ -219,6 +225,10 @@ public final class DomainCreateFlow implements MutatingFlow {
@Inject DomainPricingLogic pricingLogic;
@Inject DomainDeletionTimeCache domainDeletionTimeCache;
@Inject
@Config("domainExpiryAccessPeriodTotalLength")
Duration domainExpiryAccessPeriodTotalLength;
@Inject
DomainCreateFlow() {}
@@ -245,6 +255,13 @@ public final class DomainCreateFlow implements MutatingFlow {
InternetDomainName domainName = validateDomainName(command.getDomainName());
String domainLabel = domainName.parts().getFirst();
Tld tld = Tld.get(domainName.parent().toString());
Duration lookback =
tld.getExpiryAccessPeriodModeAt(now) == Tld.ExpiryAccessPeriodMode.ENABLED
? domainExpiryAccessPeriodTotalLength
: Duration.ZERO;
Optional<Domain> recentlyDeletedDomain =
verifyResourceDoesNotExist(Domain.class, targetId, now, lookback, registrarId)
.filter(domain -> isDomainEligibleForXap(domain, tld, now));
validateCreateCommandContactsAndNameservers(command, tld, domainName);
TldState tldState = tld.getTldState(now);
Optional<LaunchCreateExtension> launchCreate =
@@ -280,6 +297,10 @@ public final class DomainCreateFlow implements MutatingFlow {
if (!isSuperuser) {
checkAllowedAccessToTld(registrarId, tld.getTldStr());
checkHasBillingAccount(registrarId, tld.getTldStr());
if (recentlyDeletedDomain.isPresent()
&& !Registrar.loadByRegistrarIdCached(registrarId).get().getExpiryAccessPeriodEnabled()) {
throw new DomainReservedException(domainName.toString());
}
boolean isValidReservedCreate = isValidReservedCreate(domainName, allocationToken);
ClaimsList claimsList = ClaimsListDao.get();
verifyIsGaOrSpecialCase(
@@ -327,7 +348,14 @@ public final class DomainCreateFlow implements MutatingFlow {
eppInput.getSingleExtension(FeeCreateCommandExtension.class);
FeesAndCredits feesAndCredits =
pricingLogic.getCreatePrice(
tld, targetId, now, years, isAnchorTenant, isSunriseCreate, allocationToken);
tld,
targetId,
now,
recentlyDeletedDomain,
years,
isAnchorTenant,
isSunriseCreate,
allocationToken);
validateFeeChallenge(feeCreate, feesAndCredits, defaultTokenUsed);
Optional<SecDnsCreateExtension> secDnsCreate =
validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class));
@@ -358,7 +386,15 @@ public final class DomainCreateFlow implements MutatingFlow {
entitiesToInsert.add(createBillingEvent, autorenewBillingEvent, autorenewPollMessage);
// Bill for EAP cost, if any.
if (!feesAndCredits.getEapCost().isZero()) {
entitiesToInsert.add(createEapBillingEvent(feesAndCredits, createBillingEvent));
entitiesToInsert.add(
createAdditionalBillingEvent(
Reason.FEE_EARLY_ACCESS, feesAndCredits.getEapCost(), createBillingEvent));
}
// Bill for XAP cost, if any.
if (!feesAndCredits.getXapCost().isZero()) {
entitiesToInsert.add(
createAdditionalBillingEvent(
Reason.FEE_EXPIRY_ACCESS, feesAndCredits.getXapCost(), createBillingEvent));
}
ImmutableSet<ReservationType> reservationTypes = getReservationTypes(domainName);
@@ -434,7 +470,14 @@ public final class DomainCreateFlow implements MutatingFlow {
FeesAndCredits responseFeesAndCredits =
shouldShowDefaultPrice
? pricingLogic.getCreatePrice(
tld, targetId, now, years, isAnchorTenant, isSunriseCreate, Optional.empty())
tld,
targetId,
now,
recentlyDeletedDomain,
years,
isAnchorTenant,
isSunriseCreate,
Optional.empty())
: feesAndCredits;
BeforeResponseReturnData responseData =
@@ -656,14 +699,14 @@ public final class DomainCreateFlow implements MutatingFlow {
}
}
private static BillingEvent createEapBillingEvent(
FeesAndCredits feesAndCredits, BillingEvent createBillingEvent) {
private static BillingEvent createAdditionalBillingEvent(
Reason reason, Money cost, BillingEvent createBillingEvent) {
return new BillingEvent.Builder()
.setReason(Reason.FEE_EARLY_ACCESS)
.setReason(reason)
.setTargetId(createBillingEvent.getTargetId())
.setRegistrarId(createBillingEvent.getRegistrarId())
.setPeriodYears(1)
.setCost(feesAndCredits.getEapCost())
.setCost(cost)
.setEventTime(createBillingEvent.getEventTime())
.setBillingTime(createBillingEvent.getBillingTime())
.setFlags(createBillingEvent.getFlags())
@@ -14,6 +14,7 @@
package google.registry.flows.domain;
import static com.google.common.base.Ascii.toLowerCase;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Strings.emptyToNull;
@@ -23,6 +24,7 @@ import static com.google.common.collect.Sets.difference;
import static com.google.common.collect.Sets.intersection;
import static com.google.common.collect.Sets.union;
import static google.registry.bsa.persistence.BsaLabelUtils.isLabelBlocked;
import static google.registry.model.common.FeatureFlag.FeatureName.FORBID_INSECURE_ALGORITHMS_RFC_9904;
import static google.registry.model.domain.Domain.MAX_REGISTRATION_YEARS;
import static google.registry.model.domain.token.AllocationToken.TokenType.REGISTER_BSA;
import static google.registry.model.tld.Tld.TldState.GENERAL_AVAILABILITY;
@@ -73,6 +75,7 @@ import google.registry.model.EppResource;
import google.registry.model.billing.BillingBase.Flag;
import google.registry.model.billing.BillingBase.Reason;
import google.registry.model.billing.BillingRecurrence;
import google.registry.model.common.FeatureFlag;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainCommand.Create;
import google.registry.model.domain.DomainCommand.CreateOrUpdate;
@@ -173,6 +176,27 @@ public class DomainFlowUtils {
/** Maximum number of characters in a domain label, from RFC 2181. */
private static final int MAX_LABEL_SIZE = 63;
/// Given a tld name or domain label, returns a label that can be safely logged, or
/// `Optional.empty()` if `label` is null or blank.
///
/// If `label` contains disallowed characters, they are replaced with '-'. If the resulting
/// string has a valid length, it is returned as is; if not, the first {@link #MAX_LABEL_SIZE}
/// chars plus a suffix of `...` is returned.
///
/// This method is mainly for use by `FlowReporter#recordToLogs()` to ensure that the logs
/// can be safely and correctly queried by SQL queries. See b/534931586 for more information.
public static Optional<String> toLogSafeLabel(String label) {
if (label == null || label.isBlank()) {
return Optional.empty();
}
var newLabel = ALLOWED_CHARS.negate().replaceFrom(toLowerCase(label), '-');
if (newLabel.length() <= MAX_LABEL_SIZE) {
return Optional.of(newLabel);
} else {
return Optional.of(newLabel.substring(0, MAX_LABEL_SIZE) + "...");
}
}
/**
* Returns parsed version of {@code name} if domain name label follows our naming rules and is
* under one of the given allowed TLDs.
@@ -341,7 +365,7 @@ public class DomainFlowUtils {
}
ImmutableList<DomainDsData> invalidAlgorithms =
dsData.stream()
.filter(ds -> !validateAlgorithm(ds.getAlgorithm()))
.filter(ds -> algorithmIsInvalid(ds.getAlgorithm()))
.collect(toImmutableList());
if (!invalidAlgorithms.isEmpty()) {
throw new InvalidDsRecordException(
@@ -349,9 +373,16 @@ public class DomainFlowUtils {
"Domain contains DS record(s) with an invalid algorithm wire value: %s",
invalidAlgorithms));
}
boolean forbidInsecureTypes = FeatureFlag.isActiveNow(FORBID_INSECURE_ALGORITHMS_RFC_9904);
ImmutableList<DomainDsData> invalidDigestTypes =
dsData.stream()
.filter(ds -> DigestType.fromWireValue(ds.getDigestType()).isEmpty())
.filter(
ds -> {
Optional<DigestType> digestType = DigestType.fromWireValue(ds.getDigestType());
return digestType
.map(type -> forbidInsecureTypes && !type.isAllowedInRfc9904())
.orElse(true);
})
.collect(toImmutableList());
if (!invalidDigestTypes.isEmpty()) {
throw new InvalidDsRecordException(
@@ -376,14 +407,18 @@ public class DomainFlowUtils {
}
}
public static boolean validateAlgorithm(int alg) {
public static boolean algorithmIsInvalid(int alg) {
if (alg > 255 || alg < 0) {
return false;
return true;
}
if (DomainDsData.FORBIDDEN_ALGORITHMS.contains(alg)
&& FeatureFlag.isActiveNow(FORBID_INSECURE_ALGORITHMS_RFC_9904)) {
return true;
}
// Algorithms that are reserved or unassigned will just return a string representation of their
// integer wire value.
String algorithm = Algorithm.string(alg);
return !algorithm.equals(Integer.toString(alg));
return algorithm.equals(Integer.toString(alg));
}
/** We only allow specifying years in a period. */
@@ -629,6 +664,7 @@ public class DomainFlowUtils {
tld,
domainNameString,
now,
domain,
years,
isAnchorTenant(domainName, allocationToken, Optional.empty()),
isSunrise,
@@ -752,6 +788,9 @@ public class DomainFlowUtils {
if (!feesAndCredits.getEapCost().isZero()) {
throw new FeesRequiredDuringEarlyAccessProgramException(feesAndCredits.getEapCost());
}
if (!feesAndCredits.getXapCost().isZero()) {
throw new FeesRequiredDuringExpiryAccessPeriodException(feesAndCredits.getXapCost());
}
if (feesAndCredits.getTotalCost().isZero() || !feesAndCredits.isFeeExtensionRequired()) {
return;
}
@@ -1155,6 +1194,32 @@ public class DomainFlowUtils {
.getResultList();
}
/**
* Returns true if the domain was deleted during its Add Grace Period (AGP).
*
* <p>Per policy, domains deleted during AGP are not subject to Expiry Access Period (XAP) fees or
* reservation restrictions upon subsequent re-registration.
*/
public static boolean wasDeletedDuringAddGracePeriod(Domain domain, Tld tld) {
if (domain.getCreationTime() == null || domain.getDeletionTime() == null) {
return false;
}
return !domain
.getDeletionTime()
.isAfter(domain.getCreationTime().plus(tld.getAddGracePeriodLength()));
}
/**
* Returns true if the domain was deleted before {@code now} and is eligible for Expiry Access
* Period (XAP) evaluation.
*/
public static boolean isDomainEligibleForXap(Domain domain, Tld tld, Instant now) {
if (domain.getDeletionTime() == null || !domain.getDeletionTime().isBefore(now)) {
return false;
}
return !wasDeletedDuringAddGracePeriod(domain, tld);
}
/** Resource linked to this domain does not exist. */
static class LinkedResourcesDoNotExistException extends ObjectDoesNotExistException {
public LinkedResourcesDoNotExistException(Class<?> type, ImmutableSet<String> resourceIds) {
@@ -1342,6 +1407,18 @@ public class DomainFlowUtils {
}
}
/** Fees must be explicitly acknowledged when creating domains during the Expiry Access Period. */
static class FeesRequiredDuringExpiryAccessPeriodException
extends RequiredParameterMissingException {
public FeesRequiredDuringExpiryAccessPeriodException(Money expectedFee) {
super(
"Fees must be explicitly acknowledged when creating domains "
+ "during the Expiry Access Period. The XAP fee is: "
+ expectedFee);
}
}
/** The 'grace-period', 'applied' and 'refundable' fields are disallowed by server policy. */
static class UnsupportedFeeAttributeException extends UnimplementedOptionException {
UnsupportedFeeAttributeException() {
@@ -15,13 +15,18 @@
package google.registry.flows.domain;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.flows.domain.DomainFlowUtils.isDomainEligibleForXap;
import static google.registry.flows.domain.DomainFlowUtils.zeroInCurrency;
import static google.registry.flows.domain.token.AllocationTokenFlowUtils.discountTokenInvalidForPremiumName;
import static google.registry.pricing.PricingEngineProxy.getPricesForDomainName;
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Range;
import com.google.common.flogger.FluentLogger;
import com.google.common.net.InternetDomainName;
import google.registry.config.RegistryConfig;
import google.registry.config.RegistryConfig.Config;
import google.registry.flows.EppException;
import google.registry.flows.custom.DomainPricingCustomLogic;
import google.registry.flows.custom.DomainPricingCustomLogic.CreatePriceParameters;
@@ -31,6 +36,7 @@ import google.registry.flows.custom.DomainPricingCustomLogic.TransferPriceParame
import google.registry.flows.custom.DomainPricingCustomLogic.UpdatePriceParameters;
import google.registry.model.billing.BillingBase.RenewalPriceBehavior;
import google.registry.model.billing.BillingRecurrence;
import google.registry.model.domain.Domain;
import google.registry.model.domain.fee.BaseFee;
import google.registry.model.domain.fee.BaseFee.FeeType;
import google.registry.model.domain.fee.Fee;
@@ -40,7 +46,9 @@ import google.registry.model.domain.token.AllocationToken.TokenBehavior;
import google.registry.model.pricing.PremiumPricingEngine.DomainPrices;
import google.registry.model.tld.Tld;
import jakarta.inject.Inject;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.Instant;
import java.util.Optional;
import javax.annotation.Nullable;
@@ -54,11 +62,28 @@ import org.joda.money.Money;
*/
public final class DomainPricingLogic {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final DomainPricingCustomLogic customLogic;
private final Duration expiryAccessPeriodTotalLength;
private final Duration expiryAccessPeriodTierLength;
private final ImmutableMap<CurrencyUnit, BigDecimal> expiryAccessPeriodInitialFee;
private final ImmutableMap<CurrencyUnit, BigDecimal> expiryAccessPeriodFinalFee;
@Inject
public DomainPricingLogic(DomainPricingCustomLogic customLogic) {
public DomainPricingLogic(
DomainPricingCustomLogic customLogic,
@Config("domainExpiryAccessPeriodTotalLength") Duration expiryAccessPeriodTotalLength,
@Config("domainExpiryAccessPeriodTierLength") Duration expiryAccessPeriodTierLength,
@Config("domainExpiryAccessPeriodInitialFee")
ImmutableMap<CurrencyUnit, BigDecimal> expiryAccessPeriodInitialFee,
@Config("domainExpiryAccessPeriodFinalFee")
ImmutableMap<CurrencyUnit, BigDecimal> expiryAccessPeriodFinalFee) {
this.customLogic = customLogic;
this.expiryAccessPeriodTotalLength = expiryAccessPeriodTotalLength;
this.expiryAccessPeriodTierLength = expiryAccessPeriodTierLength;
this.expiryAccessPeriodInitialFee = expiryAccessPeriodInitialFee;
this.expiryAccessPeriodFinalFee = expiryAccessPeriodFinalFee;
}
/**
@@ -71,35 +96,23 @@ public final class DomainPricingLogic {
Tld tld,
String domainName,
Instant dateTime,
Optional<Domain> recentlyDeletedDomain,
int years,
boolean isAnchorTenant,
boolean isSunriseCreate,
Optional<AllocationToken> allocationToken)
throws EppException {
CurrencyUnit currency = tld.getCurrency();
BaseFee createFee;
// Domain create cost is always zero for anchor tenants
if (isAnchorTenant) {
createFee = Fee.create(zeroInCurrency(currency), FeeType.CREATE, false);
} else {
DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime);
if (allocationToken.isPresent()) {
// Handle any special NONPREMIUM / SPECIFIED cases configured in the token
domainPrices =
applyTokenToDomainPrices(domainPrices, tld, dateTime, years, allocationToken.get());
}
Money domainCreateCost =
getDomainCreateCostWithDiscount(domainPrices, years, allocationToken, tld);
// Apply a sunrise discount if configured and applicable
if (isSunriseCreate) {
domainCreateCost =
domainCreateCost.multipliedBy(
1.0d - RegistryConfig.getSunriseDomainCreateDiscount(), RoundingMode.HALF_EVEN);
}
createFee =
Fee.create(domainCreateCost.getAmount(), FeeType.CREATE, domainPrices.isPremium());
}
Fee createFee =
computeCreateFee(
tld,
domainName,
dateTime,
years,
isAnchorTenant,
isSunriseCreate,
allocationToken,
currency);
// Create fees for the cost and the EAP fee, if any.
Fee eapFee = tld.getEapFeeFor(dateTime);
@@ -109,6 +122,7 @@ public final class DomainPricingLogic {
if (!isAnchorTenant && !eapFee.hasZeroCost()) {
feesBuilder.addFeeOrCredit(eapFee);
}
maybeAddXapFee(tld, dateTime, recentlyDeletedDomain, currency, feesBuilder);
// Apply custom logic to the create fee, if any.
return customLogic.customizeCreatePrice(
@@ -121,6 +135,113 @@ public final class DomainPricingLogic {
.build());
}
private Fee computeCreateFee(
Tld tld,
String domainName,
Instant dateTime,
int years,
boolean isAnchorTenant,
boolean isSunriseCreate,
Optional<AllocationToken> allocationToken,
CurrencyUnit currency)
throws EppException {
// Domain create cost is always zero for anchor tenants
if (isAnchorTenant) {
return Fee.create(zeroInCurrency(currency), FeeType.CREATE, false);
}
DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime);
if (allocationToken.isPresent()) {
// Handle any special NONPREMIUM / SPECIFIED cases configured in the token
domainPrices =
applyTokenToDomainPrices(domainPrices, tld, dateTime, years, allocationToken.get());
}
Money domainCreateCost =
getDomainCreateCostWithDiscount(domainPrices, years, allocationToken, tld);
// Apply a sunrise discount if configured and applicable
if (isSunriseCreate) {
domainCreateCost =
domainCreateCost.multipliedBy(
1.0d - RegistryConfig.getSunriseDomainCreateDiscount(), RoundingMode.HALF_EVEN);
}
return Fee.create(domainCreateCost.getAmount(), FeeType.CREATE, domainPrices.isPremium());
}
private void maybeAddXapFee(
Tld tld,
Instant dateTime,
Optional<Domain> recentlyDeletedDomain,
CurrencyUnit currency,
FeesAndCredits.Builder feesBuilder) {
if (tld.getExpiryAccessPeriodModeAt(dateTime) == Tld.ExpiryAccessPeriodMode.ENABLED
&& recentlyDeletedDomain.isPresent()
&& isDomainEligibleForXap(recentlyDeletedDomain.get(), tld, dateTime)) {
Optional<Fee> xapFee =
getXapFeeFor(dateTime, recentlyDeletedDomain.get().getDeletionTime(), currency);
xapFee.ifPresent(
fee -> {
feesBuilder.addFeeOrCredit(fee);
if (!fee.hasZeroCost()) {
feesBuilder.setFeeExtensionRequired(true);
}
});
}
}
/**
* Calculates and returns the Expiry Access Fee for a recently deleted domain at the given time.
*/
Optional<Fee> getXapFeeFor(Instant dateTime, Instant deletionTime, CurrencyUnit currency) {
Duration elapsedTimeInXap = Duration.between(deletionTime, dateTime);
if (!expiryAccessPeriodInitialFee.containsKey(currency)
|| !expiryAccessPeriodFinalFee.containsKey(currency)) {
// If the XAP schedule hasn't been configured in YAML for the currency this TLD uses, log the
// error and then short-circuit return (to allow the EPP flow to continue normally).
logger.atSevere().log(
"Expiry Access Period configuration is lacking initial or final fees for currency %s.",
currency);
return Optional.empty();
}
// Determine which tier the current time falls into (0-indexed).
long tier = elapsedTimeInXap.toMillis() / expiryAccessPeriodTierLength.toMillis();
long numTiers =
expiryAccessPeriodTotalLength.toMillis() / expiryAccessPeriodTierLength.toMillis();
if (tier < 0 || tier >= numTiers) {
return Optional.empty();
}
// Calculate the parameters for the geometric sequence: XAP fee = initialFee * ratio ^ tier.
BigDecimal xapFee;
if (expiryAccessPeriodInitialFee.get(currency).signum() == 0 || numTiers <= 1 || tier == 0) {
xapFee =
expiryAccessPeriodInitialFee
.get(currency)
.setScale(currency.getDecimalPlaces(), RoundingMode.HALF_EVEN);
} else {
double base =
expiryAccessPeriodFinalFee.get(currency).doubleValue()
/ expiryAccessPeriodInitialFee.get(currency).doubleValue();
double exponent = 1.0 / (numTiers - 1.0);
BigDecimal ratio = BigDecimal.valueOf(Math.pow(base, exponent));
BigDecimal fee = expiryAccessPeriodInitialFee.get(currency).multiply(ratio.pow((int) tier));
xapFee = fee.setScale(currency.getDecimalPlaces(), RoundingMode.HALF_EVEN);
}
Range<Instant> validPeriod =
Range.closedOpen(
deletionTime.plus(expiryAccessPeriodTierLength.multipliedBy(tier)),
deletionTime.plus(expiryAccessPeriodTierLength.multipliedBy(tier + 1)));
return Optional.of(
Fee.create(
xapFee,
FeeType.XAP,
// An XAP fee does not count as premium -- it's a separate one-time fee, independent of
// which the domain is separately considered standard vs premium.
false,
validPeriod,
validPeriod.upperEndpoint()));
}
/** Returns a new renewal cost for the pricer. */
public FeesAndCredits getRenewPrice(
Tld tld,
@@ -283,7 +404,7 @@ public final class DomainPricingLogic {
return tld.getStandardRenewCost(dateTime).multipliedBy(years);
}
if (token.getRenewalPriceBehavior().equals(RenewalPriceBehavior.SPECIFIED)) {
return token.getRenewalPrice().get();
return token.getRenewalPrice().get().multipliedBy(years);
}
}
return getDomainCostWithDiscount(
@@ -328,12 +449,13 @@ public final class DomainPricingLogic {
// Apply the allocation token discount, if applicable.
if (token.getDiscountPrice().isPresent()
&& tld.getCurrency().equals(token.getDiscountPrice().get().getCurrencyUnit())) {
int nonDiscountedYears = Math.max(0, years - token.getDiscountYears());
int discountedYears = Math.min(years, token.getDiscountYears());
int nonDiscountedYears = years - discountedYears;
totalDomainFlowCost =
token
.getDiscountPrice()
.get()
.multipliedBy(token.getDiscountYears())
.multipliedBy(discountedYears)
.plus(subsequentYearCost.orElse(firstYearCost).multipliedBy(nonDiscountedYears));
} else if (token.getDiscountFraction() > 0) {
int discountedYears = Math.min(years, token.getDiscountYears());
@@ -77,6 +77,11 @@ public class FeesAndCredits extends ImmutableObject implements Buildable {
return getTotalCostForType(FeeType.EAP);
}
/** Returns the XAP cost for the event. */
public Money getXapCost() {
return getTotalCostForType(FeeType.XAP);
}
/** Returns the renew cost for the event. */
public Money getRenewCost() {
return getTotalCostForType(FeeType.RENEW);
@@ -242,7 +242,14 @@ public class AllocationTokenFlowUtils {
case CREATE ->
pricingLogic
.getCreatePrice(
tld, domainName, now, yearsForAction, false, false, Optional.of(token))
tld,
domainName,
now,
Optional.empty(),
yearsForAction,
false,
false,
Optional.of(token))
.getTotalCost()
.getAmount();
case RENEW ->
@@ -270,11 +277,14 @@ public class AllocationTokenFlowUtils {
return maybeTokenEntity.get();
}
maybeTokenEntity = AllocationToken.get(VKey.create(AllocationToken.class, token));
if (maybeTokenEntity.isEmpty()) {
throw new NonexistentAllocationTokenException();
VKey<AllocationToken> tokenKey = VKey.create(AllocationToken.class, token);
AllocationToken tokenEntity =
AllocationToken.get(tokenKey).orElseThrow(NonexistentAllocationTokenException::new);
if (tokenEntity.getTokenType().equals(AllocationToken.TokenType.SINGLE_USE)) {
// Reload the token to avoid possible cache race conditions where the token may have already
// been redeemed
tokenEntity = tm().loadByKey(tokenKey);
}
AllocationToken tokenEntity = maybeTokenEntity.get();
validateTokenEntity(tokenEntity, registrarId, domainName, now);
return tokenEntity;
}
@@ -116,15 +116,21 @@ public class HostFlowUtils {
if (inetAddresses == null) {
return;
}
if (inetAddresses.stream().anyMatch(InetAddress::isLoopbackAddress)) {
throw new LoopbackIpNotValidForHostException();
for (InetAddress inetAddress : inetAddresses) {
if (inetAddress.isLoopbackAddress()
|| inetAddress.isLinkLocalAddress()
|| inetAddress.isSiteLocalAddress()
|| inetAddress.isAnyLocalAddress()
|| inetAddress.isMulticastAddress()) {
throw new IpAddressNotRoutableException(inetAddress.getHostAddress());
}
}
}
/** Loopback IPs are not valid for hosts. */
static class LoopbackIpNotValidForHostException extends ParameterValuePolicyErrorException {
public LoopbackIpNotValidForHostException() {
super("Loopback IPs are not valid for hosts");
/** IP address is not a public, routable address. */
static class IpAddressNotRoutableException extends ParameterValuePolicyErrorException {
public IpAddressNotRoutableException(String ipAddress) {
super(String.format("IP address %s is not a public, globally routable address", ipAddress));
}
}
@@ -14,6 +14,7 @@
package google.registry.flows.picker;
import com.google.common.base.Ascii;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableTable;
@@ -255,6 +256,17 @@ public class FlowPicker {
if (innerCommand == null && !(eppInput.getCommandWrapper() instanceof Hello)) {
throw new MissingCommandException();
}
if (innerCommand instanceof ResourceCommandWrapper resourceCommandWrapper) {
ResourceCommand resourceCommand = resourceCommandWrapper.getResourceCommand();
if (resourceCommand != null) {
String wrapperName = innerCommand.getClass().getSimpleName();
String commandName = resourceCommand.getClass().getSimpleName();
if (!wrapperName.equals(commandName)) {
throw new MismatchedCommandException(
Ascii.toLowerCase(wrapperName), Ascii.toLowerCase(commandName));
}
}
}
// Try the FlowProviders until we find a match. The order matters because it's possible to
// match multiple FlowProviders and so more specific matches are tried first.
for (FlowProvider flowProvider : FLOW_PROVIDERS) {
@@ -279,4 +291,14 @@ public class FlowPicker {
super("Command missing");
}
}
/** Command wrapper and inner resource command do not match. */
static class MismatchedCommandException extends SyntaxErrorException {
public MismatchedCommandException(String wrapperName, String commandName) {
super(
String.format(
"EPP command wrapper <%s> does not match resource command <%s>",
wrapperName, commandName));
}
}
}
@@ -14,12 +14,15 @@
package google.registry.gcs;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.Iterables.getLast;
import com.google.cloud.storage.Blob;
import com.google.cloud.storage.BlobId;
import com.google.cloud.storage.BlobInfo;
import com.google.cloud.storage.Bucket;
import com.google.cloud.storage.BucketInfo;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.Storage.BlobListOption;
import com.google.cloud.storage.StorageException;
@@ -33,12 +36,14 @@ import com.google.common.flogger.FluentLogger;
import com.google.common.net.MediaType;
import google.registry.config.CredentialModule.ApplicationDefaultCredential;
import google.registry.util.GoogleCredentialsBundle;
import google.registry.util.RegistryEnvironment;
import jakarta.inject.Inject;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.nio.channels.Channels;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.CheckReturnValue;
/**
@@ -50,6 +55,8 @@ public class GcsUtils implements Serializable {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final ConcurrentHashMap<String, Boolean> PAP_CACHE = new ConcurrentHashMap<>();
private static final ImmutableMap<String, MediaType> EXTENSIONS =
new ImmutableMap.Builder<String, MediaType>()
.put("ghostryde", MediaType.APPLICATION_BINARY)
@@ -85,6 +92,7 @@ public class GcsUtils implements Serializable {
/** Opens a GCS file for writing as an {@link OutputStream}, overwriting existing files. */
@CheckReturnValue
public OutputStream openOutputStream(BlobId blobId) {
verifyPublicAccessPrevention(blobId.getBucket());
return Channels.newOutputStream(storage().writer(createBlobInfo(blobId)));
}
@@ -94,6 +102,7 @@ public class GcsUtils implements Serializable {
*/
@CheckReturnValue
public OutputStream openOutputStream(BlobId blobId, ImmutableMap<String, String> metadata) {
verifyPublicAccessPrevention(blobId.getBucket());
return Channels.newOutputStream(
storage().writer(BlobInfo.newBuilder(blobId).setMetadata(metadata).build()));
}
@@ -105,6 +114,7 @@ public class GcsUtils implements Serializable {
/** Creates a GCS file with the given byte contents and metadata, overwriting existing files. */
public void createFromBytes(BlobInfo blobInfo, byte[] bytes) throws StorageException {
verifyPublicAccessPrevention(blobInfo.getBucket());
storage().create(blobInfo, bytes);
}
@@ -120,6 +130,7 @@ public class GcsUtils implements Serializable {
/** Update file content type on existing GCS file */
public void updateContentType(BlobId blobId, String contentType) throws StorageException {
verifyPublicAccessPrevention(blobId.getBucket());
if (existsAndNotEmpty(blobId)) {
Blob blob = storage().get(blobId);
blob.toBuilder().setContentType(contentType).build().update();
@@ -154,12 +165,6 @@ public class GcsUtils implements Serializable {
}
}
/** Returns the user defined metadata of a GCS file if the file exists, or an empty map. */
public ImmutableMap<String, String> getMetadata(BlobId blobId) throws StorageException {
Blob blob = storage().get(blobId);
return blob == null ? ImmutableMap.of() : ImmutableMap.copyOf(blob.getMetadata());
}
/**
* Returns the {@link BlobInfo} of the given GCS file.
*
@@ -179,6 +184,37 @@ public class GcsUtils implements Serializable {
return builder.build();
}
/**
* Asserts that Public Access Prevention (PAP) is enforced on the GCS bucket.
*
* @throws IllegalStateException if PAP is not ENFORCED.
*/
@VisibleForTesting
void verifyPublicAccessPrevention(String bucketName) {
if (RegistryEnvironment.get() != RegistryEnvironment.PRODUCTION) {
return;
}
PAP_CACHE.computeIfAbsent(
bucketName,
name -> {
Bucket bucket = storage().get(name);
checkState(bucket != null, "Bucket %s does not exist", name);
BucketInfo.PublicAccessPrevention pap =
bucket.getIamConfiguration().getPublicAccessPrevention();
checkState(
pap == BucketInfo.PublicAccessPrevention.ENFORCED,
"Public Access Prevention is not enforced on bucket %s. Current state: %s",
name,
pap);
return true;
});
}
@VisibleForTesting
static void clearPapCache() {
PAP_CACHE.clear();
}
// These two methods are needed to check whether serialization is done correctly in tests.
@Override
public boolean equals(Object obj) {
@@ -21,6 +21,7 @@ import static google.registry.util.DateTimeUtils.parseInstant;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.KeyDeserializer;
@@ -38,6 +39,7 @@ import com.google.common.collect.ImmutableSortedSet;
import google.registry.model.common.FeatureFlag.FeatureStatus;
import google.registry.model.common.TimedTransitionProperty;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.tld.Tld.ExpiryAccessPeriodMode;
import google.registry.model.tld.Tld.TldState;
import google.registry.persistence.VKey;
import java.io.IOException;
@@ -336,7 +338,8 @@ public class EntityYamlUtils {
@Override
public TimedTransitionProperty<TldState> deserialize(
JsonParser jp, DeserializationContext context) throws IOException {
SortedMap<String, String> valueMap = jp.readValueAs(SortedMap.class);
SortedMap<String, String> valueMap =
jp.readValueAs(new TypeReference<SortedMap<String, String>>() {});
return TimedTransitionProperty.fromValueMap(
valueMap.keySet().stream()
.collect(
@@ -347,6 +350,37 @@ public class EntityYamlUtils {
}
}
/**
* A custom JSON deserializer for a {@link TimedTransitionProperty} of {@link
* ExpiryAccessPeriodMode}.
*/
public static class TimedTransitionPropertyExpiryAccessPeriodModeDeserializer
extends StdDeserializer<TimedTransitionProperty<ExpiryAccessPeriodMode>> {
public TimedTransitionPropertyExpiryAccessPeriodModeDeserializer() {
this(null);
}
public TimedTransitionPropertyExpiryAccessPeriodModeDeserializer(
Class<TimedTransitionProperty<ExpiryAccessPeriodMode>> t) {
super(t);
}
@Override
public TimedTransitionProperty<ExpiryAccessPeriodMode> deserialize(
JsonParser jp, DeserializationContext context) throws IOException {
SortedMap<String, String> valueMap =
jp.readValueAs(new TypeReference<SortedMap<String, String>>() {});
return TimedTransitionProperty.fromValueMap(
valueMap.keySet().stream()
.collect(
toImmutableSortedMap(
natural(),
key -> parseInstant(key),
key -> ExpiryAccessPeriodMode.valueOf(valueMap.get(key)))));
}
}
/** A custom JSON deserializer for a {@link TimedTransitionProperty} of {@link Money}. */
public static class TimedTransitionPropertyMoneyDeserializer
extends StdDeserializer<TimedTransitionProperty<Money>> {
@@ -362,7 +396,8 @@ public class EntityYamlUtils {
@Override
public TimedTransitionProperty<Money> deserialize(JsonParser jp, DeserializationContext context)
throws IOException {
SortedMap<String, LinkedHashMap<String, Object>> valueMap = jp.readValueAs(SortedMap.class);
SortedMap<String, LinkedHashMap<String, Object>> valueMap =
jp.readValueAs(new TypeReference<SortedMap<String, LinkedHashMap<String, Object>>>() {});
return TimedTransitionProperty.fromValueMap(
valueMap.keySet().stream()
.collect(
@@ -392,7 +427,8 @@ public class EntityYamlUtils {
@Override
public TimedTransitionProperty<FeatureStatus> deserialize(
JsonParser jp, DeserializationContext context) throws IOException {
SortedMap<String, String> valueMap = jp.readValueAs(SortedMap.class);
SortedMap<String, String> valueMap =
jp.readValueAs(new TypeReference<SortedMap<String, String>>() {});
return TimedTransitionProperty.fromValueMap(
valueMap.keySet().stream()
.collect(
@@ -16,6 +16,7 @@ package google.registry.model;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.config.RegistryConfig.getEppResourceCachingDuration;
import static google.registry.config.RegistryConfig.getEppResourceMaxCachedEntries;
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
@@ -400,4 +401,24 @@ public final class ForeignKeyUtils {
.filter(e -> now.isBefore(e.getDeletionTime()))
.map(e -> e.cloneProjectedAtTime(now));
}
/**
* Loads the last created version of multiple {@link EppResource}s from the replica database by
* foreign keys, using a cache.
*
* <p>This method ignores the config setting for caching, and is reserved for use cases that can
* tolerate slightly stale data.
*/
@SuppressWarnings("unchecked")
public static <E extends EppResource> ImmutableMap<String, E> loadResourcesByCache(
Class<E> clazz, Collection<String> foreignKeys, Instant now) {
ImmutableSet<VKey<? extends EppResource>> vkeys =
foreignKeys.stream().map(fk -> VKey.create(clazz, fk)).collect(toImmutableSet());
return foreignKeyToResourceCache.getAll(vkeys).entrySet().stream()
.filter(e -> e.getValue().isPresent() && now.isBefore(e.getValue().get().getDeletionTime()))
.collect(
toImmutableMap(
e -> (String) e.getKey().getKey(),
e -> (E) e.getValue().get().cloneProjectedAtTime(now)));
}
}
@@ -176,11 +176,12 @@ public class OteStats {
* Check if the {@link HistoryEntry} type matches as well as the {@link EppInput} if supplied.
*/
private boolean matches(HistoryEntry.Type historyType, Optional<EppInput> eppInput) {
if (eppInputFilter.isPresent() && eppInput.isPresent()) {
return typeFilter.test(historyType) && eppInputFilter.get().test(eppInput.get());
} else {
return typeFilter.test(historyType);
if (!typeFilter.test(historyType)) {
return false;
}
return eppInputFilter
.map(filter -> eppInput.isPresent() && filter.test(eppInput.get()))
.orElse(true);
}
}
@@ -48,6 +48,7 @@ public abstract class BillingBase extends ImmutableObject
@Deprecated // DO NOT USE THIS REASON. IT REMAINS BECAUSE OF HISTORICAL DATA. SEE b/31676071.
ERROR(false),
FEE_EARLY_ACCESS(true),
FEE_EXPIRY_ACCESS(true),
RENEW(true),
RESTORE(true),
SERVER_STATUS(false),
@@ -200,7 +200,7 @@ public class BillingEvent extends BillingBase {
checkState(
instance.reason.hasPeriodYears() == (instance.periodYears != null),
"Period years must be set if and only if reason is "
+ "CREATE, FEE_EARLY_ACCESS, RENEW, RESTORE or TRANSFER.");
+ "CREATE, FEE_EARLY_ACCESS, FEE_EXPIRY_ACCESS, RENEW, RESTORE or TRANSFER.");
}
checkState(
instance.getFlags().contains(Flag.SYNTHETIC) == (instance.syntheticCreationTime != null),
@@ -191,6 +191,10 @@ public class BillingRecurrence extends BillingBase {
^ instance.renewalPrice == null,
"Renewal price can have a value if and only if the renewal price behavior is"
+ " SPECIFIED");
if (instance.renewalPrice != null) {
checkArgument(
instance.renewalPrice.isPositiveOrZero(), "SPECIFIED renewal price cannot be negative");
}
instance.recurrenceTimeOfYear = TimeOfYear.fromInstant(instance.eventTime);
instance.recurrenceEndTime =
Optional.ofNullable(instance.recurrenceEndTime).orElse(END_INSTANT);
@@ -84,7 +84,10 @@ public class FeatureFlag extends ImmutableObject implements Buildable {
INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS(FeatureStatus.INACTIVE),
/** If we're prohibiting the inclusion of the contact object URI on login. */
PROHIBIT_CONTACT_OBJECTS_ON_LOGIN(FeatureStatus.INACTIVE);
PROHIBIT_CONTACT_OBJECTS_ON_LOGIN(FeatureStatus.INACTIVE),
/** If we're prohibiting insecure algorithms as detailed by RFC 9904. */
FORBID_INSECURE_ALGORITHMS_RFC_9904(FeatureStatus.INACTIVE);
private final FeatureStatus defaultStatus;
@@ -118,7 +118,9 @@ public class PasswordResetRequest extends ImmutableObject implements Buildable {
checkArgumentNotNull(getInstance().requester, "Requester must be specified");
checkArgumentNotNull(getInstance().destinationEmail, "Destination email must be specified");
checkArgumentNotNull(getInstance().registrarId, "Registrar ID must be specified");
getInstance().verificationCode = UUID.randomUUID().toString();
if (getInstance().verificationCode == null) {
getInstance().verificationCode = UUID.randomUUID().toString();
}
return super.build();
}
@@ -158,12 +158,12 @@ public class DomainCommand {
throws InvalidReferencesException, ParameterValuePolicyErrorException {
Create clone = clone(this);
clone.nameservers = linkHosts(nullSafeImmutableCopy(clone.nameserverHostNames), now);
if (registrantContactId != null) {
throw new RegistrantProhibitedException();
}
if (!isNullOrEmpty(foreignKeyedDesignatedContacts)) {
throw new ContactsProhibitedException();
}
if (registrantContactId != null) {
throw new RegistrantProhibitedException();
}
return clone;
}
@@ -51,6 +51,10 @@ public abstract class BaseFee extends ImmutableObject {
public enum FeeType {
CREATE("create"),
EAP("Early Access Period, fee expires: %s", "Early Access Period"),
/**
* A one-time fee for registering a recently dropped domain, during the Expiry Access Period.
*/
XAP("Expiry Access Period, fee expires: %s", "Expiry Access Period"),
RENEW("renew"),
RESTORE("restore"),
/**
@@ -14,6 +14,7 @@
package google.registry.model.domain.secdns;
import com.google.common.collect.ImmutableSet;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
import jakarta.persistence.Access;
@@ -26,6 +27,7 @@ import jakarta.xml.bind.annotation.XmlTransient;
import jakarta.xml.bind.annotation.XmlType;
import jakarta.xml.bind.annotation.adapters.HexBinaryAdapter;
import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.xbill.DNS.DNSSEC.Algorithm;
/** Base class for {@link DomainDsData} and {@link DomainDsDataHistory}. */
@MappedSuperclass
@@ -33,6 +35,16 @@ import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlType(propOrder = {"keyTag", "algorithm", "digestType", "digest"})
public abstract class DomainDsDataBase extends ImmutableObject implements UnsafeSerializable {
// A set of algorithms that we do not allow. See RFC 9904 for more details.
public static final ImmutableSet<Integer> FORBIDDEN_ALGORITHMS =
ImmutableSet.of(
Algorithm.RSAMD5,
Algorithm.RSASHA1,
Algorithm.RSA_NSEC3_SHA1,
Algorithm.DSA,
Algorithm.DSA_NSEC3_SHA1,
Algorithm.ECC_GOST);
@XmlTransient @Transient @Insignificant String domainRepoId;
/** The identifier for this particular key in the domain. */
@@ -52,6 +52,7 @@ import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.persistence.VKey;
import google.registry.persistence.WithVKey;
import google.registry.persistence.converter.AllocationTokenStatusTransitionUserType;
import google.registry.util.NonFinalForTesting;
import jakarta.persistence.AttributeOverride;
import jakarta.persistence.AttributeOverrides;
import jakarta.persistence.Column;
@@ -61,6 +62,7 @@ import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import java.time.Duration;
import java.time.Instant;
import java.util.Map;
import java.util.Optional;
@@ -374,28 +376,39 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
return ALLOCATION_TOKENS_CACHE.getAll(keys);
}
/** A cache that loads the {@link AllocationToken} object for a given AllocationToken VKey. */
private static final LoadingCache<VKey<AllocationToken>, Optional<AllocationToken>>
ALLOCATION_TOKENS_CACHE =
CacheUtils.newCacheBuilder(getSingletonCacheRefreshDuration())
.build(
new CacheLoader<>() {
@Override
public Optional<AllocationToken> load(VKey<AllocationToken> key) {
return tm().reTransact(() -> tm().loadByKeyIfPresent(key));
}
@VisibleForTesting
public static void setCacheForTest(Optional<Duration> expiry) {
Duration effectiveExpiry = expiry.orElse(getSingletonCacheRefreshDuration());
ALLOCATION_TOKENS_CACHE = createAllocationTokensCache(effectiveExpiry);
}
@Override
public Map<? extends VKey<AllocationToken>, ? extends Optional<AllocationToken>>
loadAll(Set<? extends VKey<AllocationToken>> keys) {
return tm().reTransact(
() ->
keys.stream()
.collect(
toImmutableMap(
key -> key, key -> tm().loadByKeyIfPresent(key))));
}
});
/** A cache that loads the {@link AllocationToken} object for a given AllocationToken VKey. */
@NonFinalForTesting
private static LoadingCache<VKey<AllocationToken>, Optional<AllocationToken>>
ALLOCATION_TOKENS_CACHE = createAllocationTokensCache(getSingletonCacheRefreshDuration());
private static LoadingCache<VKey<AllocationToken>, Optional<AllocationToken>>
createAllocationTokensCache(Duration expiry) {
return CacheUtils.newCacheBuilder(expiry)
.build(
new CacheLoader<>() {
@Override
public Optional<AllocationToken> load(VKey<AllocationToken> key) {
return tm().reTransact(() -> tm().loadByKeyIfPresent(key));
}
@Override
public Map<? extends VKey<AllocationToken>, ? extends Optional<AllocationToken>>
loadAll(Set<? extends VKey<AllocationToken>> keys) {
return tm().reTransact(
() ->
keys.stream()
.collect(
toImmutableMap(
key -> key, key -> tm().loadByKeyIfPresent(key))));
}
});
}
@Override
public VKey<AllocationToken> createVKey() {
@@ -462,6 +475,10 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
getInstance().renewalPriceBehavior.equals(RenewalPriceBehavior.SPECIFIED)
== (getInstance().renewalPrice != null),
"renewalPrice must be specified iff renewalPriceBehavior is SPECIFIED");
if (getInstance().renewalPrice != null) {
checkArgument(
getInstance().renewalPrice.isPositiveOrZero(), "Renewal price cannot be negative");
}
if (getInstance().tokenType.equals(TokenType.BULK_PRICING)) {
checkArgument(
@@ -265,6 +265,18 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
@Column(nullable = false)
boolean blockPremiumNames;
/**
* Whether this registrar has opted into participating in the Expiry Access Period (XAP).
*
* <p>If this is false, any domain currently in XAP will appear as reserved/unavailable on checks
* and creates.
*
* <p>TODO(mcilwain): Drop the temporary database default for expiry_access_period_enabled in a
* subsequent schema migration once this code is deployed.
*/
@Column(nullable = false)
boolean expiryAccessPeriodEnabled;
// Authentication.
/** X.509 PEM client certificate(s) used to authenticate registrar to EPP service. */
@@ -560,6 +572,10 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
return blockPremiumNames;
}
public boolean getExpiryAccessPeriodEnabled() {
return expiryAccessPeriodEnabled;
}
public boolean getContactsRequireSyncing() {
return contactsRequireSyncing;
}
@@ -654,6 +670,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
.put("whoisServer", getWhoisServer())
.putListOfStrings("rdapBaseUrls", getRdapBaseUrls())
.put("blockPremiumNames", blockPremiumNames)
.put("expiryAccessPeriodEnabled", expiryAccessPeriodEnabled)
.put("url", url)
.put("icannReferralEmail", getIcannReferralEmail())
.put("driveFolderId", driveFolderId)
@@ -871,7 +888,10 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
}
public Builder setIpAddressAllowList(Iterable<CidrAddressBlock> ipAddressAllowList) {
getInstance().ipAddressAllowList = ImmutableList.copyOf(ipAddressAllowList);
getInstance().ipAddressAllowList =
ipAddressAllowList == null
? ImmutableList.of()
: ImmutableList.copyOf(ipAddressAllowList);
return this;
}
@@ -915,6 +935,11 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
return this;
}
public Builder setExpiryAccessPeriodEnabled(boolean expiryAccessPeriodEnabled) {
getInstance().expiryAccessPeriodEnabled = expiryAccessPeriodEnabled;
return this;
}
public Builder setUrl(String url) {
getInstance().url = url;
return this;
@@ -1030,6 +1055,11 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
return tm().transact(() -> tm().loadAllOf(Registrar.class));
}
/** Loads all registrar entities directly from the database, sorted by the given field names. */
public static Iterable<Registrar> loadAllSorted(String... sortFields) {
return tm().transact(() -> tm().loadAllOfSorted(Registrar.class, sortFields));
}
/** Loads all registrar entities using an in-memory cache. */
public static Iterable<Registrar> loadAllCached() {
return CACHE_BY_REGISTRAR_ID.get().values();
@@ -56,6 +56,7 @@ import google.registry.model.EntityYamlUtils.OptionalDurationSerializer;
import google.registry.model.EntityYamlUtils.OptionalStringSerializer;
import google.registry.model.EntityYamlUtils.SortedEnumSetSerializer;
import google.registry.model.EntityYamlUtils.SortedSetSerializer;
import google.registry.model.EntityYamlUtils.TimedTransitionPropertyExpiryAccessPeriodModeDeserializer;
import google.registry.model.EntityYamlUtils.TimedTransitionPropertyMoneyDeserializer;
import google.registry.model.EntityYamlUtils.TimedTransitionPropertyTldStateDeserializer;
import google.registry.model.EntityYamlUtils.TokenVKeyListDeserializer;
@@ -71,9 +72,12 @@ import google.registry.model.domain.token.VKeyConverter_AllocationToken;
import google.registry.model.tld.label.PremiumList;
import google.registry.model.tld.label.ReservedList;
import google.registry.persistence.EntityCallbacksListener.RecursivePostPersist;
import google.registry.persistence.EntityCallbacksListener.RecursivePostRemove;
import google.registry.persistence.EntityCallbacksListener.RecursivePostUpdate;
import google.registry.persistence.VKey;
import google.registry.persistence.converter.AllocationTokenVkeyListUserType;
import google.registry.persistence.converter.BillingCostTransitionUserType;
import google.registry.persistence.converter.ExpiryAccessPeriodModeTransitionUserType;
import google.registry.persistence.converter.TldStateTransitionUserType;
import google.registry.tldconfig.idn.IdnTableEnum;
import google.registry.util.Idn;
@@ -118,6 +122,8 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
public static final boolean DEFAULT_ESCROW_ENABLED = false;
public static final boolean DEFAULT_DNS_PAUSED = false;
public static final ExpiryAccessPeriodMode DEFAULT_EXPIRY_ACCESS_PERIOD_MODE =
ExpiryAccessPeriodMode.DISABLED;
public static final Duration DEFAULT_ADD_GRACE_PERIOD = Duration.ofDays(5);
public static final Duration DEFAULT_AUTO_RENEW_GRACE_PERIOD = Duration.ofDays(45);
public static final Duration DEFAULT_REDEMPTION_GRACE_PERIOD = Duration.ofDays(30);
@@ -195,6 +201,12 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
PDT
}
/** The modes an Expiry Access Period (XAP) can be in at any given point in time. */
public enum ExpiryAccessPeriodMode {
DISABLED,
ENABLED
}
/** Returns the TLD for a given TLD, throwing if none exists. */
public static Tld get(String tld) {
return CACHE.get(tld).orElseThrow(() -> new TldNotFoundException(tld));
@@ -219,9 +231,12 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
* Invalidates the cache entry.
*
* <p>This is called automatically when the tld is saved. One should also call it when a tld is
* deleted.
* deleted. This only affects the pod-local cache so most pods won't catch it, but it's still the
* right thing to do.
*/
@RecursivePostPersist
@RecursivePostRemove
@RecursivePostUpdate
public void invalidateInCache() {
CACHE.invalidate(tldStr);
}
@@ -421,6 +436,19 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
@Column(nullable = false)
boolean dnsPaused = DEFAULT_DNS_PAUSED;
/**
* Whether the Expiry Access Period following domain deletes is enabled for this TLD.
*
* <p>TODO(mcilwain): Once this Java code has been fully deployed to production, drop the
* temporary database-level DEFAULT constraint on the "expiry_access_period_transitions" column in
* the "Tld" table in a subsequent schema release.
*/
@Column(nullable = false, columnDefinition = "hstore")
@Type(ExpiryAccessPeriodModeTransitionUserType.class)
@JsonDeserialize(using = TimedTransitionPropertyExpiryAccessPeriodModeDeserializer.class)
TimedTransitionProperty<ExpiryAccessPeriodMode> expiryAccessPeriodTransitions =
TimedTransitionProperty.withInitialValue(DEFAULT_EXPIRY_ACCESS_PERIOD_MODE);
/**
* The length of the add grace period for this TLD.
*
@@ -629,6 +657,14 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
return dnsPaused;
}
public ExpiryAccessPeriodMode getExpiryAccessPeriodModeAt(Instant now) {
return expiryAccessPeriodTransitions.getValueAtTime(now);
}
public ImmutableSortedMap<Instant, ExpiryAccessPeriodMode> getExpiryAccessPeriodTransitions() {
return expiryAccessPeriodTransitions.toValueMap();
}
@Nullable
public String getDriveFolderId() {
return driveFolderId;
@@ -809,6 +845,56 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
return new Builder(clone(this));
}
/** Checks the validity of the TLD object, for use during building or deserializing. */
public void validateState() {
checkArgument(tldStr != null, "No registry TLD specified");
// Check for canonical form by converting to an InternetDomainName and then back.
checkArgument(
InternetDomainName.isValid(tldStr)
&& tldStr.equals(InternetDomainName.from(tldStr).toString()),
"Cannot create registry for TLD that is not a valid, canonical domain name");
// Check the validity of all TimedTransitionProperties to ensure that they have values for
// START_INSTANT. The setters above have already checked this for new values, but also check
// here to catch cases where we loaded an invalid TimedTransitionProperty from the database
// and cloned it into a new builder, to block re-building a Tld in an invalid state.
tldStateTransitions.checkValidity();
createBillingCostTransitions.checkValidity();
renewBillingCostTransitions.checkValidity();
eapFeeSchedule.checkValidity();
expiryAccessPeriodTransitions.checkValidity();
// All costs must be in the expected currency.
checkArgumentNotNull(getCurrency(), "Currency must be set");
Predicate<Money> currencyCheck =
(Money money) -> money.getCurrencyUnit().equals(currency) && money.isPositiveOrZero();
checkArgument(
currencyCheck.test(getRestoreBillingCost()),
"Restore cost is negative or in the wrong currency");
checkArgument(
currencyCheck.test(getServerStatusChangeBillingCost()),
"Server status change cost is negative or in the wrong currency");
checkArgument(
currencyCheck.test(getRegistryLockOrUnlockBillingCost()),
"Registry lock/unlock cost is negative or in the wrong currency");
checkArgument(
getRenewBillingCostTransitions().values().stream().allMatch(currencyCheck),
"Some renew cost(s) are negative or in the wrong currency");
checkArgument(
getCreateBillingCostTransitions().values().stream().allMatch(currencyCheck),
"Some create cost(s) are negative or in the wrong currency");
checkArgument(
eapFeeSchedule.toValueMap().values().stream().allMatch(currencyCheck),
"Some EAP fee cost(s) are negative or in the wrong currency'");
checkArgumentNotNull(
pricingEngineClassName, "All registries must have a configured pricing engine");
checkArgument(
dnsWriters != null && !dnsWriters.isEmpty(),
"At least one DNS writer must be specified."
+ " VoidDnsWriter can be used if DNS writing isn't desired");
checkArgument(
numDnsPublishLocks > 0,
"Number of DNS publish locks must be positive. Use 1 for TLD-wide locks.");
}
/** A builder for constructing {@link Tld} objects, since they are immutable. */
public static class Builder extends Buildable.Builder<Tld> {
public Builder() {}
@@ -859,6 +945,13 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
return this;
}
public Builder setExpiryAccessPeriodTransitions(
ImmutableSortedMap<Instant, ExpiryAccessPeriodMode> transitionsMap) {
getInstance().expiryAccessPeriodTransitions =
TimedTransitionProperty.fromValueMap(transitionsMap);
return this;
}
public Builder setDriveFolderId(String driveFolderId) {
getInstance().driveFolderId = driveFolderId;
return this;
@@ -961,10 +1054,6 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
public Builder setCreateBillingCostTransitions(
ImmutableSortedMap<Instant, Money> createCostsMap) {
checkArgumentNotNull(createCostsMap, "Create billing costs map cannot be null");
checkArgument(
createCostsMap.values().stream().allMatch(Money::isPositiveOrZero),
"Create billing cost cannot be negative");
getInstance().createBillingCostTransitions =
TimedTransitionProperty.fromValueMap(createCostsMap);
return this;
@@ -1007,17 +1096,12 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
}
public Builder setRestoreBillingCost(Money amount) {
checkArgument(amount.isPositiveOrZero(), "restoreBillingCost cannot be negative");
getInstance().restoreBillingCost = amount;
return this;
}
public Builder setRenewBillingCostTransitions(
ImmutableSortedMap<Instant, Money> renewCostsMap) {
checkArgumentNotNull(renewCostsMap, "Renew billing costs map cannot be null");
checkArgument(
renewCostsMap.values().stream().allMatch(Money::isPositiveOrZero),
"Renew billing cost cannot be negative");
getInstance().renewBillingCostTransitions =
TimedTransitionProperty.fromValueMap(renewCostsMap);
return this;
@@ -1025,10 +1109,6 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
/** Sets the EAP fee schedule for the TLD. */
public Builder setEapFeeSchedule(ImmutableSortedMap<Instant, Money> eapFeeSchedule) {
checkArgumentNotNull(eapFeeSchedule, "EAP fee schedule cannot be null");
checkArgument(
eapFeeSchedule.values().stream().allMatch(Money::isPositiveOrZero),
"EAP fee cannot be negative");
getInstance().eapFeeSchedule = TimedTransitionProperty.fromValueMap(eapFeeSchedule);
return this;
}
@@ -1046,14 +1126,11 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
}
public Builder setServerStatusChangeBillingCost(Money amount) {
checkArgument(
amount.isPositiveOrZero(), "Server status change billing cost cannot be negative");
getInstance().serverStatusChangeBillingCost = amount;
return this;
}
public Builder setRegistryLockOrUnlockBillingCost(Money amount) {
checkArgument(amount.isPositiveOrZero(), "Registry lock/unlock cost cannot be negative");
getInstance().registryLockOrUnlockBillingCost = amount;
return this;
}
@@ -1115,58 +1192,10 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
@Override
public Tld build() {
final Tld instance = getInstance();
// Pick up the name of the associated TLD from the instance object.
String tldName = instance.tldStr;
checkArgument(tldName != null, "No registry TLD specified");
// Check for canonical form by converting to an InternetDomainName and then back.
checkArgument(
InternetDomainName.isValid(tldName)
&& tldName.equals(InternetDomainName.from(tldName).toString()),
"Cannot create registry for TLD that is not a valid, canonical domain name");
// Check the validity of all TimedTransitionProperties to ensure that they have values for
// START_INSTANT. The setters above have already checked this for new values, but also check
// here to catch cases where we loaded an invalid TimedTransitionProperty from the database
// and cloned it into a new builder, to block re-building a Tld in an invalid state.
instance.tldStateTransitions.checkValidity();
instance.createBillingCostTransitions.checkValidity();
instance.renewBillingCostTransitions.checkValidity();
instance.eapFeeSchedule.checkValidity();
// All costs must be in the expected currency.
checkArgumentNotNull(instance.getCurrency(), "Currency must be set");
checkArgument(
instance.getRestoreBillingCost().getCurrencyUnit().equals(instance.currency),
"Restore cost must be in the TLD's currency");
checkArgument(
instance.getServerStatusChangeBillingCost().getCurrencyUnit().equals(instance.currency),
"Server status change cost must be in the TLD's currency");
checkArgument(
instance.getRegistryLockOrUnlockBillingCost().getCurrencyUnit().equals(instance.currency),
"Registry lock/unlock cost must be in the TLD's currency");
Predicate<Money> currencyCheck =
(Money money) -> money.getCurrencyUnit().equals(instance.currency);
checkArgument(
instance.getRenewBillingCostTransitions().values().stream().allMatch(currencyCheck),
"Renew cost must be in the TLD's currency");
checkArgument(
instance.getCreateBillingCostTransitions().values().stream().allMatch(currencyCheck),
"Create cost must be in the TLD's currency");
checkArgument(
instance.eapFeeSchedule.toValueMap().values().stream().allMatch(currencyCheck),
"All EAP fees must be in the TLD's currency");
checkArgumentNotNull(
instance.pricingEngineClassName, "All registries must have a configured pricing engine");
checkArgument(
instance.dnsWriters != null && !instance.dnsWriters.isEmpty(),
"At least one DNS writer must be specified."
+ " VoidDnsWriter can be used if DNS writing isn't desired");
// If not set explicitly, numDnsPublishLocks defaults to 1.
instance.setDefaultNumDnsPublishLocks();
checkArgument(
instance.numDnsPublishLocks > 0,
"Number of DNS publish locks must be positive. Use 1 for TLD-wide locks.");
instance.tldStr = tldName;
instance.tldUnicode = Idn.toUnicode(tldName);
getInstance().setDefaultNumDnsPublishLocks();
getInstance().validateState();
getInstance().tldUnicode = Idn.toUnicode(getInstance().tldStr);
return super.build();
}
}
@@ -169,6 +169,14 @@ public final class PremiumList extends BaseDomainLabelList<BigDecimal, PremiumEn
getInstance().revisionId = revisionId;
return this;
}
@Override
public PremiumEntry build() {
checkArgument(getInstance().price != null, "Price must not be null");
checkArgument(
getInstance().price.compareTo(BigDecimal.ZERO) >= 0, "Price must not be negative");
return super.build();
}
}
}
@@ -164,7 +164,7 @@ import google.registry.ui.server.console.settings.SecurityAction;
ToolsServerModule.class,
WhiteboxModule.class
})
interface RequestComponent {
public interface RequestComponent {
FlowComponent.Builder flowComponentBuilder();
BrdaCopyAction brdaCopyAction();
@@ -0,0 +1,33 @@
// Copyright 2026 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.persistence.converter;
import google.registry.model.common.TimedTransitionProperty;
import google.registry.model.tld.Tld.ExpiryAccessPeriodMode;
/** Hibernate custom type for {@link TimedTransitionProperty} of {@link ExpiryAccessPeriodMode}. */
public class ExpiryAccessPeriodModeTransitionUserType
extends TimedTransitionBaseUserType<ExpiryAccessPeriodMode> {
@Override
String valueToString(ExpiryAccessPeriodMode value) {
return value.name();
}
@Override
ExpiryAccessPeriodMode stringToValue(String string) {
return ExpiryAccessPeriodMode.valueOf(string);
}
}
@@ -257,11 +257,21 @@ public class DelegatingReplicaJpaTransactionManager implements JpaTransactionMan
return getReplica().loadAllOf(clazz);
}
@Override
public <T> ImmutableList<T> loadAllOfSorted(Class<T> clazz, String... sortFields) {
return getReplica().loadAllOfSorted(clazz, sortFields);
}
@Override
public <T> Stream<T> loadAllOfStream(Class<T> clazz) {
return getReplica().loadAllOfStream(clazz);
}
@Override
public <T> Stream<T> loadAllOfSortedStream(Class<T> clazz, String... sortFields) {
return getReplica().loadAllOfSortedStream(clazz, sortFields);
}
@Override
public <T> Optional<T> loadSingleton(Class<T> clazz) {
return getReplica().loadSingleton(clazz);
@@ -76,6 +76,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import javax.annotation.Nullable;
@@ -88,6 +89,16 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final Retrier retrier = new Retrier(new SystemSleeper(), 6);
/**
* Strict allowlist regex for property/field names in dynamic JPQL ORDER BY clauses.
*
* <p>JPA and database engines forbid bind parameters (e.g. ? or :param) for schema identifiers or
* property names in ORDER BY clauses. To prevent JPQL/SQL injection when dynamically constructing
* sort queries, every sort field MUST be validated against this pattern before concatenation.
*/
private static final Pattern VALID_SORT_FIELD_PATTERN = Pattern.compile("^[a-zA-Z0-9_.]+$");
private static final String NESTED_TRANSACTION_MESSAGE =
"Nested transaction detected. Try refactoring to avoid nested transactions. If unachievable,"
+ " use reTransact() in nested transactions";
@@ -528,15 +539,38 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
@Override
public <T> ImmutableList<T> loadAllOf(Class<T> clazz) {
return loadAllOfStream(clazz).collect(toImmutableList());
return loadAllOfSorted(clazz);
}
@Override
public <T> Stream<T> loadAllOfStream(Class<T> clazz) {
return loadAllOfSortedStream(clazz);
}
@Override
public <T> ImmutableList<T> loadAllOfSorted(Class<T> clazz, String... sortFields) {
return loadAllOfSortedStream(clazz, sortFields).collect(toImmutableList());
}
@Override
public <T> Stream<T> loadAllOfSortedStream(Class<T> clazz, String... sortFields) {
checkArgumentNotNull(clazz, "clazz must be specified");
checkArgumentNotNull(sortFields, "sortFields must not be null");
assertInTransaction();
StringBuilder queryString =
new StringBuilder(String.format("FROM %s", getEntityType(clazz).getName()));
if (sortFields.length > 0) {
for (String field : sortFields) {
checkArgument(
VALID_SORT_FIELD_PATTERN.matcher(field).matches(),
"Invalid sort field name: %s",
field);
}
queryString.append(" ORDER BY ");
queryString.append(String.join(", ", sortFields));
}
return getEntityManager()
.createQuery(String.format("FROM %s", getEntityType(clazz).getName()), clazz)
.createQuery(queryString.toString(), clazz)
.getResultStream()
.map(this::detach);
}
@@ -219,6 +219,14 @@ public interface TransactionManager {
*/
<T> ImmutableList<T> loadAllOf(Class<T> clazz);
/**
* Returns a list of all entities of the given type that exist in the database, ordered by the
* specified field names in ascending order.
*
* <p>The resulting list is empty if there are no entities of this type.
*/
<T> ImmutableList<T> loadAllOfSorted(Class<T> clazz, String... sortFields);
/**
* Returns a stream of all entities of the given type that exist in the database.
*
@@ -226,6 +234,14 @@ public interface TransactionManager {
*/
<T> Stream<T> loadAllOfStream(Class<T> clazz);
/**
* Returns a stream of all entities of the given type that exist in the database, ordered by the
* specified field names in ascending order.
*
* <p>The resulting stream is empty if there are no entities of this type.
*/
<T> Stream<T> loadAllOfSortedStream(Class<T> clazz, String... sortFields);
/**
* Loads the only instance of this particular class, or empty if none exists.
*
@@ -454,9 +454,8 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
// We must break the query up into chunks, because the in operator is limited to 30 subqueries.
// Since it is possible for the same domain to show up more than once in our result list (if
// we do a wildcard nameserver search that returns multiple nameservers used by the same
// domain), we must create a set of resulting {@link Domain} objects. Use a sorted set,
// and fetch all domains, to make sure that we can return the first domains in alphabetical
// order.
// domain), we must create a set of resulting {@link Domain}s. Use a sorted set, fetch all
// domains, to make sure that we can return the first domains in alphabetical order.
ImmutableSortedSet.Builder<Domain> domainSetBuilder =
ImmutableSortedSet.orderedBy(Comparator.comparing(Domain::getDomainName));
int numHostKeysSearched = 0;
@@ -465,7 +464,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
replicaTm()
.transact(
() -> {
for (VKey<Host> hostKey : hostKeys) {
for (VKey<Host> hostKey : chunk) {
CriteriaQueryBuilder<Domain> queryBuilder =
CriteriaQueryBuilder.create(replicaTm(), Domain.class)
.whereFieldContains("nsHosts", hostKey)
@@ -185,7 +185,7 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
throw new UnprocessableEntityException(
"A suffix after a wildcard in a nameserver lookup must be an in-bailiwick domain");
}
List<Host> hostList = new ArrayList<>();
List<String> matchingFqhns = new ArrayList<>();
for (String fqhn : ImmutableSortedSet.copyOf(domain.get().getSubordinateHosts())) {
if (cursorString.isPresent() && (fqhn.compareTo(cursorString.get()) <= 0)) {
continue;
@@ -193,10 +193,22 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
// We can't just check that the host name starts with the initial query string, because
// then the query ns.exam*.example.com would match against nameserver ns.example.com.
if (partialStringQuery.matches(fqhn)) {
Optional<Host> host =
ForeignKeyUtils.loadResourceByCache(Host.class, fqhn, getRequestTime());
if (shouldBeVisible(host)) {
hostList.add(host.get());
matchingFqhns.add(fqhn);
}
}
List<Host> hostList = new ArrayList<>();
int chunkSize = getStandardQuerySizeLimit();
// Batch load from cache in chunks to avoid sequential N+1 database queries on cache misses.
for (List<String> fqhnChunk : Iterables.partition(matchingFqhns, chunkSize)) {
if (hostList.size() > rdapResultSetMaxSize) {
break;
}
ImmutableMap<String, Host> cachedHosts =
ForeignKeyUtils.loadResourcesByCache(Host.class, fqhnChunk, getRequestTime());
for (String fqhn : fqhnChunk) {
Host host = cachedHosts.get(fqhn);
if (host != null && shouldBeVisible(host)) {
hostList.add(host);
if (hostList.size() > rdapResultSetMaxSize) {
break;
}
@@ -204,10 +216,7 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
}
}
return makeSearchResults(
hostList,
IncompletenessWarningType.COMPLETE,
domain.get().getSubordinateHosts().size(),
CursorType.NAME);
hostList, IncompletenessWarningType.COMPLETE, hostList.size(), CursorType.NAME);
}
/**
@@ -24,7 +24,6 @@ import com.jcraft.jsch.SftpException;
import google.registry.config.RegistryConfig.Config;
import jakarta.inject.Inject;
import java.io.Closeable;
import java.net.URI;
import java.time.Duration;
/**
@@ -57,8 +56,7 @@ final class JSchSshSession implements Closeable {
*
* @throws JSchException if we fail to open the connection.
*/
JSchSshSession create(JSch jsch, URI uri) throws JSchException {
RdeUploadUrl url = RdeUploadUrl.create(uri);
JSchSshSession create(JSch jsch, RdeUploadUrl url) throws JSchException {
logger.atInfo().log("Connecting to SSH endpoint: %s", url);
Session session = jsch.getSession(
url.getUser().orElse("domain-registry"),
@@ -30,7 +30,7 @@ public enum RdeResourceType {
REGISTRAR("urn:ietf:params:xml:ns:rdeRegistrar-1.0", EnumSet.of(FULL, THIN)),
IDN("urn:ietf:params:xml:ns:rdeIDN-1.0", EnumSet.of(FULL)),
HEADER("urn:ietf:params:xml:ns:rdeHeader-1.0", EnumSet.of(FULL, THIN)),
EPP_PARAMS("urn:ietf:params:xml:ns:rdeEppParams-1.0", EnumSet.of(FULL, THIN));
EPP_PARAMS("urn:ietf:params:xml:ns:rdeEppParams-1.0", EnumSet.of(FULL));
private final String uri;
private final ImmutableSet<RdeMode> modes;
@@ -312,7 +312,8 @@ public final class RdeStagingAction implements Runnable {
jobRegion,
new LaunchFlexTemplateRequest().setLaunchParameter(parameter))
.execute();
logger.atInfo().log("Got response: %s", launchResponse.getJob().toPrettyString());
logger.atInfo().log(
"Launched RDE staging Dataflow job: %s", launchResponse.getJob().getId());
jobNameBuilder.add(launchResponse.getJob().getId());
} catch (IOException e) {
logger.atWarning().withCause(e).log("Pipeline Launch failed");
@@ -64,7 +64,6 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.time.Duration;
import java.time.Instant;
import java.util.Optional;
@@ -116,11 +115,16 @@ public final class RdeUploadAction implements Runnable, EscrowTask {
@Inject @Config("rdeInterval") Duration interval;
@Inject @Config("rdeUploadLockTimeout") Duration timeout;
@Inject @Config("rdeUploadSftpCooldown") Duration sftpCooldown;
@Inject @Config("rdeUploadUrl") URI uploadUrl;
@Inject @Key("rdeReceiverKey") PGPPublicKey receiverKey;
@Inject @Key("rdeSigningKey") PGPKeyPair signingKey;
@Inject @Key("rdeStagingDecryptionKey") PGPPrivateKey stagingDecryptionKey;
@Inject
@Config("rdeUploadUrl")
RdeUploadUrl rdeUploadUrl;
@Inject RdeUploadAction() {}
@Override
public void run() {
logger.atInfo().log("Attempting to acquire RDE upload lock for TLD '%s'.", tld);
@@ -135,7 +139,7 @@ public final class RdeUploadAction implements Runnable, EscrowTask {
@Override
public void runWithLock(Instant watermark) throws Exception {
// If a prefix is not provided,try to determine the prefix. This should only happen when the RDE
// upload cron job runs to catch up any un-retried (i. e. expected) RDE failures.
// upload cron job runs to catch up any un-retried (i.e. expected) RDE failures.
String actualPrefix =
prefix.orElseGet(() -> findMostRecentPrefixForWatermark(watermark, bucket, tld, gcsUtils));
logger.atInfo().log("Verifying readiness to upload the RDE deposit.");
@@ -181,7 +185,7 @@ public final class RdeUploadAction implements Runnable, EscrowTask {
verifyFileExists(xmlFilename);
verifyFileExists(xmlLengthFilename);
verifyFileExists(reportFilename);
logger.atInfo().log("Commencing RDE upload for TLD '%s' to '%s'.", tld, uploadUrl);
logger.atInfo().log("Commencing RDE upload for TLD '%s' to '%s'.", tld, rdeUploadUrl);
final long xmlLength = readXmlLength(xmlLengthFilename);
retrier.callWithRetry(
() -> upload(xmlFilename, xmlLength, watermark, name, nameWithoutPrefix),
@@ -221,10 +225,10 @@ public final class RdeUploadAction implements Runnable, EscrowTask {
private void upload(
BlobId xmlFile, long xmlLength, Instant watermark, String name, String nameWithoutPrefix)
throws Exception {
logger.atInfo().log("Uploading XML file '%s' to remote path '%s'.", xmlFile, uploadUrl);
logger.atInfo().log("Uploading XML file '%s' to remote path '%s'.", xmlFile, rdeUploadUrl);
try (InputStream gcsInput = gcsUtils.openInputStream(xmlFile);
InputStream ghostrydeDecoder = Ghostryde.decoder(gcsInput, stagingDecryptionKey)) {
try (JSchSshSession session = jschSshSessionFactory.create(lazyJsch.get(), uploadUrl);
try (JSchSshSession session = jschSshSessionFactory.create(lazyJsch.get(), rdeUploadUrl);
JSchSftpChannel ftpChan = session.openSftpChannel()) {
ByteArrayOutputStream sigOut = new ByteArrayOutputStream();
String rydeFilename = nameWithoutPrefix + ".ryde";
@@ -37,7 +37,7 @@ import javax.annotation.concurrent.Immutable;
* @see RdeUploadAction
*/
@Immutable
final class RdeUploadUrl implements Comparable<RdeUploadUrl> {
public final class RdeUploadUrl implements Comparable<RdeUploadUrl> {
public static final Protocol SFTP = new Protocol("sftp", 22);
private static final ImmutableMap<String, Protocol> ALLOWED_PROTOCOLS =
@@ -21,10 +21,8 @@ import static jakarta.servlet.http.HttpServletResponse.SC_OK;
import static java.util.stream.Collectors.joining;
import com.google.cloud.storage.BlobId;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Iterables;
import com.google.common.flogger.FluentLogger;
import com.google.common.io.ByteStreams;
import com.google.common.net.MediaType;
@@ -41,6 +39,8 @@ import jakarta.inject.Inject;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** Copy all registrar detail reports in a given bucket's subdirectory from GCS to Drive. */
@Action(
@@ -54,6 +54,9 @@ public final class CopyDetailReportsAction implements Runnable {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final Pattern FILENAME_PATTERN =
Pattern.compile("^invoice_details_[0-9]{4}-[0-9]{2}_(.+)_.+\\.csv$");
private final String billingBucket;
private final String invoiceDirectoryPrefix;
private final DriveConnection driveConnection;
@@ -101,8 +104,13 @@ public final class CopyDetailReportsAction implements Runnable {
new ImmutableMultimap.Builder<>();
for (String detailReportName : detailReportObjectNames) {
// The standard report format is "invoice_details_yyyy-MM_registrarId_tld.csv
// TODO(larryruili): Determine a safer way of enforcing this.
String registrarId = Iterables.get(Splitter.on('_').split(detailReportName), 3);
Matcher matcher = FILENAME_PATTERN.matcher(detailReportName);
if (!matcher.matches()) {
logger.atWarning().log(
"Detail report filename '%s' does not match the expected pattern.", detailReportName);
continue;
}
String registrarId = matcher.group(1);
Optional<Registrar> registrar = Registrar.loadByRegistrarId(registrarId);
if (registrar.isEmpty()) {
logger.atWarning().log(
@@ -14,10 +14,14 @@
package google.registry.reporting.billing;
import static com.google.common.base.Preconditions.checkState;
import static google.registry.beam.BeamUtils.createJobName;
import static google.registry.model.common.Cursor.CursorType.RECURRING_BILLING;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.request.Action.Method.POST;
import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
import static java.time.ZoneOffset.UTC;
import com.google.api.services.dataflow.Dataflow;
import com.google.api.services.dataflow.model.LaunchFlexTemplateParameter;
@@ -29,6 +33,7 @@ import com.google.common.flogger.FluentLogger;
import com.google.common.net.MediaType;
import google.registry.batch.CloudTasksUtils;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.common.Cursor;
import google.registry.persistence.PersistenceModule;
import google.registry.reporting.ReportingModule;
import google.registry.request.Action;
@@ -40,7 +45,9 @@ import google.registry.util.RegistryEnvironment;
import jakarta.inject.Inject;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.time.YearMonth;
import java.util.Optional;
/**
* Invokes the {@code InvoicingPipeline} beam template via the REST api, and enqueues the {@link
@@ -107,6 +114,7 @@ public class GenerateInvoicesAction implements Runnable {
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
logger.atInfo().log("Launching invoicing pipeline for %s.", yearMonth);
try {
checkBillingRecurrenceCursor();
LaunchFlexTemplateParameter parameter =
new LaunchFlexTemplateParameter()
.setJobName(createJobName("invoicing", clock))
@@ -156,4 +164,20 @@ public class GenerateInvoicesAction implements Runnable {
response.setPayload(String.format("Pipeline launch failed: %s", e.getMessage()));
}
}
private void checkBillingRecurrenceCursor() {
Optional<Cursor> previousCursor =
tm().transact(() -> tm().loadByKeyIfPresent(Cursor.createGlobalVKey(RECURRING_BILLING)));
checkState(
previousCursor.isPresent(),
"BillingRecurrence expansion cursor is not present. Run ExpandBillingRecurrencesAction.");
Instant startOfNextMonth = yearMonth.plusMonths(1).atDay(1).atStartOfDay(UTC).toInstant();
Instant previousCursorTime = previousCursor.get().getCursorTime();
checkState(
!previousCursorTime.isBefore(startOfNextMonth),
"BillingRecurrence expansion cursor (%s) is before the start of the next month (%s). "
+ "Run ExpandBillingRecurrencesAction.",
previousCursorTime,
startOfNextMonth);
}
}
@@ -127,7 +127,7 @@ public class GenerateSpec11ReportAction implements Runnable {
jobRegion,
new LaunchFlexTemplateRequest().setLaunchParameter(parameter))
.execute();
logger.atInfo().log("Got response: %s", launchResponse.getJob().toPrettyString());
logger.atInfo().log("Launched Spec11 Dataflow job: %s", launchResponse.getJob().getId());
String jobId = launchResponse.getJob().getId();
if (sendEmail) {
cloudTasksUtils.enqueue(
@@ -119,6 +119,18 @@ public abstract class HttpException extends RuntimeException {
}
}
/** Exception that causes a 413 response. */
public static final class PayloadTooLargeException extends HttpException {
public PayloadTooLargeException(String message) {
super(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, message, null);
}
@Override
public String getResponseCodeString() {
return "Payload Too Large";
}
}
/** Exception that causes a 404 response. */
public static final class NotFoundException extends HttpException {
public NotFoundException() {
@@ -18,8 +18,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.net.HttpHeaders.CONTENT_DISPOSITION;
import static com.google.common.net.HttpHeaders.X_CONTENT_TYPE_OPTIONS;
import static com.google.common.net.MediaType.JSON_UTF_8;
import static org.json.simple.JSONValue.toJSONString;
import com.google.gson.Gson;
import jakarta.inject.Inject;
import java.time.Instant;
import java.util.Map;
@@ -31,10 +31,12 @@ public class JsonResponse {
public static final String JSON_SAFETY_PREFIX = ")]}'\n";
protected final Response response;
protected final Gson gson;
@Inject
public JsonResponse(Response rsp) {
public JsonResponse(Response rsp, Gson gson) {
this.response = rsp;
this.gson = gson;
}
/**
@@ -55,7 +57,7 @@ public class JsonResponse {
// response, even if all else fails. It's basically another anti-sniffing mechanism in the sense
// that if you hit this url directly, it would try to download the file instead of showing it.
response.setHeader(CONTENT_DISPOSITION, "attachment");
response.setPayload(JSON_SAFETY_PREFIX + toJSONString(checkNotNull(responseMap)));
response.setPayload(JSON_SAFETY_PREFIX + gson.toJson(checkNotNull(responseMap)));
}
/**
@@ -14,6 +14,7 @@
package google.registry.request;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.net.MediaType.JSON_UTF_8;
import static google.registry.dns.PublishDnsUpdatesAction.CLOUD_TASKS_RETRY_HEADER;
import static google.registry.model.tld.Tlds.assertTldExists;
@@ -28,18 +29,19 @@ import static google.registry.request.RequestParameters.extractSetOfParameters;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.VerifyException;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.ByteStreams;
import com.google.common.io.CharStreams;
import com.google.common.net.MediaType;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import com.google.protobuf.ByteString;
import dagger.Module;
import dagger.Provides;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.HttpException.PayloadTooLargeException;
import google.registry.request.HttpException.UnsupportedMediaTypeException;
import google.registry.request.auth.AuthResult;
import google.registry.request.lock.LockHandler;
@@ -48,15 +50,16 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.Optional;
import org.json.simple.JSONValue;
import org.json.simple.parser.ParseException;
/** Dagger module for servlets. */
@Module
public final class RequestModule {
public static final int MAX_PAYLOAD_BYTES = 50 * 1024 * 1024;
private final HttpServletRequest req;
private final HttpServletResponse rsp;
private final AuthResult authResult;
@@ -167,9 +170,37 @@ public final class RequestModule {
@Provides
@Payload
static String providePayloadAsString(HttpServletRequest req) {
public static String providePayloadAsString(
@Payload byte[] payloadBytes, HttpServletRequest req) {
String charsetName = req.getCharacterEncoding();
Charset charset;
try {
return CharStreams.toString(req.getReader());
charset = (charsetName != null) ? Charset.forName(charsetName) : UTF_8;
} catch (IllegalArgumentException e) {
throw new UnsupportedMediaTypeException("Unsupported charset: " + charsetName, e);
}
return new String(payloadBytes, charset);
}
@Provides
@Payload
public static byte[] providePayloadAsBytes(HttpServletRequest req) {
try {
if (req.getContentLengthLong() > MAX_PAYLOAD_BYTES) {
throw new PayloadTooLargeException(
String.format(
"Payload size %d exceeds limit of %d bytes",
req.getContentLengthLong(), MAX_PAYLOAD_BYTES));
}
if (req.getInputStream() == null) {
return new byte[0];
}
byte[] bytes =
ByteStreams.toByteArray(ByteStreams.limit(req.getInputStream(), MAX_PAYLOAD_BYTES + 1));
if (bytes.length > MAX_PAYLOAD_BYTES) {
throw new PayloadTooLargeException("Payload exceeds maximum allowed size");
}
return bytes;
} catch (IOException e) {
throw new RuntimeException(e);
}
@@ -177,22 +208,8 @@ public final class RequestModule {
@Provides
@Payload
static byte[] providePayloadAsBytes(HttpServletRequest req) {
try {
return ByteStreams.toByteArray(req.getInputStream());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Provides
@Payload
static ByteString providePayloadAsByteString(HttpServletRequest req) {
try {
return ByteString.copyFrom(ByteStreams.toByteArray(req.getInputStream()));
} catch (IOException e) {
throw new RuntimeException(e);
}
public static ByteString providePayloadAsByteString(@Payload byte[] payloadBytes) {
return ByteString.copyFrom(payloadBytes);
}
@Provides
@@ -202,18 +219,16 @@ public final class RequestModule {
@Provides
@JsonPayload
@SuppressWarnings("unchecked")
static Map<String, Object> provideJsonPayload(
@Header("Content-Type") MediaType contentType, @Payload String payload) {
@Header("Content-Type") MediaType contentType, @Payload String payload, Gson gson) {
if (!JSON_UTF_8.is(contentType.withCharset(UTF_8))) {
throw new UnsupportedMediaTypeException(
String.format("Expected %s Content-Type", JSON_UTF_8.withoutParameters()));
}
try {
return (Map<String, Object>) JSONValue.parseWithException(payload);
} catch (ParseException e) {
throw new BadRequestException(
"Malformed JSON", new VerifyException("Malformed JSON:\n" + payload, e));
return checkNotNull(gson.fromJson(payload, new TypeToken<>() {}));
} catch (JsonSyntaxException | NullPointerException e) {
throw new BadRequestException("Malformed JSON:\n" + payload);
}
}
@@ -253,12 +268,10 @@ public final class RequestModule {
@Provides
@OptionalJsonPayload
public static Optional<JsonElement> provideJsonBody(HttpServletRequest req, Gson gson) {
try {
// GET requests return a null reader and thus a null JsonObject, which is fine
return Optional.ofNullable(gson.fromJson(req.getReader(), JsonElement.class));
} catch (IOException e) {
public static Optional<JsonElement> provideJsonBody(@Payload String payloadString, Gson gson) {
if (payloadString.isEmpty()) {
return Optional.empty();
}
return Optional.ofNullable(gson.fromJson(payloadString, JsonElement.class));
}
}
@@ -14,11 +14,9 @@
package google.registry.request;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.util.stream.Collectors.joining;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Streams;
import java.util.Comparator;
@@ -64,18 +62,6 @@ public class RouterDisplayHelper {
return formatRoutes(Router.extractRoutesFromComponent(componentClass).values());
}
public static ImmutableList<String> extractHumanReadableRoutesWithWrongService(
Class<?> componentClass, Action.Service expectedService) {
return Router.extractRoutesFromComponent(componentClass).values().stream()
.filter(route -> route.action().service() != expectedService)
.map(
route ->
String.format(
"%s (%s%s)",
route.actionClass(), route.action().service(), route.action().path()))
.collect(toImmutableList());
}
private static String getFormatString(Map<String, Integer> columnWidths) {
return String.format(
FORMAT,
@@ -22,6 +22,7 @@ import static com.google.common.net.HttpHeaders.CONTENT_LENGTH;
import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.flogger.FluentLogger;
import com.google.common.io.ByteStreams;
import com.google.common.net.MediaType;
import java.io.ByteArrayInputStream;
@@ -37,6 +38,11 @@ import org.apache.commons.compress.utils.IOUtils;
/** Utilities for common functionality relating to {@link URLConnection}s. */
public final class UrlConnectionUtils {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
// 100 MB is arbitrary, not specifically selected for any reason
public static final int MAX_RESPONSE_PAYLOAD_BYTES = 100 * 1024 * 1024;
private UrlConnectionUtils() {}
/**
@@ -48,10 +54,26 @@ public final class UrlConnectionUtils {
* @see HttpURLConnection#getErrorStream()
*/
public static byte[] getResponseBytes(HttpURLConnection connection) throws IOException {
int responseCode = connection.getResponseCode();
try (InputStream is =
responseCode < 400 ? connection.getInputStream() : connection.getErrorStream()) {
return ByteStreams.toByteArray(is);
try {
if (connection.getContentLengthLong() > MAX_RESPONSE_PAYLOAD_BYTES) {
throw new IOException(
String.format(
"Response size %d exceeds limit of %d bytes",
connection.getContentLengthLong(), MAX_RESPONSE_PAYLOAD_BYTES));
} else if ((double) connection.getContentLengthLong() / MAX_RESPONSE_PAYLOAD_BYTES >= 0.9) {
logger.atSevere().log(
"Response from %s was within 90%% of the maximum response size", connection.getURL());
}
int responseCode = connection.getResponseCode();
try (InputStream is =
responseCode < 400 ? connection.getInputStream() : connection.getErrorStream()) {
byte[] bytes =
ByteStreams.toByteArray(ByteStreams.limit(is, MAX_RESPONSE_PAYLOAD_BYTES + 1));
if (bytes.length > MAX_RESPONSE_PAYLOAD_BYTES) {
throw new IOException("Response exceeds maximum allowed size");
}
return bytes;
}
} catch (NullPointerException e) {
return new byte[] {};
}
@@ -43,6 +43,7 @@ import jakarta.inject.Qualifier;
import jakarta.inject.Singleton;
import java.io.IOException;
import java.time.Duration;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import javax.annotation.Nullable;
@@ -88,8 +89,8 @@ public class AuthModule {
TokenVerifier provideIapTokenVerifier(
@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);
ConcurrentHashMap<String, com.google.auth.oauth2.TokenVerifier> tokenVerifiers =
new ConcurrentHashMap<>();
return (String service, String token) -> {
Long backendServiceId = backendServiceIdMap.get().get(service);
checkNotNull(
@@ -98,7 +99,15 @@ public class AuthModule {
service,
backendServiceIdMap);
String audience = String.format(IAP_AUDIENCE_FORMAT, projectIdNumber, backendServiceId);
return tokenVerifierBuilder.setAudience(audience).build().verify(token);
com.google.auth.oauth2.TokenVerifier verifier =
tokenVerifiers.computeIfAbsent(
audience,
aud ->
com.google.auth.oauth2.TokenVerifier.newBuilder()
.setIssuer(IAP_ISSUER_URL)
.setAudience(aud)
.build());
return verifier.verify(token);
};
}
@@ -40,10 +40,9 @@ import javax.annotation.Nullable;
* An authentication mechanism that verifies the OIDC token.
*
* <p>Currently, two flavors are supported: one that checks for the OIDC token as a regular bearer
* token, and another that checks for the OIDC token passed by IAP. In both cases, the {@link
* AuthResult} with the highest {@link AuthLevel} possible is returned. So, if the email address for
* which the token is minted exists both as a {@link User} and as a service account, the returned
* {@link AuthResult} is at {@link AuthLevel#USER}.
* token, and another that checks for the OIDC token passed by IAP. In the case where the email
* address for which the token is minted exists both as a {link User} and as a service account, the
* returned {@link AuthResult} is at {@link AuthLevel#APP} to avoid database lookups when possible.
*
* @see <a href="https://developers.google.com/identity/openid-connect/openid-connect">OpenID
* Connect </a>
@@ -18,10 +18,13 @@ import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.net.HttpHeaders.CONTENT_DISPOSITION;
import static com.google.common.net.HttpHeaders.X_CONTENT_TYPE_OPTIONS;
import static com.google.common.net.MediaType.JSON_UTF_8;
import static org.json.simple.JSONValue.writeJSONString;
import com.google.common.flogger.FluentLogger;
import com.google.common.net.MediaType;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import google.registry.tools.GsonUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@@ -29,8 +32,6 @@ import java.io.Reader;
import java.io.Writer;
import java.util.Map;
import javax.annotation.Nullable;
import org.json.simple.JSONValue;
import org.json.simple.parser.ParseException;
/**
* Helper class for servlets that read or write JSON.
@@ -41,6 +42,8 @@ public final class JsonHttp {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final Gson GSON = GsonUtils.provideGson();
/** String prefixed to all JSON-like responses. */
public static final String JSON_SAFETY_PREFIX = ")]}'\n";
@@ -51,7 +54,6 @@ public final class JsonHttp {
* @throws IOException if we failed to read from {@code req}.
*/
@Nullable
@SuppressWarnings("unchecked")
public static Map<String, ?> read(HttpServletRequest req) throws IOException {
if (!"POST".equals(req.getMethod())
&& !"PUT".equals(req.getMethod())) {
@@ -64,8 +66,8 @@ public final class JsonHttp {
}
try (Reader jsonReader = req.getReader()) {
try {
return checkNotNull((Map<String, ?>) JSONValue.parseWithException(jsonReader));
} catch (ParseException | NullPointerException | ClassCastException e) {
return checkNotNull(GSON.fromJson(jsonReader, new TypeToken<>() {}));
} catch (JsonSyntaxException | NullPointerException | ClassCastException e) {
logger.atWarning().withCause(e).log("Malformed JSON.");
return null;
}
@@ -88,7 +90,7 @@ public final class JsonHttp {
rsp.setHeader(CONTENT_DISPOSITION, "attachment");
try (Writer writer = rsp.getWriter()) {
writer.write(JSON_SAFETY_PREFIX);
writeJSONString(jsonObject, writer);
GSON.toJson(jsonObject, writer);
}
}
}
@@ -25,6 +25,7 @@ import com.google.common.hash.Hashing;
import google.registry.model.server.ServerSecret;
import google.registry.util.Clock;
import jakarta.inject.Inject;
import java.security.MessageDigest;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
@@ -102,9 +103,8 @@ public final class XsrfTokenManager {
}
// Reconstruct the token to verify validity.
String reconstructedToken = encodeToken(ServerSecret.get().asBytes(), email, timestampMillis);
if (!token.equals(reconstructedToken)) {
logger.atWarning().log(
"Reconstructed XSRF mismatch (got != expected): %s != %s", token, reconstructedToken);
if (!MessageDigest.isEqual(token.getBytes(UTF_8), reconstructedToken.getBytes(UTF_8))) {
logger.atWarning().log("Token %s didn't match expected reconstructed token", token);
return false;
}
return true;
@@ -14,12 +14,14 @@
package google.registry.tmch;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.request.UrlConnectionUtils.getResponseBytes;
import static jakarta.servlet.http.HttpServletResponse.SC_NO_CONTENT;
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Ascii;
import com.google.common.flogger.FluentLogger;
import com.google.common.io.ByteSource;
import google.registry.request.Action;
@@ -62,6 +64,8 @@ public final class NordnVerifyAction implements Runnable {
static final String NORDN_URL_PARAM = "nordnUrl";
static final String NORDN_LOG_ID_PARAM = "nordnLogId";
private static final String MARKSDB_URL_BEGINNING = "ry.marksdb.org";
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@Inject LordnRequestInitializer lordnRequestInitializer;
@@ -104,6 +108,12 @@ public final class NordnVerifyAction implements Runnable {
*/
@VisibleForTesting
LordnLog verify() throws IOException, GeneralSecurityException {
String host = Ascii.toLowerCase(url.getHost());
checkArgument(
host.startsWith(MARKSDB_URL_BEGINNING),
"URL %s must start with %s",
url,
MARKSDB_URL_BEGINNING);
logger.atInfo().log("LORDN verify task %s: Sending request to URL %s", actionLogId, url);
HttpURLConnection connection = urlConnectionService.createConnection(url);
lordnRequestInitializer.initialize(connection, tld);
@@ -25,6 +25,7 @@ import google.registry.request.HttpException.BadRequestException;
import google.registry.request.Parameter;
import jakarta.servlet.http.HttpServletRequest;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import org.bouncycastle.openpgp.PGPPublicKey;
@@ -53,7 +54,7 @@ public final class TmchModule {
@Parameter(NordnVerifyAction.NORDN_URL_PARAM)
static URL provideNordnUrl(HttpServletRequest req) {
try {
return new URL(extractRequiredParameter(req, NordnVerifyAction.NORDN_URL_PARAM));
return URI.create(extractRequiredParameter(req, NordnVerifyAction.NORDN_URL_PARAM)).toURL();
} catch (MalformedURLException e) {
throw new BadRequestException("Bad URL: " + NordnVerifyAction.NORDN_URL_PARAM);
}
@@ -164,6 +164,8 @@ public class ConfigureTldCommand extends MutatingCommand {
if (Boolean.TRUE.equals(breakGlass)) {
newTld = newTld.asBuilder().setBreakglassMode(true).build();
}
// Enforce any restrictions, e.g. "no negative fees"
newTld.validateState();
stageEntityChange(oldTld, newTld);
}
@@ -14,6 +14,7 @@
package google.registry.tools;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.beust.jcommander.Parameter;
@@ -39,6 +40,19 @@ abstract class CreateOrUpdatePremiumListCommand extends ConfirmingCommand {
protected List<String> inputData;
protected CurrencyUnit currency;
@Parameter(
names = {"-d", "--dry_run"},
description = "Does not execute the entity mutation")
boolean dryRun;
@Parameter(
names = {"--build_environment"},
description =
"DO NOT USE THIS FLAG ON THE COMMAND LINE! This flag indicates the command is being run"
+ " by the build environment tools. This flag should never be used by a human user"
+ " from the command line.")
boolean buildEnv;
@Nullable
@Parameter(
names = {"-n", "--name"},
@@ -68,4 +82,17 @@ abstract class CreateOrUpdatePremiumListCommand extends ConfirmingCommand {
}
return message;
}
@Override
protected boolean dontRunCommand() {
return dryRun;
}
@Override
protected boolean checkExecutionState() {
checkArgument(
!RegistryToolEnvironment.get().equals(RegistryToolEnvironment.PRODUCTION) || buildEnv,
"The --build_environment flag must be used when running in production");
return super.checkExecutionState();
}
}
@@ -14,6 +14,8 @@
package google.registry.tools;
import static com.google.common.base.Preconditions.checkArgument;
import com.beust.jcommander.Parameter;
import com.google.common.flogger.FluentLogger;
import google.registry.model.tld.label.ReservedList;
@@ -35,6 +37,19 @@ public abstract class CreateOrUpdateReservedListCommand extends ConfirmingComman
static final FluentLogger logger = FluentLogger.forEnclosingClass();
@Parameter(
names = {"-d", "--dry_run"},
description = "Does not execute the entity mutation")
boolean dryRun;
@Parameter(
names = {"--build_environment"},
description =
"DO NOT USE THIS FLAG ON THE COMMAND LINE! This flag indicates the command is being run"
+ " by the build environment tools. This flag should never be used by a human user"
+ " from the command line.")
boolean buildEnv;
@Nullable
@Parameter(
names = {"-n", "--name"},
@@ -74,4 +89,17 @@ public abstract class CreateOrUpdateReservedListCommand extends ConfirmingComman
.collect(Collectors.joining(", "))
+ "]";
}
@Override
protected boolean dontRunCommand() {
return dryRun;
}
@Override
protected boolean checkExecutionState() {
checkArgument(
!RegistryToolEnvironment.get().equals(RegistryToolEnvironment.PRODUCTION) || buildEnv,
"The --build_environment flag must be used when running in production");
return super.checkExecutionState();
}
}
@@ -29,21 +29,26 @@ import java.util.Optional;
* https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml
*/
public enum DigestType {
SHA1(1, 20),
SHA256(2, 32),
// Algorithm number 1 is SHA-1 and will be is deliberately NOT SUPPORTED.
// RFC 9904 specifies that this algorithm MUST NOT be used for DNSSEC delegations.
// This prohibition is gated behind a feature flag.
SHA1(1, 20, false),
SHA256(2, 32, true),
// Algorithm number 3 is GOST R 34.11-94 and is deliberately NOT SUPPORTED.
// This algorithm was reviewed by ise-crypto and deemed academically broken (b/207029800).
// In addition, RFC 8624 specifies that this algorithm MUST NOT be used for DNSSEC delegations.
// TODO(sarhabot@): Add note in Cloud DNS code to notify the Registry of any new changes to
// supported digest types.
SHA384(4, 48);
SHA384(4, 48, true);
private final int wireValue;
private final int bytes;
private final boolean allowedInRfc9904;
DigestType(int wireValue, int bytes) {
DigestType(int wireValue, int bytes, boolean allowedInRfc9904) {
this.wireValue = wireValue;
this.bytes = bytes;
this.allowedInRfc9904 = allowedInRfc9904;
}
private static final ImmutableMap<Integer, DigestType> WIRE_VALUE_TO_DIGEST_TYPE =
@@ -63,4 +68,9 @@ public enum DigestType {
public int getBytes() {
return bytes;
}
/** Whether this digest type is supported as of RFC 9904. */
public boolean isAllowedInRfc9904() {
return allowedInRfc9904;
}
}
@@ -28,6 +28,9 @@ import google.registry.config.RegistryConfig.Config;
import google.registry.model.ForeignKeyUtils;
import google.registry.model.billing.BillingBase.Reason;
import google.registry.model.billing.BillingEvent;
import google.registry.model.console.ConsolePermission;
import google.registry.model.console.User;
import google.registry.model.console.UserRoles;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.RegistryLock;
@@ -100,16 +103,16 @@ public final class DomainLockUtils {
*
* <p>This assumes that the lock object / domain in question has a pending lock or unlock.
*/
public RegistryLock verifyVerificationCode(String verificationCode, boolean isAdmin) {
public RegistryLock verifyVerificationCode(String verificationCode, User user) {
RegistryLock result =
tm().transact(
() -> {
RegistryLock lock = getByVerificationCode(verificationCode);
if (lock.getLockCompletionTime().isEmpty()) {
return verifyAndApplyLock(lock, isAdmin);
return verifyAndApplyLock(lock, user);
} else if (lock.getUnlockRequestTime().isPresent()
&& lock.getUnlockCompletionTime().isEmpty()) {
return verifyAndApplyUnlock(lock, isAdmin);
return verifyAndApplyUnlock(lock, user);
} else {
throw new IllegalArgumentException(
String.format(
@@ -203,11 +206,18 @@ public final class DomainLockUtils {
countdown));
}
private RegistryLock verifyAndApplyLock(RegistryLock lock, boolean isAdmin) {
private RegistryLock verifyAndApplyLock(RegistryLock lock, User user) {
Instant now = tm().getTxTime();
checkArgument(
!lock.isLockRequestExpired(now), "The pending lock has expired; please try again");
UserRoles userRoles = user.getUserRoles();
boolean isAdmin = userRoles.isAdmin();
checkArgument(
userRoles.hasPermission(lock.getRegistrarId(), ConsolePermission.REGISTRY_LOCK),
"User %s does not have registry lock permission on registrar %s",
user.getEmailAddress(),
lock.getRegistrarId());
checkArgument(!lock.isSuperuser() || isAdmin, "Non-admin user cannot complete admin lock");
RegistryLock newLock =
@@ -217,13 +227,29 @@ public final class DomainLockUtils {
return newLock;
}
private RegistryLock verifyAndApplyUnlock(RegistryLock lock, boolean isAdmin) {
private RegistryLock verifyAndApplyUnlock(RegistryLock lock, User user) {
Instant now = tm().getTxTime();
checkArgument(
!lock.isUnlockRequestExpired(now), "The pending unlock has expired; please try again");
checkArgument(isAdmin || !lock.isSuperuser(), "Non-admin user cannot complete admin unlock");
UserRoles userRoles = user.getUserRoles();
boolean isAdmin = userRoles.isAdmin();
checkArgument(
userRoles.hasPermission(lock.getRegistrarId(), ConsolePermission.REGISTRY_LOCK),
"User %s does not have registry lock permission on registrar %s",
user.getEmailAddress(),
lock.getRegistrarId());
checkArgument(!lock.isSuperuser() || isAdmin, "Non-admin user cannot complete admin unlock");
// The pending unlock must still be the most recent RegistryLock for this domain. If a newer
// lock exists (e.g. an admin/superuser lock applied after this unlock was requested or a
// different unlock+relock), the verification code being redeemed is for a superseded row and
// must not be allowed to strip the lock statuses that the newer row applied.
Optional<RegistryLock> mostRecent = RegistryLockDao.getMostRecentByRepoId(lock.getRepoId());
checkArgument(
mostRecent.isPresent() && mostRecent.get().getRevisionId().equals(lock.getRevisionId()),
"The pending unlock on %s has been superseded; please request a new unlock",
lock.getDomainName());
RegistryLock newLock =
RegistryLockDao.save(lock.asBuilder().setUnlockCompletionTime(now).build());
removeLockStatuses(newLock, isAdmin, now);
@@ -15,6 +15,7 @@
package google.registry.tools;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
import com.beust.jcommander.IStringConverter;
@@ -46,7 +47,7 @@ record DsRecord(int keyTag, int alg, int digestType, String digest) {
String.format("DS record has an invalid digest length: %s", digest));
}
if (!DomainFlowUtils.validateAlgorithm(alg)) {
if (tm().reTransact(() -> DomainFlowUtils.algorithmIsInvalid(alg))) {
throw new IllegalArgumentException(
String.format("DS record uses an unrecognized algorithm: %d", alg));
}
@@ -25,6 +25,7 @@ import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import google.registry.model.domain.Domain;
import google.registry.model.host.Host;
import google.registry.persistence.transaction.QueryComposer.Comparator;
@@ -38,7 +39,6 @@ import java.nio.file.Paths;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import org.json.simple.JSONValue;
/** Command to generate a report of all DNS data. */
@Parameters(separators = " =", commandDescription = "Generate report of all DNS data in a TLD.")
@@ -57,6 +57,7 @@ final class GenerateDnsReportCommand implements Command {
private Path output = Paths.get("/dev/stdout");
@Inject Clock clock;
@Inject Gson gson;
@Override
public void run() throws Exception {
@@ -144,7 +145,7 @@ final class GenerateDnsReportCommand implements Command {
} else {
result.append(",\n");
}
result.append(JSONValue.toJSONString(map));
result.append(gson.toJson(map));
}
}
}
@@ -14,27 +14,17 @@
package google.registry.tools;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import google.registry.module.RequestComponent;
import google.registry.request.RouterDisplayHelper;
/** Generates the routing map file used for unit testing. */
@Parameters(commandDescription = "Generate a routing map file")
final class GetRoutingMapCommand implements Command {
@Parameter(
names = {"-c", "--class"},
description =
"Request component class (e.g. google.registry.module.backend.BackendRequestComponent)"
+ " for which routing map should be generated",
required = true
)
private String serviceClassName;
@Override
public void run() throws Exception {
System.out.println(
RouterDisplayHelper.extractHumanReadableRoutesFromComponent(
Class.forName(serviceClassName)));
RouterDisplayHelper.extractHumanReadableRoutesFromComponent(RequestComponent.class));
}
}

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