Compare commits

..
Author SHA1 Message Date
Michael MullerandGitHub b75eb0ad95 Replay Cloud SQL transactions against datastore (#738)
* Replay Cloud SQL transactions against datastore

Implement the ReplicateToDatastore cron job that will apply all Cloud SQL
transactions to the datastore.  The last transaction id is stored in a
LastSqlTransaction entity in datastore.

Note that this will not be activated in production until a) the cron
configuration is updated and b) the cloudSql.replicateTransactions flag is set
to true in the nomulus config file.

* Post-review changes

Fixed immutability issues with LastSqlTransaction, write a single transaction
at a time to datastore.

* Changes requested in review

* Get a batch of SQL transactions

Read a batch of SQL transactions at a time and then process them
transactionally against datastore.

* Bring this up-to-date with the codebase

* Changes requested in review

* Fixed date in copyright
2021-03-05 10:35:30 -05:00
Weimin YuandGitHub 63f8fcef18 Clean up Gradle Flyway tasks in :db (#990)
* Clean up Gradle Flyway tasks in :db

Simplified the command line by revising the semantics of some
properties.

Added examples of Flyway task invocations.

This script still uses the GCS file-based credential. We will migrate it
to the Secret Manager soon.
2021-03-04 19:58:09 -05:00
Michael MullerandGitHub 8d563c4516 Add replay & compare to more flow tests (#975)
* Add replay & compare to more flow tests

Add replay and comparison testing to another batch of flow tests, apply fixes
as needed.
2021-03-04 08:25:47 -05:00
Lai JiangandGitHub a58c4a6492 Log forbidden HTTP request method at warning (#983)
* Log forbidden HTTP request method at warning

This seems like more reasonable. It will potential issues with how
requests are generated more discoverable in the log.
2021-03-03 21:02:06 -05:00
Weimin YuandGitHub e59475a5f6 Allow AppEngine deployment to qa environment (#986)
* Allow AppEngine deployment to qa environment
2021-03-03 19:31:08 -05:00
sarahcaseybotandGitHub c35f92f54b Reject handshakes with bad TLS protocols and ciphers (#970)
* Reject handshakes with bad TLS protocols and ciphers

* Fix protocols

* make cipher suite list static and fix tests

* Delete unnecessary line

* Add start time configuration for enforcement

* small format fix

* Add multiple ciphersuite test

* fix gradle lint

* fix indentation
2021-03-03 15:47:42 -05:00
Ben McIlwainandGitHub cd415fe846 Actually log the error in DeleteExpiredDomainsAction (#985)
* Actually log the error in DeleteExpiredDomainsAction
2021-03-03 15:21:13 -05:00
Lai JiangandGitHub 4ca2c11b20 Use fanout action to run ICANN report upload job in cron (#984)
GAE cron only issuse HTTP GET requests to the endpoint in question. This
particular only allows POSTs. As a result this cron job never succeeded.
This is not a big problem as this job is meant to catch up any
unforeseen upload failures and in case it needs to catch up but fails,
every month the staging job (which is enqueued corrected by cron) will
eventually catch everything to date.
2021-03-03 09:00:34 -05:00
Lai JiangandGitHub 4e44a98139 Update a few plugins for Java 11 compatibility (#966)
* Update a few plugins for Java 11 compatibility

Guice 5.0.1 is now compatible with Java 11. However we don't
directly depend on Guice. Rather Soy depends on Guice. So I added a
direct dependency on Guice 5.0 just before Soy in order to frontload Soy
and pull in the newer version.

Mockito 3.7.7 is now compatible with Java 11. The complication is that
we need to use the inline version of Mockito, which among other things
also allows mocking for final classes (hooray!). It will eventually
become the default Mockito mock maker but for now it needs to be
manually activated.

Note that the inline version now introduces another warning:

```
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
```

Which I think is WAI due to how the inline mock maker works. Waiting on
the author to confirm.

After these to changes the only illegal reflective access is caused by
App Engine SDK tools, which we will rid ourselves of when we migrate off
of GAE.

* Restore package-lock.json
2021-03-02 21:01:30 -05:00
gbrodmanandGitHub d4371a880e Add GetPremiumListCommand (#972)
* Add GetPremiumListCommand

When testing the premium list refactor, it would have been nice and
convenient to have this. Currently we have no way of inspecting premium
list contents that I'm aware of.
2021-03-02 19:59:12 -05:00
gbrodmanandGitHub 7312bc9e60 Add SQL searching to RdapEntitySearchAction and RdapSearchActionBase (#969)
- Adds a CriteriaQueryBuilder class that allows us to build
CriteriaQuery objects with sane and modular WHERE and ORDER BY clauses.
CriteriaQuery requires that all WHERE and ORDER BY clauses be specified
at the same time (else later ones will overwrite the earlier ones) so in
order to have a proper builder pattern we need to wait to build the
query object until we are done adding clauses.

- In addition, encapsulating the query logic in the CriteriaQueryBuilder
class means that we don't need to deal with the complicated Root/Path
branching, otherwise we'd have to keep track of CriteriaQuery and Root
objects everywhere.

- Added a REPLAYED_ENTITIES TransitionId that will represent all
replayed entities, e.g. EppResources. Also sets this, by default, to
always be CLOUD_SQL if we're using the SQL transaction manager in tests.

- Added branching logic in RdapEntitySearchAction based on that transition
ID that determines whether we do the existing ofy query logic or JPA
logic.
2021-03-02 13:13:55 -05:00
Michael MullerandGitHub 83ca9d82df Add replay & compare to ContactCreateFlowTest test (#976)
* Add replay & compare to ContactCreateFlowTest test

This test exposed a write priority issue between ContactResource and
ContactHistory entries.
2021-03-02 11:50:03 -05:00
Lai JiangandGitHub 8724ef6c70 Upgrade to Gradle 6.8.3 (#979)
* Upgrade to Gradle 6.8.3
2021-03-01 21:11:06 -05:00
96 changed files with 1849 additions and 328 deletions
+2 -2
View File
@@ -121,7 +121,7 @@ task stage {
// App-engine environment configuration. We set up all of the variables in
// the root project.
def environments = ['production', 'sandbox', 'alpha', 'crash']
def environments = ['production', 'sandbox', 'alpha', 'crash', 'qa']
def gcpProject = null
@@ -155,7 +155,7 @@ def verifyDeploymentParams() {
System.err.println('-----------------------------------------------------------------')
throw new GradleException('Aborting. See prominent error above.')
} else if (gcpProject == null) {
def error = 'You must specify -P environment={alpha,crash}'
def error = 'You must specify -P environment={alpha,crash,qa}'
System.err.println("\033[33;1m${error}\033[0m")
throw GradleException("Aborting: ${error}")
}
+4 -3
View File
@@ -71,12 +71,13 @@ dependencies {
def deps = dependencyMap
compile deps['com.google.auth:google-auth-library-credentials']
compile deps['com.google.auth:google-auth-library-oauth2-http']
compile deps['com.google.cloud:google-cloud-core']
compile deps['com.google.guava:guava']
compile deps['com.google.auto.value:auto-value-annotations']
compile deps['com.google.cloud:google-cloud-core']
compile deps['com.google.cloud:google-cloud-storage']
compile deps['org.apache.commons:commons-text']
compile deps['com.google.guava:guava']
compile deps['com.google.protobuf:protobuf-java']
compile deps['com.google.template:soy']
compile deps['org.apache.commons:commons-text']
annotationProcessor deps['com.google.auto.value:auto-value']
testCompile deps['com.google.truth:truth']
testCompile deps['com.google.truth.extensions:truth-java8-extension']
@@ -33,7 +33,7 @@ com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.27.0
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
com.google.protobuf:protobuf-java:3.13.0
com.google.template:soy:2018-03-14
com.ibm.icu:icu4j:57.1
commons-codec:commons-codec:1.11
@@ -33,7 +33,7 @@ com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.27.0
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
com.google.protobuf:protobuf-java:3.13.0
com.google.template:soy:2018-03-14
com.ibm.icu:icu4j:57.1
commons-codec:commons-codec:1.11
@@ -33,7 +33,7 @@ com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.27.0
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
com.google.protobuf:protobuf-java:3.13.0
com.google.template:soy:2018-03-14
com.ibm.icu:icu4j:57.1
commons-codec:commons-codec:1.11
@@ -33,7 +33,7 @@ com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.27.0
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
com.google.protobuf:protobuf-java:3.13.0
com.google.template:soy:2018-03-14
com.ibm.icu:icu4j:57.1
commons-codec:commons-codec:1.11
@@ -33,7 +33,7 @@ com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.27.0
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
com.google.protobuf:protobuf-java:3.13.0
com.google.template:soy:2018-03-14
com.ibm.icu:icu4j:57.1
commons-codec:commons-codec:1.11
@@ -33,7 +33,7 @@ com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.27.0
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
com.google.protobuf:protobuf-java:3.13.0
com.google.template:soy:2018-03-14
com.google.truth.extensions:truth-java8-extension:1.0
com.google.truth:truth:1.0
@@ -50,8 +50,8 @@ javax.inject:javax.inject:1
javax.validation:validation-api:1.0.0.GA
joda-time:joda-time:2.9.2
junit:junit:4.12
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.5
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
org.apache.commons:commons-lang3:3.8.1
org.apache.commons:commons-text:1.6
org.apache.httpcomponents:httpclient:4.5.8
@@ -66,8 +66,8 @@ org.junit.jupiter:junit-jupiter-engine:5.6.2
org.junit.platform:junit-platform-commons:1.6.2
org.junit.platform:junit-platform-engine:1.6.2
org.junit:junit-bom:5.6.2
org.mockito:mockito-core:3.3.3
org.objenesis:objenesis:2.6
org.mockito:mockito-core:3.7.7
org.objenesis:objenesis:3.1
org.opentest4j:opentest4j:1.2.0
org.ow2.asm:asm-analysis:6.0
org.ow2.asm:asm-commons:6.0
@@ -33,7 +33,7 @@ com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.27.0
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
com.google.protobuf:protobuf-java:3.13.0
com.google.template:soy:2018-03-14
com.google.truth.extensions:truth-java8-extension:1.0
com.google.truth:truth:1.0
@@ -50,8 +50,8 @@ javax.inject:javax.inject:1
javax.validation:validation-api:1.0.0.GA
joda-time:joda-time:2.9.2
junit:junit:4.12
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.5
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
org.apache.commons:commons-lang3:3.8.1
org.apache.commons:commons-text:1.6
org.apache.httpcomponents:httpclient:4.5.8
@@ -66,8 +66,8 @@ org.junit.jupiter:junit-jupiter-engine:5.6.2
org.junit.platform:junit-platform-commons:1.6.2
org.junit.platform:junit-platform-engine:1.6.2
org.junit:junit-bom:5.6.2
org.mockito:mockito-core:3.3.3
org.objenesis:objenesis:2.6
org.mockito:mockito-core:3.7.7
org.objenesis:objenesis:3.1
org.opentest4j:opentest4j:1.2.0
org.ow2.asm:asm-analysis:6.0
org.ow2.asm:asm-commons:6.0
@@ -33,7 +33,7 @@ com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.27.0
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
com.google.protobuf:protobuf-java:3.13.0
com.google.template:soy:2018-03-14
com.google.truth.extensions:truth-java8-extension:1.0
com.google.truth:truth:1.0
@@ -50,8 +50,8 @@ javax.inject:javax.inject:1
javax.validation:validation-api:1.0.0.GA
joda-time:joda-time:2.9.2
junit:junit:4.12
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.5
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
org.apache.commons:commons-lang3:3.8.1
org.apache.commons:commons-text:1.6
org.apache.httpcomponents:httpclient:4.5.8
@@ -66,8 +66,8 @@ org.junit.jupiter:junit-jupiter-engine:5.6.2
org.junit.platform:junit-platform-commons:1.6.2
org.junit.platform:junit-platform-engine:1.6.2
org.junit:junit-bom:5.6.2
org.mockito:mockito-core:3.3.3
org.objenesis:objenesis:2.6
org.mockito:mockito-core:3.7.7
org.objenesis:objenesis:3.1
org.opentest4j:opentest4j:1.2.0
org.ow2.asm:asm-analysis:6.0
org.ow2.asm:asm-commons:6.0
@@ -33,7 +33,7 @@ com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
com.google.oauth-client:google-oauth-client:1.27.0
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
com.google.protobuf:protobuf-java:3.13.0
com.google.template:soy:2018-03-14
com.google.truth.extensions:truth-java8-extension:1.0
com.google.truth:truth:1.0
@@ -50,8 +50,8 @@ javax.inject:javax.inject:1
javax.validation:validation-api:1.0.0.GA
joda-time:joda-time:2.9.2
junit:junit:4.12
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.5
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
org.apache.commons:commons-lang3:3.8.1
org.apache.commons:commons-text:1.6
org.apache.httpcomponents:httpclient:4.5.8
@@ -66,8 +66,8 @@ org.junit.jupiter:junit-jupiter-engine:5.6.2
org.junit.platform:junit-platform-commons:1.6.2
org.junit.platform:junit-platform-engine:1.6.2
org.junit:junit-bom:5.6.2
org.mockito:mockito-core:3.3.3
org.objenesis:objenesis:2.6
org.mockito:mockito-core:3.7.7
org.objenesis:objenesis:3.1
org.opentest4j:opentest4j:1.2.0
org.ow2.asm:asm-analysis:6.0
org.ow2.asm:asm-commons:6.0
@@ -0,0 +1 @@
mock-maker-inline
+2 -2
View File
@@ -111,8 +111,8 @@ PROPERTIES = [
# Cloud SQL properties
Property('dbServer',
'A registry environment name (e.g., "alpha") or a host[:port] '
'string'),
'The host[:port] of a database that accepts direct IP access. '
'This is typically used with a testing database.'),
Property('dbName',
'Database name to use in connection.',
'postgres'),
+3
View File
@@ -207,6 +207,9 @@ PRESUBMITS = {
"ForeignKeyIndex.java",
"HistoryEntryDao.java",
"JpaTransactionManagerImpl.java",
# CriteriaQueryBuilder is a false positive
"CriteriaQueryBuilder.java",
"RdapSearchActionBase.java",
},
):
"The first String parameter to EntityManager.create(Native)Query "
+2
View File
@@ -344,6 +344,8 @@ dependencies {
jaxb deps['com.sun.xml.bind:jaxb-osgi']
// Dependency needed for soy to java compilation.
soy deps['com.google.inject:guice']
soy deps['com.google.protobuf:protobuf-java']
soy deps['com.google.template:soy']
// Dependencies needed for compiling stylesheets to javascript
+9 -4
View File
@@ -3,19 +3,24 @@
# This file is expected to be part of source control.
aopalliance:aopalliance:1.0
args4j:args4j:2.0.23
com.google.code.findbugs:jsr305:2.0.3
com.google.code.findbugs:jsr305:3.0.2
com.google.code.gson:gson:2.7
com.google.common.html.types:types:1.0.4
com.google.guava:guava:21.0
com.google.errorprone:error_prone_annotations:2.3.4
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:30.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.8.0-beta1
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.protobuf:protobuf-java:3.3.0
com.google.inject:guice:5.0.1
com.google.j2objc:j2objc-annotations:1.3
com.google.protobuf:protobuf-java:3.13.0
com.google.template:soy:2018-03-14
com.ibm.icu:icu4j:57.1
javax.annotation:jsr250-api:1.0
javax.inject:javax.inject:1
javax.validation:validation-api:1.0.0.GA
org.checkerframework:checker-qual:3.5.0
org.json:json:20160212
org.ow2.asm:asm-analysis:6.0
org.ow2.asm:asm-commons:6.0
@@ -198,8 +198,8 @@ javax.xml.bind:jaxb-api:2.3.1
jline:jline:1.0
joda-time:joda-time:2.10.5
junit:junit:4.13.1
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.17
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
net.java.dev.jna:jna:5.5.0
org.apache.avro:avro:1.8.2
org.apache.beam:beam-model-fn-execution:2.27.0
@@ -273,11 +273,11 @@ org.junit.platform:junit-platform-runner:1.7.0
org.junit.platform:junit-platform-suite-api:1.7.0
org.junit:junit-bom:5.7.0
org.jvnet.staxex:stax-ex:1.8
org.mockito:mockito-core:3.3.3
org.mockito:mockito-junit-jupiter:3.3.3
org.mockito:mockito-core:3.7.7
org.mockito:mockito-junit-jupiter:3.7.7
org.mortbay.jetty:jetty-util:6.1.26
org.mortbay.jetty:jetty:6.1.26
org.objenesis:objenesis:2.6
org.objenesis:objenesis:3.1
org.opentest4j:opentest4j:1.2.0
org.ow2.asm:asm-analysis:8.0.1
org.ow2.asm:asm-commons:7.1
@@ -193,8 +193,8 @@ javax.xml.bind:jaxb-api:2.3.1
jline:jline:1.0
joda-time:joda-time:2.10.5
junit:junit:4.13.1
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.17
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
net.java.dev.jna:jna:5.5.0
org.apache.avro:avro:1.8.2
org.apache.beam:beam-model-fn-execution:2.27.0
@@ -267,11 +267,11 @@ org.junit.platform:junit-platform-runner:1.7.0
org.junit.platform:junit-platform-suite-api:1.7.0
org.junit:junit-bom:5.7.0
org.jvnet.staxex:stax-ex:1.8
org.mockito:mockito-core:3.3.3
org.mockito:mockito-junit-jupiter:3.3.3
org.mockito:mockito-core:3.7.7
org.mockito:mockito-junit-jupiter:3.7.7
org.mortbay.jetty:jetty-util:6.1.26
org.mortbay.jetty:jetty:6.1.26
org.objenesis:objenesis:2.6
org.objenesis:objenesis:3.1
org.opentest4j:opentest4j:1.2.0
org.ow2.asm:asm-analysis:8.0.1
org.ow2.asm:asm-commons:7.1
@@ -208,8 +208,8 @@ jline:jline:1.0
joda-time:joda-time:2.10.5
junit:junit:4.13.1
net.arnx:nashorn-promise:0.1.1
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.17
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
net.java.dev.jna:jna:5.5.0
org.apache.avro:avro:1.8.2
org.apache.beam:beam-model-fn-execution:2.27.0
@@ -285,11 +285,11 @@ org.junit.platform:junit-platform-runner:1.7.0
org.junit.platform:junit-platform-suite-api:1.7.0
org.junit:junit-bom:5.7.0
org.jvnet.staxex:stax-ex:1.8
org.mockito:mockito-core:3.3.3
org.mockito:mockito-junit-jupiter:3.3.3
org.mockito:mockito-core:3.7.7
org.mockito:mockito-junit-jupiter:3.7.7
org.mortbay.jetty:jetty-util:6.1.26
org.mortbay.jetty:jetty:6.1.26
org.objenesis:objenesis:2.6
org.objenesis:objenesis:3.1
org.opentest4j:opentest4j:1.2.0
org.ow2.asm:asm-analysis:8.0.1
org.ow2.asm:asm-commons:7.1
@@ -208,8 +208,8 @@ jline:jline:1.0
joda-time:joda-time:2.10.5
junit:junit:4.13.1
net.arnx:nashorn-promise:0.1.1
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.17
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
net.java.dev.jna:jna:5.5.0
org.apache.avro:avro:1.8.2
org.apache.beam:beam-model-fn-execution:2.27.0
@@ -285,11 +285,11 @@ org.junit.platform:junit-platform-runner:1.7.0
org.junit.platform:junit-platform-suite-api:1.7.0
org.junit:junit-bom:5.7.0
org.jvnet.staxex:stax-ex:1.8
org.mockito:mockito-core:3.3.3
org.mockito:mockito-junit-jupiter:3.3.3
org.mockito:mockito-core:3.7.7
org.mockito:mockito-junit-jupiter:3.7.7
org.mortbay.jetty:jetty-util:6.1.26
org.mortbay.jetty:jetty:6.1.26
org.objenesis:objenesis:2.6
org.objenesis:objenesis:3.1
org.opentest4j:opentest4j:1.2.0
org.ow2.asm:asm-analysis:8.0.1
org.ow2.asm:asm-commons:7.1
@@ -104,8 +104,9 @@ public class DeleteExpiredDomainsAction implements Runnable {
runLocked();
response.setStatus(SC_OK);
} catch (Exception e) {
logger.atSevere().withCause(e).log("Errored out during execution.");
response.setStatus(SC_INTERNAL_SERVER_ERROR);
response.setPayload("Encountered error; see GCP logs for full details.");
response.setPayload(String.format("Errored out with cause: %s", e));
}
return null;
};
@@ -280,7 +280,7 @@
</cron>
<cron>
<url><![CDATA[/_dr/task/icannReportingUpload]]></url>
<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.
@@ -31,6 +31,7 @@ import com.google.common.flogger.FluentLogger;
import com.google.common.net.MediaType;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.registry.Registry;
import google.registry.model.registry.label.PremiumList.PremiumListEntry;
import google.registry.model.registry.label.PremiumListDualDao;
import google.registry.request.Action;
import google.registry.request.Parameter;
@@ -141,7 +142,7 @@ public class ExportPremiumTermsAction implements Runnable {
PremiumListDualDao.exists(premiumListName), "Could not load premium list for " + tld);
SortedSet<String> premiumTerms =
Streams.stream(PremiumListDualDao.loadAllPremiumListEntries(premiumListName))
.map(entry -> Joiner.on(",").join(entry.getLabel(), entry.getValue()))
.map(PremiumListEntry::toString)
.collect(ImmutableSortedSet.toImmutableSortedSet(String::compareTo));
return Joiner.on("\n")
@@ -52,6 +52,7 @@ import google.registry.model.tmch.ClaimsListShard;
import google.registry.model.tmch.ClaimsListShard.ClaimsListRevision;
import google.registry.model.tmch.ClaimsListShard.ClaimsListSingleton;
import google.registry.model.tmch.TmchCrl;
import google.registry.schema.replay.LastSqlTransaction;
/** Sets of classes of the Objectify-registered entities in use throughout the model. */
public final class EntityClasses {
@@ -90,6 +91,7 @@ public final class EntityClasses {
HostResource.class,
KmsSecret.class,
KmsSecretRevision.class,
LastSqlTransaction.class,
Lock.class,
PollMessage.class,
PollMessage.Autorenew.class,
@@ -62,6 +62,8 @@ public class DatabaseTransitionSchedule extends ImmutableObject implements Datas
DOMAIN_LABEL_LISTS,
/** The schedule for the migration of the {@link SignedMarkRevocationList} entity. */
SIGNED_MARK_REVOCATION_LIST,
/** The schedule for all asynchronously-replayed entities, ones not dually-written. */
REPLAYED_ENTITIES,
}
/**
@@ -41,9 +41,9 @@ public class EntityWritePriorities {
*/
static final ImmutableMap<String, Integer> CLASS_PRIORITIES =
ImmutableMap.of(
"ContactResource", -15,
"HistoryEntry", -10,
"AllocationToken", -9,
"ContactResource", 5,
"DomainBase", 10);
// The beginning of the range of priority numbers reserved for delete. This must be greater than
@@ -116,7 +116,7 @@ public class Ofy {
return ofy().getTransaction() != null;
}
void assertInTransaction() {
public void assertInTransaction() {
checkState(inTransaction(), "Must be called in a transaction");
}
@@ -208,6 +208,12 @@ public final class PremiumList extends BaseDomainLabelList<Money, PremiumList.Pr
return price;
}
@Override
public String toString() {
// Don't include the comment so that we can use this when exporting the premium list
return String.format("%s,%s", label, price);
}
@Override
public Builder asBuilder() {
return new Builder(clone(this));
@@ -0,0 +1,97 @@
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.persistence.transaction;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
/**
* An extension of {@link CriteriaQuery} that uses a Builder-style pattern when adding "WHERE"
* and/or "ORDER BY" clauses.
*
* <p>{@link CriteriaQuery}, as is, requires that all clauses must be passed in at once -- if one
* calls "WHERE" multiple times, the later call overwrites the earlier call.
*/
public class CriteriaQueryBuilder<T> {
/** Functional interface that defines the 'where' operator, e.g. {@link CriteriaBuilder#equal}. */
public interface WhereClause<U> {
Predicate predicate(Expression<U> expression, U object);
}
/** Functional interface that defines the order-by operator, e.g. {@link CriteriaBuilder#asc}. */
public interface OrderByClause<U> {
Order order(Expression<U> expression);
}
private final CriteriaQuery<T> query;
private final Root<T> root;
private final ImmutableList.Builder<Predicate> predicates = new ImmutableList.Builder<>();
private final ImmutableList.Builder<Order> orders = new ImmutableList.Builder<>();
private CriteriaQueryBuilder(CriteriaQuery<T> query, Root<T> root) {
this.query = query;
this.root = root;
}
/** Adds a WHERE clause to the query, given the specified operation, field, and value. */
public <V> CriteriaQueryBuilder<T> where(WhereClause<V> whereClause, String fieldName, V value) {
Expression<V> expression = root.get(fieldName);
return where(whereClause.predicate(expression, value));
}
/** Adds a WHERE clause to the query specifying that a value must be in the given collection. */
public CriteriaQueryBuilder<T> whereFieldIsIn(String fieldName, Collection<?> values) {
return where(root.get(fieldName).in(values));
}
/** Orders the result by the given operation applied to the given field. */
public <U> CriteriaQueryBuilder<T> orderBy(OrderByClause<U> orderByClause, String fieldName) {
Expression<U> expression = root.get(fieldName);
return orderBy(orderByClause.order(expression));
}
/** Builds and returns the query, applying all WHERE and ORDER BY clauses at once. */
public CriteriaQuery<T> build() {
Predicate[] predicateArray = predicates.build().toArray(new Predicate[0]);
return query.where(predicateArray).orderBy(orders.build());
}
private CriteriaQueryBuilder<T> where(Predicate predicate) {
predicates.add(predicate);
return this;
}
private CriteriaQueryBuilder<T> orderBy(Order order) {
orders.add(order);
return this;
}
/** Creates a query builder that will SELECT from the given class. */
public static <T> CriteriaQueryBuilder<T> create(Class<T> clazz) {
CriteriaQuery<T> query = jpaTm().getEntityManager().getCriteriaBuilder().createQuery(clazz);
Root<T> root = query.from(clazz);
query = query.select(root);
return new CriteriaQueryBuilder<>(query, root);
}
}
@@ -86,7 +86,7 @@ public class Transaction extends ImmutableObject implements Buildable {
}
}
static Transaction deserialize(byte[] serializedTransaction) throws IOException {
public static Transaction deserialize(byte[] serializedTransaction) throws IOException {
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(serializedTransaction));
// Verify that the data is what we expect.
@@ -34,9 +34,9 @@ public class TransactionEntity implements SqlEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
long id;
private long id;
byte[] contents;
private byte[] contents;
TransactionEntity() {}
@@ -48,4 +48,12 @@ public class TransactionEntity implements SqlEntity {
public Optional<DatastoreEntity> toDatastoreEntity() {
return Optional.empty(); // Not persisted in Datastore per se
}
public long getId() {
return id;
}
public byte[] getContents() {
return contents;
}
}
@@ -17,6 +17,7 @@ package google.registry.rdap;
import static com.google.common.base.Charsets.UTF_8;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.net.HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.request.Actions.getPathForAction;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
@@ -28,7 +29,10 @@ import com.google.common.net.MediaType;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.DatabaseMigrationUtils;
import google.registry.model.EppResource;
import google.registry.model.common.DatabaseTransitionSchedule.PrimaryDatabase;
import google.registry.model.common.DatabaseTransitionSchedule.TransitionId;
import google.registry.model.registrar.Registrar;
import google.registry.rdap.RdapMetrics.EndpointType;
import google.registry.rdap.RdapObjectClasses.ErrorResponse;
@@ -256,4 +260,11 @@ public abstract class RdapActionBase implements Runnable {
DateTime getRequestTime() {
return rdapJsonFormatter.getRequestTime();
}
static boolean isDatastore() {
return tm().transact(
() ->
DatabaseMigrationUtils.getPrimaryDatabase(TransitionId.REPLAYED_ENTITIES)
.equals(PrimaryDatabase.DATASTORE));
}
}
@@ -15,7 +15,9 @@
package google.registry.rdap;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
import static google.registry.rdap.RdapUtils.getRegistrarByIanaIdentifier;
import static google.registry.request.Action.Method.GET;
import static google.registry.request.Action.Method.HEAD;
@@ -29,6 +31,9 @@ import com.google.common.primitives.Longs;
import com.googlecode.objectify.cmd.Query;
import google.registry.model.contact.ContactResource;
import google.registry.model.registrar.Registrar;
import google.registry.persistence.VKey;
import google.registry.persistence.transaction.CriteriaQueryBuilder;
import google.registry.rdap.RdapAuthorization.Role;
import google.registry.rdap.RdapJsonFormatter.OutputDataType;
import google.registry.rdap.RdapMetrics.EndpointType;
import google.registry.rdap.RdapMetrics.SearchType;
@@ -112,7 +117,6 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
/** Parses the parameters and calls the appropriate search function. */
@Override
public EntitySearchResponse getSearchResponse(boolean isHeadRequest) {
// RDAP syntax example: /rdap/entities?fn=Bobby%20Joe*.
if (Booleans.countTrue(fnParam.isPresent(), handleParam.isPresent()) != 1) {
throw new BadRequestException("You must specify either fn=XXXX or handle=YYYY");
@@ -214,9 +218,7 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
// Don't allow wildcard suffixes when searching for entities.
if (partialStringQuery.getHasWildcard() && (partialStringQuery.getSuffix() != null)) {
throw new UnprocessableEntityException(
partialStringQuery.getHasWildcard()
? "Suffixes not allowed in wildcard entity name searches"
: "Suffixes not allowed when searching for deleted entities");
"Suffixes not allowed in wildcard entity name searches");
}
// For wildcards, make sure the initial string is long enough, except in the special case of
// searching for all registrars, where we aren't worried about inefficient searches.
@@ -225,9 +227,7 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
&& (partialStringQuery.getInitialString().length()
< RdapSearchPattern.MIN_INITIAL_STRING_LENGTH)) {
throw new UnprocessableEntityException(
partialStringQuery.getHasWildcard()
? "Initial search string required in wildcard entity name searches"
: "Initial search string required when searching for deleted entities");
"Initial search string required in wildcard entity name searches");
}
// Get the registrar matches. If we have a registrar cursor, weed out registrars up to and
// including the one we ended with last time. We can skip registrars if subtype is CONTACTS.
@@ -262,18 +262,39 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
|| (cursorType == CursorType.REGISTRAR)) {
resultSet = RdapResultSet.create(ImmutableList.of());
} else {
Query<ContactResource> query =
queryItems(
ContactResource.class,
"searchName",
partialStringQuery,
cursorQueryString, // if we get this far, and there's a cursor, it must be a contact
DeletedItemHandling.EXCLUDE,
rdapResultSetMaxSize + 1);
if (rdapAuthorization.role() != RdapAuthorization.Role.ADMINISTRATOR) {
query = query.filter("currentSponsorClientId in", rdapAuthorization.clientIds());
if (isDatastore()) {
Query<ContactResource> query =
queryItems(
ContactResource.class,
"searchName",
partialStringQuery,
cursorQueryString, // if we get here and there's a cursor, it must be a contact
DeletedItemHandling.EXCLUDE,
rdapResultSetMaxSize + 1);
if (!rdapAuthorization.role().equals(Role.ADMINISTRATOR)) {
query = query.filter("currentSponsorClientId in", rdapAuthorization.clientIds());
}
resultSet = getMatchingResources(query, false, rdapResultSetMaxSize + 1);
} else {
resultSet =
jpaTm()
.transact(
() -> {
CriteriaQueryBuilder<ContactResource> builder =
queryItemsSql(
ContactResource.class,
"searchName",
partialStringQuery,
cursorQueryString,
DeletedItemHandling.EXCLUDE);
if (!rdapAuthorization.role().equals(Role.ADMINISTRATOR)) {
builder =
builder.whereFieldIsIn(
"currentSponsorClientId", rdapAuthorization.clientIds());
}
return getMatchingResourcesSql(builder, false, rdapResultSetMaxSize + 1);
});
}
resultSet = getMatchingResources(query, false, rdapResultSetMaxSize + 1);
}
}
return makeSearchResults(resultSet, registrars, QueryType.FULL_NAME);
@@ -303,15 +324,15 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
if (subtype == Subtype.REGISTRARS) {
contactResourceList = ImmutableList.of();
} else {
ContactResource contactResource =
ofy()
.load()
.type(ContactResource.class)
.id(partialStringQuery.getInitialString())
.now();
Optional<ContactResource> contactResource =
transactIfJpaTm(
() ->
tm().loadByKeyIfPresent(
VKey.create(
ContactResource.class, partialStringQuery.getInitialString())));
contactResourceList =
((contactResource != null) && shouldBeVisible(contactResource))
? ImmutableList.of(contactResource)
(contactResource.isPresent() && shouldBeVisible(contactResource.get()))
? ImmutableList.of(contactResource.get())
: ImmutableList.of();
}
ImmutableList<Registrar> registrarList;
@@ -365,16 +386,31 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
if (subtype == Subtype.REGISTRARS) {
contactResultSet = RdapResultSet.create(ImmutableList.of());
} else {
contactResultSet =
getMatchingResources(
queryItemsByKey(
ContactResource.class,
partialStringQuery,
cursorQueryString,
getDeletedItemHandling(),
querySizeLimit),
shouldIncludeDeleted(),
querySizeLimit);
if (isDatastore()) {
contactResultSet =
getMatchingResources(
queryItemsByKey(
ContactResource.class,
partialStringQuery,
cursorQueryString,
getDeletedItemHandling(),
querySizeLimit),
shouldIncludeDeleted(),
querySizeLimit);
} else {
contactResultSet =
jpaTm()
.transact(
() ->
getMatchingResourcesSql(
queryItemsByKeySql(
ContactResource.class,
partialStringQuery,
cursorQueryString,
getDeletedItemHandling()),
shouldIncludeDeleted(),
querySizeLimit));
}
}
return makeSearchResults(contactResultSet, registrars, QueryType.HANDLE);
}
@@ -16,6 +16,7 @@ package google.registry.rdap;
import static com.google.common.base.Charsets.UTF_8;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.collect.ImmutableList;
@@ -24,6 +25,7 @@ import com.googlecode.objectify.Key;
import com.googlecode.objectify.cmd.Query;
import google.registry.model.EppResource;
import google.registry.model.registrar.Registrar;
import google.registry.persistence.transaction.CriteriaQueryBuilder;
import google.registry.rdap.RdapMetrics.EndpointType;
import google.registry.rdap.RdapMetrics.WildcardType;
import google.registry.rdap.RdapSearchResults.BaseSearchResponse;
@@ -40,8 +42,10 @@ import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import javax.inject.Inject;
import javax.persistence.criteria.CriteriaBuilder;
/**
* Base RDAP (new WHOIS) action for domain, nameserver and entity search requests.
@@ -161,14 +165,63 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
if (desiredRegistrar.isPresent()) {
query = query.filter("currentSponsorClientId", desiredRegistrar.get());
}
if (!checkForVisibility) {
return RdapResultSet.create(query.list());
List<T> queryResult = query.list();
if (checkForVisibility) {
return filterResourcesByVisibility(queryResult, querySizeLimit);
} else {
return RdapResultSet.create(queryResult);
}
}
/**
* In Cloud SQL, builds and runs the given query, and checks for permissioning if necessary.
*
* @param builder a query builder that represents the various SELECT FROM, WHERE, ORDER BY and
* (etc) clauses that make up this SQL query
* @param checkForVisibility true if the results should be checked to make sure they are visible;
* normally this should be equal to the shouldIncludeDeleted setting, but in cases where the
* query could not check deletion status (due to Datastore limitations such as the limit of
* one field queried for inequality, for instance), it may need to be set to true even when
* not including deleted records
* @param querySizeLimit the maximum number of items the query is expected to return, usually
* because the limit has been set
* @return an {@link RdapResultSet} object containing the list of resources and an incompleteness
* warning flag, which is set to MIGHT_BE_INCOMPLETE iff any resources were excluded due to
* lack of visibility, and the resulting list of resources is less than the maximum allowable,
* and the number of items returned by the query is greater than or equal to the maximum
* number we might have expected
*/
<T extends EppResource> RdapResultSet<T> getMatchingResourcesSql(
CriteriaQueryBuilder<T> builder, boolean checkForVisibility, int querySizeLimit) {
jpaTm().assertInTransaction();
Optional<String> desiredRegistrar = getDesiredRegistrar();
if (desiredRegistrar.isPresent()) {
builder =
builder.where(
jpaTm().getEntityManager().getCriteriaBuilder()::equal,
"currentSponsorClientId",
desiredRegistrar.get());
}
List<T> queryResult =
jpaTm()
.getEntityManager()
.createQuery(builder.build())
.setMaxResults(querySizeLimit)
.getResultList();
if (checkForVisibility) {
return filterResourcesByVisibility(queryResult, querySizeLimit);
} else {
return RdapResultSet.create(queryResult);
}
}
private <T extends EppResource> RdapResultSet<T> filterResourcesByVisibility(
List<T> queryResult, int querySizeLimit) {
// If we are including deleted resources, we need to check that we're authorized for each one.
List<T> resources = new ArrayList<>();
int numResourcesQueried = 0;
boolean someExcluded = false;
for (T resource : query) {
for (T resource : queryResult) {
if (shouldBeVisible(resource)) {
resources.add(resource);
} else {
@@ -268,8 +321,8 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
// to be (more) sure we got everything.
int getStandardQuerySizeLimit() {
return shouldIncludeDeleted()
? (RESULT_SET_SIZE_SCALING_FACTOR * (rdapResultSetMaxSize + 1))
: (rdapResultSetMaxSize + 1);
? (RESULT_SET_SIZE_SCALING_FACTOR * (rdapResultSetMaxSize + 1))
: (rdapResultSetMaxSize + 1);
}
/**
@@ -311,9 +364,10 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
query = query.filter(filterField, partialStringQuery.getInitialString());
} else {
// Ignore the suffix; the caller will need to filter on the suffix, if any.
query = query
.filter(filterField + " >=", partialStringQuery.getInitialString())
.filter(filterField + " <", partialStringQuery.getNextInitialString());
query =
query
.filter(filterField + " >=", partialStringQuery.getInitialString())
.filter(filterField + " <", partialStringQuery.getNextInitialString());
}
if (cursorString.isPresent()) {
query = query.filter(filterField + " >", cursorString.get());
@@ -321,6 +375,59 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
return setOtherQueryAttributes(query, deletedItemHandling, resultSetMaxSize);
}
/**
* In Cloud SQL, handles prefix searches in cases where, if we need to filter out deleted items,
* there are no pending deletes.
*
* <p>In such cases, it is sufficient to check whether {@code deletionTime} is equal to {@code
* END_OF_TIME}, because any other value means it has already been deleted. This allows us to use
* an equality query for the deletion time.
*
* @param clazz the type of resource to be queried
* @param filterField the database field of interest
* @param partialStringQuery the details of the search string; if there is no wildcard, an
* equality query is used; if there is a wildcard, a range query is used instead; the initial
* string should not be empty, and any search suffix will be ignored, so the caller must
* filter the results if a suffix is specified
* @param cursorString if a cursor is present, this parameter should specify the cursor string, to
* skip any results up to and including the string; empty() if there is no cursor
* @param deletedItemHandling whether to include or exclude deleted items
* @return a {@link CriteriaQueryBuilder} object representing the query so far
*/
static <T extends EppResource> CriteriaQueryBuilder<T> queryItemsSql(
Class<T> clazz,
String filterField,
RdapSearchPattern partialStringQuery,
Optional<String> cursorString,
DeletedItemHandling deletedItemHandling) {
jpaTm().assertInTransaction();
if (partialStringQuery.getInitialString().length()
< RdapSearchPattern.MIN_INITIAL_STRING_LENGTH) {
throw new UnprocessableEntityException(
String.format(
"Initial search string must be at least %d characters",
RdapSearchPattern.MIN_INITIAL_STRING_LENGTH));
}
CriteriaBuilder criteriaBuilder = jpaTm().getEntityManager().getCriteriaBuilder();
CriteriaQueryBuilder<T> builder = CriteriaQueryBuilder.create(clazz);
if (partialStringQuery.getHasWildcard()) {
builder =
builder.where(
criteriaBuilder::like,
filterField,
String.format("%s%%", partialStringQuery.getInitialString()));
} else {
// no wildcard means we use a standard equals query
builder =
builder.where(criteriaBuilder::equal, filterField, partialStringQuery.getInitialString());
}
if (cursorString.isPresent()) {
builder = builder.where(criteriaBuilder::greaterThan, filterField, cursorString.get());
}
builder = builder.orderBy(criteriaBuilder::asc, filterField);
return setDeletedItemHandlingSql(builder, deletedItemHandling);
}
/**
* Handles searches using a simple string rather than an {@link RdapSearchPattern}.
*
@@ -331,9 +438,9 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
* @param filterField the database field of interest
* @param queryString the search string
* @param cursorField the field which should be compared to the cursor string, or empty() if the
* key should be compared to a key created from the cursor string
* key should be compared to a key created from the cursor string
* @param cursorString if a cursor is present, this parameter should specify the cursor string, to
* skip any results up to and including the string; empty() if there is no cursor
* skip any results up to and including the string; empty() if there is no cursor
* @param deletedItemHandling whether to include or exclude deleted items
* @param resultSetMaxSize the maximum number of results to return
* @return the query object
@@ -363,6 +470,50 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
return setOtherQueryAttributes(query, deletedItemHandling, resultSetMaxSize);
}
/**
* In Cloud SQL, handles searches using a simple string rather than an {@link RdapSearchPattern}.
*
* <p>Since the filter is not an inequality, we can support also checking a cursor string against
* a different field (which involves an inequality on that field).
*
* @param clazz the type of resource to be queried
* @param filterField the database field of interest
* @param queryString the search string
* @param cursorField the field which should be compared to the cursor string, or empty() if the
* key should be compared to a key created from the cursor string
* @param cursorString if a cursor is present, this parameter should specify the cursor string, to
* skip any results up to and including the string; empty() if there is no cursor
* @param deletedItemHandling whether to include or exclude deleted items
* @return a {@link CriteriaQueryBuilder} object representing the query so far
*/
static <T extends EppResource> CriteriaQueryBuilder<T> queryItemsSql(
Class<T> clazz,
String filterField,
String queryString,
Optional<String> cursorField,
Optional<String> cursorString,
DeletedItemHandling deletedItemHandling) {
if (queryString.length() < RdapSearchPattern.MIN_INITIAL_STRING_LENGTH) {
throw new UnprocessableEntityException(
String.format(
"Initial search string must be at least %d characters",
RdapSearchPattern.MIN_INITIAL_STRING_LENGTH));
}
jpaTm().assertInTransaction();
CriteriaQueryBuilder<T> builder = CriteriaQueryBuilder.create(clazz);
CriteriaBuilder criteriaBuilder = jpaTm().getEntityManager().getCriteriaBuilder();
builder = builder.where(criteriaBuilder::equal, filterField, queryString);
if (cursorString.isPresent()) {
if (cursorField.isPresent()) {
builder =
builder.where(criteriaBuilder::greaterThan, cursorField.get(), cursorString.get());
} else {
builder = builder.where(criteriaBuilder::greaterThan, "repoId", cursorString.get());
}
}
return setDeletedItemHandlingSql(builder, deletedItemHandling);
}
/** Handles searches where the field to be searched is the key. */
static <T extends EppResource> Query<T> queryItemsByKey(
Class<T> clazz,
@@ -382,9 +533,10 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
query = query.filterKey("=", Key.create(clazz, partialStringQuery.getInitialString()));
} else {
// Ignore the suffix; the caller will need to filter on the suffix, if any.
query = query
.filterKey(">=", Key.create(clazz, partialStringQuery.getInitialString()))
.filterKey("<", Key.create(clazz, partialStringQuery.getNextInitialString()));
query =
query
.filterKey(">=", Key.create(clazz, partialStringQuery.getInitialString()))
.filterKey("<", Key.create(clazz, partialStringQuery.getNextInitialString()));
}
if (cursorString.isPresent()) {
query = query.filterKey(">", Key.create(clazz, cursorString.get()));
@@ -392,6 +544,26 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
return setOtherQueryAttributes(query, deletedItemHandling, resultSetMaxSize);
}
/** In Cloud SQL, handles searches where the field to be searched is the key. */
static <T extends EppResource> CriteriaQueryBuilder<T> queryItemsByKeySql(
Class<T> clazz,
RdapSearchPattern partialStringQuery,
Optional<String> cursorString,
DeletedItemHandling deletedItemHandling) {
jpaTm().assertInTransaction();
return queryItemsSql(clazz, "repoId", partialStringQuery, cursorString, deletedItemHandling);
}
static <T extends EppResource> CriteriaQueryBuilder<T> setDeletedItemHandlingSql(
CriteriaQueryBuilder<T> builder, DeletedItemHandling deletedItemHandling) {
if (!Objects.equals(deletedItemHandling, DeletedItemHandling.INCLUDE)) {
builder =
builder.where(
jpaTm().getEntityManager().getCriteriaBuilder()::equal, "deletionTime", END_OF_TIME);
}
return builder;
}
static <T extends EppResource> Query<T> setOtherQueryAttributes(
Query<T> query, DeletedItemHandling deletedItemHandling, int resultSetMaxSize) {
if (deletedItemHandling != DeletedItemHandling.INCLUDE) {
@@ -135,7 +135,7 @@ public class RequestHandler<C> {
return;
}
if (!route.get().isMethodAllowed(method)) {
logger.atInfo().log("Method %s not allowed for: %s", method, path);
logger.atWarning().log("Method %s not allowed for: %s", method, path);
rsp.sendError(SC_METHOD_NOT_ALLOWED);
return;
}
@@ -0,0 +1,71 @@
// Copyright 2021 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.schema.replay;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.model.ofy.ObjectifyService.ofy;
import com.google.common.annotations.VisibleForTesting;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import google.registry.model.ImmutableObject;
/** Datastore entity to keep track of the last SQL transaction imported into the datastore. */
@Entity
public class LastSqlTransaction extends ImmutableObject implements DatastoreOnlyEntity {
/** The key for this singleton. */
public static final Key<LastSqlTransaction> KEY = Key.create(LastSqlTransaction.class, 1);
@SuppressWarnings("unused")
@Id
private long id = 1;
private long transactionId;
LastSqlTransaction() {}
@VisibleForTesting
LastSqlTransaction(long newTransactionId) {
transactionId = newTransactionId;
}
LastSqlTransaction cloneWithNewTransactionId(long transactionId) {
checkArgument(
transactionId > this.transactionId,
"New transaction id (%s) must be greater than the current transaction id (%s)",
transactionId,
this.transactionId);
return new LastSqlTransaction(transactionId);
}
long getTransactionId() {
return transactionId;
}
/**
* Loads the instance.
*
* <p>Must be called within an Ofy transaction.
*
* <p>Creates a new instance of the singleton if it is not already present in Cloud Datastore,
*/
static LastSqlTransaction load() {
ofy().assertInTransaction();
LastSqlTransaction result = ofy().load().key(KEY).now();
return result == null ? new LastSqlTransaction() : result;
}
}
@@ -0,0 +1,136 @@
// Copyright 2021 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.schema.replay;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
import static google.registry.request.Action.Method.GET;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
import google.registry.persistence.transaction.Transaction;
import google.registry.persistence.transaction.TransactionEntity;
import google.registry.request.Action;
import google.registry.request.auth.Auth;
import java.io.IOException;
import java.util.List;
import javax.persistence.NoResultException;
/** Cron task to replicate from Cloud SQL to datastore. */
@Action(
service = Action.Service.BACKEND,
path = ReplicateToDatastoreAction.PATH,
method = GET,
automaticallyPrintOk = true,
auth = Auth.AUTH_INTERNAL_OR_ADMIN)
class ReplicateToDatastoreAction implements Runnable {
public static final String PATH = "/_dr/cron/replicateToDatastore";
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
/**
* Number of transactions to fetch from SQL. The rationale for 200 is that we're processing these
* every minute and our production instance currently does about 2 mutations per second, so this
* should generally be enough to scoop up all of the transactions for the past minute.
*/
public static final int BATCH_SIZE = 200;
@VisibleForTesting
List<TransactionEntity> getTransactionBatch() {
// Get the next batch of transactions that we haven't replicated.
LastSqlTransaction lastSqlTxnBeforeBatch = ofyTm().transact(() -> LastSqlTransaction.load());
try {
return jpaTm()
.transact(
() ->
jpaTm()
.getEntityManager()
.createQuery(
"SELECT txn FROM TransactionEntity txn WHERE id >"
+ " :lastId ORDER BY id")
.setParameter("lastId", lastSqlTxnBeforeBatch.getTransactionId())
.setMaxResults(BATCH_SIZE)
.getResultList());
} catch (NoResultException e) {
return ImmutableList.of();
}
}
/**
* Apply a transaction to datastore, returns true if there was a fatal error and the batch should
* be aborted.
*/
@VisibleForTesting
boolean applyTransaction(TransactionEntity txnEntity) {
logger.atInfo().log("Applying a single transaction Cloud SQL -> Cloud Datastore");
return ofyTm()
.transact(
() -> {
// Reload the last transaction id, which could possibly have changed.
LastSqlTransaction lastSqlTxn = LastSqlTransaction.load();
long nextTxnId = lastSqlTxn.getTransactionId() + 1;
if (nextTxnId < txnEntity.getId()) {
// We're missing a transaction. This is bad. Transaction ids are supposed to
// increase monotonically, so we abort rather than applying anything out of
// order.
logger.atSevere().log(
"Missing transaction: last transaction id = %s, next available transaction "
+ "= %s",
nextTxnId - 1, txnEntity.getId());
return true;
} else if (nextTxnId > txnEntity.getId()) {
// We've already replayed this transaction. This shouldn't happen, as GAE cron
// is supposed to avoid overruns and this action shouldn't be executed from any
// other context, but it's not harmful as we can just ignore the transaction. Log
// it so that we know about it and move on.
logger.atWarning().log(
"Ignoring transaction %s, which appears to have already been applied.",
txnEntity.getId());
return false;
}
logger.atInfo().log("Applying transaction %s to Cloud Datastore", txnEntity.getId());
// At this point, we know txnEntity is the correct next transaction, so write it
// to datastore.
try {
Transaction.deserialize(txnEntity.getContents()).writeToDatastore();
} catch (IOException e) {
throw new RuntimeException("Error during transaction deserialization.", e);
}
// Write the updated last transaction id to datastore as part of this datastore
// transaction.
ofy().save().entity(lastSqlTxn.cloneWithNewTransactionId(nextTxnId));
logger.atInfo().log(
"Finished applying single transaction Cloud SQL -> Cloud Datastore");
return false;
});
}
@Override
public void run() {
// TODO(b/181758163): Deal with objects that don't exist in Cloud SQL, e.g. ForeignKeyIndex,
// EppResourceIndex.
logger.atInfo().log("Processing transaction replay batch Cloud SQL -> Cloud Datastore");
for (TransactionEntity txnEntity : getTransactionBatch()) {
if (applyTransaction(txnEntity)) {
break;
}
}
logger.atInfo().log("Done processing transaction replay batch Cloud SQL -> Cloud Datastore");
}
}
@@ -0,0 +1,49 @@
// Copyright 2021 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 com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.google.common.collect.Streams;
import google.registry.model.registry.label.PremiumList.PremiumListEntry;
import google.registry.model.registry.label.PremiumListDualDao;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
/** Retrieves and prints one or more premium lists. */
@Parameters(separators = " =", commandDescription = "Show one or more premium lists")
public class GetPremiumListCommand implements CommandWithRemoteApi {
@Parameter(description = "Name(s) of the premium list(s) to retrieve", required = true)
private List<String> mainParameters;
@Override
public void run() throws Exception {
for (String premiumListName : mainParameters) {
if (PremiumListDualDao.exists(premiumListName)) {
System.out.printf(
"%s:\n%s\n",
premiumListName,
Streams.stream(PremiumListDualDao.loadAllPremiumListEntries(premiumListName))
.sorted(Comparator.comparing(PremiumListEntry::getLabel))
.map(PremiumListEntry::toString)
.collect(Collectors.joining("\n")));
} else {
System.out.println(String.format("No list found with name %s.", premiumListName));
}
}
}
}
@@ -76,6 +76,7 @@ public final class RegistryTool {
.put("get_host", GetHostCommand.class)
.put("get_keyring_secret", GetKeyringSecretCommand.class)
.put("get_operation_status", GetOperationStatusCommand.class)
.put("get_premium_list", GetPremiumListCommand.class)
.put("get_registrar", GetRegistrarCommand.class)
.put("get_resource_by_key", GetResourceByKeyCommand.class)
.put("get_routing_map", GetRoutingMapCommand.class)
@@ -25,12 +25,19 @@ import google.registry.flows.ResourceCheckFlowTestCase;
import google.registry.flows.exceptions.TooManyResourceChecksException;
import google.registry.model.contact.ContactResource;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.ReplayExtension;
import google.registry.testing.TestOfyAndSql;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link ContactCheckFlow}. */
@DualDatabaseTest
class ContactCheckFlowTest extends ResourceCheckFlowTestCase<ContactCheckFlow, ContactResource> {
@Order(value = Order.DEFAULT - 2)
@RegisterExtension
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
ContactCheckFlowTest() {
setEppInput("contact_check.xml");
}
@@ -33,13 +33,20 @@ import google.registry.flows.exceptions.ResourceAlreadyExistsForThisClientExcept
import google.registry.flows.exceptions.ResourceCreateContentionException;
import google.registry.model.contact.ContactResource;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.ReplayExtension;
import google.registry.testing.TestOfyAndSql;
import org.joda.time.DateTime;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link ContactCreateFlow}. */
@DualDatabaseTest
class ContactCreateFlowTest extends ResourceFlowTestCase<ContactCreateFlow, ContactResource> {
@Order(value = Order.DEFAULT - 2)
@RegisterExtension
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
ContactCreateFlowTest() {
setEppInput("contact_create.xml");
clock.setTo(DateTime.parse("1999-04-03T22:00:00.0Z"));
@@ -38,13 +38,20 @@ import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppcommon.Trid;
import google.registry.model.reporting.HistoryEntry;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.ReplayExtension;
import google.registry.testing.TestOfyAndSql;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link ContactDeleteFlow}. */
@DualDatabaseTest
class ContactDeleteFlowTest extends ResourceFlowTestCase<ContactDeleteFlow, ContactResource> {
@Order(value = Order.DEFAULT - 2)
@RegisterExtension
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
@BeforeEach
void initFlowTest() {
setEppInput("contact_delete.xml");
@@ -39,13 +39,20 @@ import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
import google.registry.model.eppcommon.PresenceMarker;
import google.registry.model.eppcommon.StatusValue;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.ReplayExtension;
import google.registry.testing.TestOfyAndSql;
import org.joda.time.DateTime;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link ContactInfoFlow}. */
@DualDatabaseTest
class ContactInfoFlowTest extends ResourceFlowTestCase<ContactInfoFlow, ContactResource> {
@Order(value = Order.DEFAULT - 2)
@RegisterExtension
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
ContactInfoFlowTest() {
setEppInput("contact_info.xml");
}
@@ -38,14 +38,21 @@ import google.registry.model.transfer.TransferData;
import google.registry.model.transfer.TransferResponse;
import google.registry.model.transfer.TransferStatus;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.ReplayExtension;
import google.registry.testing.TestOfyAndSql;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link ContactTransferCancelFlow}. */
@DualDatabaseTest
class ContactTransferCancelFlowTest
extends ContactTransferFlowTestCase<ContactTransferCancelFlow, ContactResource> {
@Order(value = Order.DEFAULT - 2)
@RegisterExtension
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
@BeforeEach
void setUp() {
this.setEppInput("contact_transfer_cancel.xml");
@@ -40,14 +40,21 @@ import google.registry.model.transfer.TransferData;
import google.registry.model.transfer.TransferResponse;
import google.registry.model.transfer.TransferStatus;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.ReplayExtension;
import google.registry.testing.TestOfyAndSql;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link ContactTransferRejectFlow}. */
@DualDatabaseTest
class ContactTransferRejectFlowTest
extends ContactTransferFlowTestCase<ContactTransferRejectFlow, ContactResource> {
@Order(value = Order.DEFAULT - 2)
@RegisterExtension
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
@BeforeEach
void setUp() {
setEppInput("contact_transfer_reject.xml");
@@ -52,15 +52,22 @@ import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.ContactTransferData;
import google.registry.model.transfer.TransferStatus;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.ReplayExtension;
import google.registry.testing.TestOfyAndSql;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link ContactTransferRequestFlow}. */
@DualDatabaseTest
class ContactTransferRequestFlowTest
extends ContactTransferFlowTestCase<ContactTransferRequestFlow, ContactResource> {
@Order(value = Order.DEFAULT - 2)
@RegisterExtension
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
ContactTransferRequestFlowTest() {
// We need the transfer to happen at exactly this time in order for the response to match up.
clock.setTo(DateTime.parse("2000-06-08T22:00:00.0Z"));
@@ -71,16 +71,29 @@ import google.registry.model.registry.Registry;
import google.registry.model.registry.Registry.TldState;
import google.registry.model.registry.label.ReservedList;
import google.registry.model.reporting.HistoryEntry;
import google.registry.testing.ReplayExtension;
import org.joda.money.CurrencyUnit;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link DomainCheckFlow}. */
class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, DomainBase> {
@Order(value = Order.DEFAULT - 3)
@RegisterExtension
final SetClockExtension setClockExtension = new SetClockExtension();
@Order(value = Order.DEFAULT - 2)
@RegisterExtension
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
DomainCheckFlowTest() {
setEppInput("domain_check_one_tld.xml");
}
@@ -1338,4 +1351,12 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
.build());
}
class SetClockExtension implements BeforeEachCallback {
@Override
public void beforeEach(ExtensionContext context) {
// Kick the clock back to before the earliest date that we set the clock to.
clock.setTo(DateTime.parse("2009-01-01T10:00:00Z"));
}
}
}
@@ -36,13 +36,20 @@ import google.registry.flows.exceptions.TooManyResourceChecksException;
import google.registry.model.domain.DomainBase;
import google.registry.model.registry.Registry;
import google.registry.model.registry.Registry.TldState;
import google.registry.testing.ReplayExtension;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link DomainClaimsCheckFlow}. */
public class DomainClaimsCheckFlowTest
extends ResourceFlowTestCase<DomainClaimsCheckFlow, DomainBase> {
@Order(value = Order.DEFAULT - 2)
@RegisterExtension
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
DomainClaimsCheckFlowTest() {
setEppInput("domain_check_claims.xml");
}
@@ -26,6 +26,7 @@ import static google.registry.testing.DatabaseHelper.persistActiveHost;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
import static google.registry.testing.TestDataHelper.updateSubstitutions;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -45,7 +46,8 @@ import google.registry.flows.domain.DomainFlowUtils.FeeChecksDontSupportPhasesEx
import google.registry.flows.domain.DomainFlowUtils.RestoresAreAlwaysForOneYearException;
import google.registry.flows.domain.DomainFlowUtils.TransfersAreAlwaysForOneYearException;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.billing.BillingEvent.Flag;
import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.contact.ContactAuthInfo;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DesignatedContact;
@@ -63,13 +65,26 @@ import google.registry.model.registry.Registry;
import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.ReplayExtension;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link DomainInfoFlow}. */
class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase> {
@Order(value = Order.DEFAULT - 3)
@RegisterExtension
final SetClockExtension setClockExtension = new SetClockExtension();
@Order(value = Order.DEFAULT - 2)
@RegisterExtension
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
/**
* The domain_info_fee.xml default substitutions common to most tests.
*
@@ -93,7 +108,6 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
void setup() {
setEppInput("domain_info.xml");
sessionMetadata.setClientId("NewRegistrar");
clock.setTo(DateTime.parse("2005-03-03T22:00:00.000Z"));
createTld("tld");
persistResource(AppEngineExtension.makeRegistrar1().asBuilder().setClientId("ClientZ").build());
}
@@ -149,7 +163,10 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
}
private void doSuccessfulTest(
String expectedXmlFilename, boolean inactive, ImmutableMap<String, String> substitutions)
String expectedXmlFilename,
boolean inactive,
ImmutableMap<String, String> substitutions,
boolean expectHistoryAndBilling)
throws Exception {
assertTransactionalFlow(false);
String expected =
@@ -158,12 +175,20 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
expected = expected.replaceAll("\"ok\"", "\"inactive\"");
}
runFlowAssertResponse(expected);
assertNoHistory();
assertNoBillingEvents();
if (!expectHistoryAndBilling) {
assertNoHistory();
assertNoBillingEvents();
}
}
private void doSuccessfulTest(
String expectedXmlFilename, boolean inactive, ImmutableMap<String, String> substitutions)
throws Exception {
doSuccessfulTest(expectedXmlFilename, inactive, substitutions, false);
}
private void doSuccessfulTest(String expectedXmlFilename, boolean inactive) throws Exception {
doSuccessfulTest(expectedXmlFilename, inactive, ImmutableMap.of());
doSuccessfulTest(expectedXmlFilename, inactive, ImmutableMap.of(), false);
}
private void doSuccessfulTest(String expectedXmlFilename) throws Exception {
@@ -309,7 +334,11 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
.asBuilder()
.addGracePeriod(
GracePeriod.create(
gracePeriodStatus, domain.getRepoId(), clock.nowUtc().plusDays(1), "foo", null))
gracePeriodStatus,
domain.getRepoId(),
clock.nowUtc().plusDays(1),
"TheRegistrar",
null))
.setCreationClientId("NewRegistrar")
.setCreationTimeForTest(DateTime.parse("2003-11-26T22:00:00.0Z"))
.setRegistrationExpirationTime(DateTime.parse("2005-11-26T22:00:00.0Z"))
@@ -328,10 +357,25 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
@Test
void testSuccess_autoRenewGracePeriod() throws Exception {
persistTestEntities(false);
Key<HistoryEntry> historyEntry =
Key.create(domain.createVKey().getOfyKey(), HistoryEntry.class, 67890);
VKey<BillingEvent.Recurring> recurringVKey =
VKey.from(Key.create(historyEntry, Recurring.class, 12345));
HistoryEntry historyEntry =
persistResource(
new HistoryEntry.Builder()
.setParent(domain)
.setType(HistoryEntry.Type.DOMAIN_CREATE)
.setModificationTime(clock.nowUtc())
.build());
BillingEvent.Recurring renewEvent =
persistResource(
new BillingEvent.Recurring.Builder()
.setReason(Reason.RENEW)
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
.setTargetId(getUniqueIdFromCommand())
.setClientId("TheRegistrar")
.setEventTime(clock.nowUtc())
.setRecurrenceEndTime(END_OF_TIME)
.setParent(historyEntry)
.build());
VKey<BillingEvent.Recurring> recurringVKey = renewEvent.createVKey();
// Add an AUTO_RENEW grace period to the saved resource.
persistResource(
domain
@@ -341,10 +385,10 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
GracePeriodStatus.AUTO_RENEW,
domain.getRepoId(),
clock.nowUtc().plusDays(1),
"foo",
"TheRegistrar",
recurringVKey))
.build());
doSuccessfulTest("domain_info_response_autorenewperiod.xml", false);
doSuccessfulTest("domain_info_response_autorenewperiod.xml", false, ImmutableMap.of(), true);
}
@Test
@@ -360,7 +404,7 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
GracePeriodStatus.REDEMPTION,
domain.getRepoId(),
clock.nowUtc().plusDays(1),
"foo",
"TheRegistrar",
null))
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
.build());
@@ -379,7 +423,7 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
GracePeriodStatus.RENEW,
domain.getRepoId(),
clock.nowUtc().plusDays(1),
"foo",
"TheRegistrar",
null))
.build());
doSuccessfulTest("domain_info_response_renewperiod.xml", false);
@@ -397,14 +441,14 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
GracePeriodStatus.RENEW,
domain.getRepoId(),
clock.nowUtc().plusDays(1),
"foo",
"TheRegistrar",
null))
.addGracePeriod(
GracePeriod.create(
GracePeriodStatus.RENEW,
domain.getRepoId(),
clock.nowUtc().plusDays(2),
"foo",
"TheRegistrar",
null))
.build());
doSuccessfulTest("domain_info_response_renewperiod.xml", false);
@@ -422,7 +466,7 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
GracePeriodStatus.TRANSFER,
domain.getRepoId(),
clock.nowUtc().plusDays(1),
"foo",
"TheRegistrar",
null))
.build());
doSuccessfulTest("domain_info_response_transferperiod.xml", false);
@@ -450,14 +494,14 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
GracePeriodStatus.ADD,
domain.getRepoId(),
clock.nowUtc().plusDays(1),
"foo",
"TheRegistrar",
null))
.addGracePeriod(
GracePeriod.create(
GracePeriodStatus.RENEW,
domain.getRepoId(),
clock.nowUtc().plusDays(2),
"foo",
"TheRegistrar",
null))
.build());
doSuccessfulTest("domain_info_response_stackedaddrenewperiod.xml", false);
@@ -475,7 +519,7 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
GracePeriodStatus.ADD,
domain.getRepoId(),
clock.nowUtc().plusDays(1),
"foo",
"TheRegistrar",
null))
.setDsData(
ImmutableSet.of(
@@ -929,4 +973,12 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, DomainBase
assertIcannReportingActivityFieldLogged("srs-dom-info");
assertTldsFieldLogged("tld");
}
class SetClockExtension implements BeforeEachCallback {
@Override
public void beforeEach(ExtensionContext context) {
// Kick the clock back to before the earliest date that we set the clock to.
clock.setTo(DateTime.parse("2005-03-03T22:00:00.000Z"));
}
}
}
@@ -70,16 +70,23 @@ import google.registry.model.registry.Registry;
import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
import google.registry.model.reporting.HistoryEntry;
import google.registry.testing.ReplayExtension;
import java.util.Map;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link DomainRestoreRequestFlow}. */
class DomainRestoreRequestFlowTest
extends ResourceFlowTestCase<DomainRestoreRequestFlow, DomainBase> {
@Order(value = Order.DEFAULT - 2)
@RegisterExtension
final ReplayExtension replayExtension = ReplayExtension.createWithCompare(clock);
private static final ImmutableMap<String, String> FEE_06_MAP =
ImmutableMap.of("FEE_VERSION", "0.6", "FEE_NS", "fee", "CURRENCY", "USD");
private static final ImmutableMap<String, String> FEE_11_MAP =
@@ -99,11 +106,12 @@ class DomainRestoreRequestFlowTest
}
void persistPendingDeleteDomain(DateTime expirationTime) throws Exception {
DomainBase domain = newDomainBase(getUniqueIdFromCommand());
DomainBase domain = persistResource(newDomainBase(getUniqueIdFromCommand()));
HistoryEntry historyEntry =
persistResource(
new HistoryEntry.Builder()
.setType(HistoryEntry.Type.DOMAIN_DELETE)
.setModificationTime(clock.nowUtc())
.setParent(domain)
.build());
persistResource(
@@ -116,7 +124,7 @@ class DomainRestoreRequestFlowTest
GracePeriodStatus.REDEMPTION,
domain.getRepoId(),
clock.nowUtc().plusDays(1),
"foo",
"TheRegistrar",
null))
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE))
.setDeletePollMessage(
@@ -0,0 +1,202 @@
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.persistence.transaction;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import com.google.common.collect.ImmutableList;
import google.registry.model.ImmutableObject;
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestExtension;
import google.registry.testing.FakeClock;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.criteria.CriteriaQuery;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Tests for {@link CriteriaQueryBuilder}. */
class CriteriaQueryBuilderTest {
private final FakeClock fakeClock = new FakeClock();
private CriteriaQueryBuilderTestEntity entity1 =
new CriteriaQueryBuilderTestEntity("name1", "data");
private CriteriaQueryBuilderTestEntity entity2 =
new CriteriaQueryBuilderTestEntity("name2", "zztz");
private CriteriaQueryBuilderTestEntity entity3 = new CriteriaQueryBuilderTestEntity("zzz", "aaa");
@RegisterExtension
final JpaUnitTestExtension jpaExtension =
new JpaTestRules.Builder()
.withClock(fakeClock)
.withEntityClass(CriteriaQueryBuilderTestEntity.class)
.buildUnitTestRule();
@BeforeEach
void beforeEach() {
jpaTm().transact(() -> jpaTm().putAll(ImmutableList.of(entity1, entity2, entity3)));
}
@Test
void testSuccess_noWhereClause() {
assertThat(
jpaTm()
.transact(
() ->
jpaTm()
.getEntityManager()
.createQuery(
CriteriaQueryBuilder.create(CriteriaQueryBuilderTestEntity.class)
.build())
.getResultList()))
.containsExactly(entity1, entity2, entity3)
.inOrder();
}
@Test
void testSuccess_where_exactlyOne() {
List<CriteriaQueryBuilderTestEntity> result =
jpaTm()
.transact(
() -> {
CriteriaQuery<CriteriaQueryBuilderTestEntity> query =
CriteriaQueryBuilder.create(CriteriaQueryBuilderTestEntity.class)
.where(
jpaTm().getEntityManager().getCriteriaBuilder()::equal,
"data",
"zztz")
.build();
return jpaTm().getEntityManager().createQuery(query).getResultList();
});
assertThat(result).containsExactly(entity2);
}
@Test
void testSuccess_where_like_oneResult() {
List<CriteriaQueryBuilderTestEntity> result =
jpaTm()
.transact(
() -> {
CriteriaQuery<CriteriaQueryBuilderTestEntity> query =
CriteriaQueryBuilder.create(CriteriaQueryBuilderTestEntity.class)
.where(
jpaTm().getEntityManager().getCriteriaBuilder()::like, "data", "a%")
.build();
return jpaTm().getEntityManager().createQuery(query).getResultList();
});
assertThat(result).containsExactly(entity3);
}
@Test
void testSuccess_where_like_twoResults() {
List<CriteriaQueryBuilderTestEntity> result =
jpaTm()
.transact(
() -> {
CriteriaQuery<CriteriaQueryBuilderTestEntity> query =
CriteriaQueryBuilder.create(CriteriaQueryBuilderTestEntity.class)
.where(
jpaTm().getEntityManager().getCriteriaBuilder()::like, "data", "%a%")
.build();
return jpaTm().getEntityManager().createQuery(query).getResultList();
});
assertThat(result).containsExactly(entity1, entity3).inOrder();
}
@Test
void testSuccess_multipleWheres() {
List<CriteriaQueryBuilderTestEntity> result =
jpaTm()
.transact(
() -> {
CriteriaQuery<CriteriaQueryBuilderTestEntity> query =
CriteriaQueryBuilder.create(CriteriaQueryBuilderTestEntity.class)
// first "where" matches 1 and 3
.where(
jpaTm().getEntityManager().getCriteriaBuilder()::like, "data", "%a%")
// second "where" matches 1 and 2
.where(
jpaTm().getEntityManager().getCriteriaBuilder()::like, "data", "%t%")
.build();
return jpaTm().getEntityManager().createQuery(query).getResultList();
});
assertThat(result).containsExactly(entity1);
}
@Test
void testSuccess_where_in_oneResult() {
List<CriteriaQueryBuilderTestEntity> result =
jpaTm()
.transact(
() -> {
CriteriaQuery<CriteriaQueryBuilderTestEntity> query =
CriteriaQueryBuilder.create(CriteriaQueryBuilderTestEntity.class)
.whereFieldIsIn("data", ImmutableList.of("aaa", "bbb"))
.build();
return jpaTm().getEntityManager().createQuery(query).getResultList();
});
assertThat(result).containsExactly(entity3).inOrder();
}
@Test
void testSuccess_where_in_twoResults() {
List<CriteriaQueryBuilderTestEntity> result =
jpaTm()
.transact(
() -> {
CriteriaQuery<CriteriaQueryBuilderTestEntity> query =
CriteriaQueryBuilder.create(CriteriaQueryBuilderTestEntity.class)
.whereFieldIsIn("data", ImmutableList.of("aaa", "bbb", "data"))
.build();
return jpaTm().getEntityManager().createQuery(query).getResultList();
});
assertThat(result).containsExactly(entity1, entity3).inOrder();
}
@Test
void testSuccess_orderBy() {
List<CriteriaQueryBuilderTestEntity> result =
jpaTm()
.transact(
() -> {
CriteriaQuery<CriteriaQueryBuilderTestEntity> query =
CriteriaQueryBuilder.create(CriteriaQueryBuilderTestEntity.class)
.orderBy(jpaTm().getEntityManager().getCriteriaBuilder()::asc, "data")
.where(
jpaTm().getEntityManager().getCriteriaBuilder()::like, "data", "%a%")
.build();
return jpaTm().getEntityManager().createQuery(query).getResultList();
});
assertThat(result).containsExactly(entity3, entity1).inOrder();
}
@Entity(name = "CriteriaQueryBuilderTestEntity")
private static class CriteriaQueryBuilderTestEntity extends ImmutableObject {
@Id private String name;
@SuppressWarnings("unused")
private String data;
private CriteriaQueryBuilderTestEntity() {}
private CriteriaQueryBuilderTestEntity(String name, String data) {
this.name = name;
this.data = data;
}
}
}
@@ -112,7 +112,7 @@ class TransactionTest {
});
TransactionEntity txnEnt =
jpaTm().transact(() -> jpaTm().loadByKey(VKey.createSql(TransactionEntity.class, 1L)));
Transaction txn = Transaction.deserialize(txnEnt.contents);
Transaction txn = Transaction.deserialize(txnEnt.getContents());
txn.writeToDatastore();
ofyTm()
.transact(
@@ -43,14 +43,16 @@ import google.registry.model.reporting.HistoryEntry;
import google.registry.rdap.RdapMetrics.EndpointType;
import google.registry.rdap.RdapMetrics.SearchType;
import google.registry.rdap.RdapSearchResults.IncompletenessWarningType;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.FakeResponse;
import google.registry.testing.TestOfyAndSql;
import java.net.URLDecoder;
import java.util.Optional;
import javax.annotation.Nullable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link RdapEntitySearchAction}. */
@DualDatabaseTest
class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySearchAction> {
RdapEntitySearchActionTest() {
@@ -209,9 +211,9 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
.asBuilder()
.setRepoId(String.format("%04d-ROID", i))
.build();
resourcesBuilder.add(contact);
resourcesBuilder.add(makeHistoryEntry(
contact, HistoryEntry.Type.CONTACT_CREATE, null, "created", clock.nowUtc()));
resourcesBuilder.add(contact);
}
persistResources(resourcesBuilder.build());
for (int i = 1; i <= numRegistrars; i++) {
@@ -386,7 +388,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
}
}
@Test
@TestOfyAndSql
void testInvalidPath_rejected() {
action.requestPath = actionPath + "/path";
action.run();
@@ -394,7 +396,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyErrorMetrics(Optional.empty(), 400);
}
@Test
@TestOfyAndSql
void testInvalidRequest_rejected() {
action.run();
assertThat(parseJsonObject(response.getPayload()))
@@ -405,7 +407,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyErrorMetrics(Optional.empty(), 400);
}
@Test
@TestOfyAndSql
void testNameMatch_suffixRejected() {
assertThat(generateActualJsonWithFullName("exam*ple"))
.isEqualTo(
@@ -417,7 +419,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyErrorMetrics(Optional.empty(), 422);
}
@Test
@TestOfyAndSql
void testHandleMatch_suffixRejected() {
assertThat(generateActualJsonWithHandle("exam*ple"))
.isEqualTo(
@@ -429,7 +431,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyErrorMetrics(Optional.empty(), 422);
}
@Test
@TestOfyAndSql
void testMultipleWildcards_rejected() {
assertThat(generateActualJsonWithHandle("*.*"))
.isEqualTo(
@@ -441,7 +443,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyErrorMetrics(Optional.empty(), 422);
}
@Test
@TestOfyAndSql
void testNoCharactersToMatch_rejected() {
rememberWildcardType("*");
assertThat(generateActualJsonWithHandle("*"))
@@ -453,7 +455,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyErrorMetrics(Optional.empty(), 422);
}
@Test
@TestOfyAndSql
void testFewerThanTwoCharactersToMatch_rejected() {
rememberWildcardType("a*");
assertThat(generateActualJsonWithHandle("a*"))
@@ -465,7 +467,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyErrorMetrics(Optional.empty(), 422);
}
@Test
@TestOfyAndSql
void testInvalidSubtype_rejected() {
action.subtypeParam = Optional.of("Space Aliens");
assertThat(generateActualJsonWithFullName("Blinky (赤ベイ)"))
@@ -478,14 +480,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyErrorMetrics(Optional.empty(), 400);
}
@Test
@TestOfyAndSql
void testNameMatchContact_found() {
login("2-RegistrarTest");
runSuccessfulNameTestWithBlinky("Blinky (赤ベイ)", "rdap_contact.json");
verifyMetrics(1);
}
@Test
@TestOfyAndSql
void testNameMatchContact_found_subtypeAll() {
login("2-RegistrarTest");
action.subtypeParam = Optional.of("aLl");
@@ -493,7 +495,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(1);
}
@Test
@TestOfyAndSql
void testNameMatchContact_found_subtypeContacts() {
login("2-RegistrarTest");
action.subtypeParam = Optional.of("cONTACTS");
@@ -501,7 +503,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(1);
}
@Test
@TestOfyAndSql
void testNameMatchContact_notFound_subtypeRegistrars() {
login("2-RegistrarTest");
action.subtypeParam = Optional.of("Registrars");
@@ -509,7 +511,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchContact_found_specifyingSameRegistrar() {
login("2-RegistrarTest");
action.registrarParam = Optional.of("2-RegistrarTest");
@@ -517,7 +519,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(1);
}
@Test
@TestOfyAndSql
void testNameMatchContact_notFound_specifyingOtherRegistrar() {
login("2-RegistrarTest");
action.registrarParam = Optional.of("2-RegistrarInact");
@@ -525,7 +527,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchContact_found_asAdministrator() {
loginAsAdmin();
rememberWildcardType("Blinky (赤ベイ)");
@@ -533,27 +535,27 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(1);
}
@Test
@TestOfyAndSql
void testNameMatchContact_notFound_notLoggedIn() {
runNotFoundNameTest("Blinky (赤ベイ)");
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchContact_notFound_loggedInAsOtherRegistrar() {
login("2-Registrar");
runNotFoundNameTest("Blinky (赤ベイ)");
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchContact_found_wildcard() {
login("2-RegistrarTest");
runSuccessfulNameTestWithBlinky("Blinky*", "rdap_contact.json");
verifyMetrics(1);
}
@Test
@TestOfyAndSql
void testNameMatchContact_found_wildcardSpecifyingSameRegistrar() {
login("2-RegistrarTest");
action.registrarParam = Optional.of("2-RegistrarTest");
@@ -561,7 +563,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(1);
}
@Test
@TestOfyAndSql
void testNameMatchContact_notFound_wildcardSpecifyingOtherRegistrar() {
login("2-RegistrarTest");
action.registrarParam = Optional.of("2-RegistrarInact");
@@ -569,7 +571,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchContact_found_wildcardBoth() {
login("2-RegistrarTest");
rememberWildcardType("Blin*");
@@ -579,14 +581,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(2);
}
@Test
@TestOfyAndSql
void testNameMatchContact_notFound_deleted() {
login("2-RegistrarTest");
runNotFoundNameTest("Cl*");
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchContact_notFound_deletedWhenLoggedInAsOtherRegistrar() {
login("2-RegistrarTest");
action.includeDeletedParam = Optional.of(true);
@@ -594,7 +596,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchContact_notFound_deletedWhenLoggedInAsSameRegistrar() {
login("2-Registrar");
action.includeDeletedParam = Optional.of(true);
@@ -602,7 +604,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchContact_notFound_deletedWhenLoggedInAsAdmin() {
loginAsAdmin();
action.includeDeletedParam = Optional.of(true);
@@ -610,7 +612,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchRegistrar_found() {
login("2-RegistrarTest");
runSuccessfulNameTest(
@@ -618,7 +620,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchRegistrar_found_subtypeAll() {
login("2-RegistrarTest");
action.subtypeParam = Optional.of("all");
@@ -627,7 +629,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchRegistrar_found_subtypeRegistrars() {
login("2-RegistrarTest");
action.subtypeParam = Optional.of("REGISTRARS");
@@ -636,7 +638,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchRegistrar_notFound_subtypeContacts() {
login("2-RegistrarTest");
action.subtypeParam = Optional.of("contacts");
@@ -644,7 +646,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchRegistrar_found_specifyingSameRegistrar() {
action.registrarParam = Optional.of("2-Registrar");
runSuccessfulNameTest(
@@ -652,25 +654,26 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchRegistrar_notFound_specifyingDifferentRegistrar() {
action.registrarParam = Optional.of("2-RegistrarTest");
runNotFoundNameTest("Yes Virginia <script>");
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchContacts_nonTruncated() {
login("2-RegistrarTest");
createManyContactsAndRegistrars(4, 0, registrarTest);
rememberWildcardType("Entity *");
// JsonObject foo = generateActualJsonWithFullName("Entity *");
assertThat(generateActualJsonWithFullName("Entity *"))
.isEqualTo(generateExpectedJson("rdap_nontruncated_contacts.json"));
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(4);
}
@Test
@TestOfyAndSql
void testNameMatchContacts_truncated() {
login("2-RegistrarTest");
createManyContactsAndRegistrars(5, 0, registrarTest);
@@ -683,7 +686,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(5, IncompletenessWarningType.TRUNCATED);
}
@Test
@TestOfyAndSql
void testNameMatchContacts_reallyTruncated() {
login("2-RegistrarTest");
createManyContactsAndRegistrars(9, 0, registrarTest);
@@ -697,7 +700,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(5, IncompletenessWarningType.TRUNCATED);
}
@Test
@TestOfyAndSql
void testNameMatchContacts_cursorNavigation() throws Exception {
login("2-RegistrarTest");
createManyContactsAndRegistrars(9, 0, registrarTest);
@@ -716,7 +719,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
"Entity 9"));
}
@Test
@TestOfyAndSql
void testNameMatchRegistrars_nonTruncated() {
createManyContactsAndRegistrars(0, 4, registrarTest);
rememberWildcardType("Entity *");
@@ -726,7 +729,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchRegistrars_truncated() {
createManyContactsAndRegistrars(0, 5, registrarTest);
rememberWildcardType("Entity *");
@@ -738,7 +741,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(0, IncompletenessWarningType.TRUNCATED);
}
@Test
@TestOfyAndSql
void testNameMatchRegistrars_reallyTruncated() {
createManyContactsAndRegistrars(0, 9, registrarTest);
rememberWildcardType("Entity *");
@@ -750,7 +753,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(0, IncompletenessWarningType.TRUNCATED);
}
@Test
@TestOfyAndSql
void testNameMatchRegistrars_cursorNavigation() throws Exception {
createManyContactsAndRegistrars(0, 13, registrarTest);
checkCursorNavigation(
@@ -772,7 +775,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
"Entity 9"));
}
@Test
@TestOfyAndSql
void testNameMatchRegistrars_cursorNavigationThroughAll() throws Exception {
createManyContactsAndRegistrars(0, 13, registrarTest);
action.subtypeParam = Optional.of("registrars");
@@ -798,7 +801,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
"Yes Virginia <script>"));
}
@Test
@TestOfyAndSql
void testNameMatchMix_truncated() {
login("2-RegistrarTest");
createManyContactsAndRegistrars(3, 3, registrarTest);
@@ -811,7 +814,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(3, IncompletenessWarningType.TRUNCATED);
}
@Test
@TestOfyAndSql
void testNameMatchMix_cursorNavigation() throws Exception {
login("2-RegistrarTest");
createManyContactsAndRegistrars(3, 3, registrarTest);
@@ -827,7 +830,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
"Entity 6"));
}
@Test
@TestOfyAndSql
void testNameMatchMix_subtypeContacts() {
login("2-RegistrarTest");
action.subtypeParam = Optional.of("contacts");
@@ -839,7 +842,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(4);
}
@Test
@TestOfyAndSql
void testNameMatchMix_subtypeRegistrars() {
login("2-RegistrarTest");
action.subtypeParam = Optional.of("registrars");
@@ -849,13 +852,13 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchRegistrar_notFound_inactive() {
runNotFoundNameTest("No Way");
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchRegistrar_notFound_inactiveAsDifferentRegistrar() {
action.includeDeletedParam = Optional.of(true);
login("2-Registrar");
@@ -863,7 +866,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchRegistrar_found_inactiveAsSameRegistrar() {
action.includeDeletedParam = Optional.of(true);
login("2-RegistrarInact");
@@ -871,7 +874,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchRegistrar_found_inactiveAsAdmin() {
action.includeDeletedParam = Optional.of(true);
loginAsAdmin();
@@ -879,13 +882,13 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchRegistrar_notFound_test() {
runNotFoundNameTest("Da Test Registrar");
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchRegistrar_notFound_testAsDifferentRegistrar() {
action.includeDeletedParam = Optional.of(true);
login("2-Registrar");
@@ -893,7 +896,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchRegistrar_found_testAsSameRegistrar() {
action.includeDeletedParam = Optional.of(true);
login("2-RegistrarTest");
@@ -902,7 +905,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(0);
}
@Test
@TestOfyAndSql
void testNameMatchRegistrar_found_testAsAdmin() {
action.includeDeletedParam = Optional.of(true);
loginAsAdmin();
@@ -911,14 +914,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(0);
}
@Test
@TestOfyAndSql
void testHandleMatchContact_found() {
login("2-RegistrarTest");
runSuccessfulHandleTestWithBlinky("2-ROID", "rdap_contact.json");
verifyMetrics(1);
}
@Test
@TestOfyAndSql
void testHandleMatchContact_found_subtypeAll() {
login("2-RegistrarTest");
action.subtypeParam = Optional.of("all");
@@ -926,7 +929,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(1);
}
@Test
@TestOfyAndSql
void testHandleMatchContact_found_subtypeContacts() {
login("2-RegistrarTest");
action.subtypeParam = Optional.of("contacts");
@@ -934,7 +937,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(1);
}
@Test
@TestOfyAndSql
void testHandleMatchContact_notFound_subtypeRegistrars() {
login("2-RegistrarTest");
action.subtypeParam = Optional.of("reGistrars");
@@ -942,28 +945,28 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testHandleMatchContact_found_specifyingSameRegistrar() {
action.registrarParam = Optional.of("2-RegistrarTest");
runSuccessfulHandleTestWithBlinky("2-ROID", "rdap_contact_no_personal_data_with_remark.json");
verifyMetrics(1);
}
@Test
@TestOfyAndSql
void testHandleMatchContact_notFound_specifyingDifferentRegistrar() {
action.registrarParam = Optional.of("2-Registrar");
runNotFoundHandleTest("2-ROID");
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testHandleMatchContact_notFound_deleted() {
login("2-RegistrarTest");
runNotFoundHandleTest("6-ROID");
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testHandleMatchContact_notFound_deletedWhenLoggedInAsOtherRegistrar() {
login("2-RegistrarTest");
action.includeDeletedParam = Optional.of(true);
@@ -971,7 +974,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyErrorMetrics(1);
}
@Test
@TestOfyAndSql
void testHandleMatchContact_found_deletedWhenLoggedInAsSameRegistrar() {
login("2-Registrar");
action.includeDeletedParam = Optional.of(true);
@@ -985,7 +988,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(1);
}
@Test
@TestOfyAndSql
void testHandleMatchContact_found_deletedWhenLoggedInAsAdmin() {
loginAsAdmin();
action.includeDeletedParam = Optional.of(true);
@@ -999,14 +1002,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(1);
}
@Test
@TestOfyAndSql
void testHandleMatchContact_notFound_deletedWildcard() {
login("2-RegistrarTest");
runNotFoundHandleTest("6-ROI*");
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testHandleMatchContact_notFound_deletedWildcardWhenLoggedInAsOtherRegistrar() {
login("2-RegistrarTest");
action.includeDeletedParam = Optional.of(true);
@@ -1014,7 +1017,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyErrorMetrics(1);
}
@Test
@TestOfyAndSql
void testHandleMatchContact_found_deletedWildcardWhenLoggedInAsSameRegistrar() {
login("2-Registrar");
action.includeDeletedParam = Optional.of(true);
@@ -1028,7 +1031,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(1);
}
@Test
@TestOfyAndSql
void testHandleMatchContact_found_deletedWildcardWhenLoggedInAsAdmin() {
loginAsAdmin();
action.includeDeletedParam = Optional.of(true);
@@ -1042,48 +1045,48 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(1);
}
@Test
@TestOfyAndSql
void testHandleMatchRegistrar_found() {
runSuccessfulHandleTest("20", "20", "Yes Virginia <script>", "rdap_registrar.json");
verifyMetrics(0);
}
@Test
@TestOfyAndSql
void testHandleMatchRegistrar_found_subtypeAll() {
action.subtypeParam = Optional.of("all");
runSuccessfulHandleTest("20", "20", "Yes Virginia <script>", "rdap_registrar.json");
verifyMetrics(0);
}
@Test
@TestOfyAndSql
void testHandleMatchRegistrar_found_subtypeRegistrars() {
action.subtypeParam = Optional.of("registrars");
runSuccessfulHandleTest("20", "20", "Yes Virginia <script>", "rdap_registrar.json");
verifyMetrics(0);
}
@Test
@TestOfyAndSql
void testHandleMatchRegistrar_notFound_subtypeContacts() {
action.subtypeParam = Optional.of("contacts");
runNotFoundHandleTest("20");
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testHandleMatchRegistrar_found_specifyingSameRegistrar() {
action.registrarParam = Optional.of("2-Registrar");
runSuccessfulHandleTest("20", "20", "Yes Virginia <script>", "rdap_registrar.json");
verifyMetrics(0);
}
@Test
@TestOfyAndSql
void testHandleMatchRegistrar_notFound_specifyingDifferentRegistrar() {
action.registrarParam = Optional.of("2-RegistrarTest");
runNotFoundHandleTest("20");
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testHandleMatchContact_found_wildcardWithResultSetSizeOne() {
login("2-RegistrarTest");
action.rdapResultSetMaxSize = 1;
@@ -1091,14 +1094,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(1);
}
@Test
@TestOfyAndSql
void testHandleMatchContact_found_wildcard() {
login("2-RegistrarTest");
runSuccessfulHandleTestWithBlinky("2-RO*", "rdap_contact.json");
verifyMetrics(1);
}
@Test
@TestOfyAndSql
void testHandleMatchContact_found_wildcardSpecifyingSameRegistrar() {
action.registrarParam = Optional.of("2-RegistrarTest");
login("2-RegistrarTest");
@@ -1106,7 +1109,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(1);
}
@Test
@TestOfyAndSql
void testHandleMatchContact_notFound_wildcardSpecifyingDifferentRegistrar() {
action.registrarParam = Optional.of("2-Registrar");
login("2-RegistrarTest");
@@ -1114,20 +1117,20 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testHandleMatchContact_found_deleted() {
login("2-RegistrarTest");
runSuccessfulHandleTestWithBlinky("2-RO*", "rdap_contact.json");
verifyMetrics(1);
}
@Test
@TestOfyAndSql
void testHandleMatchContact_notFound_wildcard() {
runNotFoundHandleTest("20*");
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testHandleMatchContact_cursorNavigationWithFullLastPage() throws Exception {
login("2-RegistrarTest");
createManyContactsAndRegistrars(12, 0, registrarTest);
@@ -1150,7 +1153,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
"Entity 12"));
}
@Test
@TestOfyAndSql
void testHandleMatchContact_cursorNavigationWithPartialLastPage() throws Exception {
login("2-RegistrarTest");
createManyContactsAndRegistrars(13, 0, registrarTest);
@@ -1174,13 +1177,13 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
"Entity 13"));
}
@Test
@TestOfyAndSql
void testHandleMatchRegistrar_notFound_wildcard() {
runNotFoundHandleTest("3test*");
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testHandleMatchRegistrars_cursorNavigationThroughAll() throws Exception {
createManyContactsAndRegistrars(0, 13, registrarTest);
action.subtypeParam = Optional.of("registrars");
@@ -1206,7 +1209,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
"Yes Virginia <script>"));
}
@Test
@TestOfyAndSql
void testHandleMatchMix_found_truncated() {
createManyContactsAndRegistrars(30, 0, registrarTest);
rememberWildcardType("00*");
@@ -1216,13 +1219,13 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(5, IncompletenessWarningType.TRUNCATED);
}
@Test
@TestOfyAndSql
void testHandleMatchRegistrar_notFound_inactive() {
runNotFoundHandleTest("21");
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testHandleMatchRegistrar_notFound_inactiveAsDifferentRegistrar() {
action.includeDeletedParam = Optional.of(true);
login("2-Registrar");
@@ -1230,7 +1233,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyErrorMetrics(0);
}
@Test
@TestOfyAndSql
void testHandleMatchRegistrar_found_inactiveAsSameRegistrar() {
action.includeDeletedParam = Optional.of(true);
login("2-RegistrarInact");
@@ -1238,7 +1241,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
verifyMetrics(0);
}
@Test
@TestOfyAndSql
void testHandleMatchRegistrar_found_inactiveAsAdmin() {
action.includeDeletedParam = Optional.of(true);
loginAsAdmin();
@@ -0,0 +1,188 @@
// Copyright 2021 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.schema.replay;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
import static google.registry.testing.LogsSubject.assertAboutLogs;
import com.google.common.testing.TestLogHandler;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import google.registry.config.RegistryConfig;
import google.registry.model.ImmutableObject;
import google.registry.persistence.VKey;
import google.registry.persistence.transaction.TransactionEntity;
import google.registry.testing.AppEngineExtension;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
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;
public class ReplicateToDatastoreActionTest {
@RegisterExtension
public final AppEngineExtension appEngine =
AppEngineExtension.builder()
.withDatastoreAndCloudSql()
.withOfyTestEntities(TestEntity.class)
.withJpaUnitTestEntities(TestEntity.class)
.build();
ReplicateToDatastoreAction task = new ReplicateToDatastoreAction();
TestLogHandler logHandler;
public ReplicateToDatastoreActionTest() {}
@BeforeEach
public void setUp() {
RegistryConfig.overrideCloudSqlReplicateTransactions(true);
logHandler = new TestLogHandler();
Logger.getLogger(ReplicateToDatastoreAction.class.getCanonicalName()).addHandler(logHandler);
}
@AfterEach
public void tearDown() {
RegistryConfig.overrideCloudSqlReplicateTransactions(false);
}
@Test
public void testReplication() {
TestEntity foo = new TestEntity("foo");
TestEntity bar = new TestEntity("bar");
TestEntity baz = new TestEntity("baz");
jpaTm()
.transact(
() -> {
jpaTm().insert(foo);
jpaTm().insert(bar);
});
task.run();
assertThat(ofyTm().transact(() -> ofyTm().loadByKey(foo.key()))).isEqualTo(foo);
assertThat(ofyTm().transact(() -> ofyTm().loadByKey(bar.key()))).isEqualTo(bar);
assertThat(ofyTm().transact(() -> ofyTm().loadByKeyIfPresent(baz.key())).isPresent()).isFalse();
jpaTm()
.transact(
() -> {
jpaTm().delete(bar.key());
jpaTm().insert(baz);
});
task.run();
assertThat(ofyTm().transact(() -> ofyTm().loadByKeyIfPresent(bar.key()).isPresent())).isFalse();
assertThat(ofyTm().transact(() -> ofyTm().loadByKey(baz.key()))).isEqualTo(baz);
}
@Test
public void testReplayFromLastTxn() {
TestEntity foo = new TestEntity("foo");
TestEntity bar = new TestEntity("bar");
// Write a transaction containing "foo".
jpaTm().transact(() -> jpaTm().insert(foo));
task.run();
// Verify that it propagated to datastore, then remove "foo" directly from datastore.
assertThat(ofyTm().transact(() -> ofyTm().loadByKey(foo.key()))).isEqualTo(foo);
ofyTm().transact(() -> ofyTm().delete(foo.key()));
// Write "bar"
jpaTm().transact(() -> jpaTm().insert(bar));
task.run();
// If we replayed only the most recent transaction, we should have "bar" but not "foo".
assertThat(ofyTm().transact(() -> ofyTm().loadByKey(bar.key()))).isEqualTo(bar);
assertThat(ofyTm().transact(() -> ofyTm().loadByKeyIfPresent(foo.key()).isPresent())).isFalse();
}
@Test
public void testUnintentionalConcurrency() {
TestEntity foo = new TestEntity("foo");
TestEntity bar = new TestEntity("bar");
// Write a transaction and run just the batch fetch.
jpaTm().transact(() -> jpaTm().insert(foo));
List<TransactionEntity> txns1 = task.getTransactionBatch();
assertThat(txns1).hasSize(1);
// Write a second transaction and do another batch fetch.
jpaTm().transact(() -> jpaTm().insert(bar));
List<TransactionEntity> txns2 = task.getTransactionBatch();
assertThat(txns2).hasSize(2);
// Apply the first batch.
assertThat(task.applyTransaction(txns1.get(0))).isFalse();
// Remove the foo record so we can ensure that this transaction doesn't get doublle-played.
ofyTm().transact(() -> ofyTm().delete(foo.key()));
// Apply the second batch.
for (TransactionEntity txn : txns2) {
assertThat(task.applyTransaction(txn)).isFalse();
}
// Verify that the first transaction didn't get replayed but the second one did.
assertThat(ofyTm().transact(() -> ofyTm().loadByKeyIfPresent(foo.key()).isPresent())).isFalse();
assertThat(ofyTm().transact(() -> ofyTm().loadByKey(bar.key()))).isEqualTo(bar);
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(
Level.WARNING, "Ignoring transaction 1, which appears to have already been applied.");
}
@Test
public void testMissingTransactions() {
// Write a transaction (should have a transaction id of 1).
TestEntity foo = new TestEntity("foo");
jpaTm().transact(() -> jpaTm().insert(foo));
// Force the last transaction id back to -1 so that we look for transaction 0.
ofyTm().transact(() -> ofyTm().insert(new LastSqlTransaction(-1)));
List<TransactionEntity> txns = task.getTransactionBatch();
assertThat(txns).hasSize(1);
assertThat(task.applyTransaction(txns.get(0))).isTrue();
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(
Level.SEVERE,
"Missing transaction: last transaction id = -1, next available transaction = 1");
}
@Entity(name = "ReplicationTestEntity")
@javax.persistence.Entity(name = "TestEntity")
private static class TestEntity extends ImmutableObject {
@Id @javax.persistence.Id private String name;
private TestEntity() {}
private TestEntity(String name) {
this.name = name;
}
public VKey<TestEntity> key() {
return VKey.create(TestEntity.class, name, Key.create(this));
}
}
}
@@ -17,10 +17,12 @@ package google.registry.testing;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.io.Files.asCharSink;
import static com.google.common.truth.Truth.assertWithMessage;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatabaseHelper.persistSimpleResources;
import static google.registry.testing.DualDatabaseTestInvocationContextProvider.injectTmForDualDatabaseTest;
import static google.registry.testing.DualDatabaseTestInvocationContextProvider.restoreTmAfterDualDatabaseTest;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import static google.registry.util.ResourceUtils.readResourceUtf8;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -40,10 +42,16 @@ import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.ObjectifyFilter;
import google.registry.model.common.DatabaseTransitionSchedule;
import google.registry.model.common.DatabaseTransitionSchedule.PrimaryDatabase;
import google.registry.model.common.DatabaseTransitionSchedule.PrimaryDatabaseTransition;
import google.registry.model.common.DatabaseTransitionSchedule.TransitionId;
import google.registry.model.common.TimedTransitionProperty;
import google.registry.model.ofy.ObjectifyService;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.Registrar.State;
@@ -390,6 +398,14 @@ public final class AppEngineExtension implements BeforeEachCallback, AfterEachCa
loadInitialData();
}
} else {
// If we're using SQL, set replayed entities to use SQL
DatabaseTransitionSchedule schedule =
DatabaseTransitionSchedule.create(
TransitionId.REPLAYED_ENTITIES,
TimedTransitionProperty.fromValueMap(
ImmutableSortedMap.of(START_OF_TIME, PrimaryDatabase.CLOUD_SQL),
PrimaryDatabaseTransition.class));
tm().transactNew(() -> ofy().saveWithoutBackup().entity(schedule).now());
if (withCloudSql && !withJpaUnitTest && !withoutCannedData) {
loadInitialData();
}
@@ -0,0 +1,71 @@
// Copyright 2021 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.testing.DatabaseHelper.createTld;
import static org.junit.Assert.assertThrows;
import com.beust.jcommander.ParameterException;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import org.junit.jupiter.api.BeforeEach;
@DualDatabaseTest
public class GetPremiumListCommandTest extends CommandTestCase<GetPremiumListCommand> {
private static final String BASE_LIST_CONTENTS =
"tld:\n"
+ "aluminum,USD 11.00\n"
+ "brass,USD 20.00\n"
+ "copper,USD 15.00\n"
+ "diamond,USD 1000000.00\n"
+ "gold,USD 24317.00\n"
+ "iridium,USD 13117.00\n"
+ "palladium,USD 877.00\n"
+ "platinum,USD 87741.00\n"
+ "rhodium,USD 88415.00\n"
+ "rich,USD 100.00\n"
+ "richer,USD 1000.00\n"
+ "silver,USD 588.00\n";
@BeforeEach
void beforeEach() {
createTld("tld");
}
@TestOfyAndSql
void testSuccess_list() throws Exception {
runCommand("tld");
assertStdoutIs(BASE_LIST_CONTENTS);
}
@TestOfyAndSql
void testSuccess_onlyOneExists() throws Exception {
runCommand("tld", "nonexistent");
assertStdoutIs(BASE_LIST_CONTENTS + "No list found with name nonexistent.\n");
}
@TestOfyAndSql
void testFailure_nonexistent() throws Exception {
runCommand("nonexistent", "othernonexistent");
assertStdoutIs(
"No list found with name nonexistent.\nNo list found with name othernonexistent.\n");
}
@TestOfyAndSql
void testFailure_noArgs() {
assertThrows(ParameterException.class, this::runCommand);
}
}
@@ -13,6 +13,7 @@ HistoryEntry
HostResource
KmsSecret
KmsSecretRevision
LastSqlTransaction
Modification
OneTime
PollMessage
@@ -28,7 +28,7 @@
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-03T10:00:00.000Z">50.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-03T10:00:00.002Z">50.00</fee:fee>
<fee:date>2010-01-02T13:22:21Z</fee:date>
<fee:notAfter>2010-01-03T10:00:00.000Z</fee:notAfter>
</fee:command>
@@ -40,7 +40,7 @@
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-03T10:00:00.000Z">50.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-03T10:00:00.002Z">50.00</fee:fee>
<fee:date>2010-01-02T13:22:21Z</fee:date>
<fee:notAfter>2010-01-03T10:00:00.000Z</fee:notAfter>
</fee:command>
@@ -52,7 +52,7 @@
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-03T10:00:00.000Z">50.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-03T10:00:00.002Z">50.00</fee:fee>
<fee:date>2010-01-02T13:22:21Z</fee:date>
<fee:notAfter>2010-01-03T10:00:00.000Z</fee:notAfter>
</fee:command>
@@ -25,7 +25,7 @@
<fee:command>create</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.000Z">100.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.002Z">100.00</fee:fee>
</fee:cd>
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
<fee:name>example3.tld</fee:name>
@@ -33,7 +33,7 @@
<fee:command>create</fee:command>
<fee:period unit="y">2</fee:period>
<fee:fee description="create">26.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.000Z">100.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.002Z">100.00</fee:fee>
</fee:cd>
</fee:chkData>
</extension>
@@ -28,7 +28,7 @@
<fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.000Z">100.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.002Z">100.00</fee:fee>
</fee:cd>
<fee:cd avail="1">
<fee:object>
@@ -38,7 +38,7 @@
<fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.000Z">100.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.002Z">100.00</fee:fee>
</fee:cd>
<fee:cd avail="1">
<fee:object>
@@ -48,7 +48,7 @@
<fee:currency>USD</fee:currency>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.000Z">100.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.002Z">100.00</fee:fee>
</fee:cd>
</fee:chkData>
</extension>
@@ -28,7 +28,7 @@
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.000Z">100.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.002Z">100.00</fee:fee>
<fee:notAfter>2010-01-02T10:00:00.000Z</fee:notAfter>
</fee:command>
</fee:cd>
@@ -39,7 +39,7 @@
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.000Z">100.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.002Z">100.00</fee:fee>
<fee:notAfter>2010-01-02T10:00:00.000Z</fee:notAfter>
</fee:command>
</fee:cd>
@@ -50,7 +50,7 @@
<fee:command name="create">
<fee:period unit="y">1</fee:period>
<fee:fee description="create">13.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.000Z">100.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.002Z">100.00</fee:fee>
<fee:notAfter>2010-01-02T10:00:00.000Z</fee:notAfter>
</fee:command>
</fee:cd>
@@ -19,7 +19,7 @@
<fee:command>create</fee:command>
<fee:period unit="y">1</fee:period>
<fee:fee description="create">100.00</fee:fee>
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.000Z">100.00
<fee:fee description="Early Access Period, fee expires: 2010-01-02T10:00:00.002Z">100.00
</fee:fee>
<fee:class>premium</fee:class>
</fee:cd>
@@ -916,3 +916,7 @@ class google.registry.persistence.DomainHistoryVKey {
java.lang.Long historyRevisionId;
java.lang.String repoId;
}
class google.registry.schema.replay.LastSqlTransaction {
@Id long id;
long transactionId;
}
@@ -0,0 +1 @@
mock-maker-inline
+13 -5
View File
@@ -20,8 +20,6 @@ plugins {
ext {
Set restrictedDbEnv =
[ 'sandbox', 'production' ].asUnmodifiable()
Set allDbEnv =
[ 'alpha', 'crash' ].plus(restrictedDbEnv).asUnmodifiable()
def dbServerProperty = 'dbServer'
def dbNameProperty = 'dbName'
@@ -57,12 +55,12 @@ ext {
}
getJdbcAccessInfo = {
if (allDbEnv.contains(dbServer)) {
return getSocketFactoryAccessInfo(dbServer)
if (rootProject.projects.keySet().contains(environment)) {
return getSocketFactoryAccessInfo(environment)
} else if (!dbServer.isEmpty()) {
return getAccessInfoByHostPort(dbServer)
} else {
// Not connecting to a database. Return a dummy object for Flyway config.
// Not running flyway tasks. Return a dummy object for Flyway config.
return [ url: '', user: '', password: '' ]
}
}
@@ -137,6 +135,16 @@ publishing {
}
}
// Adds flyway tasks such as: flywayInfo, flywayValidate, flywayMigrate (deploying the schema in
// local repository), and flywayClean (dropping all data in the database). The latter two commands
// are disallowed in environments listed in ext.restrictedDbEnv.
//
// Examples:
// Get info in alpha: nom_build :db:flywayInfo --environment=alpha
// Deploy schema to a local test instance and override the database name:
// nom_build :db:flywayMigrate --dbServer=localhost:5432 --dbName=not-default \
// --dbUser=... --dbPassword=...
flyway {
def accessInfo = project.ext.getJdbcAccessInfo()
+3 -2
View File
@@ -73,6 +73,7 @@ ext {
'com.google.http-client:google-http-client-appengine:1.34.1',
'com.google.http-client:google-http-client-jackson2:1.34.1',
'com.google.http-client:google-http-client:1.34.1',
'com.google.inject:guice:5.0.1',
'com.google.javascript:closure-compiler:v20190301',
'com.google.monitoring-client:contrib:1.0.7',
'com.google.monitoring-client:metrics:1.0.7',
@@ -157,8 +158,8 @@ ext {
'org.joda:joda-money:1.0.1',
'org.json:json:20160810',
'org.jsoup:jsoup:1.13.1',
'org.mockito:mockito-core:3.3.3',
'org.mockito:mockito-junit-jupiter:3.3.3',
'org.mockito:mockito-core:3.7.7',
'org.mockito:mockito-junit-jupiter:3.7.7',
'org.mortbay.jetty:jetty:6.1.26',
'org.postgresql:postgresql:42.2.18',
'org.seleniumhq.selenium:selenium-api:3.141.59',
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+3
View File
@@ -28,6 +28,7 @@ dependencies {
compile deps['org.bouncycastle:bcpkix-jdk15on']
compile deps['org.bouncycastle:bcprov-jdk15on']
compile project(':util')
compile project(':common')
runtime deps['com.google.flogger:flogger-system-backend']
runtime deps['io.netty:netty-tcnative-boringssl-static']
@@ -38,9 +39,11 @@ dependencies {
testCompile deps['org.junit.jupiter:junit-jupiter-params']
testCompile deps['org.bouncycastle:bcpkix-jdk15on']
testCompile deps['org.bouncycastle:bcprov-jdk15on']
testCompile project(path: ':common', configuration: 'testing')
annotationProcessor deps['com.google.dagger:dagger-compiler']
testAnnotationProcessor deps['com.google.dagger:dagger-compiler']
compile 'joda-time:joda-time:2.9.2'
}
// Make testing artifacts available to be depended up on by other projects.
@@ -19,6 +19,7 @@ import static google.registry.util.X509Utils.getCertificateHash;
import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
import google.registry.util.Clock;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler.Sharable;
@@ -29,6 +30,7 @@ import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.Future;
@@ -41,6 +43,7 @@ import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.util.function.Supplier;
import javax.net.ssl.SSLSession;
import org.joda.time.DateTime;
/**
* Adds a server side SSL handler to the channel pipeline.
@@ -66,6 +69,29 @@ public class SslServerInitializer<C extends Channel> extends ChannelInitializer<
public static final AttributeKey<Promise<X509Certificate>> CLIENT_CERTIFICATE_PROMISE_KEY =
AttributeKey.valueOf("CLIENT_CERTIFICATE_PROMISE_KEY");
/**
* The list of cipher suites that are currently acceptable to create a successful handshake.
*
* <p>This list includes all of the current TLS1.3 ciphers and a collection of TLS1.2 ciphers with
* no known security vulnerabilities. Note that OpenSSL uses a separate nomenclature for the
* ciphers internally but the IANA names listed here will be transparently translated by the
* OpenSSL provider (if used), so there is no need to include the OpenSSL name variants here. More
* information about these cipher suites and their OpenSSL names can be found at ciphersuite.info.
*/
private static final ImmutableList ALLOWED_TLS_CIPHERS =
ImmutableList.of(
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_CHACHA20_POLY1305_SHA256",
"TLS_AES_128_CCM_SHA256",
"TLS_AES_128_CCM_8_SHA256");
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final boolean requireClientCert;
// TODO(jianglai): Always validate client certs (if required).
@@ -76,13 +102,19 @@ public class SslServerInitializer<C extends Channel> extends ChannelInitializer<
private final Supplier<PrivateKey> privateKeySupplier;
private final Supplier<ImmutableList<X509Certificate>> certificatesSupplier;
private final ImmutableList<String> supportedSslVersions;
// TODO(sarahbot): Remove this variable and its check after enforcement start date has passed.
private final ImmutableList<String> oldSupportedSslVersions;
private final DateTime enforcementStartTime;
private final Clock clock;
public SslServerInitializer(
boolean requireClientCert,
boolean validateClientCert,
SslProvider sslProvider,
Supplier<PrivateKey> privateKeySupplier,
Supplier<ImmutableList<X509Certificate>> certificatesSupplier) {
Supplier<ImmutableList<X509Certificate>> certificatesSupplier,
DateTime enforcementStartTime,
Clock clock) {
logger.atInfo().log("Server SSL Provider: %s", sslProvider);
checkArgument(
requireClientCert || !validateClientCert,
@@ -94,10 +126,16 @@ public class SslServerInitializer<C extends Channel> extends ChannelInitializer<
this.certificatesSupplier = certificatesSupplier;
this.supportedSslVersions =
sslProvider == SslProvider.OPENSSL
? ImmutableList.of("TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1")
// JDK support for TLS 1.3 won't be available until 2020-07-14 at the earliest.
? ImmutableList.of("TLSv1.3", "TLSv1.2")
// JDK support for TLS 1.3 won't be available until 2021-04-20 at the earliest.
// See: https://java.com/en/jre-jdk-cryptoroadmap.html
: ImmutableList.of("TLSv1.2");
this.oldSupportedSslVersions =
sslProvider == SslProvider.OPENSSL
? ImmutableList.of("TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1")
: ImmutableList.of("TLSv1.2", "TLSv1.1", "TLSv1");
this.enforcementStartTime = enforcementStartTime;
this.clock = clock;
}
@Override
@@ -109,8 +147,15 @@ public class SslServerInitializer<C extends Channel> extends ChannelInitializer<
.sslProvider(sslProvider)
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.clientAuth(requireClientCert ? ClientAuth.REQUIRE : ClientAuth.NONE)
.protocols(supportedSslVersions)
.protocols(
enforcementStartTime.isBefore(clock.nowUtc())
? supportedSslVersions
: oldSupportedSslVersions)
.ciphers(
enforcementStartTime.isBefore(clock.nowUtc()) ? ALLOWED_TLS_CIPHERS : null,
SupportedCipherSuiteFilter.INSTANCE)
.build();
logger.atInfo().log("Available Cipher Suites: %s", sslContext.cipherSuites());
SslHandler sslHandler = sslContext.newHandler(channel.alloc());
if (requireClientCert) {
@@ -23,6 +23,7 @@ import static google.registry.networking.handler.SslServerInitializer.CLIENT_CER
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import google.registry.testing.FakeClock;
import google.registry.util.SelfSignedCaCertificate;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
@@ -42,13 +43,16 @@ import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.time.Instant;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Stream;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSession;
import org.joda.time.DateTime;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
@@ -97,19 +101,29 @@ class SslServerInitializerTest {
validateClientCert,
sslProvider,
Suppliers.ofInstance(privateKey),
Suppliers.ofInstance(ImmutableList.copyOf(certificates)));
Suppliers.ofInstance(ImmutableList.copyOf(certificates)),
DateTime.parse("2021-04-01T16:00:00Z"),
new FakeClock(DateTime.parse("2021-05-01T16:00:00Z")));
}
private ChannelHandler getClientHandler(
SslProvider sslProvider,
X509Certificate trustedCertificate,
PrivateKey privateKey,
X509Certificate certificate) {
X509Certificate certificate,
String protocol,
List<String> cipher) {
return new ChannelInitializer<LocalChannel>() {
@Override
protected void initChannel(LocalChannel ch) throws Exception {
SslContextBuilder sslContextBuilder =
SslContextBuilder.forClient().trustManager(trustedCertificate).sslProvider(sslProvider);
SslContextBuilder.forClient()
.trustManager(trustedCertificate)
.sslProvider(sslProvider)
.ciphers(cipher);
if (protocol != null) {
sslContextBuilder.protocols(protocol);
}
if (privateKey != null && certificate != null) {
sslContextBuilder.keyManager(privateKey, certificate);
}
@@ -127,6 +141,14 @@ class SslServerInitializerTest {
};
}
private ChannelHandler getClientHandler(
SslProvider sslProvider,
X509Certificate trustedCertificate,
PrivateKey privateKey,
X509Certificate certificate) {
return getClientHandler(sslProvider, trustedCertificate, privateKey, certificate, null, null);
}
@ParameterizedTest
@MethodSource("provideTestCombinations")
void testSuccess_swappedInitializerWithSslHandler(SslProvider sslProvider) throws Exception {
@@ -137,7 +159,9 @@ class SslServerInitializerTest {
false,
sslProvider,
Suppliers.ofInstance(ssc.key()),
Suppliers.ofInstance(ImmutableList.of(ssc.cert())));
Suppliers.ofInstance(ImmutableList.of(ssc.cert())),
DateTime.parse("2021-04-01T16:00:00Z"),
new FakeClock(DateTime.parse("2021-05-01T16:00:00Z")));
EmbeddedChannel channel = new EmbeddedChannel();
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(sslServerInitializer);
@@ -172,6 +196,174 @@ class SslServerInitializerTest {
assertThat(sslSession.getPeerCertificates()).asList().containsExactly(serverSsc.cert());
}
@ParameterizedTest
@MethodSource("provideTestCombinations")
void testFailure_cipherNotAccepted(SslProvider sslProvider) throws Exception {
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
LocalAddress localAddress = new LocalAddress("CIPHER_NOT_ACCEPTED_" + sslProvider);
nettyExtension.setUpServer(
localAddress, getServerHandler(true, true, sslProvider, serverSsc.key(), serverSsc.cert()));
SelfSignedCaCertificate clientSsc =
SelfSignedCaCertificate.create(
"CLIENT",
Date.from(Instant.now().minus(Duration.ofDays(2))),
Date.from(Instant.now().plus(Duration.ofDays(1))));
nettyExtension.setUpClient(
localAddress,
getClientHandler(
sslProvider,
serverSsc.cert(),
clientSsc.key(),
clientSsc.cert(),
"TLSv1.2",
Collections.singletonList("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA")));
verifySslException(
nettyExtension.getServerChannel(),
channel -> channel.attr(CLIENT_CERTIFICATE_PROMISE_KEY).get().get(),
SSLHandshakeException.class);
}
@ParameterizedTest
@MethodSource("provideTestCombinations")
void testSuccess_someCiphersNotAccepted(SslProvider sslProvider) throws Exception {
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
LocalAddress localAddress = new LocalAddress("SOME_CIPHERS_NOT_ACCEPTED_" + sslProvider);
nettyExtension.setUpServer(
localAddress,
new SslServerInitializer<LocalChannel>(
true,
true,
sslProvider,
Suppliers.ofInstance(serverSsc.key()),
Suppliers.ofInstance(ImmutableList.of(serverSsc.cert())),
DateTime.parse("2021-04-01T16:00:00Z"),
new FakeClock(DateTime.parse("2021-05-01T16:00:00Z"))));
SelfSignedCaCertificate clientSsc =
SelfSignedCaCertificate.create(
"CLIENT",
Date.from(Instant.now().minus(Duration.ofDays(2))),
Date.from(Instant.now().plus(Duration.ofDays(1))));
nettyExtension.setUpClient(
localAddress,
getClientHandler(
sslProvider,
serverSsc.cert(),
clientSsc.key(),
clientSsc.cert(),
"TLSv1.2",
ImmutableList.of(
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", // Only accepted cipher
"TLS_RSA_WITH_AES_256_CBC_SHA")));
SSLSession sslSession = setUpSslChannel(nettyExtension.getClientChannel(), serverSsc.cert());
nettyExtension.assertThatMessagesWork();
assertThat(sslSession.getLocalCertificates()).asList().containsExactly(clientSsc.cert());
assertThat(sslSession.getPeerCertificates()).asList().containsExactly(serverSsc.cert());
assertThat(sslSession.getCipherSuite()).isEqualTo("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
}
@ParameterizedTest
@MethodSource("provideTestCombinations")
void testSuccess_cipherNotAccepted_beforeEnforcementDate(SslProvider sslProvider)
throws Exception {
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
LocalAddress localAddress = new LocalAddress("CIPHER_ACCEPTED_BEFORE_DATE_" + sslProvider);
nettyExtension.setUpServer(
localAddress,
new SslServerInitializer<LocalChannel>(
true,
true,
sslProvider,
Suppliers.ofInstance(serverSsc.key()),
Suppliers.ofInstance(ImmutableList.of(serverSsc.cert())),
DateTime.parse("2021-04-01T16:00:00Z"),
new FakeClock(DateTime.parse("2021-03-01T16:00:00Z"))));
SelfSignedCaCertificate clientSsc =
SelfSignedCaCertificate.create(
"CLIENT",
Date.from(Instant.now().minus(Duration.ofDays(2))),
Date.from(Instant.now().plus(Duration.ofDays(1))));
nettyExtension.setUpClient(
localAddress,
getClientHandler(
sslProvider,
serverSsc.cert(),
clientSsc.key(),
clientSsc.cert(),
"TLSv1.2",
Collections.singletonList("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA")));
SSLSession sslSession = setUpSslChannel(nettyExtension.getClientChannel(), serverSsc.cert());
nettyExtension.assertThatMessagesWork();
assertThat(sslSession.getLocalCertificates()).asList().containsExactly(clientSsc.cert());
assertThat(sslSession.getPeerCertificates()).asList().containsExactly(serverSsc.cert());
}
@ParameterizedTest
@MethodSource("provideTestCombinations")
void testFailure_protocolNotAccepted(SslProvider sslProvider) throws Exception {
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
LocalAddress localAddress = new LocalAddress("PROTOCOL_NOT_ACCEPTED_" + sslProvider);
nettyExtension.setUpServer(
localAddress, getServerHandler(true, true, sslProvider, serverSsc.key(), serverSsc.cert()));
SelfSignedCaCertificate clientSsc =
SelfSignedCaCertificate.create(
"CLIENT",
Date.from(Instant.now().minus(Duration.ofDays(2))),
Date.from(Instant.now().plus(Duration.ofDays(1))));
nettyExtension.setUpClient(
localAddress,
getClientHandler(
sslProvider, serverSsc.cert(), clientSsc.key(), clientSsc.cert(), "TLSv1.1", null));
verifySslException(
nettyExtension.getServerChannel(),
channel -> channel.attr(CLIENT_CERTIFICATE_PROMISE_KEY).get().get(),
SSLHandshakeException.class);
}
@ParameterizedTest
@MethodSource("provideTestCombinations")
void testSuccess_protocolNotAccepted_beforeEnforcementDate(SslProvider sslProvider)
throws Exception {
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
LocalAddress localAddress = new LocalAddress("PROTOCOL_ACCEPTED_BEFORE_DATE_" + sslProvider);
nettyExtension.setUpServer(
localAddress,
new SslServerInitializer<LocalChannel>(
true,
true,
sslProvider,
Suppliers.ofInstance(serverSsc.key()),
Suppliers.ofInstance(ImmutableList.of(serverSsc.cert())),
DateTime.parse("2021-04-01T16:00:00Z"),
new FakeClock(DateTime.parse("2021-03-01T16:00:00Z"))));
SelfSignedCaCertificate clientSsc =
SelfSignedCaCertificate.create(
"CLIENT",
Date.from(Instant.now().minus(Duration.ofDays(2))),
Date.from(Instant.now().plus(Duration.ofDays(1))));
nettyExtension.setUpClient(
localAddress,
getClientHandler(
sslProvider, serverSsc.cert(), clientSsc.key(), clientSsc.cert(), "TLSv1.1", null));
SSLSession sslSession = setUpSslChannel(nettyExtension.getClientChannel(), serverSsc.cert());
nettyExtension.assertThatMessagesWork();
assertThat(sslSession.getLocalCertificates()).asList().containsExactly(clientSsc.cert());
assertThat(sslSession.getPeerCertificates()).asList().containsExactly(serverSsc.cert());
}
@ParameterizedTest
@MethodSource("provideTestCombinations")
void testFailure_clientCertExpired(SslProvider sslProvider) throws Exception {
@@ -48,8 +48,8 @@ javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
junit:junit:4.12
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.5
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apiguardian:apiguardian-api:1.1.0
@@ -64,8 +64,8 @@ org.junit.jupiter:junit-jupiter-params:5.6.2
org.junit.platform:junit-platform-commons:1.6.2
org.junit.platform:junit-platform-engine:1.6.2
org.junit:junit-bom:5.6.2
org.mockito:mockito-core:3.3.3
org.objenesis:objenesis:2.6
org.mockito:mockito-core:3.7.7
org.objenesis:objenesis:3.1
org.opentest4j:opentest4j:1.2.0
org.yaml:snakeyaml:1.17
xerces:xmlParserAPIs:2.6.2
@@ -48,8 +48,8 @@ javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
junit:junit:4.12
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.5
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apiguardian:apiguardian-api:1.1.0
@@ -64,8 +64,8 @@ org.junit.jupiter:junit-jupiter-params:5.6.2
org.junit.platform:junit-platform-commons:1.6.2
org.junit.platform:junit-platform-engine:1.6.2
org.junit:junit-bom:5.6.2
org.mockito:mockito-core:3.3.3
org.objenesis:objenesis:2.6
org.mockito:mockito-core:3.7.7
org.objenesis:objenesis:3.1
org.opentest4j:opentest4j:1.2.0
org.yaml:snakeyaml:1.17
xerces:xmlParserAPIs:2.6.2
@@ -48,8 +48,8 @@ javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
junit:junit:4.12
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.5
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apiguardian:apiguardian-api:1.1.0
@@ -64,8 +64,8 @@ org.junit.jupiter:junit-jupiter-params:5.6.2
org.junit.platform:junit-platform-commons:1.6.2
org.junit.platform:junit-platform-engine:1.6.2
org.junit:junit-bom:5.6.2
org.mockito:mockito-core:3.3.3
org.objenesis:objenesis:2.6
org.mockito:mockito-core:3.7.7
org.objenesis:objenesis:3.1
org.opentest4j:opentest4j:1.2.0
org.yaml:snakeyaml:1.17
xerces:xmlParserAPIs:2.6.2
@@ -48,8 +48,8 @@ javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
junit:junit:4.12
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.5
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apiguardian:apiguardian-api:1.1.0
@@ -64,8 +64,8 @@ org.junit.jupiter:junit-jupiter-params:5.6.2
org.junit.platform:junit-platform-commons:1.6.2
org.junit.platform:junit-platform-engine:1.6.2
org.junit:junit-bom:5.6.2
org.mockito:mockito-core:3.3.3
org.objenesis:objenesis:2.6
org.mockito:mockito-core:3.7.7
org.objenesis:objenesis:3.1
org.opentest4j:opentest4j:1.2.0
org.yaml:snakeyaml:1.17
xerces:xmlParserAPIs:2.6.2
@@ -0,0 +1 @@
mock-maker-inline
+2 -1
View File
@@ -19,7 +19,8 @@
rootProject.ext.projects = ['production': 'your-production-project',
'sandbox' : 'your-sandbox-project',
'alpha' : 'your-alpha-project',
'crash' : 'your-crash-project']
'crash' : 'your-crash-project',
'qa' : 'your-qa-project']
// The project to host your development/deployment infrastructure. It hosts
// things like release artifacts, CI/CD system, etc.
@@ -53,8 +53,8 @@ javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
junit:junit:4.12
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.5
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apiguardian:apiguardian-api:1.1.0
@@ -69,7 +69,7 @@ org.junit.jupiter:junit-jupiter-params:5.6.2
org.junit.platform:junit-platform-commons:1.6.2
org.junit.platform:junit-platform-engine:1.6.2
org.junit:junit-bom:5.6.2
org.mockito:mockito-core:3.3.3
org.objenesis:objenesis:2.6
org.mockito:mockito-core:3.7.7
org.objenesis:objenesis:3.1
org.opentest4j:opentest4j:1.2.0
org.yaml:snakeyaml:1.17
@@ -53,8 +53,8 @@ javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
junit:junit:4.12
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.5
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apiguardian:apiguardian-api:1.1.0
@@ -69,7 +69,7 @@ org.junit.jupiter:junit-jupiter-params:5.6.2
org.junit.platform:junit-platform-commons:1.6.2
org.junit.platform:junit-platform-engine:1.6.2
org.junit:junit-bom:5.6.2
org.mockito:mockito-core:3.3.3
org.objenesis:objenesis:2.6
org.mockito:mockito-core:3.7.7
org.objenesis:objenesis:3.1
org.opentest4j:opentest4j:1.2.0
org.yaml:snakeyaml:1.17
@@ -53,8 +53,8 @@ javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
junit:junit:4.12
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.5
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apiguardian:apiguardian-api:1.1.0
@@ -69,7 +69,7 @@ org.junit.jupiter:junit-jupiter-params:5.6.2
org.junit.platform:junit-platform-commons:1.6.2
org.junit.platform:junit-platform-engine:1.6.2
org.junit:junit-bom:5.6.2
org.mockito:mockito-core:3.3.3
org.objenesis:objenesis:2.6
org.mockito:mockito-core:3.7.7
org.objenesis:objenesis:3.1
org.opentest4j:opentest4j:1.2.0
org.yaml:snakeyaml:1.17
@@ -53,8 +53,8 @@ javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
junit:junit:4.12
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.5
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apiguardian:apiguardian-api:1.1.0
@@ -69,7 +69,7 @@ org.junit.jupiter:junit-jupiter-params:5.6.2
org.junit.platform:junit-platform-commons:1.6.2
org.junit.platform:junit-platform-engine:1.6.2
org.junit:junit-bom:5.6.2
org.mockito:mockito-core:3.3.3
org.objenesis:objenesis:2.6
org.mockito:mockito-core:3.7.7
org.objenesis:objenesis:3.1
org.opentest4j:opentest4j:1.2.0
org.yaml:snakeyaml:1.17
@@ -50,6 +50,7 @@ import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Qualifier;
import javax.inject.Singleton;
import org.joda.time.DateTime;
/** A module that provides the {@link FrontendProtocol} used for epp protocol. */
@Module
@@ -159,11 +160,19 @@ public final class EppProtocolModule {
@Provides
@EppProtocol
static SslServerInitializer<NioSocketChannel> provideSslServerInitializer(
ProxyConfig config,
SslProvider sslProvider,
Supplier<PrivateKey> privateKeySupplier,
Supplier<ImmutableList<X509Certificate>> certificatesSupplier) {
Supplier<ImmutableList<X509Certificate>> certificatesSupplier,
Clock clock) {
return new SslServerInitializer<>(
true, false, sslProvider, privateKeySupplier, certificatesSupplier);
true,
false,
sslProvider,
privateKeySupplier,
certificatesSupplier,
DateTime.parse(config.tlsEnforcementStartTime),
clock);
}
@Provides
@@ -48,6 +48,7 @@ public class ProxyConfig {
public WebWhois webWhois;
public HttpsRelay httpsRelay;
public Metrics metrics;
public String tlsEnforcementStartTime;
/** Configuration options that apply to GCS. */
public static class Gcs {
@@ -21,6 +21,8 @@ import dagger.multibindings.IntoSet;
import google.registry.networking.handler.SslServerInitializer;
import google.registry.proxy.Protocol.FrontendProtocol;
import google.registry.proxy.handler.WebWhoisRedirectHandler;
import google.registry.util.Clock;
import google.registry.util.DateTimeUtils;
import io.netty.channel.ChannelHandler;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
@@ -133,8 +135,15 @@ public final class WebWhoisProtocolsModule {
static SslServerInitializer<NioSocketChannel> provideSslServerInitializer(
SslProvider sslProvider,
Supplier<PrivateKey> privateKeySupplier,
Supplier<ImmutableList<X509Certificate>> certificatesSupplier) {
Supplier<ImmutableList<X509Certificate>> certificatesSupplier,
Clock clock) {
return new SslServerInitializer<>(
false, false, sslProvider, privateKeySupplier, certificatesSupplier);
false,
false,
sslProvider,
privateKeySupplier,
certificatesSupplier,
DateTimeUtils.END_OF_TIME,
clock);
}
}
@@ -8,6 +8,9 @@
# GCP project ID
projectId: your-gcp-project-id
# Time to begin enforcement of TLS versions and cipher suites.
tlsEnforcementStartTime: "2021-04-01T16:00:00Z"
# OAuth scope that the GoogleCredential will be constructed with. This list
# should include all service scopes that the proxy depends on.
gcpScopes:
@@ -1 +1,4 @@
# Add environment-specific proxy configuration here.
# Time to begin enforcement of TLS versions and cipher suites.
tlsEnforcementStartTime: "1970-01-01T00:00:00Z"
@@ -1 +1,4 @@
# Add environment-specific proxy configuration here.
# Time to begin enforcement of TLS versions and cipher suites.
tlsEnforcementStartTime: "1970-01-01T00:00:00Z"
@@ -1 +1,4 @@
# Add environment-specific proxy configuration here.
# Time to begin enforcement of TLS versions and cipher suites.
tlsEnforcementStartTime: "1970-01-01T00:00:00Z"
@@ -1 +1,4 @@
# Add environment-specific proxy configuration here.
# Time to begin enforcement of TLS versions and cipher suites.
tlsEnforcementStartTime: "1970-01-01T00:00:00Z"
@@ -1 +1,4 @@
# Add environment-specific proxy configuration here.
# Time to begin enforcement of TLS versions and cipher suites.
tlsEnforcementStartTime: "1970-01-01T00:00:00Z"
@@ -1 +1,4 @@
# Add environment-specific proxy configuration here.
# Time to begin enforcement of TLS versions and cipher suites.
tlsEnforcementStartTime: "1970-01-01T00:00:00Z"
@@ -0,0 +1 @@
mock-maker-inline
@@ -37,8 +37,8 @@ javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
junit:junit:4.13
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.5
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apiguardian:apiguardian-api:1.1.0
@@ -48,13 +48,13 @@ org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:3.5.0
org.hamcrest:hamcrest-core:2.2
org.hamcrest:hamcrest:2.2
org.junit.jupiter:junit-jupiter-api:5.6.2
org.junit.jupiter:junit-jupiter-engine:5.6.2
org.junit.platform:junit-platform-commons:1.6.2
org.junit.platform:junit-platform-engine:1.6.2
org.junit:junit-bom:5.6.2
org.mockito:mockito-core:3.3.3
org.mockito:mockito-junit-jupiter:3.3.3
org.objenesis:objenesis:2.6
org.junit.jupiter:junit-jupiter-api:5.7.0
org.junit.jupiter:junit-jupiter-engine:5.7.0
org.junit.platform:junit-platform-commons:1.7.0
org.junit.platform:junit-platform-engine:1.7.0
org.junit:junit-bom:5.7.0
org.mockito:mockito-core:3.7.7
org.mockito:mockito-junit-jupiter:3.7.7
org.objenesis:objenesis:3.1
org.opentest4j:opentest4j:1.2.0
org.yaml:snakeyaml:1.17
@@ -37,8 +37,8 @@ javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
junit:junit:4.13
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.5
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apiguardian:apiguardian-api:1.1.0
@@ -53,8 +53,8 @@ org.junit.jupiter:junit-jupiter-engine:5.6.2
org.junit.platform:junit-platform-commons:1.6.2
org.junit.platform:junit-platform-engine:1.6.2
org.junit:junit-bom:5.6.2
org.mockito:mockito-core:3.3.3
org.mockito:mockito-junit-jupiter:3.3.3
org.objenesis:objenesis:2.6
org.mockito:mockito-core:3.7.7
org.mockito:mockito-junit-jupiter:3.7.7
org.objenesis:objenesis:3.1
org.opentest4j:opentest4j:1.2.0
org.yaml:snakeyaml:1.17
@@ -39,8 +39,8 @@ javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
junit:junit:4.13
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.5
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apiguardian:apiguardian-api:1.1.0
@@ -50,13 +50,13 @@ org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:3.5.0
org.hamcrest:hamcrest-core:2.2
org.hamcrest:hamcrest:2.2
org.junit.jupiter:junit-jupiter-api:5.6.2
org.junit.jupiter:junit-jupiter-engine:5.6.2
org.junit.platform:junit-platform-commons:1.6.2
org.junit.platform:junit-platform-engine:1.6.2
org.junit:junit-bom:5.6.2
org.mockito:mockito-core:3.3.3
org.mockito:mockito-junit-jupiter:3.3.3
org.objenesis:objenesis:2.6
org.junit.jupiter:junit-jupiter-api:5.7.0
org.junit.jupiter:junit-jupiter-engine:5.7.0
org.junit.platform:junit-platform-commons:1.7.0
org.junit.platform:junit-platform-engine:1.7.0
org.junit:junit-bom:5.7.0
org.mockito:mockito-core:3.7.7
org.mockito:mockito-junit-jupiter:3.7.7
org.objenesis:objenesis:3.1
org.opentest4j:opentest4j:1.2.0
org.yaml:snakeyaml:1.17
@@ -39,8 +39,8 @@ javax.mail:mail:1.4
javax.xml.bind:jaxb-api:2.3.0
joda-time:joda-time:2.9.2
junit:junit:4.13
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.5
net.bytebuddy:byte-buddy-agent:1.10.19
net.bytebuddy:byte-buddy:1.10.19
org.apache.httpcomponents:httpclient:4.5.11
org.apache.httpcomponents:httpcore:4.4.13
org.apiguardian:apiguardian-api:1.1.0
@@ -50,13 +50,13 @@ org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:3.5.0
org.hamcrest:hamcrest-core:2.2
org.hamcrest:hamcrest:2.2
org.junit.jupiter:junit-jupiter-api:5.6.2
org.junit.jupiter:junit-jupiter-engine:5.6.2
org.junit.platform:junit-platform-commons:1.6.2
org.junit.platform:junit-platform-engine:1.6.2
org.junit:junit-bom:5.6.2
org.mockito:mockito-core:3.3.3
org.mockito:mockito-junit-jupiter:3.3.3
org.objenesis:objenesis:2.6
org.junit.jupiter:junit-jupiter-api:5.7.0
org.junit.jupiter:junit-jupiter-engine:5.7.0
org.junit.platform:junit-platform-commons:1.7.0
org.junit.platform:junit-platform-engine:1.7.0
org.junit:junit-bom:5.7.0
org.mockito:mockito-core:3.7.7
org.mockito:mockito-junit-jupiter:3.7.7
org.objenesis:objenesis:3.1
org.opentest4j:opentest4j:1.2.0
org.yaml:snakeyaml:1.17
@@ -0,0 +1 @@
mock-maker-inline