mirror of
https://github.com/google/nomulus
synced 2026-07-15 04:22:24 +00:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 83e9e7fb5c | |||
| 438c523fcb | |||
| 025a2faff2 | |||
| fd822dd333 | |||
| 9b93749d43 | |||
| 71a8579ece | |||
| cda51f13dc | |||
| 1de5b5dcc1 | |||
| 32279e42e4 | |||
| ba0f90bdaf | |||
| 85308eb975 | |||
| ed62f27a4a | |||
| 75851399ba | |||
| 6d54c8d113 | |||
| 34dfa2760e | |||
| ff39a4a763 | |||
| b1cd8c5a6f | |||
| 28c7bc3085 | |||
| f36d22f4b1 | |||
| ef3ce79b8a | |||
| 85317e3982 | |||
| a53b71ecd5 | |||
| fc9446876f | |||
| 654b165dff | |||
| 14d68d4cb2 | |||
| bbf405d566 |
@@ -270,6 +270,10 @@
|
||||
"moduleLicense": "Public Domain",
|
||||
"moduleName": "org.tukaani:xz"
|
||||
},
|
||||
{
|
||||
"moduleLicense": "Public Domain",
|
||||
"moduleName": "org.json:json"
|
||||
},
|
||||
{
|
||||
// "Apache License, Version 2.0".
|
||||
"moduleLicense": null,
|
||||
|
||||
Generated
+6
-6
@@ -6824,9 +6824,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/http-cache-semantics": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
|
||||
"integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
|
||||
"integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/http-deceiver": {
|
||||
@@ -17202,9 +17202,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"http-cache-semantics": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
|
||||
"integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
|
||||
"integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==",
|
||||
"dev": true
|
||||
},
|
||||
"http-deceiver": {
|
||||
|
||||
+43
-16
@@ -138,9 +138,15 @@ public class ExpandRecurringBillingEventsPipeline implements Serializable {
|
||||
private final boolean isDryRun;
|
||||
private final boolean advanceCursor;
|
||||
private final Counter recurringsInScopeCounter =
|
||||
Metrics.counter("ExpandBilling", "RecurringsInScope");
|
||||
private final Counter expandedOneTimeCounter =
|
||||
Metrics.counter("ExpandBilling", "ExpandedOneTime");
|
||||
Metrics.counter("ExpandBilling", "Recurrings in scope for expansion");
|
||||
// Note that this counter is only accurate when running in dry run mode. Because SQL persistence
|
||||
// is a side effect and not idempotent, a transaction to save OneTimes could be successful but the
|
||||
// transform that contains it could be still be retried, rolling back the counter increment. The
|
||||
// same transform, when retried, would skip the already expanded OneTime, causing this counter to
|
||||
// be lower than it should be when not in dry run mode.
|
||||
// See: https://beam.apache.org/documentation/programming-guide/#user-code-idempotence
|
||||
private final Counter oneTimesToExpandCounter =
|
||||
Metrics.counter("ExpandBilling", "OneTimes that would be expanded");
|
||||
|
||||
ExpandRecurringBillingEventsPipeline(
|
||||
ExpandRecurringBillingEventsPipelineOptions options, Clock clock) {
|
||||
@@ -182,7 +188,7 @@ public class ExpandRecurringBillingEventsPipeline implements Serializable {
|
||||
+ "AND event_Time < :endTime "
|
||||
// Recurrence should not close before start time.
|
||||
+ "AND :startTime < recurrence_end_time "
|
||||
// Last expansion should happen at least one year before start time.
|
||||
// Last expansion should happen at least one year before end time.
|
||||
+ "AND recurrence_last_expansion < :oneYearAgo "
|
||||
// The recurrence should not close before next expansion time.
|
||||
+ "AND recurrence_last_expansion + INTERVAL '1 YEAR' < recurrence_end_time",
|
||||
@@ -195,6 +201,7 @@ public class ExpandRecurringBillingEventsPipeline implements Serializable {
|
||||
endTime.minusYears(1)),
|
||||
true,
|
||||
(BigInteger id) -> {
|
||||
recurringsInScopeCounter.inc();
|
||||
// Note that because all elements are mapped to the same dummy key, the next
|
||||
// batching transform will effectively be serial. This however does not matter for
|
||||
// our use case because the elements were obtained from a SQL read query, which
|
||||
@@ -239,20 +246,40 @@ public class ExpandRecurringBillingEventsPipeline implements Serializable {
|
||||
|
||||
private void expandOneRecurring(Long recurringId, ImmutableSet.Builder<ImmutableObject> results) {
|
||||
Recurring recurring = tm().loadByKey(Recurring.createVKey(recurringId));
|
||||
recurringsInScopeCounter.inc();
|
||||
Domain domain = tm().loadByKey(Domain.createVKey(recurring.getDomainRepoId()));
|
||||
Registry tld = Registry.get(domain.getTld());
|
||||
|
||||
// Determine the complete set of EventTimes this recurring event should expand to within
|
||||
// [max(recurrenceLastExpansion + 1 yr, startTime), min(recurrenceEndTime, endTime)).
|
||||
ImmutableSet<DateTime> eventTimes =
|
||||
ImmutableSet.copyOf(
|
||||
recurring
|
||||
.getRecurrenceTimeOfYear()
|
||||
.getInstancesInRange(
|
||||
Range.closedOpen(
|
||||
latestOf(recurring.getRecurrenceLastExpansion().plusYears(1), startTime),
|
||||
earliestOf(recurring.getRecurrenceEndTime(), endTime))));
|
||||
//
|
||||
// This range should always be legal for recurrings that are returned from the query. However,
|
||||
// it is possible that the recurring has changed between when the read transformation occurred
|
||||
// and now. This could be caused by some out-of-process mutations (such as a domain deletion
|
||||
// closing out a previously open-ended recurrence), or more subtly, Beam could execute the same
|
||||
// work multiple times due to transient communication issues between workers and the scheduler.
|
||||
// Such opportunistic retries are OK for pure functional transformations, but can cause
|
||||
// unexpected behavior when side effects are executed more than once. For example, the
|
||||
// recurrence_last_expansion field could be updated by a worker after a success expansion, which
|
||||
// failed to report the status to the scheduler in time, which in turn scheduled another worker
|
||||
// to work on the same batch. The second worker would see a new recurrence_last_expansion that
|
||||
// causes the range to be illegal.
|
||||
//
|
||||
// The best way to handle any unexpected behavior is to simply drop the recurring from
|
||||
// expansion, if its new state still calls for an expansion, it would be picked up the next time
|
||||
// the pipeline runs.
|
||||
ImmutableSet<DateTime> eventTimes;
|
||||
try {
|
||||
eventTimes =
|
||||
ImmutableSet.copyOf(
|
||||
recurring
|
||||
.getRecurrenceTimeOfYear()
|
||||
.getInstancesInRange(
|
||||
Range.closedOpen(
|
||||
latestOf(recurring.getRecurrenceLastExpansion().plusYears(1), startTime),
|
||||
earliestOf(recurring.getRecurrenceEndTime(), endTime))));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return;
|
||||
}
|
||||
Domain domain = tm().loadByKey(Domain.createVKey(recurring.getDomainRepoId()));
|
||||
Registry tld = Registry.get(domain.getTld());
|
||||
|
||||
// Find the times for which the OneTime billing event are already created, making this expansion
|
||||
// idempotent. There is no need to match to the domain repo ID as the cancellation matching
|
||||
@@ -277,7 +304,7 @@ public class ExpandRecurringBillingEventsPipeline implements Serializable {
|
||||
// Create new OneTime and DomainHistory for EventTimes that needs to be expanded.
|
||||
for (DateTime eventTime : eventTimesToExpand) {
|
||||
recurrenceLastExpansionTime = latestOf(recurrenceLastExpansionTime, eventTime);
|
||||
expandedOneTimeCounter.inc();
|
||||
oneTimesToExpandCounter.inc();
|
||||
DateTime billingTime = eventTime.plus(tld.getAutoRenewGracePeriodLength());
|
||||
// Note that the DomainHistory is created as of transaction time, as opposed to event time.
|
||||
// This might be counterintuitive because other DomainHistories are created at the time
|
||||
|
||||
@@ -106,6 +106,12 @@ public final class RegistryConfig {
|
||||
return config.gcpProject.projectId;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("cloudSchedulerServiceAccountEmail")
|
||||
public static String provideCloudSchedulerServiceAccountEmail(RegistryConfigSettings config) {
|
||||
return config.gcpProject.cloudSchedulerServiceAccountEmail;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("projectIdNumber")
|
||||
public static long provideProjectIdNumber(RegistryConfigSettings config) {
|
||||
|
||||
@@ -54,6 +54,7 @@ public class RegistryConfigSettings {
|
||||
public String backendServiceUrl;
|
||||
public String toolsServiceUrl;
|
||||
public String pubapiServiceUrl;
|
||||
public String cloudSchedulerServiceAccountEmail;
|
||||
}
|
||||
|
||||
/** Configuration options for OAuth settings for authenticating users. */
|
||||
|
||||
@@ -22,6 +22,8 @@ gcpProject:
|
||||
backendServiceUrl: https://localhost
|
||||
toolsServiceUrl: https://localhost
|
||||
pubapiServiceUrl: https://localhost
|
||||
# Service account used by Cloud Scheduler to send authenticated requests.
|
||||
cloudSchedulerServiceAccountEmail: cloud-scheduler-email@email.com
|
||||
|
||||
gSuite:
|
||||
# Publicly accessible domain name of the running G Suite instance.
|
||||
@@ -427,7 +429,7 @@ misc:
|
||||
|
||||
beam:
|
||||
# The default region to run Apache Beam (Cloud Dataflow) jobs in.
|
||||
defaultJobRegion: us-east1
|
||||
defaultJobRegion: us-central1
|
||||
# The GCE machine type to use when a job is CPU-intensive (e. g. RDE). Be sure
|
||||
# to check the VM CPU quota for the job region. In a massively parallel
|
||||
# pipeline this quota can be easily reached and needs to be raised, otherwise
|
||||
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<taskentries>
|
||||
<task>
|
||||
<url>/_dr/task/rdeStaging</url>
|
||||
<name>rdeStaging</name>
|
||||
<description>
|
||||
This job generates a full RDE escrow deposit as a single gigantic XML document
|
||||
and streams it to cloud storage. When this job has finished successfully, it'll
|
||||
launch a separate task that uploads the deposit file to Iron Mountain via SFTP.
|
||||
</description>
|
||||
<schedule>7 0 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=rde-upload&endpoint=/_dr/task/rdeUpload&forEachRealTld]]></url>
|
||||
<name>rdeUpload</name>
|
||||
<description>
|
||||
This job is a no-op unless RdeUploadCursor falls behind for some reason.
|
||||
</description>
|
||||
<schedule>0 */4 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchDnl&runInEmpty]]></url>
|
||||
<name>tmchDnl</name>
|
||||
<description>
|
||||
This job downloads the latest DNL from MarksDB and inserts it into the database.
|
||||
(See: TmchDnlAction, ClaimsList)
|
||||
</description>
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchSmdrl&runInEmpty]]></url>
|
||||
<name>tmchSmdrl</name>
|
||||
<description>
|
||||
This job downloads the latest SMDRL from MarksDB and inserts it into the database.
|
||||
(See: TmchSmdrlAction, SignedMarkRevocationList)
|
||||
</description>
|
||||
<schedule>15 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchCrl&runInEmpty]]></url>
|
||||
<name>tmchCrl</name>
|
||||
<description>
|
||||
This job downloads the latest CRL from MarksDB and inserts it into the database.
|
||||
(See: TmchCrlAction)
|
||||
</description>
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/syncGroupMembers&runInEmpty]]></url>
|
||||
<name>syncGroupMembers</name>
|
||||
<description>
|
||||
Syncs RegistrarContact changes in the past hour to Google Groups.
|
||||
</description>
|
||||
<schedule>0 */1 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=sheet&endpoint=/_dr/task/syncRegistrarsSheet&runInEmpty]]></url>
|
||||
<name>syncRegistrarsSheet</name>
|
||||
<description>
|
||||
Synchronize Registrar entities to Google Spreadsheets.
|
||||
</description>
|
||||
<schedule>0 */1 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResourcesPipeline?fast=true]]></url>
|
||||
<name>resaveAllEppResourcesPipeline</name>
|
||||
<description>
|
||||
This job resaves all our resources, projected in time to "now".
|
||||
</description>
|
||||
<!--Deviation from cron tasks schedule: 1st monday of month 09:00 is replaced
|
||||
with 1st of the month 09:00 -->
|
||||
<schedule>0 9 1 * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/expandRecurringBillingEvents?advanceCursor]]></url>
|
||||
<name>expandRecurringBillingEvents</name>
|
||||
<description>
|
||||
This job runs an action that creates synthetic OneTime billing events from Recurring billing
|
||||
events. Events are created for all instances of Recurring billing events that should exist
|
||||
between the RECURRING_BILLING cursor's time and the execution time of the action.
|
||||
</description>
|
||||
<schedule>0 3 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/deleteExpiredDomains]]></url>
|
||||
<name>deleteExpiredDomains</name>
|
||||
<description>
|
||||
This job runs an action that deletes domains that are past their
|
||||
autorenew end date.
|
||||
</description>
|
||||
<schedule>7 3 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/deleteProberData&runInEmpty]]></url>
|
||||
<name>deleteProberData</name>
|
||||
<description>
|
||||
This job clears out data from probers and runs once a week.
|
||||
</description>
|
||||
<schedule>0 14 * * 1</schedule>
|
||||
</task>
|
||||
|
||||
<!-- TODO: Add borgmon job to check that these files are created and updated successfully. -->
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportReservedTerms&forEachRealTld]]></url>
|
||||
<name>exportReservedTerms</name>
|
||||
<description>
|
||||
Reserved terms export to Google Drive job for creating once-daily exports.
|
||||
</description>
|
||||
<schedule>30 5 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportPremiumTerms&forEachRealTld]]></url>
|
||||
<name>exportPremiumTerms</name>
|
||||
<description>
|
||||
Premium terms export to Google Drive job for creating once-daily exports.
|
||||
</description>
|
||||
<schedule>0 5 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/readDnsQueue?jitterSeconds=45]]></url>
|
||||
<name>readDnsQueue</name>
|
||||
<description>
|
||||
Lease all tasks from the dns-pull queue, group by TLD, and invoke PublishDnsUpdates for each
|
||||
group.
|
||||
</description>
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
</taskentries>
|
||||
@@ -1,150 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--TODO: ptkach - Remove this file after cloud scheduler is fully up and running -->
|
||||
<cronentries>
|
||||
|
||||
<!--
|
||||
/cron/fanout params:
|
||||
queue=<QUEUE_NAME>
|
||||
endpoint=<ENDPOINT_NAME> // URL Path of servlet, which may contain placeholders:
|
||||
runInEmpty // Run once, with no tld parameter
|
||||
forEachRealTld // Run for tlds with getTldType() == TldType.REAL
|
||||
forEachTestTld // Run for tlds with getTldType() == TldType.TEST
|
||||
exclude=TLD1[,TLD2] // exclude something otherwise included
|
||||
-->
|
||||
|
||||
<cron>
|
||||
<url>/_dr/task/rdeStaging</url>
|
||||
<description>
|
||||
This job generates a full RDE escrow deposit as a single gigantic XML document
|
||||
and streams it to cloud storage. When this job has finished successfully, it'll
|
||||
launch a separate task that uploads the deposit file to Iron Mountain via SFTP.
|
||||
</description>
|
||||
<schedule>every day 00:07</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=rde-upload&endpoint=/_dr/task/rdeUpload&forEachRealTld]]></url>
|
||||
<description>
|
||||
This job is a no-op unless RdeUploadCursor falls behind for some reason.
|
||||
</description>
|
||||
<schedule>every 4 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchDnl&runInEmpty]]></url>
|
||||
<description>
|
||||
This job downloads the latest DNL from MarksDB and inserts it into the database.
|
||||
(See: TmchDnlServlet, ClaimsList)
|
||||
</description>
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchSmdrl&runInEmpty]]></url>
|
||||
<description>
|
||||
This job downloads the latest SMDRL from MarksDB and inserts it into the database.
|
||||
(See: TmchSmdrlServlet, SignedMarkRevocationList)
|
||||
</description>
|
||||
<schedule>every 12 hours from 00:15 to 12:15</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchCrl&runInEmpty]]></url>
|
||||
<description>
|
||||
This job downloads the latest CRL from MarksDB and inserts it into the database.
|
||||
(See: TmchCrlServlet)
|
||||
</description>
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/syncGroupMembers&runInEmpty]]></url>
|
||||
<description>
|
||||
Syncs RegistrarContact changes in the past hour to Google Groups.
|
||||
</description>
|
||||
<schedule>every 1 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=sheet&endpoint=/_dr/task/syncRegistrarsSheet&runInEmpty]]></url>
|
||||
<description>
|
||||
Synchronize Registrar entities to Google Spreadsheets.
|
||||
</description>
|
||||
<schedule>every 1 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResourcesPipeline?fast=true]]></url>
|
||||
<description>
|
||||
This job resaves all our resources, projected in time to "now".
|
||||
</description>
|
||||
<schedule>1st monday of month 09:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/expandRecurringBillingEvents?advanceCursor]]></url>
|
||||
<description>
|
||||
This job runs an action that creates synthetic OneTime billing events from Recurring billing
|
||||
events. Events are created for all instances of Recurring billing events that should exist
|
||||
between the RECURRING_BILLING cursor's time and the execution time of the action.
|
||||
</description>
|
||||
<schedule>every day 03:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/deleteExpiredDomains]]></url>
|
||||
<description>
|
||||
This job runs an action that deletes domains that are past their
|
||||
autorenew end date.
|
||||
</description>
|
||||
<schedule>every day 03:07</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/deleteProberData&runInEmpty]]></url>
|
||||
<description>
|
||||
This job clears out data from probers and runs once a week.
|
||||
</description>
|
||||
<schedule>every monday 14:00</schedule>
|
||||
<timezone>UTC</timezone>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<!-- TODO: Add borgmon job to check that these files are created and updated successfully. -->
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportReservedTerms&forEachRealTld]]></url>
|
||||
<description>
|
||||
Reserved terms export to Google Drive job for creating once-daily exports.
|
||||
</description>
|
||||
<schedule>every day 05:30</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportPremiumTerms&forEachRealTld]]></url>
|
||||
<description>
|
||||
Premium terms export to Google Drive job for creating once-daily exports.
|
||||
</description>
|
||||
<schedule>every day 05:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/readDnsQueue?jitterSeconds=45]]></url>
|
||||
<description>
|
||||
Lease all tasks from the dns-pull queue, group by TLD, and invoke PublishDnsUpdates for each
|
||||
group.
|
||||
</description>
|
||||
<schedule>every 1 minutes synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
</cronentries>
|
||||
|
||||
@@ -94,6 +94,12 @@
|
||||
<url-pattern>/registry-lock-verify</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Registrar console endpoints -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>frontend-servlet</servlet-name>
|
||||
<url-pattern>/console-api/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Security config -->
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<taskentries>
|
||||
|
||||
<!--
|
||||
/cron/fanout params:
|
||||
queue=<QUEUE_NAME>
|
||||
endpoint=<ENDPOINT_NAME> // URL Path of servlet, which may contain placeholders:
|
||||
runInEmpty // Run once, with no tld parameter
|
||||
forEachRealTld // Run for tlds with getTldType() == TldType.REAL
|
||||
forEachTestTld // Run for tlds with getTldType() == TldType.TEST
|
||||
exclude=TLD1[,TLD2] // exclude something otherwise included
|
||||
-->
|
||||
|
||||
<task>
|
||||
<url>/_dr/task/rdeStaging</url>
|
||||
<name>rdeStaging</name>
|
||||
<description>
|
||||
This job generates a full RDE escrow deposit as a single gigantic XML document
|
||||
and streams it to cloud storage. When this job has finished successfully, it'll
|
||||
launch a separate task that uploads the deposit file to Iron Mountain via SFTP.
|
||||
</description>
|
||||
<!--
|
||||
This only needs to run once per day, but we launch additional jobs in case the
|
||||
cursor is lagging behind, so it'll catch up to the current date as quickly as
|
||||
possible. The only job that'll run under normal circumstances is the one that's
|
||||
close to midnight, since if the cursor is up-to-date, the task is a no-op.
|
||||
We want it to be close to midnight because that reduces the chance that the
|
||||
point-in-time code won't have to go to the extra trouble of fetching old
|
||||
versions of objects from the database. However, we don't want it to run too
|
||||
close to midnight, because there's always a chance that a change which was
|
||||
timestamped before midnight hasn't fully been committed to the database. So
|
||||
we add a 4+ minute grace period to ensure the transactions cool down, since
|
||||
our queries are not transactional.
|
||||
-->
|
||||
<schedule>7 */4 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=rde-upload&endpoint=/_dr/task/rdeUpload&forEachRealTld]]></url>
|
||||
<name>rdeUpload</name>
|
||||
<description>
|
||||
This job is a no-op unless RdeUploadCursor falls behind for some reason.
|
||||
</description>
|
||||
<schedule>0 */4 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=rde-report&endpoint=/_dr/task/rdeReport&forEachRealTld]]></url>
|
||||
<name>rdeReport</name>
|
||||
<description>
|
||||
This job is a no-op unless RdeReportCursor falls behind for some reason.
|
||||
</description>
|
||||
<schedule>0 */4 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchDnl&runInEmpty]]></url>
|
||||
<name>tmchDnl</name>
|
||||
<description>
|
||||
This job downloads the latest DNL from MarksDB and inserts it into the database.
|
||||
(See: TmchDnlAction, ClaimsList)
|
||||
</description>
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchSmdrl&runInEmpty]]></url>
|
||||
<name>tmchSmdrl</name>
|
||||
<description>
|
||||
This job downloads the latest SMDRL from MarksDB and inserts it into the database.
|
||||
(See: TmchSmdrlAction, SignedMarkRevocationList)
|
||||
</description>
|
||||
<schedule>15 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchCrl&runInEmpty]]></url>
|
||||
<name>tmchCrl</name>
|
||||
<description>
|
||||
This job downloads the latest CRL from MarksDB and inserts it into the database.
|
||||
(See: TmchCrlAction)
|
||||
</description>
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/syncGroupMembers&runInEmpty]]></url>
|
||||
<name>syncGroupMembers</name>
|
||||
<description>
|
||||
Syncs RegistrarContact changes in the past hour to Google Groups.
|
||||
</description>
|
||||
<schedule>0 */1 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=sheet&endpoint=/_dr/task/syncRegistrarsSheet&runInEmpty]]></url>
|
||||
<name>syncRegistrarsSheet</name>
|
||||
<description>
|
||||
Synchronize Registrar entities to Google Spreadsheets.
|
||||
</description>
|
||||
<schedule>0 */1 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/deleteProberData&runInEmpty]]></url>
|
||||
<name>deleteProberData</name>
|
||||
<description>
|
||||
This job clears out data from probers and runs once a week.
|
||||
</description>
|
||||
<schedule>0 14 * * 1</schedule>
|
||||
</task>
|
||||
|
||||
<!-- TODO: Add borgmon job to check that these files are created and updated successfully. -->
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportReservedTerms&forEachRealTld]]></url>
|
||||
<name>exportReservedTerms</name>
|
||||
<description>
|
||||
Reserved terms export to Google Drive job for creating once-daily exports.
|
||||
</description>
|
||||
<schedule>30 5 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportPremiumTerms&forEachRealTld]]></url>
|
||||
<name>exportPremiumTerms</name>
|
||||
<description>
|
||||
Exports premium price lists to the Google Drive folders for each TLD once per day.
|
||||
</description>
|
||||
<schedule>0 5 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/readDnsQueue?jitterSeconds=45]]></url>
|
||||
<name>readDnsQueue</name>
|
||||
<description>
|
||||
Lease all tasks from the dns-pull queue, group by TLD, and invoke PublishDnsUpdates for each
|
||||
group.
|
||||
</description>
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/deleteExpiredDomains]]></url>
|
||||
<name>deleteExpiredDomains</name>
|
||||
<description>
|
||||
This job runs an action that deletes domains that are past their
|
||||
autorenew end date.
|
||||
</description>
|
||||
<schedule>7 3 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<!--
|
||||
The next two wipeout jobs are required when crash has production data.
|
||||
-->
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/wipeOutCloudSql]]></url>
|
||||
<name>wipeOutCloudSql</name>
|
||||
<description>
|
||||
This job runs an action that deletes all data in Cloud SQL.
|
||||
</description>
|
||||
<schedule>7 3 * * 6</schedule>
|
||||
</task>
|
||||
</taskentries>
|
||||
@@ -1,165 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--TODO: ptkach - Remove this file after cloud scheduler is fully up and running -->
|
||||
<cronentries>
|
||||
|
||||
<!--
|
||||
/cron/fanout params:
|
||||
queue=<QUEUE_NAME>
|
||||
endpoint=<ENDPOINT_NAME> // URL Path of servlet, which may contain placeholders:
|
||||
runInEmpty // Run once, with no tld parameter
|
||||
forEachRealTld // Run for tlds with getTldType() == TldType.REAL
|
||||
forEachTestTld // Run for tlds with getTldType() == TldType.TEST
|
||||
exclude=TLD1[,TLD2] // exclude something otherwise included
|
||||
-->
|
||||
|
||||
<cron>
|
||||
<url>/_dr/task/rdeStaging</url>
|
||||
<description>
|
||||
This job generates a full RDE escrow deposit as a single gigantic XML document
|
||||
and streams it to cloud storage. When this job has finished successfully, it'll
|
||||
launch a separate task that uploads the deposit file to Iron Mountain via SFTP.
|
||||
</description>
|
||||
<!--
|
||||
This only needs to run once per day, but we launch additional jobs in case the
|
||||
cursor is lagging behind, so it'll catch up to the current date as quickly as
|
||||
possible. The only job that'll run under normal circumstances is the one that's
|
||||
close to midnight, since if the cursor is up-to-date, the task is a no-op.
|
||||
|
||||
We want it to be close to midnight because that reduces the chance that the
|
||||
point-in-time code won't have to go to the extra trouble of fetching old
|
||||
versions of objects from the database. However, we don't want it to run too
|
||||
close to midnight, because there's always a chance that a change which was
|
||||
timestamped before midnight hasn't fully been committed to the database. So
|
||||
we add a 4+ minute grace period to ensure the transactions cool down, since
|
||||
our queries are not transactional.
|
||||
-->
|
||||
<schedule>every 4 hours from 00:07 to 20:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=rde-upload&endpoint=/_dr/task/rdeUpload&forEachRealTld]]></url>
|
||||
<description>
|
||||
This job is a no-op unless RdeUploadCursor falls behind for some reason.
|
||||
</description>
|
||||
<schedule>every 4 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=rde-report&endpoint=/_dr/task/rdeReport&forEachRealTld]]></url>
|
||||
<description>
|
||||
This job is a no-op unless RdeReportCursor falls behind for some reason.
|
||||
</description>
|
||||
<schedule>every 4 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchDnl&runInEmpty]]></url>
|
||||
<description>
|
||||
This job downloads the latest DNL from MarksDB and inserts it into the database.
|
||||
(See: TmchDnlServlet, ClaimsList)
|
||||
</description>
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchSmdrl&runInEmpty]]></url>
|
||||
<description>
|
||||
This job downloads the latest SMDRL from MarksDB and inserts it into the database.
|
||||
(See: TmchSmdrlServlet, SignedMarkRevocationList)
|
||||
</description>
|
||||
<schedule>every 12 hours from 00:15 to 12:15</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchCrl&runInEmpty]]></url>
|
||||
<description>
|
||||
This job downloads the latest CRL from MarksDB and inserts it into the database.
|
||||
(See: TmchCrlServlet)
|
||||
</description>
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/syncGroupMembers&runInEmpty]]></url>
|
||||
<description>
|
||||
Syncs RegistrarContact changes in the past hour to Google Groups.
|
||||
</description>
|
||||
<schedule>every 1 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=sheet&endpoint=/_dr/task/syncRegistrarsSheet&runInEmpty]]></url>
|
||||
<description>
|
||||
Synchronize Registrar entities to Google Spreadsheets.
|
||||
</description>
|
||||
<schedule>every 1 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/deleteProberData&runInEmpty]]></url>
|
||||
<description>
|
||||
This job clears out data from probers and runs once a week.
|
||||
</description>
|
||||
<schedule>every monday 14:00</schedule>
|
||||
<timezone>UTC</timezone>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<!-- TODO: Add borgmon job to check that these files are created and updated successfully. -->
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportReservedTerms&forEachRealTld]]></url>
|
||||
<description>
|
||||
Reserved terms export to Google Drive job for creating once-daily exports.
|
||||
</description>
|
||||
<schedule>every day 05:30</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportPremiumTerms&forEachRealTld]]></url>
|
||||
<description>
|
||||
Exports premium price lists to the Google Drive folders for each TLD once per day.
|
||||
</description>
|
||||
<schedule>every day 05:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/readDnsQueue?jitterSeconds=45]]></url>
|
||||
<description>
|
||||
Lease all tasks from the dns-pull queue, group by TLD, and invoke PublishDnsUpdates for each
|
||||
group.
|
||||
</description>
|
||||
<schedule>every 1 minutes synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/deleteExpiredDomains]]></url>
|
||||
<description>
|
||||
This job runs an action that deletes domains that are past their
|
||||
autorenew end date.
|
||||
</description>
|
||||
<schedule>every day 03:07</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<!--
|
||||
The next two wipeout jobs are required when crash has production data.
|
||||
-->
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/wipeOutCloudSql]]></url>
|
||||
<description>
|
||||
This job runs an action that deletes all data in Cloud SQL.
|
||||
</description>
|
||||
<schedule>every saturday 03:07</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
</cronentries>
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<manual-scaling>
|
||||
<instances>12</instances>
|
||||
<instances>24</instances>
|
||||
</manual-scaling>
|
||||
|
||||
<system-properties>
|
||||
|
||||
Vendored
+288
@@ -0,0 +1,288 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<taskentries>
|
||||
<task>
|
||||
<url>/_dr/task/rdeStaging</url>
|
||||
<name>rdeStaging</name>
|
||||
<description>
|
||||
This job generates a full RDE escrow deposit as a single gigantic XML document
|
||||
and streams it to cloud storage. When this job has finished successfully, it'll
|
||||
launch a separate task that uploads the deposit file to Iron Mountain via SFTP.
|
||||
</description>
|
||||
<!--
|
||||
This only needs to run once per day, but we launch additional jobs in case the
|
||||
cursor is lagging behind, so it'll catch up to the current date as quickly as
|
||||
possible. The only job that'll run under normal circumstances is the one that's
|
||||
close to midnight, since if the cursor is up-to-date, the task is a no-op.
|
||||
We want it to be close to midnight because that reduces the chance that the
|
||||
point-in-time code won't have to go to the extra trouble of fetching old
|
||||
versions of objects from the database. However, we don't want it to run too
|
||||
close to midnight, because there's always a chance that a change which was
|
||||
timestamped before midnight hasn't fully been committed to the database. So
|
||||
we add a 4+ minute grace period to ensure the transactions cool down, since
|
||||
our queries are not transactional.
|
||||
-->
|
||||
<schedule>7 */8 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=rde-upload&endpoint=/_dr/task/rdeUpload&forEachRealTld]]></url>
|
||||
<name>rdeUpload</name>
|
||||
<description>
|
||||
This job is a no-op unless RdeUploadCursor falls behind for some reason.
|
||||
</description>
|
||||
<schedule>0 */4 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=rde-report&endpoint=/_dr/task/rdeReport&forEachRealTld]]></url>
|
||||
<name>rdeReport</name>
|
||||
<description>
|
||||
This job is a no-op unless RdeReportCursor falls behind for some reason.
|
||||
</description>
|
||||
<schedule>0 */4 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchDnl&runInEmpty]]></url>
|
||||
<name>tmchDnl</name>
|
||||
<description>
|
||||
This job downloads the latest DNL from MarksDB and inserts it into the database.
|
||||
(See: TmchDnlAction, ClaimsList)
|
||||
</description>
|
||||
<schedule>0 0,12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchSmdrl&runInEmpty]]></url>
|
||||
<name>tmchSmdrl</name>
|
||||
<description>
|
||||
This job downloads the latest SMDRL from MarksDB and inserts it into the database.
|
||||
(See: TmchSmdrlAction, SignedMarkRevocationList)
|
||||
</description>
|
||||
<schedule>15 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchCrl&runInEmpty]]></url>
|
||||
<name>tmchCrl</name>
|
||||
<description>
|
||||
This job downloads the latest CRL from MarksDB and inserts it into the database.
|
||||
(See: TmchCrlAction)
|
||||
</description>
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/syncGroupMembers&runInEmpty]]></url>
|
||||
<name>syncGroupMembers</name>
|
||||
<description>
|
||||
Syncs RegistrarContact changes in the past hour to Google Groups.
|
||||
</description>
|
||||
<schedule>0 */1 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=sheet&endpoint=/_dr/task/syncRegistrarsSheet&runInEmpty]]></url>
|
||||
<name>syncRegistrarsSheet</name>
|
||||
<description>
|
||||
Synchronize Registrar entities to Google Spreadsheets.
|
||||
</description>
|
||||
<schedule>0 */1 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<!-- TODO: @ptkach add resaveAllEppResourcesPipelineAction https://cs.opensource.google/nomulus/nomulus/+/master:core/src/main/java/google/registry/env/production/default/WEB-INF/cron.xml;l=105 -->
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/updateRegistrarRdapBaseUrls]]></url>
|
||||
<name>updateRegistrarRdapBaseUrls</name>
|
||||
<description>
|
||||
This job reloads all registrar RDAP base URLs from ICANN.
|
||||
</description>
|
||||
<schedule>34 2 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportDomainLists&runInEmpty]]></url>
|
||||
<name>exportDomainLists</name>
|
||||
<description>
|
||||
This job exports lists of all active domain names to Google Drive and Google Cloud Storage.
|
||||
</description>
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/expandRecurringBillingEvents?advanceCursor]]></url>
|
||||
<name>expandRecurringBillingEvents</name>
|
||||
<description>
|
||||
This job runs an action that creates synthetic OneTime billing events from Recurring billing
|
||||
events. Events are created for all instances of Recurring billing events that should exist
|
||||
between the RECURRING_BILLING cursor's time and the execution time of the action.
|
||||
</description>
|
||||
<schedule>0 3 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/deleteExpiredDomains]]></url>
|
||||
<name>deleteExpiredDomains</name>
|
||||
<description>
|
||||
This job runs an action that deletes domains that are past their
|
||||
autorenew end date.
|
||||
</description>
|
||||
<schedule>7 3 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/sendExpiringCertificateNotificationEmail]]></url>
|
||||
<name>sendExpiringCertificateNotificationEmail</name>
|
||||
<description>
|
||||
This job runs an action that sends emails to partners if their certificates are expiring soon.
|
||||
</description>
|
||||
<schedule>30 4 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=nordn&endpoint=/_dr/task/nordnUpload&forEachRealTld&lordnPhase=sunrise]]></url>
|
||||
<name>nordnUploadSunrise</name>
|
||||
<description>
|
||||
This job uploads LORDN Sunrise CSV files for each TLD to MarksDB. It should be
|
||||
run at most every three hours, or at absolute minimum every 26 hours.
|
||||
</description>
|
||||
<!-- This may be set anywhere between "every 3 hours" and "every 25 hours". -->
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=nordn&endpoint=/_dr/task/nordnUpload&forEachRealTld&lordnPhase=claims]]></url>
|
||||
<name>nordnUploadClaims</name>
|
||||
<description>
|
||||
This job uploads LORDN Claims CSV files for each TLD to MarksDB. It should be
|
||||
run at most every three hours, or at absolute minimum every 26 hours.
|
||||
</description>
|
||||
<!-- This may be set anywhere between "every 3 hours" and "every 25 hours". -->
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=nordn&endpoint=/_dr/task/nordnUpload&forEachRealTld&lordnPhase=sunrise&pullQueue]]></url>
|
||||
<name>nordnUploadSunrisePullQueue</name>
|
||||
<description>
|
||||
This job uploads LORDN Sunrise CSV files for each TLD to MarksDB using
|
||||
pull queue. It should be run at most every three hours, or at absolute
|
||||
minimum every 26 hours.
|
||||
</description>
|
||||
<!-- This may be set anywhere between "every 3 hours" and "every 25 hours". -->
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=nordn&endpoint=/_dr/task/nordnUpload&forEachRealTld&lordnPhase=claims&pullQueue]]></url>
|
||||
<name>nordnUploadClaimsPullQueue</name>
|
||||
<description>
|
||||
This job uploads LORDN Claims CSV files for each TLD to MarksDB using pull
|
||||
queue. It should be run at most every three hours, or at absolute minimum
|
||||
every 26 hours.
|
||||
</description>
|
||||
<!-- This may be set anywhere between "every 3 hours" and "every 25 hours". -->
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/deleteProberData&runInEmpty]]></url>
|
||||
<name>deleteProberData</name>
|
||||
<description>
|
||||
This job clears out data from probers and runs once a week.
|
||||
</description>
|
||||
<schedule>0 14 * * 1</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportReservedTerms&forEachRealTld]]></url>
|
||||
<name>exportReservedTerms</name>
|
||||
<description>
|
||||
Reserved terms export to Google Drive job for creating once-daily exports.
|
||||
</description>
|
||||
<schedule>30 5 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportPremiumTerms&forEachRealTld]]></url>
|
||||
<name>exportPremiumTerms</name>
|
||||
<description>
|
||||
Exports premium price lists to the Google Drive folders for each TLD once per day.
|
||||
</description>
|
||||
<schedule>0 5 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/readDnsQueue?jitterSeconds=45]]></url>
|
||||
<name>readDnsQueue</name>
|
||||
<description>
|
||||
Lease all tasks from the dns-pull queue, group by TLD, and invoke PublishDnsUpdates for each
|
||||
group.
|
||||
</description>
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/icannReportingStaging&runInEmpty]]></url>
|
||||
<name>icannReportingStaging</name>
|
||||
<description>
|
||||
Create ICANN activity and transaction reports for last month, storing them in
|
||||
gs://[PROJECT-ID]-reporting/icann/monthly/yyyy-MM
|
||||
Upon success, enqueues the icannReportingUpload task to POST these files to ICANN.
|
||||
</description>
|
||||
<schedule>0 9 2 * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/icannReportingUpload&runInEmpty]]></url>
|
||||
<name>icannReportingUpload</name>
|
||||
<description>
|
||||
Checks if the monthly ICANN reports have been successfully uploaded. If they have not, attempts to upload them again.
|
||||
Most of the time, this job should not do anything since the uploads are triggered when the reports are staged.
|
||||
However, in the event that an upload failed for any reason (e.g. ICANN server is down, IP allow list issues),
|
||||
this cron job will continue to retry uploads daily until they succeed.
|
||||
</description>
|
||||
<schedule>0 15 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/generateInvoices?shouldPublish=true&runInEmpty]]></url>
|
||||
<name>generateInvoices</name>
|
||||
<description>
|
||||
Starts the beam/billing/InvoicingPipeline Dataflow template, which creates the overall invoice and
|
||||
detail report CSVs for last month, storing them in gs://[PROJECT-ID]-billing/invoices/yyyy-MM.
|
||||
Upon success, sends an e-mail copy of the invoice to billing personnel, and copies detail
|
||||
reports to the associated registrars' drive folders.
|
||||
See GenerateInvoicesAction for more details.
|
||||
</description>
|
||||
<!--WARNING: This must occur AFTER expandRecurringBillingEvents and AFTER exportSnapshot, as
|
||||
it uses Bigquery as the source of truth for billable events. ExportSnapshot usually takes
|
||||
about 2 hours to complete, so we give 11 hours to be safe. Normally, we give 24+ hours (see
|
||||
icannReportingStaging), but the invoicing team prefers receiving the e-mail on the first of
|
||||
each month. -->
|
||||
<schedule>0 19 1 * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/generateSpec11&runInEmpty]]></url>
|
||||
<name>generateSpec11</name>
|
||||
<description>
|
||||
Starts the beam/spec11/Spec11Pipeline Dataflow template, which creates today's Spec11
|
||||
report. This report is stored in gs://[PROJECT-ID]-reporting/icann/spec11/yyyy-MM/.
|
||||
This job will only send email notifications on the second of every month.
|
||||
See GenerateSpec11ReportAction for more details.
|
||||
</description>
|
||||
<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>
|
||||
</taskentries>
|
||||
+1
-285
@@ -1,288 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--TODO: ptkach - Remove this file after cloud scheduler is fully up and running -->
|
||||
<cronentries>
|
||||
|
||||
<!--
|
||||
/cron/fanout params:
|
||||
queue=<QUEUE_NAME>
|
||||
endpoint=<ENDPOINT_NAME> // URL Path of servlet, which may contain placeholders:
|
||||
runInEmpty // Run once, with no tld parameter
|
||||
forEachRealTld // Run for tlds with getTldType() == TldType.REAL
|
||||
forEachTestTld // Run for tlds with getTldType() == TldType.TEST
|
||||
exclude=TLD1[,TLD2] // exclude something otherwise included
|
||||
-->
|
||||
|
||||
<cron>
|
||||
<url>/_dr/task/rdeStaging</url>
|
||||
<description>
|
||||
This job generates a full RDE escrow deposit as a single gigantic XML document
|
||||
and streams it to cloud storage. When this job has finished successfully, it'll
|
||||
launch a separate task that uploads the deposit file to Iron Mountain via SFTP.
|
||||
</description>
|
||||
<!--
|
||||
This only needs to run once per day, but we launch additional jobs in case the
|
||||
cursor is lagging behind, so it'll catch up to the current date as quickly as
|
||||
possible. The only job that'll run under normal circumstances is the one that's
|
||||
close to midnight, since if the cursor is up-to-date, the task is a no-op.
|
||||
|
||||
We want it to be close to midnight because that reduces the chance that the
|
||||
point-in-time code won't have to go to the extra trouble of fetching old
|
||||
versions of objects from the database. However, we don't want it to run too
|
||||
close to midnight, because there's always a chance that a change which was
|
||||
timestamped before midnight hasn't fully been committed to the database. So
|
||||
we add a 4+ minute grace period to ensure the transactions cool down, since
|
||||
our queries are not transactional.
|
||||
-->
|
||||
<schedule>every 8 hours from 00:07 to 20:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=rde-upload&endpoint=/_dr/task/rdeUpload&forEachRealTld]]></url>
|
||||
<description>
|
||||
This job is a no-op unless RdeUploadCursor falls behind for some reason.
|
||||
</description>
|
||||
<schedule>every 4 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=rde-report&endpoint=/_dr/task/rdeReport&forEachRealTld]]></url>
|
||||
<description>
|
||||
This job is a no-op unless RdeReportCursor falls behind for some reason.
|
||||
</description>
|
||||
<schedule>every 4 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchDnl&runInEmpty]]></url>
|
||||
<description>
|
||||
This job downloads the latest DNL from MarksDB and inserts it into the database.
|
||||
(See: TmchDnlServlet, ClaimsList)
|
||||
</description>
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchSmdrl&runInEmpty]]></url>
|
||||
<description>
|
||||
This job downloads the latest SMDRL from MarksDB and inserts it into the database.
|
||||
(See: TmchSmdrlServlet, SignedMarkRevocationList)
|
||||
</description>
|
||||
<schedule>every 12 hours from 00:15 to 12:15</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchCrl&runInEmpty]]></url>
|
||||
<description>
|
||||
This job downloads the latest CRL from MarksDB and inserts it into the database.
|
||||
(See: TmchCrlServlet)
|
||||
</description>
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/syncGroupMembers&runInEmpty]]></url>
|
||||
<description>
|
||||
Syncs RegistrarContact changes in the past hour to Google Groups.
|
||||
</description>
|
||||
<schedule>every 1 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=sheet&endpoint=/_dr/task/syncRegistrarsSheet&runInEmpty]]></url>
|
||||
<description>
|
||||
Synchronize Registrar entities to Google Spreadsheets.
|
||||
</description>
|
||||
<schedule>every 1 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<!-- TODO(b/249863289): disable until it is safe to run this pipeline
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResourcesPipeline?fast=true]]></url>
|
||||
<description>
|
||||
This job resaves all our resources, projected in time to "now".
|
||||
</description>
|
||||
<schedule>1st monday of month 09:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
-->
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/updateRegistrarRdapBaseUrls]]></url>
|
||||
<description>
|
||||
This job reloads all registrar RDAP base URLs from ICANN.
|
||||
</description>
|
||||
<schedule>every day 02:34</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportDomainLists&runInEmpty]]></url>
|
||||
<description>
|
||||
This job exports lists of all active domain names to Google Drive and Google Cloud Storage.
|
||||
</description>
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/expandRecurringBillingEvents?advanceCursor]]></url>
|
||||
<description>
|
||||
This job runs an action that creates synthetic OneTime billing events from Recurring billing
|
||||
events. Events are created for all instances of Recurring billing events that should exist
|
||||
between the RECURRING_BILLING cursor's time and the execution time of the action.
|
||||
</description>
|
||||
<schedule>every day 03:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/deleteExpiredDomains]]></url>
|
||||
<description>
|
||||
This job runs an action that deletes domains that are past their
|
||||
autorenew end date.
|
||||
</description>
|
||||
<schedule>every day 03:07</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/sendExpiringCertificateNotificationEmail]]></url>
|
||||
<description>
|
||||
This job runs an action that sends emails to partners if their certificates are expiring soon.
|
||||
</description>
|
||||
<schedule>every day 04:30</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=nordn&endpoint=/_dr/task/nordnUpload&forEachRealTld&lordn-phase=sunrise]]></url>
|
||||
<description>
|
||||
This job uploads LORDN Sunrise CSV files for each TLD to MarksDB. It should be
|
||||
run at most every three hours, or at absolute minimum every 26 hours.
|
||||
</description>
|
||||
<!-- This may be set anywhere between "every 3 hours" and "every 25 hours". -->
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<timezone>UTC</timezone>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=nordn&endpoint=/_dr/task/nordnUpload&forEachRealTld&lordn-phase=claims]]></url>
|
||||
<description>
|
||||
This job uploads LORDN Claims CSV files for each TLD to MarksDB. It should be
|
||||
run at most every three hours, or at absolute minimum every 26 hours.
|
||||
</description>
|
||||
<!-- This may be set anywhere between "every 3 hours" and "every 25 hours". -->
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<timezone>UTC</timezone>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/deleteProberData&runInEmpty]]></url>
|
||||
<description>
|
||||
This job clears out data from probers and runs once a week.
|
||||
</description>
|
||||
<schedule>every monday 14:00</schedule>
|
||||
<timezone>UTC</timezone>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportReservedTerms&forEachRealTld]]></url>
|
||||
<description>
|
||||
Reserved terms export to Google Drive job for creating once-daily exports.
|
||||
</description>
|
||||
<schedule>every day 05:30</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportPremiumTerms&forEachRealTld]]></url>
|
||||
<description>
|
||||
Exports premium price lists to the Google Drive folders for each TLD once per day.
|
||||
</description>
|
||||
<schedule>every day 05:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/readDnsQueue?jitterSeconds=45]]></url>
|
||||
<description>
|
||||
Lease all tasks from the dns-pull queue, group by TLD, and invoke PublishDnsUpdates for each
|
||||
group.
|
||||
</description>
|
||||
<schedule>every 1 minutes synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/icannReportingStaging&runInEmpty]]></url>
|
||||
<description>
|
||||
Create ICANN activity and transaction reports for last month, storing them in
|
||||
gs://[PROJECT-ID]-reporting/icann/monthly/yyyy-MM
|
||||
Upon success, enqueues the icannReportingUpload task to POST these files to ICANN.
|
||||
</description>
|
||||
<schedule>2 of month 09:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/icannReportingUpload&runInEmpty]]></url>
|
||||
<description>
|
||||
Checks if the monthly ICANN reports have been successfully uploaded. If they have not, attempts to upload them again.
|
||||
Most of the time, this job should not do anything since the uploads are triggered when the reports are staged.
|
||||
However, in the event that an upload failed for any reason (e.g. ICANN server is down, IP allow list issues),
|
||||
this cron job will continue to retry uploads daily until they succeed.
|
||||
</description>
|
||||
<schedule>every day 15:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/generateInvoices?shouldPublish=true&runInEmpty]]></url>
|
||||
<description>
|
||||
Starts the beam/billing/InvoicingPipeline Dataflow template, which creates the overall invoice and
|
||||
detail report CSVs for last month, storing them in gs://[PROJECT-ID]-billing/invoices/yyyy-MM.
|
||||
Upon success, sends an e-mail copy of the invoice to billing personnel, and copies detail
|
||||
reports to the associated registrars' drive folders.
|
||||
See GenerateInvoicesAction for more details.
|
||||
</description>
|
||||
<!--WARNING: This must occur AFTER expandRecurringBillingEvents and AFTER exportSnapshot, as
|
||||
it uses Bigquery as the source of truth for billable events. ExportSnapshot usually takes
|
||||
about 2 hours to complete, so we give 11 hours to be safe. Normally, we give 24+ hours (see
|
||||
icannReportingStaging), but the invoicing team prefers receiving the e-mail on the first of
|
||||
each month. -->
|
||||
<schedule>1 of month 19:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/generateSpec11&runInEmpty]]></url>
|
||||
<description>
|
||||
Starts the beam/spec11/Spec11Pipeline Dataflow template, which creates today's Spec11
|
||||
report. This report is stored in gs://[PROJECT-ID]-reporting/icann/spec11/yyyy-MM/.
|
||||
This job will only send email notifications on the second of every month.
|
||||
See GenerateSpec11ReportAction for more details.
|
||||
</description>
|
||||
<schedule>every day 15:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/wipeOutContactHistoryPii]]></url>
|
||||
<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>every monday 15:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
</cronentries>
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<taskentries>
|
||||
<task>
|
||||
<url>/_dr/task/rdeStaging</url>
|
||||
<name>rdeStaging</name>
|
||||
<description>
|
||||
This job generates a full RDE escrow deposit as a single gigantic XML document
|
||||
and streams it to cloud storage. When this job has finished successfully, it'll
|
||||
launch a separate task that uploads the deposit file to Iron Mountain via SFTP.
|
||||
</description>
|
||||
<schedule>7 0 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/readDnsQueue?jitterSeconds=45]]></url>
|
||||
<name>readDnsQueue</name>
|
||||
<description>
|
||||
Lease all tasks from the dns-pull queue, group by TLD, and invoke PublishDnsUpdates for each
|
||||
group.
|
||||
</description>
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=sheet&endpoint=/_dr/task/syncRegistrarsSheet&runInEmpty]]></url>
|
||||
<name>syncRegistrarsSheet</name>
|
||||
<description>
|
||||
Synchronize Registrar entities to Google Spreadsheets.
|
||||
</description>
|
||||
<schedule>0 */1 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResourcesPipeline?fast=true]]></url>
|
||||
<name>resaveAllEppResourcesPipeline</name>
|
||||
<description>
|
||||
This job resaves all our resources, projected in time to "now".
|
||||
</description>
|
||||
<!--Deviation from cron tasks schedule: 1st monday of month 09:00 is replaced
|
||||
with 1st of the month 09:00 -->
|
||||
<schedule>0 9 1 * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/syncGroupMembers&runInEmpty]]></url>
|
||||
<name>syncGroupMembers</name>
|
||||
<description>
|
||||
Syncs RegistrarContact changes in the past hour to Google Groups.
|
||||
</description>
|
||||
<schedule>0 */1 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/deleteExpiredDomains]]></url>
|
||||
<name>deleteExpiredDomains</name>
|
||||
<description>
|
||||
This job runs an action that deletes domains that are past their
|
||||
autorenew end date.
|
||||
</description>
|
||||
<schedule>7 3 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/wipeOutCloudSql]]></url>
|
||||
<name>wipeOutCloudSql</name>
|
||||
<description>
|
||||
This job runs an action that deletes all data in Cloud SQL.
|
||||
</description>
|
||||
<schedule>7 3 * * 6</schedule>
|
||||
</task>
|
||||
</taskentries>
|
||||
@@ -1,70 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--TODO: ptkach - Remove this file after cloud scheduler is fully up and running -->
|
||||
<cronentries>
|
||||
|
||||
<cron>
|
||||
<url>/_dr/task/rdeStaging</url>
|
||||
<description>
|
||||
This job generates a full RDE escrow deposit as a single gigantic XML document
|
||||
and streams it to cloud storage. When this job has finished successfully, it'll
|
||||
launch a separate task that uploads the deposit file to Iron Mountain via SFTP.
|
||||
</description>
|
||||
<schedule>every day 00:07</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/readDnsQueue?jitterSeconds=45]]></url>
|
||||
<description>
|
||||
Lease all tasks from the dns-pull queue, group by TLD, and invoke PublishDnsUpdates for each
|
||||
group.
|
||||
</description>
|
||||
<schedule>every 1 minutes synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=sheet&endpoint=/_dr/task/syncRegistrarsSheet&runInEmpty]]></url>
|
||||
<description>
|
||||
Synchronize Registrar entities to Google Spreadsheets.
|
||||
</description>
|
||||
<schedule>every 1 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResourcesPipeline?fast=true]]></url>
|
||||
<description>
|
||||
This job resaves all our resources, projected in time to "now".
|
||||
</description>
|
||||
<schedule>1st monday of month 09:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/syncGroupMembers&runInEmpty]]></url>
|
||||
<description>
|
||||
Syncs RegistrarContact changes in the past hour to Google Groups.
|
||||
</description>
|
||||
<schedule>every 1 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/deleteExpiredDomains]]></url>
|
||||
<description>
|
||||
This job runs an action that deletes domains that are past their
|
||||
autorenew end date.
|
||||
</description>
|
||||
<schedule>every day 03:07</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/wipeOutCloudSql]]></url>
|
||||
<description>
|
||||
This job runs an action that deletes all data in Cloud SQL.
|
||||
</description>
|
||||
<schedule>every saturday 03:07</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
</cronentries>
|
||||
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<taskentries>
|
||||
<task>
|
||||
<url>/_dr/task/rdeStaging</url>
|
||||
<name>rdeStaging</name>
|
||||
<description>
|
||||
This job generates a full RDE escrow deposit as a single gigantic XML document
|
||||
and streams it to cloud storage. When this job has finished successfully, it'll
|
||||
launch a separate task that uploads the deposit file to Iron Mountain via SFTP.
|
||||
</description>
|
||||
<!--
|
||||
This only needs to run once per day, but we launch additional jobs in case the
|
||||
cursor is lagging behind, so it'll catch up to the current date eventually.
|
||||
|
||||
See <a href="../../../production/default/WEB-INF/cron.xml">production config</a> for an
|
||||
explanation of job starting times.
|
||||
-->
|
||||
<schedule>7 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=rde-upload&endpoint=/_dr/task/rdeUpload&forEachRealTld]]></url>
|
||||
<name>rdeUpload</name>
|
||||
<description>
|
||||
This job is a no-op unless RdeUploadCursor falls behind for some reason.
|
||||
</description>
|
||||
<schedule>0 */4 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchDnl&runInEmpty]]></url>
|
||||
<name>tmchDnl</name>
|
||||
<description>
|
||||
This job downloads the latest DNL from MarksDB and inserts it into the database.
|
||||
(See: TmchDnlAction, ClaimsList)
|
||||
</description>
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchSmdrl&runInEmpty]]></url>
|
||||
<name>tmchSmdrl</name>
|
||||
<description>
|
||||
This job downloads the latest SMDRL from MarksDB and inserts it into the database.
|
||||
(See: TmchSmdrlAction, SignedMarkRevocationList)
|
||||
</description>
|
||||
<schedule>15 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchCrl&runInEmpty]]></url>
|
||||
<name>tmchCrl</name>
|
||||
<description>
|
||||
This job downloads the latest CRL from MarksDB and inserts it into the database.
|
||||
(See: TmchCrlAction)
|
||||
</description>
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/syncGroupMembers&runInEmpty]]></url>
|
||||
<name>syncGroupMembers</name>
|
||||
<description>
|
||||
Syncs RegistrarContact changes in the past hour to Google Groups.
|
||||
</description>
|
||||
<schedule>0 */1 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=sheet&endpoint=/_dr/task/syncRegistrarsSheet&runInEmpty]]></url>
|
||||
<name>syncRegistrarsSheet</name>
|
||||
<description>
|
||||
Synchronize Registrar entities to Google Spreadsheets.
|
||||
</description>
|
||||
<schedule>0 */1 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportDomainLists&runInEmpty]]></url>
|
||||
<name>exportDomainLists</name>
|
||||
<description>
|
||||
This job exports lists of all active domain names to Google Drive and Google Cloud Storage.
|
||||
</description>
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/expandRecurringBillingEvents?advanceCursor]]></url>
|
||||
<name>expandRecurringBillingEvents</name>
|
||||
<description>
|
||||
This job runs an action that creates synthetic OneTime billing events from Recurring billing
|
||||
events. Events are created for all instances of Recurring billing events that should exist
|
||||
between the RECURRING_BILLING cursor's time and the execution time of the action.
|
||||
</description>
|
||||
<schedule>0 3 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/deleteExpiredDomains]]></url>
|
||||
<name>deleteExpiredDomains</name>
|
||||
<description>
|
||||
This job runs an action that deletes domains that are past their
|
||||
autorenew end date.
|
||||
</description>
|
||||
<schedule>7 3 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/deleteProberData&runInEmpty]]></url>
|
||||
<name>deleteProberData</name>
|
||||
<description>
|
||||
This job clears out data from probers and runs once a week.
|
||||
</description>
|
||||
<schedule>0 14 * * 1</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportReservedTerms&forEachRealTld]]></url>
|
||||
<name>exportReservedTerms</name>
|
||||
<description>
|
||||
Reserved terms export to Google Drive job for creating once-daily exports.
|
||||
</description>
|
||||
<schedule>30 5 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportPremiumTerms&forEachRealTld]]></url>
|
||||
<name>exportPremiumTerms</name>
|
||||
<description>
|
||||
Exports premium price lists to the Google Drive folders for each TLD once per day.
|
||||
</description>
|
||||
<schedule>0 5 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<!-- TODO: @ptkach add resaveAllEppResourcesPipelineAction https://cs.opensource.google/nomulus/nomulus/+/master:core/src/main/java/google/registry/env/sandbox/default/WEB-INF/cron.xml;l=89 -->
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/readDnsQueue?jitterSeconds=45]]></url>
|
||||
<name>readDnsQueue</name>
|
||||
<description>
|
||||
Lease all tasks from the dns-pull queue, group by TLD, and invoke PublishDnsUpdates for each
|
||||
group.
|
||||
</description>
|
||||
<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>
|
||||
</taskentries>
|
||||
@@ -1,177 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--TODO: ptkach - Remove this file after cloud scheduler is fully up and running -->
|
||||
<cronentries>
|
||||
|
||||
<!--
|
||||
/cron/fanout params:
|
||||
queue=<QUEUE_NAME>
|
||||
endpoint=<ENDPOINT_NAME> // URL Path of servlet, which may contain placeholders:
|
||||
runInEmpty // Run once, with no tld parameter
|
||||
forEachRealTld // Run for tlds with getTldType() == TldType.REAL
|
||||
forEachTestTld // Run for tlds with getTldType() == TldType.TEST
|
||||
exclude=TLD1[,TLD2] // exclude something otherwise included
|
||||
-->
|
||||
|
||||
<cron>
|
||||
<url>/_dr/task/rdeStaging</url>
|
||||
<description>
|
||||
This job generates a full RDE escrow deposit as a single gigantic XML document
|
||||
and streams it to cloud storage. When this job has finished successfully, it'll
|
||||
launch a separate task that uploads the deposit file to Iron Mountain via SFTP.
|
||||
</description>
|
||||
<!--
|
||||
This only needs to run once per day, but we launch additional jobs in case the
|
||||
cursor is lagging behind, so it'll catch up to the current date eventually.
|
||||
|
||||
See <a href="../../../production/default/WEB-INF/cron.xml">production config</a> for an
|
||||
explanation of job starting times.
|
||||
-->
|
||||
<schedule>every 12 hours from 00:07 to 12:07</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=rde-upload&endpoint=/_dr/task/rdeUpload&forEachRealTld]]></url>
|
||||
<description>
|
||||
This job is a no-op unless RdeUploadCursor falls behind for some reason.
|
||||
</description>
|
||||
<schedule>every 4 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchDnl&runInEmpty]]></url>
|
||||
<description>
|
||||
This job downloads the latest DNL from MarksDB and inserts it into the database.
|
||||
(See: TmchDnlServlet, ClaimsList)
|
||||
</description>
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchSmdrl&runInEmpty]]></url>
|
||||
<description>
|
||||
This job downloads the latest SMDRL from MarksDB and inserts it into the database.
|
||||
(See: TmchSmdrlServlet, SignedMarkRevocationList)
|
||||
</description>
|
||||
<schedule>every 12 hours from 00:15 to 12:15</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchCrl&runInEmpty]]></url>
|
||||
<description>
|
||||
This job downloads the latest CRL from MarksDB and inserts it into the database.
|
||||
(See: TmchCrlServlet)
|
||||
</description>
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/syncGroupMembers&runInEmpty]]></url>
|
||||
<description>
|
||||
Syncs RegistrarContact changes in the past hour to Google Groups.
|
||||
</description>
|
||||
<schedule>every 1 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=sheet&endpoint=/_dr/task/syncRegistrarsSheet&runInEmpty]]></url>
|
||||
<description>
|
||||
Synchronize Registrar entities to Google Spreadsheets.
|
||||
</description>
|
||||
<schedule>every 1 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<!-- TODO(b/249863289): disable until it is safe to run this pipeline
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResourcesPipeline?fast=true]]></url>
|
||||
<description>
|
||||
This job resaves all our resources, projected in time to "now".
|
||||
</description>
|
||||
<schedule>1st monday of month 09:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
-->
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportDomainLists&runInEmpty]]></url>
|
||||
<description>
|
||||
This job exports lists of all active domain names to Google Drive and Google Cloud Storage.
|
||||
</description>
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/expandRecurringBillingEvents?advanceCursor]]></url>
|
||||
<description>
|
||||
This job runs an action that creates synthetic OneTime billing events from Recurring billing
|
||||
events. Events are created for all instances of Recurring billing events that should exist
|
||||
between the RECURRING_BILLING cursor's time and the execution time of the action.
|
||||
</description>
|
||||
<schedule>every day 03:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/deleteExpiredDomains]]></url>
|
||||
<description>
|
||||
This job runs an action that deletes domains that are past their
|
||||
autorenew end date.
|
||||
</description>
|
||||
<schedule>every day 03:07</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/deleteProberData&runInEmpty]]></url>
|
||||
<description>
|
||||
This job clears out data from probers and runs once a week.
|
||||
</description>
|
||||
<schedule>every monday 14:00</schedule>
|
||||
<timezone>UTC</timezone>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportReservedTerms&forEachRealTld]]></url>
|
||||
<description>
|
||||
Reserved terms export to Google Drive job for creating once-daily exports.
|
||||
</description>
|
||||
<schedule>every day 05:30</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportPremiumTerms&forEachRealTld]]></url>
|
||||
<description>
|
||||
Exports premium price lists to the Google Drive folders for each TLD once per day.
|
||||
</description>
|
||||
<schedule>every day 05:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/readDnsQueue?jitterSeconds=45]]></url>
|
||||
<description>
|
||||
Lease all tasks from the dns-pull queue, group by TLD, and invoke PublishDnsUpdates for each
|
||||
group.
|
||||
</description>
|
||||
<schedule>every 1 minutes synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/wipeOutContactHistoryPii]]></url>
|
||||
<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>every monday 15:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
</cronentries>
|
||||
|
||||
@@ -86,6 +86,8 @@ import google.registry.model.billing.BillingEvent.Flag;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainCommand;
|
||||
import google.registry.model.domain.DomainCommand.Create;
|
||||
@@ -123,6 +125,7 @@ import google.registry.model.tmch.ClaimsList;
|
||||
import google.registry.model.tmch.ClaimsListDao;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.tmch.LordnTaskUtils;
|
||||
import google.registry.tmch.LordnTaskUtils.LordnPhase;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -162,6 +165,7 @@ import org.joda.time.Duration;
|
||||
* @error {@link DomainFlowTmchUtils.NoMarksFoundMatchingDomainException}
|
||||
* @error {@link DomainFlowTmchUtils.FoundMarkNotYetValidException}
|
||||
* @error {@link DomainFlowTmchUtils.FoundMarkExpiredException}
|
||||
* @error {@link DomainFlowTmchUtils.SignedMarkRevokedErrorException}
|
||||
* @error {@link DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link DomainFlowUtils.AcceptedTooLongAgoException}
|
||||
* @error {@link DomainFlowUtils.BadDomainNameCharacterException}
|
||||
@@ -276,8 +280,12 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
registrarId,
|
||||
now,
|
||||
eppInput.getSingleExtension(AllocationTokenExtension.class));
|
||||
boolean defaultTokenUsed = false;
|
||||
if (!allocationToken.isPresent() && !registry.getDefaultPromoTokens().isEmpty()) {
|
||||
allocationToken = checkForDefaultToken(registry, command);
|
||||
if (allocationToken.isPresent()) {
|
||||
defaultTokenUsed = true;
|
||||
}
|
||||
}
|
||||
boolean isAnchorTenant =
|
||||
isAnchorTenant(
|
||||
@@ -336,7 +344,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
FeesAndCredits feesAndCredits =
|
||||
pricingLogic.getCreatePrice(
|
||||
registry, targetId, now, years, isAnchorTenant, allocationToken);
|
||||
validateFeeChallenge(feeCreate, feesAndCredits);
|
||||
validateFeeChallenge(feeCreate, feesAndCredits, defaultTokenUsed);
|
||||
Optional<SecDnsCreateExtension> secDnsCreate =
|
||||
validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class));
|
||||
DateTime registrationExpirationTime = leapSafeAddYears(now, years);
|
||||
@@ -376,7 +384,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
reservationTypes.contains(NAME_COLLISION)
|
||||
? ImmutableSet.of(SERVER_HOLD)
|
||||
: ImmutableSet.of();
|
||||
Domain domain =
|
||||
Domain.Builder domainBuilder =
|
||||
new Domain.Builder()
|
||||
.setCreationRegistrarId(registrarId)
|
||||
.setPersistedCurrentSponsorRegistrarId(registrarId)
|
||||
@@ -396,7 +404,14 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
.setContacts(command.getContacts())
|
||||
.addGracePeriod(
|
||||
GracePeriod.forBillingEvent(GracePeriodStatus.ADD, repoId, createBillingEvent))
|
||||
.build();
|
||||
.setLordnPhase(
|
||||
!DatabaseMigrationStateSchedule.getValueAtTime(tm().getTransactionTime())
|
||||
.equals(MigrationState.NORDN_SQL)
|
||||
? LordnPhase.NONE
|
||||
: hasSignedMarks
|
||||
? LordnPhase.SUNRISE
|
||||
: hasClaimsNotice ? LordnPhase.CLAIMS : LordnPhase.NONE);
|
||||
Domain domain = domainBuilder.build();
|
||||
if (allocationToken.isPresent()
|
||||
&& allocationToken.get().getTokenType().equals(TokenType.PACKAGE)) {
|
||||
if (years > 1) {
|
||||
@@ -696,7 +711,9 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
if (newDomain.shouldPublishToDns()) {
|
||||
dnsQueue.addDomainRefreshTask(newDomain.getDomainName());
|
||||
}
|
||||
if (hasClaimsNotice || hasSignedMarks) {
|
||||
if (!DatabaseMigrationStateSchedule.getValueAtTime(tm().getTransactionTime())
|
||||
.equals(MigrationState.NORDN_SQL)
|
||||
&& (hasClaimsNotice || hasSignedMarks)) {
|
||||
LordnTaskUtils.enqueueDomainTask(newDomain);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -778,12 +778,13 @@ public class DomainFlowUtils {
|
||||
*/
|
||||
public static void validateFeeChallenge(
|
||||
final Optional<? extends FeeTransformCommandExtension> feeCommand,
|
||||
FeesAndCredits feesAndCredits)
|
||||
FeesAndCredits feesAndCredits,
|
||||
boolean defaultTokenUsed)
|
||||
throws EppException {
|
||||
if (feesAndCredits.hasAnyPremiumFees() && !feeCommand.isPresent()) {
|
||||
throw new FeesRequiredForPremiumNameException();
|
||||
}
|
||||
validateFeesAckedIfPresent(feeCommand, feesAndCredits);
|
||||
validateFeesAckedIfPresent(feeCommand, feesAndCredits, defaultTokenUsed);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -795,7 +796,8 @@ public class DomainFlowUtils {
|
||||
*/
|
||||
public static void validateFeesAckedIfPresent(
|
||||
final Optional<? extends FeeTransformCommandExtension> feeCommand,
|
||||
FeesAndCredits feesAndCredits)
|
||||
FeesAndCredits feesAndCredits,
|
||||
boolean defaultTokenUsed)
|
||||
throws EppException {
|
||||
// Check for the case where a fee command extension was required but not provided.
|
||||
// This only happens when the total fees are non-zero and include custom fees requiring the
|
||||
@@ -856,7 +858,9 @@ public class DomainFlowUtils {
|
||||
// Checking if total amount is expected. Extra fees that we are not expecting may be passed in.
|
||||
// Or if there is only a single fee type expected.
|
||||
if (!feeTotal.equals(feesAndCredits.getTotalCost())) {
|
||||
throw new FeesMismatchException(feesAndCredits.getTotalCost());
|
||||
if (!defaultTokenUsed || feeTotal.isLessThan(feesAndCredits.getTotalCost())) {
|
||||
throw new FeesMismatchException(feesAndCredits.getTotalCost());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||
now,
|
||||
years,
|
||||
existingRecurringBillingEvent);
|
||||
validateFeeChallenge(feeRenew, feesAndCredits);
|
||||
validateFeeChallenge(feeRenew, feesAndCredits, false);
|
||||
flowCustomLogic.afterValidation(
|
||||
AfterValidationParameters.newBuilder()
|
||||
.setExistingDomain(existingDomain)
|
||||
|
||||
@@ -226,7 +226,7 @@ public final class DomainRestoreRequestFlow implements TransactionalFlow {
|
||||
if (!existingDomain.getGracePeriodStatuses().contains(GracePeriodStatus.REDEMPTION)) {
|
||||
throw new DomainNotEligibleForRestoreException();
|
||||
}
|
||||
validateFeeChallenge(feeUpdate, feesAndCredits);
|
||||
validateFeeChallenge(feeUpdate, feesAndCredits, false);
|
||||
}
|
||||
|
||||
private static Domain performRestore(
|
||||
|
||||
@@ -210,7 +210,7 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||
}
|
||||
|
||||
if (feesAndCredits.isPresent()) {
|
||||
validateFeeChallenge(feeTransfer, feesAndCredits.get());
|
||||
validateFeeChallenge(feeTransfer, feesAndCredits.get(), false);
|
||||
}
|
||||
HistoryEntryId domainHistoryId = createHistoryEntryId(existingDomain);
|
||||
historyBuilder
|
||||
|
||||
@@ -231,7 +231,7 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
Optional<FeeUpdateCommandExtension> feeUpdate =
|
||||
eppInput.getSingleExtension(FeeUpdateCommandExtension.class);
|
||||
FeesAndCredits feesAndCredits = pricingLogic.getUpdatePrice(registry, targetId, now);
|
||||
validateFeesAckedIfPresent(feeUpdate, feesAndCredits);
|
||||
validateFeesAckedIfPresent(feeUpdate, feesAndCredits, false);
|
||||
verifyNotInPendingDelete(
|
||||
add.getContacts(),
|
||||
command.getInnerChange().getRegistrant(),
|
||||
|
||||
@@ -68,7 +68,6 @@ U+011F # LATIN SMALL LETTER G WITH BREVE
|
||||
U+01E7 # LATIN SMALL LETTER G WITH CARON
|
||||
U+0121 # LATIN SMALL LETTER G WITH DOT ABOVE
|
||||
U+0123 # LATIN SMALL LETTER G WITH CEDILLA
|
||||
U+01E5 # LATIN SMALL LETTER G WITH STROKE
|
||||
U+0068 # LATIN SMALL LETTER H
|
||||
U+0127 # LATIN SMALL LETTER H WITH STROKE
|
||||
U+0069 # LATIN SMALL LETTER I
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.model;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
@@ -28,6 +29,7 @@ import org.joda.time.DateTime;
|
||||
public class CreateAutoTimestamp extends ImmutableObject implements UnsafeSerializable {
|
||||
|
||||
@Column(nullable = false)
|
||||
@Expose
|
||||
DateTime creationTime;
|
||||
|
||||
@PrePersist
|
||||
|
||||
@@ -31,8 +31,8 @@ import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.dns.RefreshDnsAction;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.transfer.TransferData;
|
||||
import google.registry.persistence.VKey;
|
||||
@@ -41,7 +41,6 @@ import java.time.Duration;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
import javax.persistence.AttributeOverride;
|
||||
@@ -67,7 +66,7 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc5730">RFC 5730</a>
|
||||
*/
|
||||
@Transient String repoId;
|
||||
@Expose @Transient String repoId;
|
||||
|
||||
/**
|
||||
* The ID of the registrar that is currently sponsoring this resource.
|
||||
@@ -75,7 +74,7 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
* <p>This can be null in the case of pre-Registry-3.0-migration history objects with null
|
||||
* resource fields.
|
||||
*/
|
||||
String currentSponsorRegistrarId;
|
||||
@Expose String currentSponsorRegistrarId;
|
||||
|
||||
/**
|
||||
* The ID of the registrar that created this resource.
|
||||
@@ -83,7 +82,7 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
* <p>This can be null in the case of pre-Registry-3.0-migration history objects with null
|
||||
* resource fields.
|
||||
*/
|
||||
String creationRegistrarId;
|
||||
@Expose String creationRegistrarId;
|
||||
|
||||
/**
|
||||
* The ID of the registrar that last updated this resource.
|
||||
@@ -92,7 +91,7 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
* edits; it only includes EPP-visible modifications such as {@literal <update>}. Can be null if
|
||||
* the resource has never been modified.
|
||||
*/
|
||||
String lastEppUpdateRegistrarId;
|
||||
@Expose String lastEppUpdateRegistrarId;
|
||||
|
||||
/**
|
||||
* The time when this resource was created.
|
||||
@@ -106,6 +105,7 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
*/
|
||||
// Need to override the default non-null column attribute.
|
||||
@AttributeOverride(name = "creationTime", column = @Column)
|
||||
@Expose
|
||||
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
|
||||
|
||||
/**
|
||||
@@ -130,27 +130,10 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
* edits; it only includes EPP-visible modifications such as {@literal <update>}. Can be null if
|
||||
* the resource has never been modified.
|
||||
*/
|
||||
DateTime lastEppUpdateTime;
|
||||
@Expose DateTime lastEppUpdateTime;
|
||||
|
||||
/** Status values associated with this resource. */
|
||||
Set<StatusValue> statuses;
|
||||
|
||||
/**
|
||||
* When this domain/host's DNS was requested to be refreshed, or null if its DNS is up-to-date.
|
||||
*
|
||||
* <p>This will almost always be null except in the couple of minutes' interval between when a
|
||||
* DNS-affecting create or update operation takes place and when the {@link RefreshDnsAction}
|
||||
* runs, which resets this back to null upon completion of the DNS refresh task. This is a {@link
|
||||
* DateTime} rather than a simple dirty boolean so that the DNS refresh action can order by the
|
||||
* DNS refresh request time and take action on the oldest ones first.
|
||||
*
|
||||
* <p>Note that in the {@code DomainHistory}/{@code HostHistory} table this value means something
|
||||
* slightly different: It means that the given domain/host action requested a DNS update. Unlike
|
||||
* on the {@code Domain}/{code Host} table, this value is not then subsequently nulled out once
|
||||
* the DNS refresh is complete; rather, it remains as a permanent record of which actions were
|
||||
* DNS-affecting and which were not.
|
||||
*/
|
||||
@Transient @Nullable protected DateTime dnsRefreshRequestTime;
|
||||
@Expose Set<StatusValue> statuses;
|
||||
|
||||
public String getRepoId() {
|
||||
return repoId;
|
||||
@@ -203,19 +186,6 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
return deletionTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DNS refresh request time iff this domain/host's DNS needs refreshing, otherwise
|
||||
* absent.
|
||||
*/
|
||||
public Optional<DateTime> getDnsRefreshRequestTime() {
|
||||
return Optional.ofNullable(dnsRefreshRequestTime);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void setInternalDnsRefreshRequestTime(DateTime time) {
|
||||
dnsRefreshRequestTime = time;
|
||||
}
|
||||
|
||||
/** Return a clone of the resource with timed status values modified using the given time. */
|
||||
public abstract EppResource cloneProjectedAtTime(DateTime now);
|
||||
|
||||
@@ -369,11 +339,6 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public B setDnsRefreshRequestTime(Optional<DateTime> dnsRefreshRequestTime) {
|
||||
getInstance().dnsRefreshRequestTime = dnsRefreshRequestTime.orElse(null);
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
/** Build the resource, nullifying empty strings and sets and setting defaults. */
|
||||
@Override
|
||||
public T build() {
|
||||
|
||||
+19
-5
@@ -44,6 +44,8 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private static boolean useUncachedForTest = false;
|
||||
|
||||
public enum PrimaryDatabase {
|
||||
CLOUD_SQL,
|
||||
DATASTORE
|
||||
@@ -85,7 +87,10 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton {
|
||||
SQL_ONLY(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.NO_REPLAY),
|
||||
|
||||
/** Toggles SQL Sequence based allocateId */
|
||||
SEQUENCE_BASED_ALLOCATE_ID(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.NO_REPLAY);
|
||||
SEQUENCE_BASED_ALLOCATE_ID(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.NO_REPLAY),
|
||||
|
||||
/** Use SQL-based Nordn upload flow instead of the pull queue-based one. */
|
||||
NORDN_SQL(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.NO_REPLAY);
|
||||
|
||||
private final PrimaryDatabase primaryDatabase;
|
||||
private final boolean isReadOnly;
|
||||
@@ -164,7 +169,9 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton {
|
||||
MigrationState.SQL_ONLY,
|
||||
MigrationState.SQL_PRIMARY_READ_ONLY,
|
||||
MigrationState.SQL_PRIMARY)
|
||||
.putAll(MigrationState.SQL_ONLY, MigrationState.SEQUENCE_BASED_ALLOCATE_ID);
|
||||
.putAll(MigrationState.SQL_ONLY, MigrationState.SEQUENCE_BASED_ALLOCATE_ID)
|
||||
.putAll(MigrationState.SEQUENCE_BASED_ALLOCATE_ID, MigrationState.NORDN_SQL)
|
||||
.putAll(MigrationState.NORDN_SQL, MigrationState.SEQUENCE_BASED_ALLOCATE_ID);
|
||||
|
||||
// In addition, we can always transition from a state to itself (useful when updating the map).
|
||||
Arrays.stream(MigrationState.values()).forEach(state -> builder.put(state, state));
|
||||
@@ -181,8 +188,8 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton {
|
||||
public TimedTransitionProperty<MigrationState> migrationTransitions =
|
||||
TimedTransitionProperty.withInitialValue(MigrationState.DATASTORE_ONLY);
|
||||
|
||||
// Required for Objectify initialization
|
||||
private DatabaseMigrationStateSchedule() {}
|
||||
// Required for Hibernate initialization
|
||||
protected DatabaseMigrationStateSchedule() {}
|
||||
|
||||
@VisibleForTesting
|
||||
public DatabaseMigrationStateSchedule(
|
||||
@@ -205,6 +212,11 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton {
|
||||
CACHE.invalidateAll();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void useUncachedForTest() {
|
||||
useUncachedForTest = true;
|
||||
}
|
||||
|
||||
/** Loads the currently-set migration schedule from the cache, or the default if none exists. */
|
||||
public static TimedTransitionProperty<MigrationState> get() {
|
||||
return CACHE.get(DatabaseMigrationStateSchedule.class);
|
||||
@@ -212,7 +224,9 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton {
|
||||
|
||||
/** Returns the database migration status at the given time. */
|
||||
public static MigrationState getValueAtTime(DateTime dateTime) {
|
||||
return get().getValueAtTime(dateTime);
|
||||
return useUncachedForTest
|
||||
? getUncached().getValueAtTime(dateTime)
|
||||
: get().getValueAtTime(dateTime);
|
||||
}
|
||||
|
||||
/** Loads the currently-set migration schedule from SQL, or the default if none exists. */
|
||||
|
||||
@@ -24,6 +24,7 @@ import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.UpdateAutoTimestampEntity;
|
||||
import google.registry.persistence.VKey;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
@@ -47,7 +48,6 @@ public class User extends UpdateAutoTimestampEntity implements Buildable {
|
||||
private Long id;
|
||||
|
||||
/** GAIA ID associated with the user in question. */
|
||||
@Column(nullable = false)
|
||||
private String gaiaId;
|
||||
|
||||
/** Email address of the user in question. */
|
||||
@@ -118,6 +118,11 @@ public class User extends UpdateAutoTimestampEntity implements Buildable {
|
||||
return new Builder(clone(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VKey<User> createVKey() {
|
||||
return VKey.create(User.class, getId());
|
||||
}
|
||||
|
||||
/** Builder for constructing immutable {@link User} objects. */
|
||||
public static class Builder extends Buildable.Builder<User> {
|
||||
|
||||
@@ -129,7 +134,6 @@ public class User extends UpdateAutoTimestampEntity implements Buildable {
|
||||
|
||||
@Override
|
||||
public User build() {
|
||||
checkArgumentNotNull(getInstance().gaiaId, "Gaia ID cannot be null");
|
||||
checkArgumentNotNull(getInstance().emailAddress, "Email address cannot be null");
|
||||
checkArgumentNotNull(getInstance().userRoles, "User roles cannot be null");
|
||||
return super.build();
|
||||
|
||||
@@ -58,7 +58,6 @@ import org.joda.time.DateTime;
|
||||
@Index(columnList = "techContact"),
|
||||
@Index(columnList = "tld"),
|
||||
@Index(columnList = "registrantContact"),
|
||||
@Index(columnList = "dnsRefreshRequestTime"),
|
||||
@Index(columnList = "lordnPhase"),
|
||||
@Index(columnList = "billing_recurrence_id"),
|
||||
@Index(columnList = "transfer_billing_event_id"),
|
||||
@@ -200,7 +199,6 @@ public class Domain extends DomainBase implements ForeignKeyedEppResource {
|
||||
.setSubordinateHosts(domainBase.getSubordinateHosts())
|
||||
.setStatusValues(domainBase.getStatusValues())
|
||||
.setTransferData(domainBase.getTransferData())
|
||||
.setDnsRefreshRequestTime(domainBase.getDnsRefreshRequestTime())
|
||||
.setLordnPhase(domainBase.getLordnPhase())
|
||||
.setCurrentPackageToken(domainBase.getCurrentPackageToken().orElse(null));
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.flows.ResourceFlowUtils;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.EppResource.ResourceWithTransferData;
|
||||
@@ -121,20 +122,20 @@ public class DomainBase extends EppResource
|
||||
*
|
||||
* @invariant domainName == domainName.toLowerCase(Locale.ENGLISH)
|
||||
*/
|
||||
String domainName;
|
||||
@Expose String domainName;
|
||||
|
||||
/** The top level domain this is under, de-normalized from {@link #domainName}. */
|
||||
String tld;
|
||||
|
||||
/** References to hosts that are the nameservers for the domain. */
|
||||
@Transient Set<VKey<Host>> nsHosts;
|
||||
@Expose @Transient Set<VKey<Host>> nsHosts;
|
||||
|
||||
/** Contacts. */
|
||||
VKey<Contact> adminContact;
|
||||
@Expose VKey<Contact> adminContact;
|
||||
|
||||
VKey<Contact> billingContact;
|
||||
VKey<Contact> techContact;
|
||||
VKey<Contact> registrantContact;
|
||||
@Expose VKey<Contact> billingContact;
|
||||
@Expose VKey<Contact> techContact;
|
||||
@Expose VKey<Contact> registrantContact;
|
||||
|
||||
/** Authorization info (aka transfer secret) of the domain. */
|
||||
@Embedded
|
||||
@@ -175,10 +176,10 @@ public class DomainBase extends EppResource
|
||||
String idnTableName;
|
||||
|
||||
/** Fully qualified host names of this domain's active subordinate hosts. */
|
||||
Set<String> subordinateHosts;
|
||||
@Expose Set<String> subordinateHosts;
|
||||
|
||||
/** When this domain's registration will expire. */
|
||||
DateTime registrationExpirationTime;
|
||||
@Expose DateTime registrationExpirationTime;
|
||||
|
||||
/**
|
||||
* The poll message associated with this domain being deleted.
|
||||
@@ -230,7 +231,7 @@ public class DomainBase extends EppResource
|
||||
*
|
||||
* <p>Can be null if the resource has never been transferred.
|
||||
*/
|
||||
DateTime lastTransferTime;
|
||||
@Expose DateTime lastTransferTime;
|
||||
|
||||
/**
|
||||
* When the domain's autorenewal status will expire.
|
||||
@@ -273,13 +274,6 @@ public class DomainBase extends EppResource
|
||||
return lordnPhase;
|
||||
}
|
||||
|
||||
@Access(AccessType.PROPERTY)
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "dnsRefreshRequestTime")
|
||||
private DateTime getInternalDnsRefreshRequestTime() {
|
||||
return getDnsRefreshRequestTime().orElse(null);
|
||||
}
|
||||
|
||||
public ImmutableSet<String> getSubordinateHosts() {
|
||||
return nullToEmptyImmutableCopy(subordinateHosts);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,6 @@ import javax.persistence.Table;
|
||||
@Index(columnList = "creationTime"),
|
||||
@Index(columnList = "deletionTime"),
|
||||
@Index(columnList = "currentSponsorRegistrarId"),
|
||||
@Index(columnList = "dnsRefreshRequestTime")
|
||||
})
|
||||
@ExternalMessagingName("host")
|
||||
@WithVKey(String.class)
|
||||
@@ -94,7 +93,6 @@ public class Host extends HostBase implements ForeignKeyedEppResource {
|
||||
.setPersistedCurrentSponsorRegistrarId(hostBase.getPersistedCurrentSponsorRegistrarId())
|
||||
.setRepoId(hostBase.getRepoId())
|
||||
.setSuperordinateDomain(hostBase.getSuperordinateDomain())
|
||||
.setDnsRefreshRequestTime(hostBase.getDnsRefreshRequestTime())
|
||||
.setStatusValues(hostBase.getStatusValues());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -86,13 +85,6 @@ public class HostBase extends EppResource {
|
||||
*/
|
||||
DateTime lastSuperordinateChange;
|
||||
|
||||
@Access(AccessType.PROPERTY)
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "dnsRefreshRequestTime")
|
||||
private DateTime getInternalDnsRefreshRequestTime() {
|
||||
return getDnsRefreshRequestTime().orElse(null);
|
||||
}
|
||||
|
||||
public String getHostName() {
|
||||
return hostName;
|
||||
}
|
||||
|
||||
@@ -25,8 +25,6 @@ import com.google.common.base.Strings;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.util.RequestStatusChecker;
|
||||
import google.registry.util.RequestStatusCheckerImpl;
|
||||
import java.io.Serializable;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
@@ -70,8 +68,7 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
enum LockState {
|
||||
IN_USE,
|
||||
FREE,
|
||||
TIMED_OUT,
|
||||
OWNER_DIED
|
||||
TIMED_OUT
|
||||
}
|
||||
|
||||
@VisibleForTesting static LockMetrics lockMetrics = new LockMetrics();
|
||||
@@ -79,17 +76,6 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
/** The name of the locked resource. */
|
||||
@Transient @Id String lockId;
|
||||
|
||||
/**
|
||||
* Unique log ID of the request that owns this lock.
|
||||
*
|
||||
* <p>When that request is no longer running (is finished), the lock can be considered implicitly
|
||||
* released.
|
||||
*
|
||||
* <p>See {@link RequestStatusCheckerImpl#getLogId} for details about how it's created in
|
||||
* practice.
|
||||
*/
|
||||
@Column String requestLogId;
|
||||
|
||||
/** When the lock can be considered implicitly released. */
|
||||
@Column(nullable = false)
|
||||
DateTime expirationTime;
|
||||
@@ -124,7 +110,6 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
private static Lock create(
|
||||
String resourceName,
|
||||
String scope,
|
||||
String requestLogId,
|
||||
DateTime acquiredTime,
|
||||
Duration leaseLength) {
|
||||
checkArgument(!Strings.isNullOrEmpty(resourceName), "resourceName cannot be null or empty");
|
||||
@@ -132,7 +117,6 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
// Add the scope to the Lock's id so that it is unique for locks acquiring the same resource
|
||||
// across different TLDs.
|
||||
instance.lockId = makeLockId(resourceName, scope);
|
||||
instance.requestLogId = requestLogId;
|
||||
instance.expirationTime = acquiredTime.plus(leaseLength);
|
||||
instance.acquiredTime = acquiredTime;
|
||||
instance.resourceName = resourceName;
|
||||
@@ -172,18 +156,13 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
switch (acquireResult.lockState()) {
|
||||
case IN_USE:
|
||||
logger.atInfo().log(
|
||||
"Existing lock by request %s is still valid now %s (until %s) lock: %s",
|
||||
lock.requestLogId, now, lock.expirationTime, lock.lockId);
|
||||
"Existing lock by request is still valid now %s (until %s) lock: %s",
|
||||
now, lock.expirationTime, lock.lockId);
|
||||
break;
|
||||
case TIMED_OUT:
|
||||
logger.atInfo().log(
|
||||
"Existing lock by request %s is timed out now %s (was valid until %s) lock: %s",
|
||||
lock.requestLogId, now, lock.expirationTime, lock.lockId);
|
||||
break;
|
||||
case OWNER_DIED:
|
||||
logger.atInfo().log(
|
||||
"Existing lock is valid now %s (until %s), but owner (%s) isn't running lock: %s",
|
||||
now, lock.expirationTime, lock.requestLogId, lock.lockId);
|
||||
"Existing lock by request is timed out now %s (was valid until %s) lock: %s",
|
||||
now, lock.expirationTime, lock.lockId);
|
||||
break;
|
||||
case FREE:
|
||||
// There was no existing lock
|
||||
@@ -203,11 +182,7 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
|
||||
/** Try to acquire a lock. Returns absent if it can't be acquired. */
|
||||
public static Optional<Lock> acquire(
|
||||
String resourceName,
|
||||
@Nullable String tld,
|
||||
Duration leaseLength,
|
||||
RequestStatusChecker requestStatusChecker,
|
||||
boolean checkThreadRunning) {
|
||||
String resourceName, @Nullable String tld, Duration leaseLength) {
|
||||
String scope = tld != null ? tld : GLOBAL;
|
||||
Supplier<AcquireResult> lockAcquirer =
|
||||
() -> {
|
||||
@@ -219,22 +194,19 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
.orElse(null);
|
||||
if (lock != null) {
|
||||
logger.atInfo().log(
|
||||
"Loaded existing lock: %s for request: %s", lock.lockId, lock.requestLogId);
|
||||
"Loaded existing lock: %s for resource: %s", lock.lockId, lock.resourceName);
|
||||
}
|
||||
LockState lockState;
|
||||
if (lock == null) {
|
||||
lockState = LockState.FREE;
|
||||
} else if (isAtOrAfter(now, lock.expirationTime)) {
|
||||
lockState = LockState.TIMED_OUT;
|
||||
} else if (checkThreadRunning && !requestStatusChecker.isRunning(lock.requestLogId)) {
|
||||
lockState = LockState.OWNER_DIED;
|
||||
} else {
|
||||
lockState = LockState.IN_USE;
|
||||
return AcquireResult.create(now, lock, null, lockState);
|
||||
}
|
||||
|
||||
Lock newLock =
|
||||
create(resourceName, scope, requestStatusChecker.getLogId(), now, leaseLength);
|
||||
Lock newLock = create(resourceName, scope, now, leaseLength);
|
||||
tm().put(newLock);
|
||||
|
||||
return AcquireResult.create(now, lock, newLock, lockState);
|
||||
|
||||
@@ -25,6 +25,7 @@ import google.registry.monitoring.whitebox.WhiteboxModule;
|
||||
import google.registry.request.RequestComponentBuilder;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.request.RequestScope;
|
||||
import google.registry.ui.server.console.ConsoleDomainGetAction;
|
||||
import google.registry.ui.server.registrar.ConsoleOteSetupAction;
|
||||
import google.registry.ui.server.registrar.ConsoleRegistrarCreatorAction;
|
||||
import google.registry.ui.server.registrar.ConsoleUiAction;
|
||||
@@ -61,6 +62,8 @@ interface FrontendRequestComponent {
|
||||
|
||||
RegistryLockVerifyAction registryLockVerifyAction();
|
||||
|
||||
ConsoleDomainGetAction consoleDomainGetAction();
|
||||
|
||||
@Subcomponent.Builder
|
||||
abstract class Builder implements RequestComponentBuilder<FrontendRequestComponent> {
|
||||
@Override public abstract Builder requestModule(RequestModule requestModule);
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.contact.Contact;
|
||||
@@ -52,7 +53,7 @@ public class VKey<T> extends ImmutableObject implements Serializable {
|
||||
.collect(toImmutableMap(Class::getSimpleName, identity()));
|
||||
|
||||
// The primary key for the referenced entity.
|
||||
Serializable key;
|
||||
@Expose Serializable key;
|
||||
|
||||
Class<? extends T> kind;
|
||||
|
||||
|
||||
@@ -39,8 +39,6 @@ import google.registry.request.HttpException.UnsupportedMediaTypeException;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.lock.LockHandler;
|
||||
import google.registry.request.lock.LockHandlerImpl;
|
||||
import google.registry.util.RequestStatusChecker;
|
||||
import google.registry.util.RequestStatusCheckerImpl;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -192,18 +190,6 @@ public final class RequestModule {
|
||||
return lockHandler;
|
||||
}
|
||||
|
||||
@Provides
|
||||
static RequestStatusChecker provideRequestStatusChecker(
|
||||
RequestStatusCheckerImpl requestStatusChecker) {
|
||||
return requestStatusChecker;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@RequestLogId
|
||||
static String provideRequestLogId(RequestStatusChecker requestStatusChecker) {
|
||||
return requestStatusChecker.getLogId();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@JsonPayload
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import javax.inject.Qualifier;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
@@ -30,15 +31,26 @@ import javax.inject.Singleton;
|
||||
public class AuthModule {
|
||||
|
||||
private static final String IAP_ISSUER_URL = "https://cloud.google.com/iap";
|
||||
private static final String SA_ISSUER_URL = "https://accounts.google.com";
|
||||
|
||||
/** Provides the custom authentication mechanisms (including OAuth). */
|
||||
@Provides
|
||||
ImmutableList<AuthenticationMechanism> provideApiAuthenticationMechanisms(
|
||||
OAuthAuthenticationMechanism oauthAuthenticationMechanism,
|
||||
IapHeaderAuthenticationMechanism iapHeaderAuthenticationMechanism) {
|
||||
return ImmutableList.of(oauthAuthenticationMechanism, iapHeaderAuthenticationMechanism);
|
||||
IapHeaderAuthenticationMechanism iapHeaderAuthenticationMechanism,
|
||||
ServiceAccountAuthenticationMechanism serviceAccountAuthenticationMechanism) {
|
||||
return ImmutableList.of(
|
||||
oauthAuthenticationMechanism,
|
||||
iapHeaderAuthenticationMechanism,
|
||||
serviceAccountAuthenticationMechanism);
|
||||
}
|
||||
|
||||
@Qualifier
|
||||
@interface IAP {}
|
||||
|
||||
@Qualifier
|
||||
@interface ServiceAccount {}
|
||||
|
||||
/** Provides the OAuthService instance. */
|
||||
@Provides
|
||||
OAuthService provideOauthService() {
|
||||
@@ -46,10 +58,18 @@ public class AuthModule {
|
||||
}
|
||||
|
||||
@Provides
|
||||
@IAP
|
||||
@Singleton
|
||||
TokenVerifier provideTokenVerifier(
|
||||
@Config("projectId") String projectId, @Config("projectIdNumber") long projectIdNumber) {
|
||||
String audience = String.format("/projects/%d/apps/%s", projectIdNumber, projectId);
|
||||
return TokenVerifier.newBuilder().setAudience(audience).setIssuer(IAP_ISSUER_URL).build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ServiceAccount
|
||||
@Singleton
|
||||
TokenVerifier provideServiceAccountTokenVerifier(@Config("projectId") String projectId) {
|
||||
return TokenVerifier.newBuilder().setAudience(projectId).setIssuer(SA_ISSUER_URL).build();
|
||||
}
|
||||
}
|
||||
|
||||
+10
-37
@@ -14,15 +14,11 @@
|
||||
|
||||
package google.registry.request.auth;
|
||||
|
||||
import com.google.api.client.json.webtoken.JsonWebSignature;
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.request.auth.AuthModule.IAP;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
@@ -37,41 +33,22 @@ import javax.servlet.http.HttpServletRequest;
|
||||
* @see <a href="https://cloud.google.com/iap/docs/signed-headers-howto">the documentation on GCP
|
||||
* IAP's signed headers for more information.</a>
|
||||
*/
|
||||
public class IapHeaderAuthenticationMechanism implements AuthenticationMechanism {
|
||||
public class IapHeaderAuthenticationMechanism extends IdTokenAuthenticationBase {
|
||||
|
||||
private static final String ID_TOKEN_HEADER_NAME = "X-Goog-IAP-JWT-Assertion";
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
// A workaround that allows "use" of the IAP-based authenticator when running local testing, i.e.
|
||||
// the RegistryTestServer
|
||||
private static Optional<User> userForTesting = Optional.empty();
|
||||
|
||||
private final TokenVerifier tokenVerifier;
|
||||
|
||||
@Inject
|
||||
public IapHeaderAuthenticationMechanism(TokenVerifier tokenVerifier) {
|
||||
this.tokenVerifier = tokenVerifier;
|
||||
public IapHeaderAuthenticationMechanism(@IAP TokenVerifier tokenVerifier) {
|
||||
super(tokenVerifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResult authenticate(HttpServletRequest request) {
|
||||
if (RegistryEnvironment.get().equals(RegistryEnvironment.UNITTEST)
|
||||
&& userForTesting.isPresent()) {
|
||||
return AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userForTesting.get()));
|
||||
}
|
||||
String rawIdToken = request.getHeader(ID_TOKEN_HEADER_NAME);
|
||||
if (rawIdToken == null) {
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
JsonWebSignature token;
|
||||
try {
|
||||
token = tokenVerifier.verify(rawIdToken);
|
||||
} catch (TokenVerifier.VerificationException e) {
|
||||
logger.atInfo().withCause(e).log("Error when verifying access token");
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
String emailAddress = (String) token.getPayload().get("email");
|
||||
String rawTokenFromRequest(HttpServletRequest request) {
|
||||
return request.getHeader(ID_TOKEN_HEADER_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
AuthResult authResultFromEmail(String emailAddress) {
|
||||
Optional<User> maybeUser = UserDao.loadUser(emailAddress);
|
||||
if (!maybeUser.isPresent()) {
|
||||
logger.atInfo().log("No user found for email address %s", emailAddress);
|
||||
@@ -80,8 +57,4 @@ public class IapHeaderAuthenticationMechanism implements AuthenticationMechanism
|
||||
return AuthResult.create(AuthLevel.USER, UserAuthInfo.create(maybeUser.get()));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void setUserAuthInfoForTestServer(@Nullable User user) {
|
||||
userForTesting = Optional.ofNullable(user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright 2022 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.request.auth;
|
||||
|
||||
import com.google.api.client.json.webtoken.JsonWebSignature;
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.model.console.User;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
public abstract class IdTokenAuthenticationBase implements AuthenticationMechanism {
|
||||
|
||||
public static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
// A workaround that allows "use" of the IAP-based authenticator when running local testing, i.e.
|
||||
// the RegistryTestServer
|
||||
private static Optional<User> userForTesting = Optional.empty();
|
||||
|
||||
private final TokenVerifier tokenVerifier;
|
||||
|
||||
public IdTokenAuthenticationBase(TokenVerifier tokenVerifier) {
|
||||
this.tokenVerifier = tokenVerifier;
|
||||
}
|
||||
|
||||
abstract String rawTokenFromRequest(HttpServletRequest request);
|
||||
|
||||
abstract AuthResult authResultFromEmail(String email);
|
||||
|
||||
@Override
|
||||
public AuthResult authenticate(HttpServletRequest request) {
|
||||
if (RegistryEnvironment.get().equals(RegistryEnvironment.UNITTEST)
|
||||
&& userForTesting.isPresent()) {
|
||||
return AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userForTesting.get()));
|
||||
}
|
||||
String rawIdToken = rawTokenFromRequest(request);
|
||||
if (rawIdToken == null) {
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
JsonWebSignature token;
|
||||
try {
|
||||
token = tokenVerifier.verify(rawIdToken);
|
||||
} catch (TokenVerifier.VerificationException e) {
|
||||
logger.atInfo().withCause(e).log("Error when verifying access token");
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
String emailAddress = (String) token.getPayload().get("email");
|
||||
return authResultFromEmail(emailAddress);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void setUserAuthInfoForTestServer(@Nullable User user) {
|
||||
userForTesting = Optional.ofNullable(user);
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
// Copyright 2023 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.request.auth;
|
||||
|
||||
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
||||
import static google.registry.request.auth.AuthLevel.APP;
|
||||
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.request.auth.AuthModule.ServiceAccount;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* A way to authenticate HTTP requests signed by Service Account
|
||||
*
|
||||
* <p>Currently used by cloud scheduler service account
|
||||
*/
|
||||
public class ServiceAccountAuthenticationMechanism extends IdTokenAuthenticationBase {
|
||||
|
||||
private final String cloudSchedulerEmailPrefix;
|
||||
private static final String BEARER_PREFIX = "Bearer ";
|
||||
|
||||
@Inject
|
||||
public ServiceAccountAuthenticationMechanism(
|
||||
@ServiceAccount TokenVerifier tokenVerifier,
|
||||
@Config("cloudSchedulerServiceAccountEmail") String cloudSchedulerEmailPrefix) {
|
||||
|
||||
super(tokenVerifier);
|
||||
this.cloudSchedulerEmailPrefix = cloudSchedulerEmailPrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
String rawTokenFromRequest(HttpServletRequest request) {
|
||||
String rawToken = request.getHeader(AUTHORIZATION);
|
||||
if (rawToken != null && rawToken.startsWith(BEARER_PREFIX)) {
|
||||
return rawToken.substring(BEARER_PREFIX.length());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
AuthResult authResultFromEmail(String emailAddress) {
|
||||
if (emailAddress.equals(cloudSchedulerEmailPrefix)) {
|
||||
return AuthResult.create(APP);
|
||||
} else {
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,6 @@ import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||
import google.registry.model.server.Lock;
|
||||
import google.registry.util.AppEngineTimeLimiter;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.RequestStatusChecker;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -48,12 +47,10 @@ public class LockHandlerImpl implements LockHandler {
|
||||
/** Fudge factor to make sure we kill threads before a lock actually expires. */
|
||||
private static final Duration LOCK_TIMEOUT_FUDGE = Duration.standardSeconds(5);
|
||||
|
||||
private final RequestStatusChecker requestStatusChecker;
|
||||
private final Clock clock;
|
||||
|
||||
@Inject
|
||||
public LockHandlerImpl(RequestStatusChecker requestStatusChecker, Clock clock) {
|
||||
this.requestStatusChecker = requestStatusChecker;
|
||||
public LockHandlerImpl(Clock clock) {
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@@ -114,7 +111,7 @@ public class LockHandlerImpl implements LockHandler {
|
||||
/** Allows injection of mock Lock in tests. */
|
||||
@VisibleForTesting
|
||||
Optional<Lock> acquire(String lockName, @Nullable String tld, Duration leaseLength) {
|
||||
return Lock.acquire(lockName, tld, leaseLength, requestStatusChecker, true);
|
||||
return Lock.acquire(lockName, tld, leaseLength);
|
||||
}
|
||||
|
||||
private interface LockAcquirer {
|
||||
|
||||
@@ -49,7 +49,6 @@ U+011F # LATIN SMALL LETTER G WITH BREVE
|
||||
U+01E7 # LATIN SMALL LETTER G WITH CARON
|
||||
U+0121 # LATIN SMALL LETTER G WITH DOT ABOVE
|
||||
U+0123 # LATIN SMALL LETTER G WITH CEDILLA
|
||||
U+01E5 # LATIN SMALL LETTER G WITH STROKE
|
||||
U+0068 # LATIN SMALL LETTER H
|
||||
U+0127 # LATIN SMALL LETTER H WITH STROKE
|
||||
U+0069 # LATIN SMALL LETTER I
|
||||
|
||||
@@ -66,7 +66,12 @@ public final class LordnTaskUtils {
|
||||
}
|
||||
|
||||
/** Returns the corresponding CSV LORDN line for a sunrise domain. */
|
||||
public static String getCsvLineForSunriseDomain(Domain domain, DateTime transactionTime) {
|
||||
public static String getCsvLineForSunriseDomain(Domain domain) {
|
||||
return getCsvLineForSunriseDomain(domain, domain.getCreationTime());
|
||||
}
|
||||
|
||||
// TODO: Merge into the function above after pull queue migration.
|
||||
private static String getCsvLineForSunriseDomain(Domain domain, DateTime transactionTime) {
|
||||
return Joiner.on(',')
|
||||
.join(
|
||||
domain.getRepoId(),
|
||||
@@ -77,7 +82,12 @@ public final class LordnTaskUtils {
|
||||
}
|
||||
|
||||
/** Returns the corresponding CSV LORDN line for a claims domain. */
|
||||
public static String getCsvLineForClaimsDomain(Domain domain, DateTime transactionTime) {
|
||||
public static String getCsvLineForClaimsDomain(Domain domain) {
|
||||
return getCsvLineForClaimsDomain(domain, domain.getCreationTime());
|
||||
}
|
||||
|
||||
// TODO: Merge into the function above after pull queue migration.
|
||||
private static String getCsvLineForClaimsDomain(Domain domain, DateTime transactionTime) {
|
||||
return Joiner.on(',')
|
||||
.join(
|
||||
domain.getRepoId(),
|
||||
@@ -100,16 +110,8 @@ public final class LordnTaskUtils {
|
||||
private LordnTaskUtils() {}
|
||||
|
||||
public enum LordnPhase {
|
||||
SUNRISE(QUEUE_SUNRISE),
|
||||
|
||||
CLAIMS(QUEUE_CLAIMS),
|
||||
|
||||
NONE(null);
|
||||
|
||||
final String queue;
|
||||
|
||||
LordnPhase(String queue) {
|
||||
this.queue = queue;
|
||||
}
|
||||
SUNRISE,
|
||||
CLAIMS,
|
||||
NONE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,13 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.net.HttpHeaders.LOCATION;
|
||||
import static com.google.common.net.MediaType.CSV_UTF_8;
|
||||
import static google.registry.persistence.transaction.QueryComposer.Comparator.EQ;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.UrlConnectionUtils.getResponseBytes;
|
||||
import static google.registry.tmch.LordnTaskUtils.COLUMNS_CLAIMS;
|
||||
import static google.registry.tmch.LordnTaskUtils.COLUMNS_SUNRISE;
|
||||
import static google.registry.tmch.LordnTaskUtils.getCsvLineForClaimsDomain;
|
||||
import static google.registry.tmch.LordnTaskUtils.getCsvLineForSunriseDomain;
|
||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_ACCEPTED;
|
||||
@@ -33,6 +37,7 @@ import com.google.appengine.api.taskqueue.TaskHandle;
|
||||
import com.google.appengine.api.taskqueue.TransientFailureException;
|
||||
import com.google.apphosting.api.DeadlineExceededException;
|
||||
import com.google.cloud.tasks.v2.Task;
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -42,6 +47,7 @@ import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.request.Parameter;
|
||||
@@ -49,6 +55,7 @@ import google.registry.request.RequestParameters;
|
||||
import google.registry.request.UrlConnectionService;
|
||||
import google.registry.request.UrlConnectionUtils;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.tmch.LordnTaskUtils.LordnPhase;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import google.registry.util.Retrier;
|
||||
@@ -59,6 +66,7 @@ import java.net.URL;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.inject.Inject;
|
||||
@@ -81,9 +89,11 @@ import org.joda.time.Duration;
|
||||
public final class NordnUploadAction implements Runnable {
|
||||
|
||||
static final String PATH = "/_dr/task/nordnUpload";
|
||||
static final String LORDN_PHASE_PARAM = "lordn-phase";
|
||||
static final String LORDN_PHASE_PARAM = "lordnPhase";
|
||||
// TODO: Delete after migrating off of pull queue.
|
||||
static final String PULL_QUEUE_PARAM = "pullQueue";
|
||||
|
||||
private static final int QUEUE_BATCH_SIZE = 1000;
|
||||
private static final int BATCH_SIZE = 1000;
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private static final Duration LEASE_PERIOD = Duration.standardHours(1);
|
||||
|
||||
@@ -100,31 +110,102 @@ public final class NordnUploadAction implements Runnable {
|
||||
@Inject LordnRequestInitializer lordnRequestInitializer;
|
||||
@Inject UrlConnectionService urlConnectionService;
|
||||
|
||||
@Inject @Config("tmchMarksdbUrl") String tmchMarksdbUrl;
|
||||
@Inject @Parameter(LORDN_PHASE_PARAM) String phase;
|
||||
@Inject @Parameter(RequestParameters.PARAM_TLD) String tld;
|
||||
@Inject
|
||||
@Config("tmchMarksdbUrl")
|
||||
String tmchMarksdbUrl;
|
||||
|
||||
@Inject
|
||||
@Parameter(LORDN_PHASE_PARAM)
|
||||
String phase;
|
||||
|
||||
@Inject
|
||||
@Parameter(RequestParameters.PARAM_TLD)
|
||||
String tld;
|
||||
|
||||
@Inject
|
||||
@Parameter(PULL_QUEUE_PARAM)
|
||||
Optional<Boolean> usePullQueue;
|
||||
|
||||
@Inject CloudTasksUtils cloudTasksUtils;
|
||||
|
||||
@Inject NordnUploadAction() {}
|
||||
@Inject
|
||||
NordnUploadAction() {}
|
||||
|
||||
/**
|
||||
* These LORDN parameter names correspond to the relative paths in LORDN URLs and cannot be
|
||||
* changed on our end.
|
||||
*/
|
||||
private static final String PARAM_LORDN_PHASE_SUNRISE = "sunrise";
|
||||
private static final String PARAM_LORDN_PHASE_SUNRISE =
|
||||
Ascii.toLowerCase(LordnPhase.SUNRISE.toString());
|
||||
|
||||
private static final String PARAM_LORDN_PHASE_CLAIMS = "claims";
|
||||
private static final String PARAM_LORDN_PHASE_CLAIMS =
|
||||
Ascii.toLowerCase(LordnPhase.CLAIMS.toString());
|
||||
|
||||
/** How long to wait before attempting to verify an upload by fetching the log. */
|
||||
private static final Duration VERIFY_DELAY = Duration.standardMinutes(30);
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
processLordnTasks();
|
||||
} catch (IOException | GeneralSecurityException e) {
|
||||
throw new RuntimeException(e);
|
||||
if (usePullQueue.orElse(false)) {
|
||||
try {
|
||||
processLordnTasks();
|
||||
} catch (IOException | GeneralSecurityException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
checkArgument(
|
||||
phase.equals(PARAM_LORDN_PHASE_SUNRISE) || phase.equals(PARAM_LORDN_PHASE_CLAIMS),
|
||||
"Invalid phase specified to NordnUploadAction: %s.",
|
||||
phase);
|
||||
tm().transact(
|
||||
() -> {
|
||||
// Note here that we load all domains pending Nordn in one batch, which should not
|
||||
// be a problem for the rate of domain registration that we see. If we anticipate
|
||||
// a peak in claims during TLD launch (sunrise is NOT first-come-first-serve, so
|
||||
// there should be no expectation of a peak during it), we can consider temporarily
|
||||
// increasing the frequency of Nordn upload to reduce the size of each batch.
|
||||
//
|
||||
// We did not further divide the domains into smaller batches because the
|
||||
// read-upload-write operation per small batch needs to be inside a single
|
||||
// transaction to prevent race conditions, and running several uploads in rapid
|
||||
// sucession will likely overwhelm the MarksDB upload server, which recommands a
|
||||
// maximum upload frequency of every 3 hours.
|
||||
//
|
||||
// See:
|
||||
// https://datatracker.ietf.org/doc/html/draft-ietf-regext-tmch-func-spec-01#section-5.2.3.3
|
||||
List<Domain> domains =
|
||||
tm().createQueryComposer(Domain.class)
|
||||
.where("lordnPhase", EQ, LordnPhase.valueOf(Ascii.toUpperCase(phase)))
|
||||
.where("tld", EQ, tld)
|
||||
.orderBy("creationTime")
|
||||
.list();
|
||||
if (domains.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
StringBuilder csv = new StringBuilder();
|
||||
ImmutableList.Builder<Domain> newDomains = new ImmutableList.Builder<>();
|
||||
|
||||
domains.forEach(
|
||||
domain -> {
|
||||
if (phase.equals(PARAM_LORDN_PHASE_SUNRISE)) {
|
||||
csv.append(getCsvLineForSunriseDomain(domain)).append('\n');
|
||||
} else {
|
||||
csv.append(getCsvLineForClaimsDomain(domain)).append('\n');
|
||||
}
|
||||
Domain newDomain = domain.asBuilder().setLordnPhase(LordnPhase.NONE).build();
|
||||
newDomains.add(newDomain);
|
||||
});
|
||||
String columns =
|
||||
phase.equals(PARAM_LORDN_PHASE_SUNRISE) ? COLUMNS_SUNRISE : COLUMNS_CLAIMS;
|
||||
String header =
|
||||
String.format("1,%s,%d\n%s\n", clock.nowUtc(), domains.size(), columns);
|
||||
try {
|
||||
uploadCsvToLordn(String.format("/LORDN/%s/%s", tld, phase), header + csv);
|
||||
} catch (IOException | GeneralSecurityException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
tm().updateAll(newDomains.build());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +239,7 @@ public final class NordnUploadAction implements Runnable {
|
||||
queue.leaseTasks(
|
||||
LeaseOptions.Builder.withTag(tld)
|
||||
.leasePeriod(LEASE_PERIOD.getMillis(), TimeUnit.MILLISECONDS)
|
||||
.countLimit(QUEUE_BATCH_SIZE)),
|
||||
.countLimit(BATCH_SIZE)),
|
||||
TransientFailureException.class,
|
||||
DeadlineExceededException.class);
|
||||
if (tasks.isEmpty()) {
|
||||
@@ -171,7 +252,7 @@ public final class NordnUploadAction implements Runnable {
|
||||
private void processLordnTasks() throws IOException, GeneralSecurityException {
|
||||
checkArgument(
|
||||
phase.equals(PARAM_LORDN_PHASE_SUNRISE) || phase.equals(PARAM_LORDN_PHASE_CLAIMS),
|
||||
"Invalid phase specified to Nordn servlet: %s.",
|
||||
"Invalid phase specified to NordnUploadAction: %s.",
|
||||
phase);
|
||||
DateTime now = clock.nowUtc();
|
||||
Queue queue =
|
||||
@@ -184,12 +265,12 @@ public final class NordnUploadAction implements Runnable {
|
||||
// Note: This upload/task deletion isn't done atomically (it's not clear how one would do so
|
||||
// anyway). As a result, it is possible that the upload might succeed yet the deletion of
|
||||
// enqueued tasks might fail. If so, this would result in the same lines being uploaded to NORDN
|
||||
// across mulitple uploads. This is probably OK; all that we really cannot have is a missing
|
||||
// across multiple uploads. This is probably OK; all that we really cannot have is a missing
|
||||
// line.
|
||||
if (!tasks.isEmpty()) {
|
||||
String csvData = convertTasksToCsv(tasks, now, columns);
|
||||
uploadCsvToLordn(String.format("/LORDN/%s/%s", tld, phase), csvData);
|
||||
Lists.partition(tasks, QUEUE_BATCH_SIZE)
|
||||
Lists.partition(tasks, BATCH_SIZE)
|
||||
.forEach(
|
||||
batch ->
|
||||
retrier.callWithRetry(
|
||||
|
||||
@@ -22,6 +22,7 @@ import static google.registry.util.ResourceUtils.readResourceUtf8;
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.config.RegistryConfig.ConfigModule.TmchCaMode;
|
||||
import google.registry.model.CacheUtils;
|
||||
@@ -55,6 +56,7 @@ public final class TmchCertificateAuthority {
|
||||
private static final String ROOT_CRT_PILOT_FILE = "icann-tmch-pilot.crt";
|
||||
private static final String CRL_FILE = "icann-tmch.crl";
|
||||
private static final String CRL_PILOT_FILE = "icann-tmch-pilot.crl";
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private final TmchCaMode tmchCaMode;
|
||||
private final Clock clock;
|
||||
@@ -142,8 +144,14 @@ public final class TmchCertificateAuthority {
|
||||
* @see X509Utils#verifyCrl
|
||||
*/
|
||||
public void updateCrl(String asciiCrl, String url) throws GeneralSecurityException {
|
||||
X509CRL crl = X509Utils.loadCrl(asciiCrl);
|
||||
X509Utils.verifyCrl(getAndValidateRoot(), getCrl(), crl, clock.nowUtc().toDate());
|
||||
X509CRL newCrl = X509Utils.loadCrl(asciiCrl);
|
||||
X509CRL oldCrl = null;
|
||||
try {
|
||||
oldCrl = getCrl();
|
||||
} catch (Exception e) {
|
||||
logger.atWarning().withCause(e).log("Old CRL is invalid, ignored during CRL update.");
|
||||
}
|
||||
X509Utils.verifyCrl(getAndValidateRoot(), oldCrl, newCrl, clock.nowUtc().toDate());
|
||||
TmchCrl.set(asciiCrl, url);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,10 @@ import org.bouncycastle.openpgp.bc.BcPGPPublicKeyRing;
|
||||
/** Helper class for common data loaded from the jar and SQL at runtime. */
|
||||
public final class TmchData {
|
||||
|
||||
private static final String BEGIN_ENCODED_SMD = "-----BEGIN ENCODED SMD-----";
|
||||
private static final String END_ENCODED_SMD = "-----END ENCODED SMD-----";
|
||||
static final String BEGIN_ENCODED_SMD = "-----BEGIN ENCODED SMD-----";
|
||||
static final String END_ENCODED_SMD = "-----END ENCODED SMD-----";
|
||||
|
||||
private TmchData() {}
|
||||
|
||||
static PGPPublicKey loadPublicKey(ByteSource pgpPublicKeyFile) {
|
||||
try (InputStream input = pgpPublicKeyFile.openStream();
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.tmch;
|
||||
|
||||
import static com.google.common.io.Resources.asByteSource;
|
||||
import static com.google.common.io.Resources.getResource;
|
||||
import static google.registry.request.RequestParameters.extractOptionalBooleanParameter;
|
||||
import static google.registry.request.RequestParameters.extractRequiredParameter;
|
||||
|
||||
import dagger.Module;
|
||||
@@ -25,6 +26,7 @@ import google.registry.request.HttpException.BadRequestException;
|
||||
import google.registry.request.Parameter;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Optional;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
|
||||
@@ -64,4 +66,10 @@ public final class TmchModule {
|
||||
static String provideNordnLogId(HttpServletRequest req) {
|
||||
return extractRequiredParameter(req, NordnVerifyAction.NORDN_LOG_ID_PARAM);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter(NordnUploadAction.PULL_QUEUE_PARAM)
|
||||
static Optional<Boolean> provideUsePullQueue(HttpServletRequest req) {
|
||||
return extractOptionalBooleanParameter(req, NordnUploadAction.PULL_QUEUE_PARAM);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEVjCCAz6gAwIBAgIgLrAbevoae52y3f6C2tB0Sn3p7XJm0T02FogxKCfNhXkw
|
||||
DQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxPDA6BgNVBAoTM0ludGVybmV0
|
||||
IENvcnBvcmF0aW9uIGZvciBBc3NpZ25lZCBOYW1lcyBhbmQgTnVtYmVyczEvMC0G
|
||||
A1UEAxMmSUNBTk4gVHJhZGVtYXJrIENsZWFyaW5naG91c2UgUGlsb3QgQ0EwHhcN
|
||||
MTMwNjI2MDAwMDAwWhcNMjMwNjI1MjM1OTU5WjB8MQswCQYDVQQGEwJVUzE8MDoG
|
||||
A1UEChMzSW50ZXJuZXQgQ29ycG9yYXRpb24gZm9yIEFzc2lnbmVkIE5hbWVzIGFu
|
||||
ZCBOdW1iZXJzMS8wLQYDVQQDEyZJQ0FOTiBUcmFkZW1hcmsgQ2xlYXJpbmdob3Vz
|
||||
ZSBQaWxvdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMJiRqFg
|
||||
iCoDF8zMJMKHPMEuSpjbEl9ZWII+1WawDyt+jw841HsTT+6MwZsqExbQvukgvnuS
|
||||
lA3Rg3xTFxodMaVZWsVQJy2PXGHVFRLnCp05DYZsMGZabuN9mIekYwtjePo89Lz0
|
||||
JtU3ibL3squGG3gg6TLtPjks7Txm18BYPOYLznui32GUz+1aIZuk2p5A/rSldsh3
|
||||
bke68IX5WZhKuIxT0+BjS8yfLWI0HCUs71WVxzvlJ1v22/eMK0WEA6+ZhCbOKIav
|
||||
VtGNJrwIYwhZmxqfiR1HzHTLvrV0SLlJ2bwNk/yzKm8IJfuFezQ5BBtQ2RS9opFX
|
||||
X8ft3v+uQQQvi+MCAwEAAaOBwzCBwDASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1Ud
|
||||
DgQWBBTDrT6m1hEARYBcOldKim3cMQ2ecTAOBgNVHQ8BAf8EBAMCAQYwNAYDVR0f
|
||||
BC0wKzApoCegJYYjaHR0cDovL2NybC5pY2Fubi5vcmcvdG1jaF9waWxvdC5jcmww
|
||||
RQYDVR0gBD4wPDA6BgMqAwQwMzAxBggrBgEFBQcCARYlaHR0cDovL3d3dy5pY2Fu
|
||||
bi5vcmcvcGlsb3RfcmVwb3NpdG9yeTANBgkqhkiG9w0BAQsFAAOCAQEAKUfEJ5X6
|
||||
QAttajjRVseJFQxRXGHTgCaDk8C/1nj1ielZAuZtgdUpWDUr0NnGCi+LHSsgdTYR
|
||||
+vMrxir7EVYQevrBobELkxeTEfjF9FVqjBHInyPFLOFkz15zGG2IwPJps+vhAd/7
|
||||
gT0ph1k2FEkJFGL5LwRf1ms4IX0vDkxTIX8Qxy1jczCiSsoV8pwlhh2NHAkpGQWN
|
||||
/pTS0Uqi7uU5Bm/IoGvPBzUp5n5SjUMnTZx/+1zAuerSabt483sXBcWsjgl7MqFt
|
||||
fONiAtNeMNfh60lTMu4zgVwLZTO4TQM5Q2uylPPmZtwnA88QvM2IL85cIYJHd0z9
|
||||
jpUQMBGHXF2WQA==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1,13 +0,0 @@
|
||||
-----BEGIN X509 CRL-----
|
||||
MIIB8DCB2QIBATANBgkqhkiG9w0BAQsFADB2MQswCQYDVQQGEwJVUzE8MDoGA1UE
|
||||
ChMzSW50ZXJuZXQgQ29ycG9yYXRpb24gZm9yIEFzc2lnbmVkIE5hbWVzIGFuZCBO
|
||||
dW1iZXJzMSkwJwYDVQQDEyBJQ0FOTiBUcmFkZW1hcmsgQ2xlYXJpbmdob3VzZSBD
|
||||
QRcNMTgwMzAxMDAwMDAwWhcNMTgxMDA3MjM1OTU5WqAvMC0wHwYDVR0jBBgwFoAU
|
||||
XMDxlizKTFsp8UB00xs2PkfUbgQwCgYDVR0UBAMCAQswDQYJKoZIhvcNAQELBQAD
|
||||
ggEBAGhvQtqENy2Ga+nGg6kZRCzEWKy481v111Iycku/qL5aUlqSL5BkQst2Czaq
|
||||
xdKRSxKHkMaTChoezSaw5huOTd0prdSXVHPg/tmjxyuuS2pqWpuAICkrG06FgXgh
|
||||
AG5YCHt2DvCjeA9F3TMmbOkCMILQ/x+vsyg6Yv4Oiz8rFbFcUMntUKSrymt4dKpk
|
||||
S78CTkHH/3M3YNxZCo8JPwaQohC3Rck4M30Pg8C0qC9jjSrudA6hCa4223U6aZwC
|
||||
Kz3LNXdkqGWlDJPTf0YWwnT4ZyO7KKXVuEbPzg187htz3Jcr6b0x1UUoHGAkOv7i
|
||||
W4IwhPbUJ14/7pUuUef6airQUw8=
|
||||
-----END X509 CRL-----
|
||||
@@ -1,25 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEQjCCAyqgAwIBAgIhAJNCMqhNjz3cXVJPj7yvcZvro1FKQR+dTC6tXazem5g+
|
||||
MA0GCSqGSIb3DQEBCwUAMHYxCzAJBgNVBAYTAlVTMTwwOgYDVQQKEzNJbnRlcm5l
|
||||
dCBDb3Jwb3JhdGlvbiBmb3IgQXNzaWduZWQgTmFtZXMgYW5kIE51bWJlcnMxKTAn
|
||||
BgNVBAMTIElDQU5OIFRyYWRlbWFyayBDbGVhcmluZ2hvdXNlIENBMB4XDTEzMDcy
|
||||
NDAwMDAwMFoXDTIzMDcyMzIzNTk1OVowdjELMAkGA1UEBhMCVVMxPDA6BgNVBAoT
|
||||
M0ludGVybmV0IENvcnBvcmF0aW9uIGZvciBBc3NpZ25lZCBOYW1lcyBhbmQgTnVt
|
||||
YmVyczEpMCcGA1UEAxMgSUNBTk4gVHJhZGVtYXJrIENsZWFyaW5naG91c2UgQ0Ew
|
||||
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5MX6qpRnqFzEXa9w3G0b8
|
||||
LTEVZzpOpcSq2BXJO16+iuZ964mpay2hm2BdZk89hSmZhUy2ePBR6PdS0GMmzzXL
|
||||
NiyTHJlDIPxxXTR39Iqs8QChJ8wle4pYUu8JUk2vJ0r7PhFweeCCQZ5gvHdCwopS
|
||||
bXeolj4NCqsvzU8iROsLRHSZbE83i2pkL+qBoyzjny9MO2rvMNPo5WrDNrno6hvC
|
||||
hlf8Pv77HTNCazI2MeW0ArfLin4pSe6nLnDsQA11SF9bbgwDgVMQFvmB8nEvUbZW
|
||||
Atnp3auaWqaylC+G0p3frFvMCUJMPrghiPwBABl3bk1GLjXXVl7D8SubKd2Xwv63
|
||||
AgMBAAGjgbowgbcwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUXMDxlizK
|
||||
TFsp8UB00xs2PkfUbgQwDgYDVR0PAQH/BAQDAgEGMC4GA1UdHwQnMCUwI6AhoB+G
|
||||
HWh0dHA6Ly9jcmwuaWNhbm4ub3JnL3RtY2guY3JsMEIGA1UdIAQ7MDkwBgYEVR0g
|
||||
ADAvBggrBgEFBQcCATAjMCEGCCsGAQUFBwIBFhVodHRwczovL2NhLmljYW5uLm9y
|
||||
Zy8wDQYJKoZIhvcNAQELBQADggEBAAM29FBdwQSAx8dD4ZYtCYjXxTonNCP2qveG
|
||||
wrpMJcq/I3Jp/N4etsnj+K5ej5sSlDuo8sTMF7lgMkgjrc6zgJl0+Gct2RhbRNzN
|
||||
5ittE9JwJZ3Us4vwiy6gqMO5Ie9YaKMZy2MYP2iFp6AhBKIc2Iz+8aFfnFzdSEx2
|
||||
b3xc+t1A09dzpnzU6zvHWUUkTYq9fTg1er1npni9ZErvp0jEyHVWi5GXvWap68XH
|
||||
pVF6TBmPW2UBEEnFgd3SxbMXLhaD/wzV12tlSYjxaNed+H5qbVVbSVwN9yBeWU29
|
||||
/pkZu79TqFFxTd4CJTWOBq0+yLO1Ts2ZZ1l+GgU6e3hI1XERSNc=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright 2023 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.tools;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.tools.params.KeyValueMapParameter.StringToRegistrarRoleMap;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Shared base class for commands that create or modify a {@link User}. */
|
||||
public abstract class CreateOrUpdateUserCommand extends ConfirmingCommand {
|
||||
|
||||
@Nullable
|
||||
@Parameter(names = "--email", description = "Email address of the user", required = true)
|
||||
String email;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--admin",
|
||||
description = "Whether or not the user in question is an admin",
|
||||
arity = 1)
|
||||
private Boolean isAdmin;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--global_role",
|
||||
description = "Global role, e.g. SUPPORT_LEAD, to apply to the user")
|
||||
private GlobalRole globalRole;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--registrar_roles",
|
||||
converter = StringToRegistrarRoleMap.class,
|
||||
validateWith = StringToRegistrarRoleMap.class,
|
||||
description =
|
||||
"Comma-delimited mapping of registrar name to role that the user has on that registrar")
|
||||
private ImmutableMap<String, RegistrarRole> registrarRolesMap;
|
||||
|
||||
@Nullable
|
||||
abstract User getExistingUser(String email);
|
||||
|
||||
@Override
|
||||
protected final String execute() throws Exception {
|
||||
checkArgumentNotNull(email, "Email must be provided");
|
||||
tm().transact(this::executeInTransaction);
|
||||
return String.format("Saved user with email %s", email);
|
||||
}
|
||||
|
||||
private void executeInTransaction() {
|
||||
User user = getExistingUser(email);
|
||||
UserRoles.Builder userRolesBuilder =
|
||||
(user == null) ? new UserRoles.Builder() : user.getUserRoles().asBuilder();
|
||||
|
||||
Optional.ofNullable(globalRole).ifPresent(userRolesBuilder::setGlobalRole);
|
||||
Optional.ofNullable(registrarRolesMap).ifPresent(userRolesBuilder::setRegistrarRoles);
|
||||
Optional.ofNullable(isAdmin).ifPresent(userRolesBuilder::setIsAdmin);
|
||||
|
||||
User.Builder builder =
|
||||
(user == null) ? new User.Builder().setEmailAddress(email) : user.asBuilder();
|
||||
builder.setUserRoles(userRolesBuilder.build());
|
||||
User newUser = builder.build();
|
||||
UserDao.saveUser(newUser);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2023 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.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Command to create a new User. */
|
||||
@Parameters(separators = " =", commandDescription = "Update a user account")
|
||||
public class CreateUserCommand extends CreateOrUpdateUserCommand {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
User getExistingUser(String email) {
|
||||
checkArgument(
|
||||
!UserDao.loadUser(email).isPresent(), "A user with email %s already exists", email);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2023 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.tools;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Deletes a {@link User}. */
|
||||
@Parameters(separators = " =", commandDescription = "Delete a user account")
|
||||
public class DeleteUserCommand extends ConfirmingCommand {
|
||||
|
||||
@Nullable
|
||||
@Parameter(names = "--email", description = "Email address of the user", required = true)
|
||||
String email;
|
||||
|
||||
@Override
|
||||
protected String prompt() {
|
||||
checkArgumentNotNull(email, "Email must be provided");
|
||||
checkArgumentPresent(UserDao.loadUser(email), "Email does not correspond to a valid user");
|
||||
return String.format("Delete user with email %s?", email);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String execute() throws Exception {
|
||||
tm().transact(
|
||||
() -> {
|
||||
Optional<User> optionalUser = UserDao.loadUser(email);
|
||||
checkArgumentPresent(optionalUser, "Email no longer corresponds to a valid user");
|
||||
tm().delete(optionalUser.get());
|
||||
});
|
||||
return String.format("Deleted user with email %s", email);
|
||||
}
|
||||
}
|
||||
@@ -94,10 +94,10 @@ final class GenerateLordnCommand implements Command {
|
||||
Domain domain) {
|
||||
String status = " ";
|
||||
if (domain.getLaunchNotice() == null && domain.getSmdId() != null) {
|
||||
sunriseCsv.add(LordnTaskUtils.getCsvLineForSunriseDomain(domain, domain.getCreationTime()));
|
||||
sunriseCsv.add(LordnTaskUtils.getCsvLineForSunriseDomain(domain));
|
||||
status = "S";
|
||||
} else if (domain.getLaunchNotice() != null || domain.getSmdId() != null) {
|
||||
claimsCsv.add(LordnTaskUtils.getCsvLineForClaimsDomain(domain, domain.getCreationTime()));
|
||||
claimsCsv.add(LordnTaskUtils.getCsvLineForClaimsDomain(domain));
|
||||
status = "C";
|
||||
}
|
||||
System.out.printf("%s[%s] ", domain.getDomainName(), status);
|
||||
|
||||
@@ -46,6 +46,7 @@ public final class RegistryTool {
|
||||
.put("create_registrar_groups", CreateRegistrarGroupsCommand.class)
|
||||
.put("create_reserved_list", CreateReservedListCommand.class)
|
||||
.put("create_tld", CreateTldCommand.class)
|
||||
.put("create_user", CreateUserCommand.class)
|
||||
.put("curl", CurlCommand.class)
|
||||
.put("delete_allocation_tokens", DeleteAllocationTokensCommand.class)
|
||||
.put("delete_domain", DeleteDomainCommand.class)
|
||||
@@ -53,6 +54,7 @@ public final class RegistryTool {
|
||||
.put("delete_premium_list", DeletePremiumListCommand.class)
|
||||
.put("delete_reserved_list", DeleteReservedListCommand.class)
|
||||
.put("delete_tld", DeleteTldCommand.class)
|
||||
.put("delete_user", DeleteUserCommand.class)
|
||||
.put("encrypt_escrow_deposit", EncryptEscrowDepositCommand.class)
|
||||
.put("enqueue_poll_message", EnqueuePollMessageCommand.class)
|
||||
.put("execute_epp", ExecuteEppCommand.class)
|
||||
@@ -110,6 +112,7 @@ public final class RegistryTool {
|
||||
.put("update_reserved_list", UpdateReservedListCommand.class)
|
||||
.put("update_server_locks", UpdateServerLocksCommand.class)
|
||||
.put("update_tld", UpdateTldCommand.class)
|
||||
.put("update_user", UpdateUserCommand.class)
|
||||
.put("upload_claims_list", UploadClaimsListCommand.class)
|
||||
.put("validate_escrow_deposit", ValidateEscrowDepositCommand.class)
|
||||
.put("validate_login_credentials", ValidateLoginCredentialsCommand.class)
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2023 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.tools;
|
||||
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Updates a user, assuming that the user in question already exists. */
|
||||
@Parameters(separators = " =", commandDescription = "Update a user account")
|
||||
public class UpdateUserCommand extends CreateOrUpdateUserCommand {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
User getExistingUser(String email) {
|
||||
return checkArgumentPresent(UserDao.loadUser(email), "User %s not found", email);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ package google.registry.tools.params;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import java.util.Map;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
|
||||
@@ -107,4 +108,18 @@ public abstract class KeyValueMapParameter<K, V>
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/** Combined converter/validator class for maps of registrar names to registrar roles. */
|
||||
public static class StringToRegistrarRoleMap extends KeyValueMapParameter<String, RegistrarRole> {
|
||||
|
||||
@Override
|
||||
protected String parseKey(String rawKey) {
|
||||
return rawKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RegistrarRole parseValue(String rawValue) {
|
||||
return RegistrarRole.valueOf(rawValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright 2023 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.ui.server.console;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.api.client.http.HttpStatusCodes;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.model.EppResourceUtils;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.ui.server.registrar.JsonGetAction;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Returns a JSON representation of a domain to the registrar console. */
|
||||
@Action(
|
||||
service = Action.Service.DEFAULT,
|
||||
path = ConsoleDomainGetAction.PATH,
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public class ConsoleDomainGetAction implements JsonGetAction {
|
||||
|
||||
public static final String PATH = "/console-api/domain";
|
||||
|
||||
private final AuthResult authResult;
|
||||
private final Response response;
|
||||
private final Gson gson;
|
||||
private final String paramDomain;
|
||||
|
||||
@Inject
|
||||
public ConsoleDomainGetAction(
|
||||
AuthResult authResult,
|
||||
Response response,
|
||||
Gson gson,
|
||||
@Parameter("domain") String paramDomain) {
|
||||
this.authResult = authResult;
|
||||
this.response = response;
|
||||
this.gson = gson;
|
||||
this.paramDomain = paramDomain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!authResult.isAuthenticated() || !authResult.userAuthInfo().isPresent()) {
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED);
|
||||
return;
|
||||
}
|
||||
UserAuthInfo authInfo = authResult.userAuthInfo().get();
|
||||
if (!authInfo.consoleUser().isPresent()) {
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED);
|
||||
return;
|
||||
}
|
||||
User user = authInfo.consoleUser().get();
|
||||
Optional<Domain> possibleDomain =
|
||||
tm().transact(
|
||||
() ->
|
||||
EppResourceUtils.loadByForeignKeyCached(
|
||||
Domain.class, paramDomain, tm().getTransactionTime()));
|
||||
if (!possibleDomain.isPresent()) {
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
Domain domain = possibleDomain.get();
|
||||
if (!user.getUserRoles()
|
||||
.hasPermission(domain.getCurrentSponsorRegistrarId(), ConsolePermission.DOWNLOAD_DOMAINS)) {
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_OK);
|
||||
response.setPayload(gson.toJson(domain));
|
||||
}
|
||||
}
|
||||
@@ -156,4 +156,10 @@ public final class RegistrarConsoleModule {
|
||||
static Boolean provideIsLock(HttpServletRequest req) {
|
||||
return extractBooleanParameter(req, "isLock");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("domain")
|
||||
static String provideDomain(HttpServletRequest req) {
|
||||
return extractRequiredParameter(req, "domain");
|
||||
}
|
||||
}
|
||||
|
||||
-6
@@ -23,12 +23,6 @@
|
||||
"helpText": "The exclusive upper bound of the operation window, in ISO 8601 format.",
|
||||
"is_optional": false
|
||||
},
|
||||
{
|
||||
"name": "shard",
|
||||
"label": "The exclusive upper bound of the operation window.",
|
||||
"helpText": "The exclusive upper bound of the operation window, in ISO 8601 format.",
|
||||
"is_optional": true
|
||||
},
|
||||
{
|
||||
"name": "isDryRun",
|
||||
"label": "Whether this job is a dry run.",
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
-----BEGIN X509 CRL-----
|
||||
MIIDLjCCARYCAQEwDQYJKoZIhvcNAQENBQAwfDELMAkGA1UEBhMCVVMxPDA6BgNV
|
||||
BAoMM0ludGVybmV0IENvcnBvcmF0aW9uIGZvciBBc3NpZ25lZCBOYW1lcyBhbmQg
|
||||
TnVtYmVyczEvMC0GA1UEAwwmSUNBTk4gVHJhZGVtYXJrIENsZWFyaW5naG91c2Ug
|
||||
UGlsb3QgQ0EXDTIyMTExNjEzMzIyN1oXDTIzMDQwNjEzMzIyN1owNTAzAhQc4zug
|
||||
SmVXTpNkiBlOLRFSS6qBnhcNMjIxMTE2MTMzMjI3WjAMMAoGA1UdFQQDCgEAoC8w
|
||||
LTAfBgNVHSMEGDAWgBRHe0+uelGRtdG/nTbphn82FgSpRTAKBgNVHRQEAwIBATAN
|
||||
BgkqhkiG9w0BAQ0FAAOCAgEAD5pxGf7W2nxnCyoMhhRWclaBrY2VF+lYitpQdUIA
|
||||
Li3nhCXDiXwHWNtiyGjKaWJFsCFAph0EejKTR1iHh/7t7fZ5T2KhiqyREGGs2VT5
|
||||
AXTWTDZUdGtobmc9xmXowzF7YUWoZZenj1AwUt0/tDKth1ieZvoRWEKJ+0YCpIDQ
|
||||
LyfJKBFM6NEyrYiKu1szcdT7+Meii4M8ulYHkUXEJXQBN5+LuESkRrqach6n6zSS
|
||||
urvCG2I0ijqVj36TudwRrYU+sCpk8XR6//XyJbcjW5vuUd1/uYrJ+lSZCPaKfjIo
|
||||
gLKK1UJ9wLZbg28Hqtb5KxYsLtlzMsNFCbVebatl6QdS+J4OaCGLE4SM2NnsheN/
|
||||
qxErMS1eAzNVgpW+yY4JV47k54HDBZfgHmgA8lgyShn7w08dA2mWIYbmbKHcnh2N
|
||||
QSQ5wjYbTEy3a251t32pR76W5k/uduoMXestJEXD7yViMz3pFzu97l1huAP8C0Hs
|
||||
sMEysMomthMQChqNUZw19wakdxuCyiU+YrL7bHTTL2pAKYB0AKOGE1XgecnneC89
|
||||
+K5vr5TUTWOpksUeuM5Oq2YYLicYuES1Nr/5GkszNCCY0J+fBL50AI0IItrfFjt1
|
||||
Xu/mlbiJKQZXMppWfLJUhu/8Ur1VYeYLeCNR3ju7jfvm3DkVzmqSc0b01uz5POTz
|
||||
Hak=
|
||||
-----END X509 CRL-----
|
||||
@@ -0,0 +1,34 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIF7DCCA9SgAwIBAgIUemptMV4lw+1QL1e648o+wvANH4wwDQYJKoZIhvcNAQEN
|
||||
BQAwfDELMAkGA1UEBhMCVVMxPDA6BgNVBAoMM0ludGVybmV0IENvcnBvcmF0aW9u
|
||||
IGZvciBBc3NpZ25lZCBOYW1lcyBhbmQgTnVtYmVyczEvMC0GA1UEAwwmSUNBTk4g
|
||||
VHJhZGVtYXJrIENsZWFyaW5naG91c2UgUGlsb3QgQ0EwHhcNMjIxMTE1MTg1MDA5
|
||||
WhcNNDIxMTE1MTg1MDA5WjB8MQswCQYDVQQGEwJVUzE8MDoGA1UECgwzSW50ZXJu
|
||||
ZXQgQ29ycG9yYXRpb24gZm9yIEFzc2lnbmVkIE5hbWVzIGFuZCBOdW1iZXJzMS8w
|
||||
LQYDVQQDDCZJQ0FOTiBUcmFkZW1hcmsgQ2xlYXJpbmdob3VzZSBQaWxvdCBDQTCC
|
||||
AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALQQS8gSAw6fsyqP+yWvfOtU
|
||||
D2hCe/RvbKMG6cJ6yuCMscxIEPT0xrVTkGfzG8b6T4j8iD9mVHDxO19z+y0ALog/
|
||||
IPeVLw/x/2QC5YTDlrQtbsT33BN+NFdp13mE324owiyVyYrPqN1B6MtdERQLkIFU
|
||||
PIlhTopHfnMtRNzwt2gPpNwy2q5lqK20dKzrG9W1kLd26nFfnN5T9fnv21TtBhSJ
|
||||
73qfoU8GuOZhytloJRmT/yvvZNH1JTHyvuG15Q6kDDf46E1d6PO1+tmBQnNIZBfp
|
||||
FALrqN1QX1znaHkd1smgLEC1JV/2iAOtxNUL0d1vJcQfmjHXBtMF8Yx1ulpJjN1c
|
||||
A6iLMJfO5znd8jQ9O6hkMUjYNRz+XTGHiCTZVkhlNeJiIQbBlILbsnuT4Q+Ywxu+
|
||||
83W/ymqq/G2tIJdqXapUGEulYw/09y0TMyMnkA0VO9eDNf/B1AaQxBCEhpP6OufR
|
||||
G/nEzbH/SknVhgeV8CkhZo4UeFOx5XFZVj1Y3vBsqVwQYTc0NG84Bv3HD6sDXQd0
|
||||
DtW0gjqXg2caxQBoX8F40MCkik5TvnOT8RawvWKJjakEKSgeMceu9LnjRgFk+96Z
|
||||
ig1vGVJi213rUgSv7oXcO3HgraHOLkzkMglqOBh7NrUnEdCrrDGKYZwxZ/46cofY
|
||||
yyOb+9y/UOWa8MWvoam7AgMBAAGjZjBkMB0GA1UdDgQWBBRHe0+uelGRtdG/nTbp
|
||||
hn82FgSpRTAfBgNVHSMEGDAWgBRHe0+uelGRtdG/nTbphn82FgSpRTASBgNVHRMB
|
||||
Af8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQ0FAAOCAgEA
|
||||
ElXV35T8rG25yyGzNTgI9lDntDy2D0c/WSPPn9LZQREddKEpMBSy8pztHLJurH7B
|
||||
uKeb+j0KXw+0IMG5ZKxacUbLlW8NGIXqBRkRiDfBDw374dsLIeB7Gb3PKMWsnvg5
|
||||
zZic/iW7Wrrur6xAjFsgFMz8KRuNseGFDZi3UF1O4xX6Wy2KV5HMvvbnzyQa4bN9
|
||||
mbnCEf8tVXws2ZWC4e7yEkbLFqDh5czujVGq+g8VEl82wFTn0s+u6roI3vCrqZN9
|
||||
Qh2/yOzhpnu+daDcIUqb8keNNIswE7n9A9Yl40xNfzZid8bP4fJXM2oVcXxqUnsD
|
||||
Khwk9wgk30XVuKokoWYB3ExAVTYeR6j3jZPpDp3wYHXV23hgexPykKSE/7TMByII
|
||||
K1egDjKLdUlReQ5Rca48IRRAa6f4igJoaSvmp3eEF4lmxgXMhHIDocUN0hVDDaPb
|
||||
WGkr+sChXtftIUVhE2bi746dNhiBLWWPz8FX/uZzD1srJA8kPmf8YWdH2d6yJ8BN
|
||||
2olV7QpDRpmCJwblyzLLeNWCQiwHQNN4MvtxJFFFZQfF6m6A6bZ5CKNM5GvJanRl
|
||||
/aS1ehO/DhOkcGelxAjMtTPqKhI1j+SBZqPdumrQDFhfHN/sM9F4g+I8datpu/2P
|
||||
Dop0Ki70evaFhD8jVE4z0zt59OUzEHjezWOTNoYU9GU=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,18 @@
|
||||
-----BEGIN X509 CRL-----
|
||||
MIIC8DCB2QIBATANBgkqhkiG9w0BAQ0FADB2MQswCQYDVQQGEwJVUzE8MDoGA1UE
|
||||
CgwzSW50ZXJuZXQgQ29ycG9yYXRpb24gZm9yIEFzc2lnbmVkIE5hbWVzIGFuZCBO
|
||||
dW1iZXJzMSkwJwYDVQQDDCBJQ0FOTiBUcmFkZW1hcmsgQ2xlYXJpbmdob3VzZSBD
|
||||
QRcNMjIxMTE2MTMyNzU3WhcNMjMwNDA2MTMyNzU3WqAvMC0wHwYDVR0jBBgwFoAU
|
||||
P/RCC1aVjbgjAjp0MnrSEivX8cYwCgYDVR0UBAMCAQEwDQYJKoZIhvcNAQENBQAD
|
||||
ggIBAJkrxahXQDaniHHE+8/3GVSlXz2175xVfZ/zqq3twatKd4FWxXD5sr42595Z
|
||||
5T9EP5XFii5o/j49qM6a+Qc3V++LEcTj+VHFxvhG2czYhiuZXzFu1WmdmtSCe2+C
|
||||
9N7DgGl9VNRg14DKWoW5BPdmgy6/XZCEcxlQ6ft/caZ8G9pR+Q5i9rwCXDFXB1ni
|
||||
kIuMKnp5rq95GzF8WnbEGfM0s5YY/UfSozF35oPGtRSyCWdGoTmmpnp3DXMvElMW
|
||||
wMxtCRhdhxsS6YzeCPiORTzq08IiCJcVpud5ZdDdKzja1tF8iSc5MnMJ0pygT4ds
|
||||
uZOHmj8/bAA9cf9PK/8N5fd7jZZlPDKKbhT+EoKZTXzNwBiGy68tXerWENFkR0tF
|
||||
Sy9T45RcGve9L5nEV8bqawsxrmnuatSh/6fSTDiz4HO1/mW6/GAT1QPFYrU++p8o
|
||||
BR7WcKCHvQpUt5tXhh1hAZba6j4aeWpXUquyTtrjgqThvvBIT5m76k19BcRaOroE
|
||||
qf0WJOQcvn2hPBNEbSbbHOWaXpHbTI8R6gk+/utRc9/xkHF5vEQTfUPHTkjoK4dW
|
||||
S4p4P6tRcNoxzSfh7bN9nts2VFKcvSaF10m2G1bCyJB2QjKMZG7aGVX+9Pvnoa1v
|
||||
HgNDfKNZR1a/ZYcJm7QY5TRuQF+Evd/OcsVOdRhx804d08ms
|
||||
-----END X509 CRL-----
|
||||
@@ -0,0 +1,34 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIF4DCCA8igAwIBAgIUHTVZH/TadFNIiEBCJ/OdngKfZwwwDQYJKoZIhvcNAQEN
|
||||
BQAwdjELMAkGA1UEBhMCVVMxPDA6BgNVBAoMM0ludGVybmV0IENvcnBvcmF0aW9u
|
||||
IGZvciBBc3NpZ25lZCBOYW1lcyBhbmQgTnVtYmVyczEpMCcGA1UEAwwgSUNBTk4g
|
||||
VHJhZGVtYXJrIENsZWFyaW5naG91c2UgQ0EwHhcNMjIxMTE1MTg0ODQxWhcNNDIx
|
||||
MTE1MTg0ODQxWjB2MQswCQYDVQQGEwJVUzE8MDoGA1UECgwzSW50ZXJuZXQgQ29y
|
||||
cG9yYXRpb24gZm9yIEFzc2lnbmVkIE5hbWVzIGFuZCBOdW1iZXJzMSkwJwYDVQQD
|
||||
DCBJQ0FOTiBUcmFkZW1hcmsgQ2xlYXJpbmdob3VzZSBDQTCCAiIwDQYJKoZIhvcN
|
||||
AQEBBQADggIPADCCAgoCggIBAMf5/4d2gaDgG0Tbx5ewIKZNlH/KxURjRqWj7vzc
|
||||
SYnzszAcaTuwM1sHWTJtUUC0QP4QcG90e8Q7A7zhgYVm6POBV3U9hv/NVSZB6Set
|
||||
DK5lxU/cubQrPhf2Bc7AkGvfnJV8+/0GI5x9AF8dlSUj3yvl9VWvI8Iuph4xHm0g
|
||||
Fg6BHo4beUVsbdBwnBkSPEmsmNo0D0gf1rCIb/wYfWMZBqU8bduS36TUvHshMTB/
|
||||
Luad3QYyPhqgomizVzEjXebuGlyCtwlGOHdI5t2WlM7XF3RPXZVxA+nsFwRWm9UT
|
||||
r6ygGve3+9y1bjqudWu8mt9VevYCiesLPowUy9E8Vg8xKcsntUWs1KoZTQYxlohb
|
||||
u3cHNxBdYGflQ8m8cbkDeAvWNOgMlhPzWBDFqQeVuBQhiuF9iKR3RZHFAHXJy81t
|
||||
DKQgzR6YlMd3iCGfBVK/uUVr89UgJ1xv5hniSaFDYqtD5rZftvGb2YXsyeQjs0Pl
|
||||
QkZyaynVS5P3Tax57fQUcJR3YqgeoeXQNTit+/ieZju1zGLutGikYujciPBIWoBl
|
||||
S8YnOgPNyfjE+LrQG2RqsZOumqlN66ZjqYH4pCf6lI14PJUl4mUh2YpnIyjQd8Uq
|
||||
QxZmrqDOJAawd2LVuHr4GR6Vbm/oAyyCdQ5HztvCbXOK6v+pMdrrbtb+IlvQ5hcu
|
||||
FoqrAgMBAAGjZjBkMB0GA1UdDgQWBBQ/9EILVpWNuCMCOnQyetISK9fxxjAfBgNV
|
||||
HSMEGDAWgBQ/9EILVpWNuCMCOnQyetISK9fxxjASBgNVHRMBAf8ECDAGAQH/AgEA
|
||||
MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQ0FAAOCAgEAm8QZeNzTD52hZH3I
|
||||
Vy9pti6NRcVSudtzZloVFJ4g9GbIx1uw8k61bgUX70jwk4pPbcOji8RoZoYG842p
|
||||
3tgq50rlRYsKZZx24ZUVaQDz0WKTzjhs2qlk0pRM9XZukZ8SB2knhvJ3byxogxJ0
|
||||
kIP4ysS5op90dcWsrS0O9HF8eBtHYvw2+MuA5KleWB4Qo94+GjmFJsiJiIQ6S+sG
|
||||
M9PZbHSEWW64fNNsgeozlnY9vJ4qRF55tylrB+pXOxVcKDRD3l5GstobmC0waTv8
|
||||
ZvMtR6SQ1QeHRcdznzWsASvDSxhv0vBauKbuBs1T/wgcDF9XXikTx8PbbCQUoCbC
|
||||
4mX+ZNgIXyCbhSVnNBQKN+HBrddm/AqHRLLnfOOcGBqCQ5JLicLvnsRMVrpnoLPS
|
||||
nMbq2QT7J7F5i+q2v2TqeKEwmj4zzh/hX1PbppS8IA/MujivFjLdn1SL0halidYN
|
||||
ywqEF6gUJMoImJ9YWvGRnyv7pm+kgrF83MPbD43AX6Hq8zidYOIzwT4ZnCMlBWJ2
|
||||
iZWciK3/nPe6YH8P5cPddLxRAWBstd5tbDmdPkIMZ0vHccmfIMhlMrCO/IB8N2tv
|
||||
7QJR8V0nE3BIE9RfIa4BrfY5nQpGHWAdpXJH9WTXgcwWUB3JQcxD64c3GhVvp7e4
|
||||
Jwz/ypzBX6WYeSrb3OZWylJ4sWE=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -49,6 +49,8 @@ import google.registry.model.tld.Registry.TldState;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.TaskQueueExtension;
|
||||
import google.registry.tmch.TmchData;
|
||||
import google.registry.tmch.TmchTestData;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -64,6 +66,9 @@ class EppLifecycleDomainTest extends EppTestCase {
|
||||
"ACDATE", "2002-06-04T00:00:00Z",
|
||||
"EXDATE", "2003-06-01T00:04:00Z");
|
||||
|
||||
private static final String ENCODED_SMD =
|
||||
TmchData.readEncodedSignedMark(TmchTestData.loadFile("smd/active.smd")).getEncodedData();
|
||||
|
||||
@RegisterExtension
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension();
|
||||
@@ -1184,8 +1189,8 @@ class EppLifecycleDomainTest extends EppTestCase {
|
||||
*/
|
||||
@Test
|
||||
void testDomainCreation_startDateSunriseFull() throws Exception {
|
||||
// The signed mark is valid between 2013 and 2017
|
||||
DateTime sunriseDate = DateTime.parse("2014-09-08T09:09:09Z");
|
||||
// The signed mark is valid between 2022-11-22 and 2027-10-18.
|
||||
DateTime sunriseDate = DateTime.parse("2025-09-08T09:09:09Z");
|
||||
DateTime gaDate = sunriseDate.plusDays(30);
|
||||
createTld(
|
||||
"example",
|
||||
@@ -1201,7 +1206,7 @@ class EppLifecycleDomainTest extends EppTestCase {
|
||||
createContactsAndHosts();
|
||||
|
||||
// During pre-delegation, any create should fail both with and without mark
|
||||
assertThatCommand("domain_create_sunrise_encoded_mark.xml")
|
||||
assertThatCommand("domain_create_sunrise_encoded_mark.xml", ImmutableMap.of("SMD", ENCODED_SMD))
|
||||
.atTime(sunriseDate.minusDays(2))
|
||||
.hasResponse(
|
||||
"response_error.xml",
|
||||
@@ -1219,7 +1224,9 @@ class EppLifecycleDomainTest extends EppTestCase {
|
||||
"MSG", "The current registry phase does not allow for general registrations"));
|
||||
|
||||
// During sunrise, verify that the launch phase must be set to sunrise.
|
||||
assertThatCommand("domain_create_start_date_sunrise_encoded_mark_wrong_phase.xml")
|
||||
assertThatCommand(
|
||||
"domain_create_start_date_sunrise_encoded_mark_wrong_phase.xml",
|
||||
ImmutableMap.of("SMD", ENCODED_SMD))
|
||||
.atTime(sunriseDate)
|
||||
.hasResponse(
|
||||
"response_error.xml",
|
||||
@@ -1230,14 +1237,14 @@ class EppLifecycleDomainTest extends EppTestCase {
|
||||
|
||||
// During sunrise, create with mark will succeed but without will fail.
|
||||
// We also test we can delete without a mark.
|
||||
assertThatCommand("domain_create_sunrise_encoded_mark.xml")
|
||||
assertThatCommand("domain_create_sunrise_encoded_mark.xml", ImmutableMap.of("SMD", ENCODED_SMD))
|
||||
.atTime(sunriseDate.plusDays(1))
|
||||
.hasResponse(
|
||||
"domain_create_response.xml",
|
||||
ImmutableMap.of(
|
||||
"DOMAIN", "test-validate.example",
|
||||
"CRDATE", "2014-09-09T09:09:09Z",
|
||||
"EXDATE", "2015-09-09T09:09:09Z"));
|
||||
"CRDATE", "2025-09-09T09:09:09Z",
|
||||
"EXDATE", "2026-09-09T09:09:09Z"));
|
||||
|
||||
assertThatCommand("domain_delete.xml", ImmutableMap.of("DOMAIN", "test-validate.example"))
|
||||
.atTime(sunriseDate.plusDays(1).plusMinutes(1))
|
||||
@@ -1264,15 +1271,14 @@ class EppLifecycleDomainTest extends EppTestCase {
|
||||
"Declared launch extension phase does not match the current registry phase"));
|
||||
|
||||
assertThatCommand(
|
||||
"domain_create_no_hosts_or_dsdata.xml",
|
||||
ImmutableMap.of("DOMAIN", "general.example"))
|
||||
"domain_create_no_hosts_or_dsdata.xml", ImmutableMap.of("DOMAIN", "general.example"))
|
||||
.atTime(gaDate.plusDays(2))
|
||||
.hasResponse(
|
||||
"domain_create_response.xml",
|
||||
ImmutableMap.of(
|
||||
"DOMAIN", "general.example",
|
||||
"CRDATE", "2014-10-10T09:09:09Z",
|
||||
"EXDATE", "2016-10-10T09:09:09Z"));
|
||||
"CRDATE", "2025-10-10T09:09:09Z",
|
||||
"EXDATE", "2027-10-10T09:09:09Z"));
|
||||
|
||||
assertThatLogoutSucceeds();
|
||||
}
|
||||
@@ -1280,8 +1286,8 @@ class EppLifecycleDomainTest extends EppTestCase {
|
||||
/** Test that missing type= argument on launch create works in start-date sunrise. */
|
||||
@Test
|
||||
void testDomainCreation_startDateSunrise_noType() throws Exception {
|
||||
// The signed mark is valid between 2013 and 2017
|
||||
DateTime sunriseDate = DateTime.parse("2014-09-08T09:09:09Z");
|
||||
// The signed mark is valid between 2022-11-22 and 2027-10-18.
|
||||
DateTime sunriseDate = DateTime.parse("2025-09-08T09:09:09Z");
|
||||
DateTime gaDate = sunriseDate.plusDays(30);
|
||||
createTld(
|
||||
"example",
|
||||
@@ -1306,14 +1312,16 @@ class EppLifecycleDomainTest extends EppTestCase {
|
||||
"CODE", "2303",
|
||||
"MSG", "The domain with given ID (test-validate.example) doesn't exist."));
|
||||
|
||||
assertThatCommand("domain_create_start_date_sunrise_encoded_mark_no_type.xml")
|
||||
assertThatCommand(
|
||||
"domain_create_start_date_sunrise_encoded_mark_no_type.xml",
|
||||
ImmutableMap.of("SMD", ENCODED_SMD))
|
||||
.atTime(sunriseDate.plusDays(1).plusMinutes(1))
|
||||
.hasResponse(
|
||||
"domain_create_response.xml",
|
||||
ImmutableMap.of(
|
||||
"DOMAIN", "test-validate.example",
|
||||
"CRDATE", "2014-09-09T09:10:09Z",
|
||||
"EXDATE", "2015-09-09T09:10:09Z"));
|
||||
"CRDATE", "2025-09-09T09:10:09Z",
|
||||
"EXDATE", "2026-09-09T09:10:09Z"));
|
||||
|
||||
assertThatCommand("domain_info.xml", ImmutableMap.of("DOMAIN", "test-validate.example"))
|
||||
.atTime(sunriseDate.plusDays(1).plusMinutes(2))
|
||||
@@ -1321,8 +1329,8 @@ class EppLifecycleDomainTest extends EppTestCase {
|
||||
"domain_info_response_ok_wildcard.xml",
|
||||
ImmutableMap.of(
|
||||
"DOMAIN", "test-validate.example",
|
||||
"CRDATE", "2014-09-09T09:10:09Z",
|
||||
"EXDATE", "2015-09-09T09:10:09Z"));
|
||||
"CRDATE", "2025-09-09T09:10:09Z",
|
||||
"EXDATE", "2026-09-09T09:10:09Z"));
|
||||
|
||||
assertThatLogoutSucceeds();
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.flows.domain;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.io.BaseEncoding.base16;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
@@ -72,7 +73,6 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.truth.Truth8;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.UnimplementedExtensionException;
|
||||
@@ -91,6 +91,7 @@ import google.registry.flows.domain.DomainCreateFlow.SignedMarksOnlyDuringSunris
|
||||
import google.registry.flows.domain.DomainFlowTmchUtils.FoundMarkExpiredException;
|
||||
import google.registry.flows.domain.DomainFlowTmchUtils.FoundMarkNotYetValidException;
|
||||
import google.registry.flows.domain.DomainFlowTmchUtils.NoMarksFoundMatchingDomainException;
|
||||
import google.registry.flows.domain.DomainFlowTmchUtils.SignedMarkRevokedErrorException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.AcceptedTooLongAgoException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.BadDomainNameCharacterException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.BadDomainNamePartsCountException;
|
||||
@@ -154,6 +155,8 @@ import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingEvent.Flag;
|
||||
import google.registry.model.billing.BillingEvent.Reason;
|
||||
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
@@ -181,6 +184,10 @@ import google.registry.persistence.VKey;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.TaskQueueExtension;
|
||||
import google.registry.testing.TaskQueueHelper.TaskMatcher;
|
||||
import google.registry.tmch.LordnTaskUtils.LordnPhase;
|
||||
import google.registry.tmch.SmdrlCsvParser;
|
||||
import google.registry.tmch.TmchData;
|
||||
import google.registry.tmch.TmchTestData;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -188,9 +195,14 @@ import javax.annotation.Nullable;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.junitpioneer.jupiter.cartesian.CartesianTest;
|
||||
import org.junitpioneer.jupiter.cartesian.CartesianTest.Values;
|
||||
|
||||
/** Unit tests for {@link DomainCreateFlow}. */
|
||||
class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain> {
|
||||
@@ -198,6 +210,18 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
@RegisterExtension TaskQueueExtension taskQueue = new TaskQueueExtension();
|
||||
|
||||
private static final String CLAIMS_KEY = "2013041500/2/6/9/rJ1NrDO92vDsAzf7EQzgjX4R0000000001";
|
||||
// This is a time when SMD itself is not valid, but its signing certificates are. It will
|
||||
// trigger a different exception than when any signing cert is not valid yet.
|
||||
private static final DateTime SMD_NOT_YET_VALID_TIME = DateTime.parse("2022-11-20T12:34:56Z");
|
||||
private static final DateTime SMD_VALID_TIME = DateTime.parse("2023-01-02T12:34:56Z");
|
||||
// This is a time when the imtermediate certificate in the SMD signature chain has expired. The
|
||||
// SMD itself has not expired yet. It will trigger a different exception than when the SMD itself
|
||||
// has expired.
|
||||
private static final DateTime SMD_CERT_EXPIRED_TIME = DateTime.parse("2027-11-02T12:34:56Z");
|
||||
private static final String SMD_ID = "000000851669081693741-65535";
|
||||
private static final String SMD_FILE_PATH = "smd/active.smd";
|
||||
private static final String ENCODED_SMD =
|
||||
TmchData.readEncodedSignedMark(TmchTestData.loadFile(SMD_FILE_PATH)).getEncodedData();
|
||||
|
||||
private AllocationToken allocationToken;
|
||||
|
||||
@@ -206,8 +230,13 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
clock.setTo(DateTime.parse("1999-04-03T22:00:00.0Z").minus(Duration.millis(1)));
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
DatabaseMigrationStateSchedule.useUncachedForTest();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void initCreateTest() {
|
||||
void initCreateTest() throws Exception {
|
||||
createTld("tld");
|
||||
allocationToken =
|
||||
persistResource(
|
||||
@@ -372,21 +401,33 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
.that(reloadResourceByForeignKey())
|
||||
.hasSmdId(null)
|
||||
.and()
|
||||
.hasLaunchNotice(null);
|
||||
.hasLaunchNotice(null)
|
||||
.and()
|
||||
.hasLordnPhase(LordnPhase.NONE);
|
||||
assertNoTasksEnqueued(QUEUE_CLAIMS, QUEUE_SUNRISE);
|
||||
assertNoTasksEnqueued(QUEUE_CLAIMS, QUEUE_SUNRISE);
|
||||
}
|
||||
|
||||
private void assertSunriseLordn(String domainName) throws Exception {
|
||||
assertAboutDomains()
|
||||
.that(reloadResourceByForeignKey())
|
||||
.hasSmdId("0000001761376042759136-65535")
|
||||
.hasSmdId(SMD_ID)
|
||||
.and()
|
||||
.hasLaunchNotice(null);
|
||||
String expectedPayload =
|
||||
String.format(
|
||||
"%s,%s,0000001761376042759136-65535,1,2014-09-09T09:09:09.017Z",
|
||||
reloadResourceByForeignKey().getRepoId(), domainName);
|
||||
assertTasksEnqueued(QUEUE_SUNRISE, new TaskMatcher().payload(expectedPayload));
|
||||
if (DatabaseMigrationStateSchedule.getValueAtTime(clock.nowUtc())
|
||||
.equals(MigrationState.NORDN_SQL)) {
|
||||
assertAboutDomains().that(reloadResourceByForeignKey()).hasLordnPhase(LordnPhase.SUNRISE);
|
||||
} else {
|
||||
String expectedPayload =
|
||||
String.format(
|
||||
"%s,%s,%s,1,%s",
|
||||
reloadResourceByForeignKey().getRepoId(),
|
||||
domainName,
|
||||
SMD_ID,
|
||||
SMD_VALID_TIME.plusMillis(17));
|
||||
assertTasksEnqueued(QUEUE_SUNRISE, new TaskMatcher().payload(expectedPayload));
|
||||
assertAboutDomains().that(reloadResourceByForeignKey()).hasLordnPhase(LordnPhase.NONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertClaimsLordn() throws Exception {
|
||||
@@ -400,13 +441,19 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
"tmch",
|
||||
DateTime.parse("2010-08-16T09:00:00.0Z"),
|
||||
DateTime.parse("2009-08-16T09:00:00.0Z")));
|
||||
TaskMatcher task =
|
||||
new TaskMatcher()
|
||||
.payload(
|
||||
reloadResourceByForeignKey().getRepoId()
|
||||
+ ",example-one.tld,370d0b7c9223372036854775807,1,"
|
||||
+ "2009-08-16T09:00:00.017Z,2009-08-16T09:00:00.000Z");
|
||||
assertTasksEnqueued(QUEUE_CLAIMS, task);
|
||||
if (DatabaseMigrationStateSchedule.getValueAtTime(clock.nowUtc())
|
||||
.equals(MigrationState.NORDN_SQL)) {
|
||||
assertAboutDomains().that(reloadResourceByForeignKey()).hasLordnPhase(LordnPhase.CLAIMS);
|
||||
} else {
|
||||
TaskMatcher task =
|
||||
new TaskMatcher()
|
||||
.payload(
|
||||
reloadResourceByForeignKey().getRepoId()
|
||||
+ ",example-one.tld,370d0b7c9223372036854775807,1,"
|
||||
+ "2009-08-16T09:00:00.017Z,2009-08-16T09:00:00.000Z");
|
||||
assertTasksEnqueued(QUEUE_CLAIMS, task);
|
||||
assertAboutDomains().that(reloadResourceByForeignKey()).hasLordnPhase(LordnPhase.NONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void doSuccessfulTest(
|
||||
@@ -632,7 +679,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
clock.setTo(DateTime.parse("2014-09-09T09:09:09Z"));
|
||||
setEppInput(
|
||||
"domain_create_registration_encoded_signed_mark.xml",
|
||||
ImmutableMap.of("DOMAIN", "test-validate.tld", "PHASE", "open"));
|
||||
ImmutableMap.of("DOMAIN", "test-validate.tld", "PHASE", "open", "SMD", ENCODED_SMD));
|
||||
persistContactsAndHosts();
|
||||
EppException thrown = assertThrows(SignedMarksOnlyDuringSunriseException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
@@ -641,10 +688,10 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
@Test
|
||||
void testFailure_generalAvailability_superuserMismatchedEncodedSignedMark() {
|
||||
createTld("tld", GENERAL_AVAILABILITY);
|
||||
clock.setTo(DateTime.parse("2014-09-09T09:09:09Z"));
|
||||
clock.setTo(SMD_VALID_TIME);
|
||||
setEppInput(
|
||||
"domain_create_registration_encoded_signed_mark.xml",
|
||||
ImmutableMap.of("DOMAIN", "wrong.tld", "PHASE", "open"));
|
||||
ImmutableMap.of("DOMAIN", "wrong.tld", "PHASE", "open", "SMD", ENCODED_SMD));
|
||||
persistContactsAndHosts();
|
||||
EppException thrown =
|
||||
assertThrows(NoMarksFoundMatchingDomainException.class, this::runFlowAsSuperuser);
|
||||
@@ -656,7 +703,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.6", "CURRENCY", "USD"));
|
||||
persistContactsAndHosts();
|
||||
doSuccessfulTest(
|
||||
"tld", "domain_create_response_fee.xml", ImmutableMap.of("FEE_VERSION", "0.6"));
|
||||
"tld",
|
||||
"domain_create_response_fee.xml",
|
||||
ImmutableMap.of("FEE_VERSION", "0.6", "FEE", "26.00"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -664,7 +713,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.11", "CURRENCY", "USD"));
|
||||
persistContactsAndHosts();
|
||||
doSuccessfulTest(
|
||||
"tld", "domain_create_response_fee.xml", ImmutableMap.of("FEE_VERSION", "0.11"));
|
||||
"tld",
|
||||
"domain_create_response_fee.xml",
|
||||
ImmutableMap.of("FEE_VERSION", "0.11", "FEE", "26.00"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -672,7 +723,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.12", "CURRENCY", "USD"));
|
||||
persistContactsAndHosts();
|
||||
doSuccessfulTest(
|
||||
"tld", "domain_create_response_fee.xml", ImmutableMap.of("FEE_VERSION", "0.12"));
|
||||
"tld",
|
||||
"domain_create_response_fee.xml",
|
||||
ImmutableMap.of("FEE_VERSION", "0.12", "FEE", "26.00"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -680,7 +733,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
setEppInput("domain_create_fee_defaults.xml", ImmutableMap.of("FEE_VERSION", "0.6"));
|
||||
persistContactsAndHosts();
|
||||
doSuccessfulTest(
|
||||
"tld", "domain_create_response_fee.xml", ImmutableMap.of("FEE_VERSION", "0.6"));
|
||||
"tld",
|
||||
"domain_create_response_fee.xml",
|
||||
ImmutableMap.of("FEE_VERSION", "0.6", "FEE", "26.00"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -688,7 +743,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
setEppInput("domain_create_fee_defaults.xml", ImmutableMap.of("FEE_VERSION", "0.11"));
|
||||
persistContactsAndHosts();
|
||||
doSuccessfulTest(
|
||||
"tld", "domain_create_response_fee.xml", ImmutableMap.of("FEE_VERSION", "0.11"));
|
||||
"tld",
|
||||
"domain_create_response_fee.xml",
|
||||
ImmutableMap.of("FEE_VERSION", "0.11", "FEE", "26.00"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -696,7 +753,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
setEppInput("domain_create_fee_defaults.xml", ImmutableMap.of("FEE_VERSION", "0.12"));
|
||||
persistContactsAndHosts();
|
||||
doSuccessfulTest(
|
||||
"tld", "domain_create_response_fee.xml", ImmutableMap.of("FEE_VERSION", "0.12"));
|
||||
"tld",
|
||||
"domain_create_response_fee.xml",
|
||||
ImmutableMap.of("FEE_VERSION", "0.12", "FEE", "26.00"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -918,8 +977,12 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_claimsNotice() throws Exception {
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {true, false})
|
||||
void testSuccess_claimsNotice(boolean usePullQueue) throws Exception {
|
||||
if (!usePullQueue) {
|
||||
useNordnSql();
|
||||
}
|
||||
clock.setTo(DateTime.parse("2009-08-16T09:00:00.0Z"));
|
||||
setEppInput("domain_create_claim_notice.xml");
|
||||
persistContactsAndHosts();
|
||||
@@ -929,8 +992,12 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
assertClaimsLordn();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_claimsNoticeInQuietPeriod() throws Exception {
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {true, false})
|
||||
void testSuccess_claimsNoticeInQuietPeriod(boolean usePullQueue) throws Exception {
|
||||
if (!usePullQueue) {
|
||||
useNordnSql();
|
||||
}
|
||||
allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
@@ -1056,6 +1123,57 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_wrongFeeAmountTooHigh_defaultToken_v06() throws Exception {
|
||||
AllocationToken defaultToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("bbbbb")
|
||||
.setTokenType(DEFAULT_PROMO)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setDiscountFraction(0.5)
|
||||
.build());
|
||||
persistResource(
|
||||
Registry.get("tld")
|
||||
.asBuilder()
|
||||
.setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey()))
|
||||
.setCreateBillingCost(Money.of(USD, 8))
|
||||
.build());
|
||||
// Expects fee of $26
|
||||
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.6", "CURRENCY", "USD"));
|
||||
persistContactsAndHosts();
|
||||
// $12 is equal to 50% off the first year registration and 0% 0ff the 2nd year
|
||||
runFlowAssertResponse(
|
||||
loadFile(
|
||||
"domain_create_response_fee.xml",
|
||||
ImmutableMap.of("FEE_VERSION", "0.6", "FEE", "12.00")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmountTooLow_defaultToken_v06() throws Exception {
|
||||
AllocationToken defaultToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("bbbbb")
|
||||
.setTokenType(DEFAULT_PROMO)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setDiscountFraction(0.5)
|
||||
.build());
|
||||
persistResource(
|
||||
Registry.get("tld")
|
||||
.asBuilder()
|
||||
.setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey()))
|
||||
.setCreateBillingCost(Money.of(USD, 100))
|
||||
.build());
|
||||
// Expects fee of $26
|
||||
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.6", "CURRENCY", "USD"));
|
||||
persistContactsAndHosts();
|
||||
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmount_v11() {
|
||||
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.11", "CURRENCY", "USD"));
|
||||
@@ -1066,6 +1184,57 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_wrongFeeAmountTooHigh_defaultToken_v11() throws Exception {
|
||||
AllocationToken defaultToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("bbbbb")
|
||||
.setTokenType(DEFAULT_PROMO)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setDiscountFraction(0.5)
|
||||
.build());
|
||||
persistResource(
|
||||
Registry.get("tld")
|
||||
.asBuilder()
|
||||
.setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey()))
|
||||
.setCreateBillingCost(Money.of(USD, 8))
|
||||
.build());
|
||||
// Expects fee of $26
|
||||
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.11", "CURRENCY", "USD"));
|
||||
persistContactsAndHosts();
|
||||
// $12 is equal to 50% off the first year registration and 0% 0ff the 2nd year
|
||||
runFlowAssertResponse(
|
||||
loadFile(
|
||||
"domain_create_response_fee.xml",
|
||||
ImmutableMap.of("FEE_VERSION", "0.11", "FEE", "12.00")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmountTooLow_defaultToken_v11() throws Exception {
|
||||
AllocationToken defaultToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("bbbbb")
|
||||
.setTokenType(DEFAULT_PROMO)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setDiscountFraction(0.5)
|
||||
.build());
|
||||
persistResource(
|
||||
Registry.get("tld")
|
||||
.asBuilder()
|
||||
.setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey()))
|
||||
.setCreateBillingCost(Money.of(USD, 100))
|
||||
.build());
|
||||
// Expects fee of $26
|
||||
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.11", "CURRENCY", "USD"));
|
||||
persistContactsAndHosts();
|
||||
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmount_v12() {
|
||||
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.12", "CURRENCY", "USD"));
|
||||
@@ -1076,6 +1245,57 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_wrongFeeAmountTooHigh_defaultToken_v12() throws Exception {
|
||||
AllocationToken defaultToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("bbbbb")
|
||||
.setTokenType(DEFAULT_PROMO)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setDiscountFraction(0.5)
|
||||
.build());
|
||||
persistResource(
|
||||
Registry.get("tld")
|
||||
.asBuilder()
|
||||
.setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey()))
|
||||
.setCreateBillingCost(Money.of(USD, 8))
|
||||
.build());
|
||||
// Expects fee of $26
|
||||
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.12", "CURRENCY", "USD"));
|
||||
persistContactsAndHosts();
|
||||
// $12 is equal to 50% off the first year registration and 0% 0ff the 2nd year
|
||||
runFlowAssertResponse(
|
||||
loadFile(
|
||||
"domain_create_response_fee.xml",
|
||||
ImmutableMap.of("FEE_VERSION", "0.12", "FEE", "12.00")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongFeeAmountTooLow_defaultToken_v12() throws Exception {
|
||||
AllocationToken defaultToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setToken("bbbbb")
|
||||
.setTokenType(DEFAULT_PROMO)
|
||||
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
|
||||
.setAllowedTlds(ImmutableSet.of("tld"))
|
||||
.setDiscountFraction(0.5)
|
||||
.build());
|
||||
persistResource(
|
||||
Registry.get("tld")
|
||||
.asBuilder()
|
||||
.setDefaultPromoTokens(ImmutableList.of(defaultToken.createVKey()))
|
||||
.setCreateBillingCost(Money.of(USD, 100))
|
||||
.build());
|
||||
// Expects fee of $26
|
||||
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.12", "CURRENCY", "USD"));
|
||||
persistContactsAndHosts();
|
||||
EppException thrown = assertThrows(FeesMismatchException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongCurrency_v06() {
|
||||
setEppInput("domain_create_fee.xml", ImmutableMap.of("FEE_VERSION", "0.6", "CURRENCY", "EUR"));
|
||||
@@ -1199,8 +1419,12 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
assertAllocationTokenWasRedeemed("abcDEF23456");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_anchorTenant_withClaims() throws Exception {
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {true, false})
|
||||
void testSuccess_anchorTenant_withClaims(boolean usePullQueue) throws Exception {
|
||||
if (!usePullQueue) {
|
||||
useNordnSql();
|
||||
}
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
.setDomainName("example-one.tld")
|
||||
@@ -1249,19 +1473,31 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
@Test
|
||||
void testSuccess_anchorTenantInSunrise_withMetadataExtension_andSignedMark() throws Exception {
|
||||
createTld("tld", START_DATE_SUNRISE);
|
||||
setEppInput("domain_create_anchor_tenant_sunrise_metadata_extension_signed_mark.xml");
|
||||
setEppInput(
|
||||
"domain_create_anchor_tenant_sunrise_metadata_extension_signed_mark.xml",
|
||||
ImmutableMap.of("SMD", ENCODED_SMD));
|
||||
eppRequestSource = EppRequestSource.TOOL; // Only tools can pass in metadata.
|
||||
persistContactsAndHosts();
|
||||
clock.setTo(DateTime.parse("2014-09-09T09:09:09Z"));
|
||||
clock.setTo(SMD_VALID_TIME);
|
||||
runFlowAssertResponse(
|
||||
loadFile(
|
||||
"domain_create_response_encoded_signed_mark_name.xml",
|
||||
ImmutableMap.of("DOMAIN", "test-validate.tld")));
|
||||
ImmutableMap.of(
|
||||
"DOMAIN",
|
||||
"test-validate.tld",
|
||||
"CREATE_TIME",
|
||||
SMD_VALID_TIME.toString(),
|
||||
"EXPIRATION_TIME",
|
||||
SMD_VALID_TIME.plusYears(2).toString())));
|
||||
assertSuccessfulCreate("tld", ImmutableSet.of(SUNRISE, ANCHOR_TENANT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_anchorTenantInSunrise_withSignedMark() throws Exception {
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {true, false})
|
||||
void testSuccess_anchorTenantInSunrise_withSignedMark(boolean usePullQueue) throws Exception {
|
||||
if (!usePullQueue) {
|
||||
useNordnSql();
|
||||
}
|
||||
allocationToken =
|
||||
persistResource(
|
||||
new AllocationToken.Builder()
|
||||
@@ -1276,13 +1512,19 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
persistReservedList("anchor_tenants", "test-validate,RESERVED_FOR_ANCHOR_TENANT"))
|
||||
.setTldStateTransitions(ImmutableSortedMap.of(START_OF_TIME, START_DATE_SUNRISE))
|
||||
.build());
|
||||
setEppInput("domain_create_anchor_tenant_signed_mark.xml");
|
||||
clock.setTo(DateTime.parse("2014-09-09T09:09:09Z"));
|
||||
setEppInput("domain_create_anchor_tenant_signed_mark.xml", ImmutableMap.of("SMD", ENCODED_SMD));
|
||||
clock.setTo(SMD_VALID_TIME);
|
||||
persistContactsAndHosts();
|
||||
runFlowAssertResponse(
|
||||
loadFile(
|
||||
"domain_create_response_encoded_signed_mark_name.xml",
|
||||
ImmutableMap.of("DOMAIN", "test-validate.tld")));
|
||||
ImmutableMap.of(
|
||||
"DOMAIN",
|
||||
"test-validate.tld",
|
||||
"CREATE_TIME",
|
||||
SMD_VALID_TIME.toString(),
|
||||
"EXPIRATION_TIME",
|
||||
SMD_VALID_TIME.plusYears(2).toString())));
|
||||
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT, SUNRISE), allocationToken);
|
||||
assertDnsTasksEnqueued("test-validate.tld");
|
||||
assertSunriseLordn("test-validate.tld");
|
||||
@@ -1364,7 +1606,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
.hasValue(getHistoryEntries(reloadResourceByForeignKey()).get(0).getHistoryEntryId());
|
||||
}
|
||||
|
||||
private void assertAllocationTokenWasNotRedeemed(String token) {
|
||||
private static void assertAllocationTokenWasNotRedeemed(String token) {
|
||||
AllocationToken reloadedToken =
|
||||
tm().transact(() -> tm().loadByKey(VKey.create(AllocationToken.class, token)));
|
||||
assertThat(reloadedToken.isRedeemed()).isFalse();
|
||||
@@ -1855,23 +2097,33 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
assertSuccessfulCreate("tld", ImmutableSet.of(RESERVED));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_reservedNameCollisionDomain_inSunrise_setsServerHoldAndPollMessage()
|
||||
throws Exception {
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {true, false})
|
||||
void testSuccess_reservedNameCollisionDomain_inSunrise_setsServerHoldAndPollMessage(
|
||||
boolean usePullQueue) throws Exception {
|
||||
if (!usePullQueue) {
|
||||
useNordnSql();
|
||||
}
|
||||
persistResource(
|
||||
Registry.get("tld")
|
||||
.asBuilder()
|
||||
.setTldStateTransitions(ImmutableSortedMap.of(START_OF_TIME, START_DATE_SUNRISE))
|
||||
.build());
|
||||
clock.setTo(DateTime.parse("2014-09-09T09:09:09Z"));
|
||||
clock.setTo(SMD_VALID_TIME);
|
||||
setEppInput(
|
||||
"domain_create_registration_encoded_signed_mark.xml",
|
||||
ImmutableMap.of("DOMAIN", "test-and-validate.tld", "PHASE", "sunrise"));
|
||||
ImmutableMap.of("DOMAIN", "test-and-validate.tld", "PHASE", "sunrise", "SMD", ENCODED_SMD));
|
||||
persistContactsAndHosts();
|
||||
runFlowAssertResponse(
|
||||
loadFile(
|
||||
"domain_create_response_encoded_signed_mark_name.xml",
|
||||
ImmutableMap.of("DOMAIN", "test-and-validate.tld")));
|
||||
ImmutableMap.of(
|
||||
"DOMAIN",
|
||||
"test-and-validate.tld",
|
||||
"CREATE_TIME",
|
||||
SMD_VALID_TIME.toString(),
|
||||
"EXPIRATION_TIME",
|
||||
SMD_VALID_TIME.plusYears(2).toString())));
|
||||
|
||||
assertSunriseLordn("test-and-validate.tld");
|
||||
|
||||
@@ -2394,33 +2646,57 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
"tld", "domain_create_response.xml", SUPERUSER, ImmutableMap.of("DOMAIN", "example.tld"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_startDateSunriseRegistration_withEncodedSignedMark() throws Exception {
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {true, false})
|
||||
void testSuccess_startDateSunriseRegistration_withEncodedSignedMark(boolean usePullQueue)
|
||||
throws Exception {
|
||||
if (!usePullQueue) {
|
||||
useNordnSql();
|
||||
}
|
||||
createTld("tld", START_DATE_SUNRISE);
|
||||
clock.setTo(DateTime.parse("2014-09-09T09:09:09Z"));
|
||||
clock.setTo(SMD_VALID_TIME);
|
||||
setEppInput(
|
||||
"domain_create_registration_encoded_signed_mark.xml",
|
||||
ImmutableMap.of("DOMAIN", "test-validate.tld", "PHASE", "sunrise"));
|
||||
ImmutableMap.of("DOMAIN", "test-validate.tld", "PHASE", "sunrise", "SMD", ENCODED_SMD));
|
||||
persistContactsAndHosts();
|
||||
runFlowAssertResponse(
|
||||
loadFile(
|
||||
"domain_create_response_encoded_signed_mark_name.xml",
|
||||
ImmutableMap.of("DOMAIN", "test-validate.tld")));
|
||||
ImmutableMap.of(
|
||||
"DOMAIN",
|
||||
"test-validate.tld",
|
||||
"CREATE_TIME",
|
||||
SMD_VALID_TIME.toString(),
|
||||
"EXPIRATION_TIME",
|
||||
SMD_VALID_TIME.plusYears(2).toString())));
|
||||
assertSuccessfulCreate("tld", ImmutableSet.of(SUNRISE));
|
||||
assertSunriseLordn("test-validate.tld");
|
||||
}
|
||||
|
||||
/** Test that missing type= argument on launch create works in start-date sunrise. */
|
||||
@Test
|
||||
void testSuccess_startDateSunriseRegistration_withEncodedSignedMark_noType() throws Exception {
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {true, false})
|
||||
void testSuccess_startDateSunriseRegistration_withEncodedSignedMark_noType(boolean usePullQueue)
|
||||
throws Exception {
|
||||
if (!usePullQueue) {
|
||||
useNordnSql();
|
||||
}
|
||||
createTld("tld", START_DATE_SUNRISE);
|
||||
clock.setTo(DateTime.parse("2014-09-09T09:09:09Z"));
|
||||
setEppInput("domain_create_sunrise_encoded_signed_mark_no_type.xml");
|
||||
clock.setTo(SMD_VALID_TIME);
|
||||
setEppInput(
|
||||
"domain_create_sunrise_encoded_signed_mark_no_type.xml",
|
||||
ImmutableMap.of("SMD", ENCODED_SMD));
|
||||
persistContactsAndHosts();
|
||||
runFlowAssertResponse(
|
||||
loadFile(
|
||||
"domain_create_response_encoded_signed_mark_name.xml",
|
||||
ImmutableMap.of("DOMAIN", "test-validate.tld")));
|
||||
ImmutableMap.of(
|
||||
"DOMAIN",
|
||||
"test-validate.tld",
|
||||
"CREATE_TIME",
|
||||
SMD_VALID_TIME.toString(),
|
||||
"EXPIRATION_TIME",
|
||||
SMD_VALID_TIME.plusYears(2).toString())));
|
||||
assertSuccessfulCreate("tld", ImmutableSet.of(SUNRISE));
|
||||
assertSunriseLordn("test-validate.tld");
|
||||
}
|
||||
@@ -2428,23 +2704,66 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
@Test
|
||||
void testFail_startDateSunriseRegistration_wrongEncodedSignedMark() {
|
||||
createTld("tld", START_DATE_SUNRISE);
|
||||
clock.setTo(DateTime.parse("2014-09-09T09:09:09Z"));
|
||||
clock.setTo(SMD_VALID_TIME);
|
||||
setEppInput(
|
||||
"domain_create_registration_encoded_signed_mark.xml",
|
||||
ImmutableMap.of("DOMAIN", "wrong.tld", "PHASE", "sunrise"));
|
||||
ImmutableMap.of("DOMAIN", "wrong.tld", "PHASE", "sunrise", "SMD", ENCODED_SMD));
|
||||
persistContactsAndHosts();
|
||||
EppException thrown = assertThrows(NoMarksFoundMatchingDomainException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFail_startDateSunriseRegistration_revokedSignedMark() throws Exception {
|
||||
SmdrlCsvParser.parse(TmchTestData.loadFile("smd/smdrl.csv").lines().collect(toImmutableList()))
|
||||
.save();
|
||||
createTld("tld", START_DATE_SUNRISE);
|
||||
clock.setTo(SMD_VALID_TIME);
|
||||
String revokedSmd =
|
||||
TmchData.readEncodedSignedMark(TmchTestData.loadFile("smd/revoked.smd")).getEncodedData();
|
||||
setEppInput(
|
||||
"domain_create_registration_encoded_signed_mark.xml",
|
||||
ImmutableMap.of("DOMAIN", "test-validate.tld", "PHASE", "sunrise", "SMD", revokedSmd));
|
||||
persistContactsAndHosts();
|
||||
EppException thrown = assertThrows(SignedMarkRevokedErrorException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@CartesianTest
|
||||
void testFail_startDateSunriseRegistration_IdnRevokedSignedMark(
|
||||
@Values(strings = {"Agent", "Holder"}) String contact,
|
||||
@Values(strings = {"Court", "Trademark", "TreatyStatute"}) String type,
|
||||
@Values(strings = {"Arab", "Chinese", "English", "French"}) String language)
|
||||
throws Exception {
|
||||
String filepath =
|
||||
String.format("idn/%s-%s/%s-%s-%s-Revoked.smd", contact, language, type, contact, language);
|
||||
ImmutableList<String> labels = TmchTestData.extractLabels(filepath);
|
||||
if (labels.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
SmdrlCsvParser.parse(
|
||||
TmchTestData.loadFile("idn/idn_smdrl.csv").lines().collect(toImmutableList()))
|
||||
.save();
|
||||
createTld("tld", START_DATE_SUNRISE);
|
||||
clock.setTo(SMD_VALID_TIME);
|
||||
String revokedSmd =
|
||||
TmchData.readEncodedSignedMark(TmchTestData.loadFile(filepath)).getEncodedData();
|
||||
setEppInput(
|
||||
"domain_create_registration_encoded_signed_mark.xml",
|
||||
ImmutableMap.of("DOMAIN", labels.get(0) + ".tld", "PHASE", "sunrise", "SMD", revokedSmd));
|
||||
persistContactsAndHosts();
|
||||
EppException thrown = assertThrows(SignedMarkRevokedErrorException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFail_startDateSunriseRegistration_markNotYetValid() {
|
||||
createTld("tld", START_DATE_SUNRISE);
|
||||
// If we move now back in time a bit, the mark will not have gone into effect yet.
|
||||
clock.setTo(DateTime.parse("2013-08-09T10:05:59Z").minusSeconds(1));
|
||||
clock.setTo(SMD_NOT_YET_VALID_TIME);
|
||||
setEppInput(
|
||||
"domain_create_registration_encoded_signed_mark.xml",
|
||||
ImmutableMap.of("DOMAIN", "test-validate.tld", "PHASE", "sunrise"));
|
||||
ImmutableMap.of("DOMAIN", "test-validate.tld", "PHASE", "sunrise", "SMD", ENCODED_SMD));
|
||||
persistContactsAndHosts();
|
||||
EppException thrown = assertThrows(FoundMarkNotYetValidException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
@@ -2454,10 +2773,10 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
void testFail_startDateSunriseRegistration_markExpired() {
|
||||
createTld("tld", START_DATE_SUNRISE);
|
||||
// Move time forward to the mark expiration time.
|
||||
clock.setTo(DateTime.parse("2017-07-23T22:00:00.000Z"));
|
||||
clock.setTo(SMD_CERT_EXPIRED_TIME);
|
||||
setEppInput(
|
||||
"domain_create_registration_encoded_signed_mark.xml",
|
||||
ImmutableMap.of("DOMAIN", "test-validate.tld", "PHASE", "sunrise"));
|
||||
ImmutableMap.of("DOMAIN", "test-validate.tld", "PHASE", "sunrise", "SMD", ENCODED_SMD));
|
||||
persistContactsAndHosts();
|
||||
EppException thrown = assertThrows(FoundMarkExpiredException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
@@ -3165,10 +3484,10 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
@Test
|
||||
void testSuccess_anchorTenant_inSunrise_trademarked_withSignedMark_viaToken() throws Exception {
|
||||
createTld("tld", START_DATE_SUNRISE);
|
||||
clock.setTo(DateTime.parse("2014-09-09T09:09:09Z"));
|
||||
clock.setTo(SMD_VALID_TIME);
|
||||
setEppInput(
|
||||
"domain_create_registration_encoded_signed_mark_allocationtoken.xml",
|
||||
ImmutableMap.of("DOMAIN", "test-validate.tld"));
|
||||
ImmutableMap.of("DOMAIN", "test-validate.tld", "SMD", ENCODED_SMD));
|
||||
persistResource(
|
||||
allocationToken
|
||||
.asBuilder()
|
||||
@@ -3390,7 +3709,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
.build()));
|
||||
Domain domain = reloadResourceByForeignKey();
|
||||
assertThat(domain.getCurrentPackageToken()).isPresent();
|
||||
Truth8.assertThat(domain.getCurrentPackageToken()).hasValue(token.createVKey());
|
||||
assertThat(domain.getCurrentPackageToken()).hasValue(token.createVKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -3414,4 +3733,97 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
.isEqualTo(
|
||||
"The package token abc123 cannot be used to register names for longer than 1 year.");
|
||||
}
|
||||
|
||||
private static void useNordnSql() {
|
||||
tm().transact(
|
||||
() ->
|
||||
DatabaseMigrationStateSchedule.set(
|
||||
new ImmutableSortedMap.Builder<DateTime, MigrationState>(Ordering.natural())
|
||||
.put(START_OF_TIME, MigrationState.DATASTORE_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(1), MigrationState.DATASTORE_PRIMARY)
|
||||
.build()));
|
||||
tm().transact(
|
||||
() ->
|
||||
DatabaseMigrationStateSchedule.set(
|
||||
new ImmutableSortedMap.Builder<DateTime, MigrationState>(Ordering.natural())
|
||||
.put(START_OF_TIME, MigrationState.DATASTORE_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(1), MigrationState.DATASTORE_PRIMARY)
|
||||
.put(START_OF_TIME.plusMillis(2), MigrationState.DATASTORE_PRIMARY_NO_ASYNC)
|
||||
.build()));
|
||||
tm().transact(
|
||||
() ->
|
||||
DatabaseMigrationStateSchedule.set(
|
||||
new ImmutableSortedMap.Builder<DateTime, MigrationState>(Ordering.natural())
|
||||
.put(START_OF_TIME, MigrationState.DATASTORE_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(1), MigrationState.DATASTORE_PRIMARY)
|
||||
.put(START_OF_TIME.plusMillis(2), MigrationState.DATASTORE_PRIMARY_NO_ASYNC)
|
||||
.put(
|
||||
START_OF_TIME.plusMillis(3), MigrationState.DATASTORE_PRIMARY_READ_ONLY)
|
||||
.build()));
|
||||
tm().transact(
|
||||
() ->
|
||||
DatabaseMigrationStateSchedule.set(
|
||||
new ImmutableSortedMap.Builder<DateTime, MigrationState>(Ordering.natural())
|
||||
.put(START_OF_TIME, MigrationState.DATASTORE_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(1), MigrationState.DATASTORE_PRIMARY)
|
||||
.put(START_OF_TIME.plusMillis(2), MigrationState.DATASTORE_PRIMARY_NO_ASYNC)
|
||||
.put(
|
||||
START_OF_TIME.plusMillis(3), MigrationState.DATASTORE_PRIMARY_READ_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(4), MigrationState.SQL_PRIMARY_READ_ONLY)
|
||||
.build()));
|
||||
tm().transact(
|
||||
() ->
|
||||
DatabaseMigrationStateSchedule.set(
|
||||
new ImmutableSortedMap.Builder<DateTime, MigrationState>(Ordering.natural())
|
||||
.put(START_OF_TIME, MigrationState.DATASTORE_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(1), MigrationState.DATASTORE_PRIMARY)
|
||||
.put(START_OF_TIME.plusMillis(2), MigrationState.DATASTORE_PRIMARY_NO_ASYNC)
|
||||
.put(
|
||||
START_OF_TIME.plusMillis(3), MigrationState.DATASTORE_PRIMARY_READ_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(4), MigrationState.SQL_PRIMARY_READ_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(5), MigrationState.SQL_PRIMARY)
|
||||
.build()));
|
||||
tm().transact(
|
||||
() ->
|
||||
DatabaseMigrationStateSchedule.set(
|
||||
new ImmutableSortedMap.Builder<DateTime, MigrationState>(Ordering.natural())
|
||||
.put(START_OF_TIME, MigrationState.DATASTORE_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(1), MigrationState.DATASTORE_PRIMARY)
|
||||
.put(START_OF_TIME.plusMillis(2), MigrationState.DATASTORE_PRIMARY_NO_ASYNC)
|
||||
.put(
|
||||
START_OF_TIME.plusMillis(3), MigrationState.DATASTORE_PRIMARY_READ_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(4), MigrationState.SQL_PRIMARY_READ_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(5), MigrationState.SQL_PRIMARY)
|
||||
.put(START_OF_TIME.plusMillis(6), MigrationState.SQL_ONLY)
|
||||
.build()));
|
||||
tm().transact(
|
||||
() ->
|
||||
DatabaseMigrationStateSchedule.set(
|
||||
new ImmutableSortedMap.Builder<DateTime, MigrationState>(Ordering.natural())
|
||||
.put(START_OF_TIME, MigrationState.DATASTORE_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(1), MigrationState.DATASTORE_PRIMARY)
|
||||
.put(START_OF_TIME.plusMillis(2), MigrationState.DATASTORE_PRIMARY_NO_ASYNC)
|
||||
.put(
|
||||
START_OF_TIME.plusMillis(3), MigrationState.DATASTORE_PRIMARY_READ_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(4), MigrationState.SQL_PRIMARY_READ_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(5), MigrationState.SQL_PRIMARY)
|
||||
.put(START_OF_TIME.plusMillis(6), MigrationState.SQL_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(7), MigrationState.SEQUENCE_BASED_ALLOCATE_ID)
|
||||
.build()));
|
||||
tm().transact(
|
||||
() ->
|
||||
DatabaseMigrationStateSchedule.set(
|
||||
new ImmutableSortedMap.Builder<DateTime, MigrationState>(Ordering.natural())
|
||||
.put(START_OF_TIME, MigrationState.DATASTORE_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(1), MigrationState.DATASTORE_PRIMARY)
|
||||
.put(START_OF_TIME.plusMillis(2), MigrationState.DATASTORE_PRIMARY_NO_ASYNC)
|
||||
.put(
|
||||
START_OF_TIME.plusMillis(3), MigrationState.DATASTORE_PRIMARY_READ_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(4), MigrationState.SQL_PRIMARY_READ_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(5), MigrationState.SQL_PRIMARY)
|
||||
.put(START_OF_TIME.plusMillis(6), MigrationState.SQL_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(7), MigrationState.SEQUENCE_BASED_ALLOCATE_ID)
|
||||
.put(START_OF_TIME.plusMillis(8), MigrationState.NORDN_SQL)
|
||||
.build()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,11 +72,7 @@ public class UserTest extends EntityTestCase {
|
||||
assertThat(assertThrows(IllegalArgumentException.class, () -> builder.setUserRoles(null)))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("User roles cannot be null");
|
||||
|
||||
assertThat(assertThrows(IllegalArgumentException.class, builder::build))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Gaia ID cannot be null");
|
||||
builder.setGaiaId("gaiaId");
|
||||
|
||||
assertThat(assertThrows(IllegalArgumentException.class, builder::build))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Email address cannot be null");
|
||||
|
||||
@@ -221,7 +221,6 @@ public class DomainTest {
|
||||
"TheRegistrar",
|
||||
oneTimeBillKey))
|
||||
.setAutorenewEndTime(Optional.of(fakeClock.nowUtc().plusYears(2)))
|
||||
.setDnsRefreshRequestTime(Optional.of(fakeClock.nowUtc()))
|
||||
.build()));
|
||||
}
|
||||
|
||||
|
||||
@@ -41,8 +41,6 @@ import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.util.SerializeUtils;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -97,7 +95,6 @@ public class DomainHistoryTest extends EntityTestCase {
|
||||
.asBuilder()
|
||||
.setNameservers(host.createVKey())
|
||||
.setDsData(ImmutableSet.of(DomainDsData.create(1, 2, 3, new byte[] {0, 1, 2})))
|
||||
.setDnsRefreshRequestTime(Optional.of(DateTime.parse("2020-03-09T16:40:00Z")))
|
||||
.build();
|
||||
insertInDb(domain);
|
||||
return domain;
|
||||
|
||||
@@ -18,17 +18,14 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.model.server.Lock.LockState.FREE;
|
||||
import static google.registry.model.server.Lock.LockState.IN_USE;
|
||||
import static google.registry.model.server.Lock.LockState.OWNER_DIED;
|
||||
import static google.registry.model.server.Lock.LockState.TIMED_OUT;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import google.registry.model.EntityTestCase;
|
||||
import google.registry.model.server.Lock.LockState;
|
||||
import google.registry.util.RequestStatusChecker;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
@@ -41,7 +38,6 @@ public class LockTest extends EntityTestCase {
|
||||
private static final String RESOURCE_NAME = "foo";
|
||||
private static final Duration ONE_DAY = Duration.standardDays(1);
|
||||
private static final Duration TWO_MILLIS = Duration.millis(2);
|
||||
private static final RequestStatusChecker requestStatusChecker = mock(RequestStatusChecker.class);
|
||||
|
||||
private LockMetrics origLockMetrics;
|
||||
|
||||
@@ -52,7 +48,7 @@ public class LockTest extends EntityTestCase {
|
||||
private static Optional<Lock> acquire(
|
||||
String tld, Duration leaseLength, LockState expectedLockState) {
|
||||
Lock.lockMetrics = mock(LockMetrics.class);
|
||||
Optional<Lock> lock = Lock.acquire(RESOURCE_NAME, tld, leaseLength, requestStatusChecker, true);
|
||||
Optional<Lock> lock = Lock.acquire(RESOURCE_NAME, tld, leaseLength);
|
||||
verify(Lock.lockMetrics).recordAcquire(RESOURCE_NAME, tld, expectedLockState);
|
||||
verifyNoMoreInteractions(Lock.lockMetrics);
|
||||
Lock.lockMetrics = null;
|
||||
@@ -72,8 +68,6 @@ public class LockTest extends EntityTestCase {
|
||||
void beforeEach() {
|
||||
origLockMetrics = Lock.lockMetrics;
|
||||
Lock.lockMetrics = null;
|
||||
when(requestStatusChecker.getLogId()).thenReturn("current-request-id");
|
||||
when(requestStatusChecker.isRunning("current-request-id")).thenReturn(true);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@@ -111,9 +105,6 @@ public class LockTest extends EntityTestCase {
|
||||
assertThat(acquire("", ONE_DAY, FREE)).isPresent();
|
||||
// We can't get it again while request is active
|
||||
assertThat(acquire("", ONE_DAY, IN_USE)).isEmpty();
|
||||
// But if request is finished, we can get it.
|
||||
when(requestStatusChecker.isRunning("current-request-id")).thenReturn(false);
|
||||
assertThat(acquire("", ONE_DAY, OWNER_DIED)).isPresent();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -134,9 +125,7 @@ public class LockTest extends EntityTestCase {
|
||||
@Test
|
||||
void testFailure_emptyResourceName() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> Lock.acquire("", "", TWO_MILLIS, requestStatusChecker, true));
|
||||
assertThrows(IllegalArgumentException.class, () -> Lock.acquire("", "", TWO_MILLIS));
|
||||
assertThat(thrown).hasMessageThat().contains("resourceName cannot be null or empty");
|
||||
}
|
||||
}
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
// Copyright 2023 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.request.auth;
|
||||
|
||||
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
|
||||
import com.google.api.client.json.webtoken.JsonWebSignature;
|
||||
import com.google.api.client.json.webtoken.JsonWebSignature.Header;
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
class ServiceAccountAuthenticationMechanismTest {
|
||||
|
||||
@Mock private TokenVerifier tokenVerifier;
|
||||
@Mock private HttpServletRequest request;
|
||||
|
||||
private JsonWebSignature token;
|
||||
private ServiceAccountAuthenticationMechanism serviceAccountAuthenticationMechanism;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
serviceAccountAuthenticationMechanism =
|
||||
new ServiceAccountAuthenticationMechanism(tokenVerifier, "sa-prefix@email.com");
|
||||
when(request.getHeader(AUTHORIZATION)).thenReturn("Bearer jwtValue");
|
||||
Payload payload = new Payload();
|
||||
payload.setEmail("sa-prefix@email.com");
|
||||
token = new JsonWebSignature(new Header(), payload, new byte[0], new byte[0]);
|
||||
when(tokenVerifier.verify("jwtValue")).thenReturn(token);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_authenticates() throws Exception {
|
||||
AuthResult authResult = serviceAccountAuthenticationMechanism.authenticate(request);
|
||||
assertThat(authResult.isAuthenticated()).isTrue();
|
||||
assertThat(authResult.authLevel()).isEqualTo(AuthLevel.APP);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFails_authenticateWrongEmail() throws Exception {
|
||||
token.getPayload().set("email", "not-service-account-email@email.com");
|
||||
AuthResult authResult = serviceAccountAuthenticationMechanism.authenticate(request);
|
||||
assertThat(authResult.isAuthenticated()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFails_authenticateWrongHeader() throws Exception {
|
||||
when(request.getHeader(AUTHORIZATION)).thenReturn("BEARER asd");
|
||||
AuthResult authResult = serviceAccountAuthenticationMechanism.authenticate(request);
|
||||
assertThat(authResult.isAuthenticated()).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import static org.mockito.Mockito.verify;
|
||||
import google.registry.model.server.Lock;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.UserServiceExtension;
|
||||
import google.registry.util.RequestStatusCheckerImpl;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
@@ -134,7 +133,7 @@ final class LockHandlerImplTest {
|
||||
}
|
||||
|
||||
private LockHandler createTestLockHandler(@Nullable Lock acquiredLock) {
|
||||
return new LockHandlerImpl(new RequestStatusCheckerImpl(), clock) {
|
||||
return new LockHandlerImpl(clock) {
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
@Override
|
||||
|
||||
@@ -312,6 +312,7 @@ public final class DatabaseHelper {
|
||||
return persistResource(domain.asBuilder().setDeletionTime(deletionTime).build());
|
||||
}
|
||||
|
||||
// TODO: delete after pull queue migration.
|
||||
/** Persists a domain and enqueues a LORDN task of the appropriate type for it. */
|
||||
public static Domain persistDomainAndEnqueueLordn(final Domain domain) {
|
||||
final Domain persistedDomain = persistResource(domain);
|
||||
|
||||
@@ -27,6 +27,7 @@ import google.registry.model.domain.launch.LaunchNotice;
|
||||
import google.registry.model.domain.secdns.DomainDsData;
|
||||
import google.registry.model.eppcommon.AuthInfo;
|
||||
import google.registry.testing.TruthChainer.And;
|
||||
import google.registry.tmch.LordnTaskUtils.LordnPhase;
|
||||
import java.util.Set;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -41,7 +42,7 @@ public final class DomainSubject extends AbstractEppResourceSubject<Domain, Doma
|
||||
}
|
||||
|
||||
public And<DomainSubject> hasDomainName(String domainName) {
|
||||
return hasValue(domainName, actual.getDomainName(), "has domainName");
|
||||
return hasValue(domainName, actual.getDomainName(), "domainName");
|
||||
}
|
||||
|
||||
public And<DomainSubject> hasExactlyDsData(DomainDsData... dsData) {
|
||||
@@ -49,15 +50,19 @@ public final class DomainSubject extends AbstractEppResourceSubject<Domain, Doma
|
||||
}
|
||||
|
||||
public And<DomainSubject> hasExactlyDsData(Set<DomainDsData> dsData) {
|
||||
return hasValue(dsData, actual.getDsData(), "has dsData");
|
||||
return hasValue(dsData, actual.getDsData(), "dsData");
|
||||
}
|
||||
|
||||
public And<DomainSubject> hasNumDsData(int num) {
|
||||
return hasValue(num, actual.getDsData().size(), "has num dsData");
|
||||
return hasValue(num, actual.getDsData().size(), "dsData.size()");
|
||||
}
|
||||
|
||||
public And<DomainSubject> hasLaunchNotice(LaunchNotice launchNotice) {
|
||||
return hasValue(launchNotice, actual.getLaunchNotice(), "has launchNotice");
|
||||
return hasValue(launchNotice, actual.getLaunchNotice(), "launchNotice");
|
||||
}
|
||||
|
||||
public And<DomainSubject> hasLordnPhase(LordnPhase lordnPhase) {
|
||||
return hasValue(lordnPhase, actual.getLordnPhase(), "lordnPhase");
|
||||
}
|
||||
|
||||
public And<DomainSubject> hasAuthInfoPwd(String pw) {
|
||||
@@ -67,7 +72,7 @@ public final class DomainSubject extends AbstractEppResourceSubject<Domain, Doma
|
||||
|
||||
public And<DomainSubject> hasCurrentSponsorRegistrarId(String registrarId) {
|
||||
return hasValue(
|
||||
registrarId, actual.getCurrentSponsorRegistrarId(), "has currentSponsorRegistrarId");
|
||||
registrarId, actual.getCurrentSponsorRegistrarId(), "currentSponsorRegistrarId");
|
||||
}
|
||||
|
||||
public And<DomainSubject> hasRegistrationExpirationTime(DateTime expiration) {
|
||||
|
||||
@@ -14,13 +14,17 @@
|
||||
|
||||
package google.registry.tmch;
|
||||
|
||||
import static com.google.appengine.api.taskqueue.QueueFactory.getQueue;
|
||||
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
||||
import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
|
||||
import static com.google.common.net.HttpHeaders.LOCATION;
|
||||
import static com.google.common.net.MediaType.FORM_DATA;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.ForeignKeyUtils.load;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.loadByKey;
|
||||
import static google.registry.testing.DatabaseHelper.loadRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.newDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistDomainAndEnqueueLordn;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
@@ -34,6 +38,7 @@ import static org.mockito.ArgumentMatchers.startsWith;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.appengine.api.taskqueue.LeaseOptions;
|
||||
@@ -48,15 +53,17 @@ import com.google.common.collect.ImmutableList;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.launch.LaunchNotice;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.request.RequestParameters;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeSleeper;
|
||||
import google.registry.testing.FakeUrlConnectionService;
|
||||
import google.registry.testing.TaskQueueExtension;
|
||||
import google.registry.tmch.LordnTaskUtils.LordnPhase;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import google.registry.util.Retrier;
|
||||
import google.registry.util.UrlConnectionException;
|
||||
@@ -67,25 +74,32 @@ import java.net.URL;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
/** Unit tests for {@link NordnUploadAction}. */
|
||||
class NordnUploadActionTest {
|
||||
|
||||
private static final String CLAIMS_CSV =
|
||||
"1,2010-05-01T10:11:12.000Z,1\n"
|
||||
"1,2010-05-04T10:11:12.000Z,2\n"
|
||||
+ "roid,domain-name,notice-id,registrar-id,registration-datetime,ack-datetime,"
|
||||
+ "application-datetime\n"
|
||||
+ "2-TLD,claims-landrush1.tld,landrush1tcn,99999,2010-05-01T10:11:12.000Z,"
|
||||
+ "1969-12-31T23:00:00.000Z\n";
|
||||
+ "6-TLD,claims-landrush2.tld,landrush2tcn,88888,2010-05-03T10:11:12.000Z,"
|
||||
+ "2010-05-03T08:11:12.000Z\n"
|
||||
+ "8-TLD,claims-landrush1.tld,landrush1tcn,99999,2010-05-04T10:11:12.000Z,"
|
||||
+ "2010-05-04T09:11:12.000Z\n";
|
||||
|
||||
private static final String SUNRISE_CSV =
|
||||
"1,2010-05-01T10:11:12.000Z,1\n"
|
||||
"1,2010-05-04T10:11:12.000Z,2\n"
|
||||
+ "roid,domain-name,SMD-id,registrar-id,registration-datetime,application-datetime\n"
|
||||
+ "2-TLD,sunrise1.tld,my-smdid,99999,2010-05-01T10:11:12.000Z\n";
|
||||
+ "2-TLD,sunrise2.tld,new-smdid,88888,2010-05-01T10:11:12.000Z\n"
|
||||
+ "4-TLD,sunrise1.tld,my-smdid,99999,2010-05-02T10:11:12.000Z\n";
|
||||
|
||||
private static final String LOCATION_URL = "http://trololol";
|
||||
|
||||
@@ -116,8 +130,12 @@ class NordnUploadActionTest {
|
||||
when(httpUrlConnection.getHeaderField(LOCATION)).thenReturn("http://trololol");
|
||||
when(httpUrlConnection.getOutputStream()).thenReturn(connectionOutputStream);
|
||||
persistResource(loadRegistrar("TheRegistrar").asBuilder().setIanaIdentifier(99999L).build());
|
||||
persistResource(loadRegistrar("NewRegistrar").asBuilder().setIanaIdentifier(88888L).build());
|
||||
createTld("tld");
|
||||
persistResource(Registry.get("tld").asBuilder().setLordnUsername("lolcat").build());
|
||||
persistSunriseModeDomain();
|
||||
clock.advanceBy(Duration.standardDays(1));
|
||||
persistClaimsModeDomain();
|
||||
action.clock = clock;
|
||||
action.cloudTasksUtils = cloudTasksUtils;
|
||||
action.urlConnectionService = urlConnectionService;
|
||||
@@ -127,6 +145,7 @@ class NordnUploadActionTest {
|
||||
action.tmchMarksdbUrl = "http://127.0.0.1";
|
||||
action.random = new SecureRandom();
|
||||
action.retrier = new Retrier(new FakeSleeper(clock), 3);
|
||||
action.usePullQueue = Optional.empty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -137,7 +156,7 @@ class NordnUploadActionTest {
|
||||
makeTaskHandle("task1", "example", "csvLine1", "lordn-sunrise"),
|
||||
makeTaskHandle("task3", "example", "ending", "lordn-sunrise"));
|
||||
assertThat(NordnUploadAction.convertTasksToCsv(tasks, clock.nowUtc(), "col1,col2"))
|
||||
.isEqualTo("1,2010-05-01T10:11:12.000Z,3\ncol1,col2\ncsvLine1\ncsvLine2\nending\n");
|
||||
.isEqualTo("1,2010-05-04T10:11:12.000Z,3\ncol1,col2\ncsvLine1\ncsvLine2\nending\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -149,13 +168,13 @@ class NordnUploadActionTest {
|
||||
makeTaskHandle("task3", "example", "ending", "lordn-sunrise"),
|
||||
makeTaskHandle("task1", "example", "csvLine1", "lordn-sunrise"));
|
||||
assertThat(NordnUploadAction.convertTasksToCsv(tasks, clock.nowUtc(), "col1,col2"))
|
||||
.isEqualTo("1,2010-05-01T10:11:12.000Z,3\ncol1,col2\ncsvLine1\ncsvLine2\nending\n");
|
||||
.isEqualTo("1,2010-05-04T10:11:12.000Z,3\ncol1,col2\ncsvLine1\ncsvLine2\nending\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_convertTasksToCsv_doesntFailOnEmptyTasks() {
|
||||
assertThat(NordnUploadAction.convertTasksToCsv(ImmutableList.of(), clock.nowUtc(), "col1,col2"))
|
||||
.isEqualTo("1,2010-05-01T10:11:12.000Z,0\ncol1,col2\n");
|
||||
.isEqualTo("1,2010-05-04T10:11:12.000Z,0\ncol1,col2\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -187,122 +206,105 @@ class NordnUploadActionTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRun_claimsMode_appendsTldAndClaimsToRequestUrl() throws Exception {
|
||||
persistClaimsModeDomain();
|
||||
action.run();
|
||||
assertThat(httpUrlConnection.getURL()).isEqualTo(new URL("http://127.0.0.1/LORDN/tld/claims"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRun_sunriseMode_appendsTldAndClaimsToRequestUrl() throws Exception {
|
||||
persistSunriseModeDomain();
|
||||
action.run();
|
||||
assertThat(httpUrlConnection.getURL()).isEqualTo(new URL("http://127.0.0.1/LORDN/tld/sunrise"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRun_usesMultipartContentType() throws Exception {
|
||||
persistClaimsModeDomain();
|
||||
action.run();
|
||||
verify(httpUrlConnection)
|
||||
.setRequestProperty(eq(CONTENT_TYPE), startsWith("multipart/form-data; boundary="));
|
||||
verify(httpUrlConnection).setRequestMethod("POST");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRun_hasPassword_setsAuthorizationHeader() {
|
||||
persistClaimsModeDomain();
|
||||
action.run();
|
||||
verify(httpUrlConnection)
|
||||
.setRequestProperty(
|
||||
AUTHORIZATION, "Basic bG9sY2F0OmF0dGFjaw=="); // echo -n lolcat:attack | base64
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRun_noPassword_doesntSendAuthorizationHeader() {
|
||||
void testSuccess_noPassword_doesntSendAuthorizationHeader() {
|
||||
action.lordnRequestInitializer = new LordnRequestInitializer(Optional.empty());
|
||||
persistClaimsModeDomain();
|
||||
action.run();
|
||||
verify(httpUrlConnection, times(0)).setRequestProperty(eq(AUTHORIZATION), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRun_claimsMode_payloadMatchesClaimsCsv() {
|
||||
persistClaimsModeDomain();
|
||||
void testSuccess_nothingScheduled() {
|
||||
persistResource(
|
||||
loadByKey(load(Domain.class, "claims-landrush1.tld", clock.nowUtc()))
|
||||
.asBuilder()
|
||||
.setLordnPhase(LordnPhase.NONE)
|
||||
.build());
|
||||
persistResource(
|
||||
loadByKey(load(Domain.class, "claims-landrush2.tld", clock.nowUtc()))
|
||||
.asBuilder()
|
||||
.setLordnPhase(LordnPhase.NONE)
|
||||
.build());
|
||||
action.run();
|
||||
assertThat(connectionOutputStream.toString(UTF_8)).contains(CLAIMS_CSV);
|
||||
verifyNoInteractions(httpUrlConnection);
|
||||
cloudTasksHelper.assertNoTasksEnqueued(NordnVerifyAction.QUEUE);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {true, false})
|
||||
void testSuccess_claimsMode(boolean usePullQueue) throws Exception {
|
||||
testRun("claims", "claims-landrush1.tld", "claims-landrush2.tld", CLAIMS_CSV, usePullQueue);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {true, false})
|
||||
void testSuccess_sunriseMode(boolean usePullQueue) throws Exception {
|
||||
testRun("sunrise", "sunrise1.tld", "sunrise2.tld", SUNRISE_CSV, usePullQueue);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRun_claimsMode_verifyTaskGetsEnqueuedWithClaimsCsv() {
|
||||
persistClaimsModeDomain();
|
||||
action.run();
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
NordnVerifyAction.QUEUE,
|
||||
new TaskMatcher()
|
||||
.url(NordnVerifyAction.PATH)
|
||||
.param(NordnVerifyAction.NORDN_URL_PARAM, LOCATION_URL)
|
||||
.header(CONTENT_TYPE, FORM_DATA.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRun_sunriseMode_payloadMatchesSunriseCsv() {
|
||||
persistSunriseModeDomain();
|
||||
action.run();
|
||||
assertThat(connectionOutputStream.toString(UTF_8)).contains(SUNRISE_CSV);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_noResponseContent_stillWorksNormally() throws Exception {
|
||||
void testSuccess_noResponseContent_stillWorksNormally() throws Exception {
|
||||
// Returning null only affects logging.
|
||||
when(httpUrlConnection.getInputStream()).thenReturn(new ByteArrayInputStream(new byte[] {}));
|
||||
persistSunriseModeDomain();
|
||||
action.run();
|
||||
assertThat(connectionOutputStream.toString(UTF_8)).contains(SUNRISE_CSV);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRun_sunriseMode_verifyTaskGetsEnqueuedWithSunriseCsv() {
|
||||
persistSunriseModeDomain();
|
||||
action.run();
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
NordnVerifyAction.QUEUE,
|
||||
new TaskMatcher()
|
||||
.url(NordnVerifyAction.PATH)
|
||||
.param(NordnVerifyAction.NORDN_URL_PARAM, LOCATION_URL)
|
||||
.header(CONTENT_TYPE, FORM_DATA.toString()));
|
||||
testRun("claims", "claims-landrush1.tld", "claims-landrush2.tld", CLAIMS_CSV, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_nullRegistryUser() {
|
||||
persistClaimsModeDomain();
|
||||
persistResource(Registry.get("tld").asBuilder().setLordnUsername(null).build());
|
||||
VerifyException thrown = assertThrows(VerifyException.class, action::run);
|
||||
assertThat(thrown).hasMessageThat().contains("lordnUsername is not set for tld.");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFetchFailure() throws Exception {
|
||||
persistClaimsModeDomain();
|
||||
void testFailure_errorResponseCode() throws Exception {
|
||||
when(httpUrlConnection.getResponseCode()).thenReturn(SC_INTERNAL_SERVER_ERROR);
|
||||
assertThrows(UrlConnectionException.class, action::run);
|
||||
}
|
||||
|
||||
private static void persistClaimsModeDomain() {
|
||||
Domain domain = DatabaseHelper.newDomain("claims-landrush1.tld");
|
||||
@Test
|
||||
void testFailure_noLocationHeaderInResponse() throws Exception {
|
||||
when(httpUrlConnection.getHeaderField(LOCATION)).thenReturn(null);
|
||||
assertThrows(UrlConnectionException.class, action::run);
|
||||
}
|
||||
|
||||
private void persistClaimsModeDomain() {
|
||||
persistDomainAndEnqueueLordn(
|
||||
domain
|
||||
newDomain("claims-landrush2.tld")
|
||||
.asBuilder()
|
||||
.setCreationTimeForTest(clock.nowUtc())
|
||||
.setCreationRegistrarId("NewRegistrar")
|
||||
.setLaunchNotice(
|
||||
LaunchNotice.create(
|
||||
"landrush1tcn", null, null, domain.getCreationTime().minusHours(1)))
|
||||
LaunchNotice.create("landrush2tcn", null, null, clock.nowUtc().minusHours(2)))
|
||||
.setLordnPhase(LordnPhase.CLAIMS)
|
||||
.build());
|
||||
clock.advanceBy(Duration.standardDays(1));
|
||||
persistDomainAndEnqueueLordn(
|
||||
newDomain("claims-landrush1.tld")
|
||||
.asBuilder()
|
||||
.setCreationTimeForTest(clock.nowUtc())
|
||||
.setLaunchNotice(
|
||||
LaunchNotice.create("landrush1tcn", null, null, clock.nowUtc().minusHours(1)))
|
||||
.setLordnPhase(LordnPhase.CLAIMS)
|
||||
.build());
|
||||
}
|
||||
|
||||
private void persistSunriseModeDomain() {
|
||||
action.phase = "sunrise";
|
||||
Domain domain = DatabaseHelper.newDomain("sunrise1.tld");
|
||||
persistDomainAndEnqueueLordn(domain.asBuilder().setSmdId("my-smdid").build());
|
||||
persistDomainAndEnqueueLordn(
|
||||
newDomain("sunrise2.tld")
|
||||
.asBuilder()
|
||||
.setCreationTimeForTest(clock.nowUtc())
|
||||
.setCreationRegistrarId("NewRegistrar")
|
||||
.setSmdId("new-smdid")
|
||||
.setLordnPhase(LordnPhase.SUNRISE)
|
||||
.build());
|
||||
clock.advanceBy(Duration.standardDays(1));
|
||||
persistDomainAndEnqueueLordn(
|
||||
newDomain("sunrise1.tld")
|
||||
.asBuilder()
|
||||
.setCreationTimeForTest(clock.nowUtc())
|
||||
.setSmdId("my-smdid")
|
||||
.setLordnPhase(LordnPhase.SUNRISE)
|
||||
.build());
|
||||
}
|
||||
|
||||
private static TaskHandle makeTaskHandle(
|
||||
@@ -311,4 +313,46 @@ class NordnUploadActionTest {
|
||||
TaskOptions.Builder.withPayload(payload).method(Method.PULL).tag(tag).taskName(taskName),
|
||||
queue);
|
||||
}
|
||||
|
||||
private void verifyColumnCleared(String domainName) {
|
||||
VKey<Domain> domainKey = load(Domain.class, domainName, clock.nowUtc());
|
||||
Domain domain = loadByKey(domainKey);
|
||||
assertThat(domain.getLordnPhase()).isEqualTo(LordnPhase.NONE);
|
||||
}
|
||||
|
||||
private void testRun(
|
||||
String phase, String domain1, String domain2, String csv, boolean usePullQueue)
|
||||
throws Exception {
|
||||
action.usePullQueue = Optional.of(usePullQueue);
|
||||
action.phase = phase;
|
||||
action.run();
|
||||
verify(httpUrlConnection)
|
||||
.setRequestProperty(
|
||||
AUTHORIZATION, "Basic bG9sY2F0OmF0dGFjaw=="); // echo -n lolcat:attack | base64
|
||||
verify(httpUrlConnection)
|
||||
.setRequestProperty(eq(CONTENT_TYPE), startsWith("multipart/form-data; boundary="));
|
||||
verify(httpUrlConnection).setRequestMethod("POST");
|
||||
assertThat(httpUrlConnection.getURL())
|
||||
.isEqualTo(new URL("http://127.0.0.1/LORDN/tld/" + phase));
|
||||
assertThat(connectionOutputStream.toString(UTF_8)).contains(csv);
|
||||
if (!usePullQueue) {
|
||||
verifyColumnCleared(domain1);
|
||||
verifyColumnCleared(domain2);
|
||||
} else {
|
||||
assertThat(
|
||||
getQueue("lordn-" + phase)
|
||||
.leaseTasks(
|
||||
LeaseOptions.Builder.withTag("tld")
|
||||
.leasePeriod(1, TimeUnit.HOURS)
|
||||
.countLimit(100)))
|
||||
.isEmpty();
|
||||
}
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
NordnVerifyAction.QUEUE,
|
||||
new TaskMatcher()
|
||||
.url(NordnVerifyAction.PATH)
|
||||
.param(NordnVerifyAction.NORDN_URL_PARAM, LOCATION_URL)
|
||||
.param(RequestParameters.PARAM_TLD, "tld")
|
||||
.header(CONTENT_TYPE, FORM_DATA.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class SmdrlCsvParserTest {
|
||||
private final FakeClock clock = new FakeClock();
|
||||
|
||||
private static final CharSource SMDRL_LATEST_CSV =
|
||||
TmchTestData.loadBytes("smdrl-latest.csv").asCharSource(US_ASCII);
|
||||
TmchTestData.loadBytes("smdrl/smdrl-latest.csv").asCharSource(US_ASCII);
|
||||
|
||||
@Test
|
||||
void testParse() throws Exception {
|
||||
|
||||
@@ -62,7 +62,8 @@ abstract class TmchActionTestCase {
|
||||
@BeforeEach
|
||||
public void beforeEachTmchActionTestCase() throws Exception {
|
||||
marksdb.tmchMarksdbUrl = MARKSDB_URL;
|
||||
marksdb.marksdbPublicKey = TmchData.loadPublicKey(TmchTestData.loadBytes("pubkey"));
|
||||
marksdb.marksdbPublicKey =
|
||||
TmchData.loadPublicKey(TmchTestData.loadBytes("crypto/marksdb-public-key-test.asc"));
|
||||
marksdb.urlConnectionService = urlConnectionService;
|
||||
when(httpUrlConnection.getResponseCode()).thenReturn(SC_OK);
|
||||
}
|
||||
|
||||
@@ -37,24 +37,31 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
/** Unit tests for {@link TmchCertificateAuthority}. */
|
||||
class TmchCertificateAuthorityTest {
|
||||
|
||||
private static final String GOOD_TEST_CERTIFICATE = loadFile("icann-tmch-test-good.crt");
|
||||
private static final String REVOKED_TEST_CERTIFICATE = loadFile("icann-tmch-test-revoked.crt");
|
||||
// This certificate is extracted from the SMD file smd/active.smd. It is the
|
||||
// intermediate (TMV) certificate that was signed by the root (CA) cert and signed the SMD
|
||||
// file.
|
||||
private static final String GOOD_TMV_CERTIFICATE = loadFile("crypto/icann-tmv-test-good.crt");
|
||||
// This certificate is extracted from the SMD file smd/tmv-cert-revoked.smd. It is the (revoked)
|
||||
// intermediate (TMV) certificate that was signed by the root (CA) cert and signed the SMD
|
||||
// file.
|
||||
private static final String REVOKED_TMV_CERTIFICATE =
|
||||
loadFile("crypto/icann-tmv-test-revoked.crt");
|
||||
|
||||
@RegisterExtension
|
||||
public final JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2014-01-01T00:00:00Z"));
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2022-11-20T00:00:00Z"));
|
||||
|
||||
@Test
|
||||
void testFailure_prodRootExpired() {
|
||||
TmchCertificateAuthority tmchCertificateAuthority =
|
||||
new TmchCertificateAuthority(PRODUCTION, clock);
|
||||
clock.setTo(DateTime.parse("2024-01-01T00:00:00Z"));
|
||||
clock.setTo(DateTime.parse("2500-01-01T00:00:00Z"));
|
||||
CertificateExpiredException e =
|
||||
assertThrows(
|
||||
CertificateExpiredException.class, tmchCertificateAuthority::getAndValidateRoot);
|
||||
assertThat(e).hasMessageThat().contains("NotAfter: Sun Jul 23 23:59:59 UTC 2023");
|
||||
assertThat(e).hasMessageThat().contains("NotAfter");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -65,7 +72,7 @@ class TmchCertificateAuthorityTest {
|
||||
CertificateNotYetValidException e =
|
||||
assertThrows(
|
||||
CertificateNotYetValidException.class, tmchCertificateAuthority::getAndValidateRoot);
|
||||
assertThat(e).hasMessageThat().contains("NotBefore: Wed Jul 24 00:00:00 UTC 2013");
|
||||
assertThat(e).hasMessageThat().contains("NotBefore");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -77,14 +84,14 @@ class TmchCertificateAuthorityTest {
|
||||
SignatureException e =
|
||||
assertThrows(
|
||||
SignatureException.class,
|
||||
() -> tmchCertificateAuthority.verify(loadCertificate(GOOD_TEST_CERTIFICATE)));
|
||||
() -> tmchCertificateAuthority.verify(loadCertificate(GOOD_TMV_CERTIFICATE)));
|
||||
assertThat(e).hasMessageThat().contains("Signature does not match");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_verify() throws Exception {
|
||||
TmchCertificateAuthority tmchCertificateAuthority = new TmchCertificateAuthority(PILOT, clock);
|
||||
tmchCertificateAuthority.verify(loadCertificate(GOOD_TEST_CERTIFICATE));
|
||||
tmchCertificateAuthority.verify(loadCertificate(GOOD_TMV_CERTIFICATE));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -94,7 +101,7 @@ class TmchCertificateAuthorityTest {
|
||||
SignatureException e =
|
||||
assertThrows(
|
||||
SignatureException.class,
|
||||
() -> tmchCertificateAuthority.verify(loadCertificate(GOOD_TEST_CERTIFICATE)));
|
||||
() -> tmchCertificateAuthority.verify(loadCertificate(GOOD_TMV_CERTIFICATE)));
|
||||
assertThat(e).hasMessageThat().contains("Signature does not match");
|
||||
}
|
||||
|
||||
@@ -104,7 +111,7 @@ class TmchCertificateAuthorityTest {
|
||||
CertificateRevokedException thrown =
|
||||
assertThrows(
|
||||
CertificateRevokedException.class,
|
||||
() -> tmchCertificateAuthority.verify(loadCertificate(REVOKED_TEST_CERTIFICATE)));
|
||||
assertThat(thrown).hasMessageThat().contains("revoked, reason: KEY_COMPROMISE");
|
||||
() -> tmchCertificateAuthority.verify(loadCertificate(REVOKED_TMV_CERTIFICATE)));
|
||||
assertThat(thrown).hasMessageThat().contains("revoked, reason: UNSPECIFIED");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
package google.registry.tmch;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.TestDataHelper.loadBytes;
|
||||
import static google.registry.tmch.TmchTestData.loadBytes;
|
||||
import static google.registry.util.ResourceUtils.readResourceBytes;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -29,6 +29,7 @@ import java.security.SignatureException;
|
||||
import java.security.cert.CRLException;
|
||||
import java.security.cert.CertificateNotYetValidException;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link TmchCrlAction}. */
|
||||
@@ -38,29 +39,30 @@ class TmchCrlActionTest extends TmchActionTestCase {
|
||||
TmchCrlAction action = new TmchCrlAction();
|
||||
action.marksdb = marksdb;
|
||||
action.tmchCertificateAuthority = new TmchCertificateAuthority(tmchCaMode, clock);
|
||||
action.tmchCrlUrl = new URL("http://sloth.lol/tmch.crl");
|
||||
action.tmchCrlUrl = new URL("https://sloth.lol/tmch.crl");
|
||||
return action;
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void before() {
|
||||
clock.setTo(DateTime.parse("2023-03-24TZ"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess() throws Exception {
|
||||
clock.setTo(DateTime.parse("2013-07-24TZ"));
|
||||
when(httpUrlConnection.getInputStream())
|
||||
.thenReturn(
|
||||
new ByteArrayInputStream(
|
||||
readResourceBytes(TmchCertificateAuthority.class, "icann-tmch.crl").read()));
|
||||
newTmchCrlAction(TmchCaMode.PRODUCTION).run();
|
||||
readResourceBytes(TmchCertificateAuthority.class, "icann-tmch-pilot.crl").read()));
|
||||
newTmchCrlAction(TmchCaMode.PILOT).run();
|
||||
verify(httpUrlConnection).getInputStream();
|
||||
assertThat(connectedUrls).containsExactly(new URL("http://sloth.lol/tmch.crl"));
|
||||
assertThat(connectedUrls).containsExactly(new URL("https://sloth.lol/tmch.crl"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_crlTooOld() throws Exception {
|
||||
clock.setTo(DateTime.parse("2020-01-01TZ"));
|
||||
when(httpUrlConnection.getInputStream())
|
||||
.thenReturn(
|
||||
new ByteArrayInputStream(
|
||||
loadBytes(TmchCrlActionTest.class, "icann-tmch-pilot-old.crl").read()));
|
||||
.thenReturn(new ByteArrayInputStream(loadBytes("crypto/icann-tmch-pilot-old.crl").read()));
|
||||
TmchCrlAction action = newTmchCrlAction(TmchCaMode.PILOT);
|
||||
Exception e = assertThrows(Exception.class, action::run);
|
||||
assertThat(e).hasCauseThat().isInstanceOf(CRLException.class);
|
||||
@@ -72,12 +74,11 @@ class TmchCrlActionTest extends TmchActionTestCase {
|
||||
|
||||
@Test
|
||||
void testFailure_crlNotSignedByRoot() throws Exception {
|
||||
clock.setTo(DateTime.parse("2013-07-24TZ"));
|
||||
when(httpUrlConnection.getInputStream())
|
||||
.thenReturn(
|
||||
new ByteArrayInputStream(
|
||||
readResourceBytes(TmchCertificateAuthority.class, "icann-tmch.crl").read()));
|
||||
Exception e = assertThrows(Exception.class, newTmchCrlAction(TmchCaMode.PILOT)::run);
|
||||
readResourceBytes(TmchCertificateAuthority.class, "icann-tmch-pilot.crl").read()));
|
||||
Exception e = assertThrows(Exception.class, newTmchCrlAction(TmchCaMode.PRODUCTION)::run);
|
||||
assertThat(e).hasCauseThat().isInstanceOf(SignatureException.class);
|
||||
assertThat(e).hasCauseThat().hasMessageThat().isEqualTo("Signature does not match.");
|
||||
}
|
||||
|
||||
@@ -43,8 +43,8 @@ class TmchDnlActionTest extends TmchActionTestCase {
|
||||
void testDnl() throws Exception {
|
||||
assertThat(ClaimsListDao.get().getClaimKey("xn----7sbejwbn3axu3d")).isEmpty();
|
||||
when(httpUrlConnection.getInputStream())
|
||||
.thenReturn(new ByteArrayInputStream(TmchTestData.loadBytes("dnl-latest.csv").read()))
|
||||
.thenReturn(new ByteArrayInputStream(TmchTestData.loadBytes("dnl-latest.sig").read()));
|
||||
.thenReturn(new ByteArrayInputStream(TmchTestData.loadBytes("dnl/dnl-latest.csv").read()))
|
||||
.thenReturn(new ByteArrayInputStream(TmchTestData.loadBytes("dnl/dnl-latest.sig").read()));
|
||||
newTmchDnlAction().run();
|
||||
verify(httpUrlConnection, times(2)).getInputStream();
|
||||
assertThat(connectedUrls.stream().map(URL::toString).collect(toImmutableList()))
|
||||
|
||||
@@ -46,8 +46,8 @@ class TmchSmdrlActionTest extends TmchActionTestCase {
|
||||
assertThat(smdrl.isSmdRevoked("0000001681375789102250-65535", now)).isFalse();
|
||||
assertThat(smdrl.isSmdRevoked("0000001681375789102250-65536", now)).isFalse();
|
||||
when(httpUrlConnection.getInputStream())
|
||||
.thenReturn(new ByteArrayInputStream(loadBytes("smdrl-latest.csv").read()))
|
||||
.thenReturn(new ByteArrayInputStream(loadBytes("smdrl-latest.sig").read()));
|
||||
.thenReturn(new ByteArrayInputStream(loadBytes("smdrl/smdrl-latest.csv").read()))
|
||||
.thenReturn(new ByteArrayInputStream(loadBytes("smdrl/smdrl-latest.sig").read()));
|
||||
newTmchSmdrlAction().run();
|
||||
verify(httpUrlConnection, times(2)).getInputStream();
|
||||
assertThat(connectedUrls.stream().map(URL::toString).collect(toImmutableList()))
|
||||
|
||||
@@ -16,22 +16,33 @@ package google.registry.tmch;
|
||||
|
||||
import static com.google.common.base.CharMatcher.whitespace;
|
||||
import static com.google.common.io.BaseEncoding.base64;
|
||||
import static google.registry.tmch.TmchData.BEGIN_ENCODED_SMD;
|
||||
import static google.registry.tmch.TmchData.END_ENCODED_SMD;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.io.ByteSource;
|
||||
import com.google.re2j.Matcher;
|
||||
import com.google.re2j.Pattern;
|
||||
import google.registry.testing.TestDataHelper;
|
||||
|
||||
/** Utility class providing easy access to contents of the {@code testdata/} directory. */
|
||||
/**
|
||||
* Utility class providing easy access to contents of the {@code
|
||||
* core/src/test/resources/google/registry/tmch/} directory.
|
||||
*/
|
||||
public final class TmchTestData {
|
||||
|
||||
private static final String BEGIN_ENCODED_SMD = "-----BEGIN ENCODED SMD-----";
|
||||
private static final String END_ENCODED_SMD = "-----END ENCODED SMD-----";
|
||||
private TmchTestData() {}
|
||||
|
||||
/** Returns {@link ByteSource} for file in {@code tmch/testdata/} directory. */
|
||||
/**
|
||||
* Returns {@link ByteSource} for file in {@code core/src/test/resources/google/registry/tmch/}
|
||||
* directory.
|
||||
*/
|
||||
public static ByteSource loadBytes(String filename) {
|
||||
return TestDataHelper.loadBytes(TmchTestData.class, filename);
|
||||
}
|
||||
|
||||
/** Loads data from file in {@code tmch/testdata/} as a String. */
|
||||
/** Loads data from file in {@code core/src/test/resources/google/registry/tmch/} as a String. */
|
||||
public static String loadFile(String filename) {
|
||||
return TestDataHelper.loadFile(TmchTestData.class, filename);
|
||||
}
|
||||
@@ -47,4 +58,15 @@ public final class TmchTestData {
|
||||
data.indexOf(BEGIN_ENCODED_SMD) + BEGIN_ENCODED_SMD.length(),
|
||||
data.indexOf(END_ENCODED_SMD))));
|
||||
}
|
||||
|
||||
public static ImmutableList<String> extractLabels(String filepath) {
|
||||
String pattern = "U-labels: ";
|
||||
String content = loadFile(filepath);
|
||||
Matcher matcher = Pattern.compile(pattern + ".*").matcher(content);
|
||||
matcher.find();
|
||||
String matchedString = matcher.group().replace(pattern, "");
|
||||
return matchedString.isEmpty()
|
||||
? ImmutableList.of()
|
||||
: ImmutableList.copyOf(Splitter.on(',').trimResults().split(matchedString));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,9 +42,9 @@ class TmchTestDataExpirationTest {
|
||||
* files and so that we can test various types of files, but this test will vail when the validity
|
||||
* of the ICANN-provided file expires.
|
||||
*
|
||||
* <p>When this fails, check
|
||||
* <a>https://newgtlds.icann.org/en/about/trademark-clearinghouse/registries-registrars</a> for a
|
||||
* new updated SMD file.
|
||||
* <p>When this fails, check <a
|
||||
* href="https://newgtlds.icann.org/en/about/trademark-clearinghouse/registries-registrars">Signed
|
||||
* Mark Data Files</a> for a new updated SMD file.
|
||||
*/
|
||||
@Test
|
||||
void testActiveSignedMarkFiles_areValidAndNotExpired() throws Exception {
|
||||
@@ -53,7 +53,7 @@ class TmchTestDataExpirationTest {
|
||||
new TmchXmlSignature(
|
||||
new TmchCertificateAuthority(TmchCaMode.PILOT, new SystemClock())));
|
||||
|
||||
String filePath = "active/smd-active-21aug20-en.smd";
|
||||
String filePath = "smd/active.smd";
|
||||
String tmchData = loadFile(TmchTestDataExpirationTest.class, filePath);
|
||||
EncodedSignedMark smd = TmchData.readEncodedSignedMark(tmchData);
|
||||
try {
|
||||
|
||||
@@ -28,14 +28,18 @@ import java.security.cert.CertificateNotYetValidException;
|
||||
import java.security.cert.CertificateRevokedException;
|
||||
import javax.xml.crypto.dsig.XMLSignatureException;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.junitpioneer.jupiter.cartesian.CartesianTest;
|
||||
import org.junitpioneer.jupiter.cartesian.CartesianTest.Values;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link TmchXmlSignature}.
|
||||
*
|
||||
* <p>This class does not test the {@code revoked/smd/} folder because it's not a crypto issue.
|
||||
* <p>This class does not verify if the SMD itself is revoked, which is different from if the
|
||||
* certificate signging the SMD is revoked, as it is not a crypto issue.
|
||||
*/
|
||||
class TmchXmlSignatureTest {
|
||||
|
||||
@@ -44,38 +48,54 @@ class TmchXmlSignatureTest {
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
|
||||
// This should be a date which falls within the validity range of the test files contained in the
|
||||
// testdata/active directory. Note that test files claiming to be valid for a particular date
|
||||
// smd/ directory. Note that test files claiming to be valid for a particular date
|
||||
// range in the file header may not actually be valid the whole time, because they contain an
|
||||
// embedded certificate which might have a shorter validity range.
|
||||
// embedded (intermediate) certificate which might have a shorter validity range.
|
||||
//
|
||||
// New versions of the test files are published every few years by ICANN, and available at in the
|
||||
// Signed Mark Data Files section of:
|
||||
// "Signed Mark Data Files" section of:
|
||||
//
|
||||
// https://newgtlds.icann.org/en/about/trademark-clearinghouse/registries-registrars
|
||||
//
|
||||
// The link labeled "Set of IDN test-SMDs" leads to a .tar.gz file containing the test files which
|
||||
// in our directory structure reside in testdata/active and testdata/revoked/smd (it is not clear
|
||||
// where the files in testdata/invalid and testdata/revoked/tmv come from; we probably made them
|
||||
// ourselves, since there aren't as many of them). For purposes of testing, we could probably keep
|
||||
// using old test files forever, and keep a corresponding old date, but it seems like a good idea
|
||||
// to keep up to date if possible.
|
||||
//
|
||||
// When updating this date, also update the dates below, which test to make sure that dates before
|
||||
// and after the validity window result in rejection.
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2018-05-15T23:15:37.4Z"));
|
||||
// When updating this date, also update the "time travel" dates in two tests below, which test to
|
||||
// make sure that dates before and after the validity window result in rejection.
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2023-01-15T23:15:37.4Z"));
|
||||
|
||||
private byte[] smdData;
|
||||
private TmchXmlSignature tmchXmlSignature =
|
||||
new TmchXmlSignature(new TmchCertificateAuthority(TmchCaMode.PILOT, clock));
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {}
|
||||
@Test
|
||||
void testActive() throws Exception {
|
||||
smdData = loadSmd("smd/active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRevoked() throws Exception {
|
||||
smdData = loadSmd("smd/revoked.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInvalid() {
|
||||
smdData = loadSmd("smd/invalid.smd");
|
||||
assertThrows(XMLSignatureException.class, () -> tmchXmlSignature.verify(smdData));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTmvCertRevoked() {
|
||||
smdData = loadSmd("smd/tmv-cert-revoked.smd");
|
||||
CertificateRevokedException e =
|
||||
assertThrows(CertificateRevokedException.class, () -> tmchXmlSignature.verify(smdData));
|
||||
assertThat(e).hasMessageThat().contains("Certificate has been revoked");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWrongCertificateAuthority() {
|
||||
tmchXmlSignature =
|
||||
new TmchXmlSignature(new TmchCertificateAuthority(TmchCaMode.PRODUCTION, clock));
|
||||
smdData = loadSmd("active/Court-Agent-Arab-Active.smd");
|
||||
smdData = loadSmd("smd/active.smd");
|
||||
CertificateSignatureException e =
|
||||
assertThrows(CertificateSignatureException.class, () -> tmchXmlSignature.verify(smdData));
|
||||
assertThat(e).hasMessageThat().contains("Signature does not match");
|
||||
@@ -83,265 +103,40 @@ class TmchXmlSignatureTest {
|
||||
|
||||
@Test
|
||||
void testTimeTravelBeforeCertificateWasCreated() {
|
||||
smdData = loadSmd("active/Court-Agent-Arab-Active.smd");
|
||||
clock.setTo(DateTime.parse("2013-05-01T00:00:00Z"));
|
||||
smdData = loadSmd("smd/active.smd");
|
||||
clock.setTo(DateTime.parse("2021-05-01T00:00:00Z"));
|
||||
assertThrows(CertificateNotYetValidException.class, () -> tmchXmlSignature.verify(smdData));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTimeTravelAfterCertificateHasExpired() {
|
||||
smdData = loadSmd("active/Court-Agent-Arab-Active.smd");
|
||||
clock.setTo(DateTime.parse("2023-06-01T00:00:00Z"));
|
||||
smdData = loadSmd("smd/active.smd");
|
||||
clock.setTo(DateTime.parse("2028-06-01T00:00:00Z"));
|
||||
assertThrows(CertificateExpiredException.class, () -> tmchXmlSignature.verify(smdData));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveCourtAgentArabActive() throws Exception {
|
||||
smdData = loadSmd("active/Court-Agent-Arab-Active.smd");
|
||||
@CartesianTest
|
||||
void testInd(
|
||||
@Values(strings = {"Agent", "Holder"}) String contact,
|
||||
@Values(strings = {"Court", "Trademark", "TreatyStatute"}) String type,
|
||||
@Values(strings = {"Arab", "Chinese", "English", "French"}) String language,
|
||||
@Values(strings = {"Active", "Revoked"}) String status)
|
||||
throws Exception {
|
||||
smdData =
|
||||
loadSmd(
|
||||
String.format(
|
||||
"idn/%s-%s/%s-%s-%s-%s.smd", contact, language, type, contact, language, status));
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveCourtAgentChineseActive() throws Exception {
|
||||
smdData = loadSmd("active/Court-Agent-Chinese-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveCourtAgentEnglishActive() throws Exception {
|
||||
smdData = loadSmd("active/Court-Agent-English-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveCourtAgentFrenchActive() throws Exception {
|
||||
smdData = loadSmd("active/Court-Agent-French-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveCourtAgentRussianActive() throws Exception {
|
||||
smdData = loadSmd("active/Court-Agent-Russian-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveCourtHolderArabActive() throws Exception {
|
||||
smdData = loadSmd("active/Court-Holder-Arab-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveCourtHolderChineseActive() throws Exception {
|
||||
smdData = loadSmd("active/Court-Holder-Chinese-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveCourtHolderEnglishActive() throws Exception {
|
||||
smdData = loadSmd("active/Court-Holder-English-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveCourtHolderFrenchActive() throws Exception {
|
||||
smdData = loadSmd("active/Court-Holder-French-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveCourtHolderRussianActive() throws Exception {
|
||||
smdData = loadSmd("active/Court-Holder-Russian-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveTrademarkAgentArabActive() throws Exception {
|
||||
smdData = loadSmd("active/Trademark-Agent-Arab-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveTrademarkAgentChineseActive() throws Exception {
|
||||
smdData = loadSmd("active/Trademark-Agent-Chinese-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveTrademarkAgentEnglishActive() throws Exception {
|
||||
smdData = loadSmd("active/Trademark-Agent-English-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveTrademarkAgentFrenchActive() throws Exception {
|
||||
smdData = loadSmd("active/Trademark-Agent-French-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveTrademarkAgentRussianActive() throws Exception {
|
||||
smdData = loadSmd("active/Trademark-Agent-Russian-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveTrademarkHolderArabActive() throws Exception {
|
||||
smdData = loadSmd("active/Trademark-Holder-Arab-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveTrademarkHolderChineseActive() throws Exception {
|
||||
smdData = loadSmd("active/Trademark-Holder-Chinese-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveTrademarkHolderEnglishActive() throws Exception {
|
||||
smdData = loadSmd("active/Trademark-Holder-English-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveTrademarkHolderFrenchActive() throws Exception {
|
||||
smdData = loadSmd("active/Trademark-Holder-French-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveTrademarkHolderRussianActive() throws Exception {
|
||||
smdData = loadSmd("active/Trademark-Holder-Russian-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveTreatystatuteAgentArabActive() throws Exception {
|
||||
smdData = loadSmd("active/TreatyStatute-Agent-Arab-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveTreatystatuteAgentChineseActive() throws Exception {
|
||||
smdData = loadSmd("active/TreatyStatute-Agent-Chinese-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveTreatystatuteAgentEnglishActive() throws Exception {
|
||||
smdData = loadSmd("active/TreatyStatute-Agent-English-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveTreatystatuteAgentFrenchActive() throws Exception {
|
||||
smdData = loadSmd("active/TreatyStatute-Agent-French-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveTreatystatuteAgentRussianActive() throws Exception {
|
||||
smdData = loadSmd("active/TreatyStatute-Agent-Russian-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveTreatystatuteHolderArabActive() throws Exception {
|
||||
smdData = loadSmd("active/TreatyStatute-Holder-Arab-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveTreatystatuteHolderChineseActive() throws Exception {
|
||||
smdData = loadSmd("active/TreatyStatute-Holder-Chinese-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveTreatystatuteHolderEnglishActive() throws Exception {
|
||||
smdData = loadSmd("active/TreatyStatute-Holder-English-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveTreatystatuteHolderFrenchActive() throws Exception {
|
||||
smdData = loadSmd("active/TreatyStatute-Holder-French-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiveTreatystatuteHolderRussianActive() throws Exception {
|
||||
smdData = loadSmd("active/TreatyStatute-Holder-Russian-Active.smd");
|
||||
tmchXmlSignature.verify(smdData);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInvalidInvalidsignatureCourtAgentFrenchActive() {
|
||||
smdData = loadSmd("invalid/InvalidSignature-Court-Agent-French-Active.smd");
|
||||
assertThrows(XMLSignatureException.class, () -> tmchXmlSignature.verify(smdData));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInvalidInvalidsignatureTrademarkAgentEnglishActive() {
|
||||
smdData = loadSmd("invalid/InvalidSignature-Trademark-Agent-English-Active.smd");
|
||||
assertThrows(XMLSignatureException.class, () -> tmchXmlSignature.verify(smdData));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInvalidInvalidsignatureTrademarkAgentRussianActive() {
|
||||
smdData = loadSmd("invalid/InvalidSignature-Trademark-Agent-Russian-Active.smd");
|
||||
assertThrows(XMLSignatureException.class, () -> tmchXmlSignature.verify(smdData));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInvalidInvalidsignatureTreatystatuteAgentChineseActive() {
|
||||
smdData = loadSmd("invalid/InvalidSignature-TreatyStatute-Agent-Chinese-Active.smd");
|
||||
assertThrows(XMLSignatureException.class, () -> tmchXmlSignature.verify(smdData));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInvalidInvalidsignatureTreatystatuteAgentEnglishActive() {
|
||||
smdData = loadSmd("invalid/InvalidSignature-TreatyStatute-Agent-English-Active.smd");
|
||||
assertThrows(XMLSignatureException.class, () -> tmchXmlSignature.verify(smdData));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRevokedTmvTmvrevokedCourtAgentFrenchActive() {
|
||||
smdData = loadSmd("revoked/tmv/TMVRevoked-Court-Agent-French-Active.smd");
|
||||
CertificateRevokedException e =
|
||||
assertThrows(CertificateRevokedException.class, () -> tmchXmlSignature.verify(smdData));
|
||||
assertThat(e).hasMessageThat().contains("KEY_COMPROMISE");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRevokedTmvTmvrevokedTrademarkAgentEnglishActive() {
|
||||
smdData = loadSmd("revoked/tmv/TMVRevoked-Trademark-Agent-English-Active.smd");
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"Arab", "Chinese", "English", "French"})
|
||||
void testIndTmvRevoked(String language) {
|
||||
smdData =
|
||||
loadSmd(
|
||||
String.format("idn/RevokedCert/TMVRevoked-Trademark-Agent-%s-Active.smd", language));
|
||||
CertificateRevokedException e =
|
||||
assertThrows(CertificateRevokedException.class, () -> tmchXmlSignature.verify(smdData));
|
||||
assertThat(e).hasMessageThat().contains("Certificate has been revoked");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRevokedTmvTmvrevokedTrademarkAgentRussianActive() {
|
||||
smdData = loadSmd("revoked/tmv/TMVRevoked-Trademark-Agent-Russian-Active.smd");
|
||||
CertificateRevokedException e =
|
||||
assertThrows(CertificateRevokedException.class, () -> tmchXmlSignature.verify(smdData));
|
||||
assertThat(e).hasMessageThat().contains("Certificate has been revoked");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRevokedTmvTmvrevokedTreatystatuteAgentChineseActive() {
|
||||
smdData = loadSmd("revoked/tmv/TMVRevoked-TreatyStatute-Agent-Chinese-Active.smd");
|
||||
CertificateRevokedException e =
|
||||
assertThrows(CertificateRevokedException.class, () -> tmchXmlSignature.verify(smdData));
|
||||
assertThat(e).hasMessageThat().contains("KEY_COMPROMISE");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRevokedTmvTmvrevokedTreatystatuteAgentEnglishActive() {
|
||||
smdData = loadSmd("revoked/tmv/TMVRevoked-TreatyStatute-Agent-English-Active.smd");
|
||||
CertificateRevokedException e =
|
||||
assertThrows(CertificateRevokedException.class, () -> tmchXmlSignature.verify(smdData));
|
||||
assertThat(e).hasMessageThat().contains("KEY_COMPROMISE");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright 2023 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.tools;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Tests for {@link CreateUserCommand}. */
|
||||
public class CreateUserCommandTest extends CommandTestCase<CreateUserCommand> {
|
||||
|
||||
@Test
|
||||
void testSuccess() throws Exception {
|
||||
runCommandForced("--email", "user@example.test");
|
||||
User onlyUser = Iterables.getOnlyElement(DatabaseHelper.loadAllOf(User.class));
|
||||
assertThat(onlyUser.getEmailAddress()).isEqualTo("user@example.test");
|
||||
assertThat(onlyUser.getUserRoles().isAdmin()).isFalse();
|
||||
assertThat(onlyUser.getUserRoles().getGlobalRole()).isEqualTo(GlobalRole.NONE);
|
||||
assertThat(onlyUser.getUserRoles().getRegistrarRoles()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_admin() throws Exception {
|
||||
runCommandForced("--email", "user@example.test", "--admin", "true");
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().isAdmin()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_globalRole() throws Exception {
|
||||
runCommandForced("--email", "user@example.test", "--global_role", "FTE");
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().getGlobalRole())
|
||||
.isEqualTo(GlobalRole.FTE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_registrarRoles() throws Exception {
|
||||
runCommandForced(
|
||||
"--email",
|
||||
"user@example.test",
|
||||
"--registrar_roles",
|
||||
"TheRegistrar=ACCOUNT_MANAGER,NewRegistrar=PRIMARY_CONTACT");
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().getRegistrarRoles())
|
||||
.isEqualTo(
|
||||
ImmutableMap.of(
|
||||
"TheRegistrar",
|
||||
RegistrarRole.ACCOUNT_MANAGER,
|
||||
"NewRegistrar",
|
||||
RegistrarRole.PRIMARY_CONTACT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_alreadyExists() throws Exception {
|
||||
runCommandForced("--email", "user@example.test");
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> runCommandForced("--email", "user@example.test")))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("A user with email user@example.test already exists");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2023 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.tools;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Tests for {@link DeleteUserCommand}. */
|
||||
public class DeleteUserCommandTest extends CommandTestCase<DeleteUserCommand> {
|
||||
|
||||
@Test
|
||||
void testSuccess_deletesUser() throws Exception {
|
||||
User user =
|
||||
new User.Builder()
|
||||
.setEmailAddress("email@example.test")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).setIsAdmin(true).build())
|
||||
.build();
|
||||
UserDao.saveUser(user);
|
||||
assertThat(UserDao.loadUser("email@example.test")).isPresent();
|
||||
runCommandForced("--email", "email@example.test");
|
||||
assertThat(UserDao.loadUser("email@example.test")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_nonexistent() {
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> runCommandForced("--email", "nonexistent@example.test")))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Email does not correspond to a valid user");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright 2023 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.tools;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Tests for {@link UpdateUserCommand}. */
|
||||
public class UpdateUserCommandTest extends CommandTestCase<UpdateUserCommand> {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
UserDao.saveUser(
|
||||
new User.Builder()
|
||||
.setEmailAddress("user@example.test")
|
||||
.setUserRoles(new UserRoles.Builder().build())
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_admin() throws Exception {
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().isAdmin()).isFalse();
|
||||
runCommandForced("--email", "user@example.test", "--admin", "true");
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().isAdmin()).isTrue();
|
||||
runCommandForced("--email", "user@example.test", "--admin", "false");
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().isAdmin()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_registrarRoles() throws Exception {
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().getRegistrarRoles())
|
||||
.isEmpty();
|
||||
runCommandForced(
|
||||
"--email",
|
||||
"user@example.test",
|
||||
"--registrar_roles",
|
||||
"TheRegistrar=ACCOUNT_MANAGER,NewRegistrar=PRIMARY_CONTACT");
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().getRegistrarRoles())
|
||||
.isEqualTo(
|
||||
ImmutableMap.of(
|
||||
"TheRegistrar",
|
||||
RegistrarRole.ACCOUNT_MANAGER,
|
||||
"NewRegistrar",
|
||||
RegistrarRole.PRIMARY_CONTACT));
|
||||
runCommandForced("--email", "user@example.test", "--registrar_roles", "");
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().getRegistrarRoles())
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_globalRole() throws Exception {
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().getGlobalRole())
|
||||
.isEqualTo(GlobalRole.NONE);
|
||||
runCommandForced("--email", "user@example.test", "--global_role", "FTE");
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().getGlobalRole())
|
||||
.isEqualTo(GlobalRole.FTE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_doesntExist() {
|
||||
assertThat(
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> runCommandForced("--email", "nonexistent@example.test")))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("User nonexistent@example.test not found");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
// Copyright 2023 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.ui.server.console;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import com.google.api.client.http.HttpStatusCodes;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.auth.AuthLevel;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.util.UtilsModule;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Tests for {@link google.registry.ui.server.console.ConsoleDomainGetAction}. */
|
||||
public class ConsoleDomainGetActionTest {
|
||||
|
||||
private static final Gson GSON = UtilsModule.provideGson();
|
||||
private static final FakeResponse RESPONSE = new FakeResponse();
|
||||
|
||||
@RegisterExtension
|
||||
final JpaTestExtensions.JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTld("tld");
|
||||
DatabaseHelper.persistActiveDomain("exists.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_fullJsonRepresentation() {
|
||||
ConsoleDomainGetAction action =
|
||||
createAction(
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
UserAuthInfo.create(
|
||||
createUser(
|
||||
new UserRoles.Builder()
|
||||
.setRegistrarRoles(
|
||||
ImmutableMap.of("TheRegistrar", RegistrarRole.ACCOUNT_MANAGER))
|
||||
.build()))),
|
||||
"exists.tld");
|
||||
action.run();
|
||||
assertThat(RESPONSE.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_OK);
|
||||
assertThat(RESPONSE.getPayload())
|
||||
.isEqualTo(
|
||||
"{\"domainName\":\"exists.tld\",\"adminContact\":{\"key\":\"3-ROID\"},\"techContact\":"
|
||||
+ "{\"key\":\"3-ROID\"},\"registrantContact\":{\"key\":\"3-ROID\"},\"registrationExpirationTime\":"
|
||||
+ "\"294247-01-10T04:00:54.775Z\",\"lastTransferTime\":\"null\",\"repoId\":\"2-TLD\","
|
||||
+ "\"currentSponsorRegistrarId\":\"TheRegistrar\",\"creationRegistrarId\":\"TheRegistrar\","
|
||||
+ "\"creationTime\":{\"creationTime\":\"1970-01-01T00:00:00.000Z\"},\"lastEppUpdateTime\":\"null\","
|
||||
+ "\"statuses\":[\"INACTIVE\"]}");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_emptyAuth() {
|
||||
ConsoleDomainGetAction action = createAction(AuthResult.NOT_AUTHENTICATED, "exists.tld");
|
||||
action.run();
|
||||
assertThat(RESPONSE.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_appAuth() {
|
||||
ConsoleDomainGetAction action = createAction(AuthResult.create(AuthLevel.APP), "exists.tld");
|
||||
action.run();
|
||||
assertThat(RESPONSE.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_wrongTypeOfUser() {
|
||||
ConsoleDomainGetAction action =
|
||||
createAction(
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
UserAuthInfo.create(mock(com.google.appengine.api.users.User.class), false)),
|
||||
"exists.tld");
|
||||
action.run();
|
||||
assertThat(RESPONSE.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_noAccessToRegistrar() {
|
||||
ConsoleDomainGetAction action =
|
||||
createAction(
|
||||
AuthResult.create(
|
||||
AuthLevel.USER, UserAuthInfo.create(createUser(new UserRoles.Builder().build()))),
|
||||
"exists.tld");
|
||||
action.run();
|
||||
assertThat(RESPONSE.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_nonexistentDomain() {
|
||||
ConsoleDomainGetAction action =
|
||||
createAction(
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
UserAuthInfo.create(createUser(new UserRoles.Builder().setIsAdmin(true).build()))),
|
||||
"nonexistent.tld");
|
||||
action.run();
|
||||
assertThat(RESPONSE.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_NOT_FOUND);
|
||||
}
|
||||
|
||||
private User createUser(UserRoles userRoles) {
|
||||
return new User.Builder()
|
||||
.setEmailAddress("email@email.com")
|
||||
.setGaiaId("gaiaId")
|
||||
.setUserRoles(userRoles)
|
||||
.build();
|
||||
}
|
||||
|
||||
private ConsoleDomainGetAction createAction(AuthResult authResult, String domain) {
|
||||
return new ConsoleDomainGetAction(authResult, RESPONSE, GSON, domain);
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.util;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.LogsSubject.assertAboutLogs;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.appengine.api.log.LogQuery;
|
||||
import com.google.appengine.api.log.LogService;
|
||||
import com.google.appengine.api.log.RequestLogs;
|
||||
import com.google.apphosting.api.ApiProxy;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.testing.TestLogHandler;
|
||||
import google.registry.testing.UserServiceExtension;
|
||||
import java.util.logging.Level;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link RequestStatusCheckerImpl}. */
|
||||
final class RequestStatusCheckerImplTest {
|
||||
|
||||
private static final TestLogHandler logHandler = new TestLogHandler();
|
||||
|
||||
private static final RequestStatusChecker requestStatusChecker = new RequestStatusCheckerImpl();
|
||||
|
||||
/**
|
||||
* Matcher for the expected LogQuery in {@link RequestStatusCheckerImpl#isRunning}.
|
||||
*
|
||||
* Because LogQuery doesn't have a .equals function, we have to create an actual matcher to make
|
||||
* sure we have the right argument in our mocks.
|
||||
*/
|
||||
private static LogQuery expectedLogQuery(final String requestLogId) {
|
||||
return argThat(
|
||||
object -> {
|
||||
assertThat(object).isInstanceOf(LogQuery.class);
|
||||
assertThat(object.getRequestIds()).containsExactly(requestLogId);
|
||||
assertThat(object.getIncludeAppLogs()).isFalse();
|
||||
assertThat(object.getIncludeIncomplete()).isTrue();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
// We do not actually need to set up user service, rather, we just need this extension to set up
|
||||
// App Engine environment so the status checker can make an App Engine API call.
|
||||
@RegisterExtension UserServiceExtension userService = new UserServiceExtension("");
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
JdkLoggerConfig.getConfig(RequestStatusCheckerImpl.class).addHandler(logHandler);
|
||||
RequestStatusCheckerImpl.logService = mock(LogService.class);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void afterEach() {
|
||||
JdkLoggerConfig.getConfig(RequestStatusCheckerImpl.class).removeHandler(logHandler);
|
||||
}
|
||||
|
||||
// If a logId is unrecognized, it could be that the log hasn't been uploaded yet - so we assume
|
||||
// it's a request that has just started running recently.
|
||||
@Test
|
||||
void testIsRunning_unrecognized() {
|
||||
when(RequestStatusCheckerImpl.logService.fetch(expectedLogQuery("12345678")))
|
||||
.thenReturn(ImmutableList.of());
|
||||
assertThat(requestStatusChecker.isRunning("12345678")).isTrue();
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(Level.INFO, "Queried an unrecognized requestLogId");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsRunning_notFinished() {
|
||||
RequestLogs requestLogs = new RequestLogs();
|
||||
requestLogs.setFinished(false);
|
||||
|
||||
when(RequestStatusCheckerImpl.logService.fetch(expectedLogQuery("12345678")))
|
||||
.thenReturn(ImmutableList.of(requestLogs));
|
||||
|
||||
assertThat(requestStatusChecker.isRunning("12345678")).isTrue();
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(Level.INFO, "isFinished: false");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIsRunning_finished() {
|
||||
RequestLogs requestLogs = new RequestLogs();
|
||||
requestLogs.setFinished(true);
|
||||
|
||||
when(RequestStatusCheckerImpl.logService.fetch(expectedLogQuery("12345678")))
|
||||
.thenReturn(ImmutableList.of(requestLogs));
|
||||
|
||||
assertThat(requestStatusChecker.isRunning("12345678")).isFalse();
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(Level.INFO, "isFinished: true");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetLogId_returnsRequestLogId() {
|
||||
String expectedLogId = ApiProxy.getCurrentEnvironment().getAttributes().get(
|
||||
"com.google.appengine.runtime.request_log_id").toString();
|
||||
assertThat(requestStatusChecker.getLogId()).isEqualTo(expectedLogId);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetLogId_createsLog() {
|
||||
requestStatusChecker.getLogId();
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
.hasLogAtLevelWithMessage(Level.INFO, "Current requestLogId: ");
|
||||
}
|
||||
}
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+2
-2
@@ -6,8 +6,8 @@
|
||||
<resData>
|
||||
<domain:creData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>%DOMAIN%</domain:name>
|
||||
<domain:crDate>2014-09-09T09:09:09Z</domain:crDate>
|
||||
<domain:exDate>2016-09-09T09:09:09Z</domain:exDate>
|
||||
<domain:crDate>%CREATE_TIME%</domain:crDate>
|
||||
<domain:exDate>%EXPIRATION_TIME%</domain:exDate>
|
||||
</domain:creData>
|
||||
</resData>
|
||||
<trID>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<extension>
|
||||
<fee:creData xmlns:fee="urn:ietf:params:xml:ns:fee-%FEE_VERSION%">
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:fee description="create">26.00</fee:fee>
|
||||
<fee:fee description="create">%FEE%</fee:fee>
|
||||
</fee:creData>
|
||||
</extension>
|
||||
<trID>
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user