1
0
mirror of https://github.com/google/nomulus synced 2026-05-24 08:41:48 +00:00

Compare commits

...

24 Commits

Author SHA1 Message Date
sarahcaseybot
c729a30d38 Add ECDSA key validation to Certificate Checker (#855)
* Add ecdsa key validation

* Add some comments

* fix merge conflicts

* change variable names

* Separate tests

* separate curve tests
2020-11-09 15:28:48 -05:00
gbrodman
acce1a7d3b Add DuallyWrittenEntity convenience interface (#866)
* Add DuallyWrittenEntity convenience interface

For classes that are dually written we don't need to worry about replay
conversion, so this just allows us to remove unnecessary unimportant
methods

* Rename DWE to NonReplicatedEntity and enable test
2020-11-09 15:13:40 -05:00
gbrodman
0c6363c04f Persist two singleton entities in SQL tables (#860)
* Persist two singleton entities in SQL tables

A table might not be the best place to store singleton entities, but by
doing this we ensure we can easily inspect them later and use the same
sort of persistence logic for these that we do elsewhere.

ServerSecret is stored upon retrieval so that we make sure that the same
secret is used in both Datastore and SQL (we wouldn't want to change
it).

* Responses to CR

* Don't have a separate ID for the singleton entities

* Rename secret UUID

* Rename and regenerate
2020-11-09 13:47:42 -05:00
Shicong Huang
cb764b5d30 Convert Registry realted tests to working with SQL (#862) 2020-11-09 12:10:55 -05:00
Shicong Huang
420f3bf380 Add missing foreign key constraints (#845)
* Add missing foreign key constraints

* Fix failed unit tests
2020-11-09 10:55:29 -05:00
Michael Muller
7097b0f5e6 Allow InjectExtension to apply in "before each" (#864)
* Allow InjectExtension to apply in "before each"

InjectExtension is non-standard in that, while it cleans up the changes it has
applied in its "after each" method, those changes must be applied by the test
driver or by another extension.  This breaks extension ordering, which is
something we're going to need to make heavy use of for the ReplayExtension.

Allow changes to be specified (but not activated) after construction and apply
all unactivated changes during "before each."
2020-11-09 07:52:55 -05:00
Shicong Huang
30d57d9476 Upgrade Postgres realted packages to latest version (#861) 2020-11-06 10:14:44 -05:00
sarahcaseybot
700d612ff9 Remove November 1 check (#859) 2020-11-04 13:10:48 -05:00
Shicong Huang
e3d400958c Add a command to fix duplicate id issue for BillingEvent.Recurring (#798)
* Refactor ResaveEntitiesWithUniqueIdCommand to extract common methods

* Add a command to dedupe BillingEvent.Recurring entities
2020-11-03 16:11:56 -05:00
gbrodman
aa84d5d138 Fix entity annotations for a few classes (#856)
* Fix entity annotations for a few classes

- Abstract classes shouldn't implement DatastoreEntity/SqlEntity
- We aren't persisting Modification in SQL
- Because DelegationSignerData is embedded, we don't need to worry about
converting and persisting it
- DomainDsDataHistory isn't persisted in Datastore
2020-11-03 10:28:34 -05:00
gbrodman
d685f7e2df Add a SQL schema and DAO for KmsSecretRevision (#840)
* Add a SQL schema and DAO for KmsSecretRevision

The dual-object nature of KmsSecret and KmsSecretRevision will not be
necessary once we have moved to SQL. In that world, the only object will
be the one now called KmsSecretRevision. KmsSecretRevision already
stores its parent so all we need to do is convert that key to the String
secretName (or from the secretName to the key, if loading from SQL) and
select the max revision ID for a given secret name.

In a future PR, we will add a dual-writing DAO to these objects and
perform the dual writes, similar to how ReservedList functions.

* Regenerate diagram

* Rename revisionId and cryptoKeyVersionName

* Fix SQL files and diagram
2020-10-30 18:45:43 -04:00
gbrodman
40eef2a06c Add SQL schema and DAO for SignedMarkRevocationList (#850)
* Add SQL schema and DAO for SignedMarkRevocationList

This gets saved every day so we're not concerned about history, meaning
we can dual-write and/or dual-read without concern. The structure here
is somewhat similar to the ClaimsListDao and related classes.

* Update the DB files
2020-10-30 17:52:09 -04:00
sarahcaseybot
8bd5eb4eca Move CertificateChecker to core/ (#852)
* Move CertificateChecker to core/

* rename certificates/ to certs/
2020-10-30 15:57:12 -04:00
Weimin Yu
83918e92b5 Sync the live folder after Nomulus rollback (#854)
* Sync the live folder after Nomulus rollback

To update the nomulus tool on corp desktop, the artifacts from the
rollback target release should be copied to the 'live' folder.

* Fix a test
2020-10-29 16:21:56 -04:00
Shicong Huang
5bba65835a Change primary key of DelegationSignerData and add its history table (#841)
* Change primary key of DelegationSignerData and add its history table

* Change primary key and resolve comments

* Rebase on HEAD
2020-10-29 16:19:15 -04:00
Shicong Huang
1e51f51979 Use TransactionManager APIs in DatastoreHelper (#849)
* Make DatastoreHelper support Postgresql

* Rebase on HEAD

* Resolve comments

* Use put* inside insert* and update*

* Resolve comments
2020-10-29 11:41:04 -04:00
Weimin Yu
db2e896d42 An automated rollback tool for Nomulus (#847)
* An automated rollback tool for Nomulus

A tool that directs traffic between deployed versions. It handles the
conversion between Nomulus tags and AppEngine versions, executes schema
compatibility tests, ensures that steps are executed in the correct order,
and updates deployment records appropriately.
2020-10-29 10:37:20 -04:00
Michael Muller
478064f32b Add a test for ImmutableObject significant fields (#853)
This should have been introduced in #846.  Better late than never.
2020-10-28 11:11:42 -04:00
Shicong Huang
0db535b838 Add ER diagram links to db/README.md (#851) 2020-10-28 10:13:39 -04:00
Shicong Huang
3705f37fab Add a build task to upload ER diagrams to GCS (#844)
* Add a build task to upload ER diagrams to GCS

* Merge ER diagram task into cloudbuild-javadoc
2020-10-27 10:41:12 -04:00
Michael Muller
86bdd154bc Restore ofy keys in GracePeriod objects (#846)
* Restore ofy keys in GracePeriod objects

Restore the ofy keys when loading GracePeriod object from SQL.  There's no
clear way to do this using the normal approach (fix-up during a PostLoad
method) because fixups to these violate immutability after hibernate has
already obtained their hash values.  Instead, we force reconstitution of the
ofy keys in all public methods that access them (including equals() and
hashCode()) so that they can be generated before an invalid hash is generated.

As part of this change, convert the GracePeriod id from an autogenerated
sequence to a UUID allocated from ObjectifyService and enhance ImmutableObject
to allow it to exclude certain fields from hash/equals and print.

The ImmutableObject enhancements are necessary because we compare grace
periods against locally created test objects in a number of unit tests and
there's no way this can work with GracePeriods loaded from SQL currently, as
they will have an identifier field generated from the database and the test
objects will have an identifier field of null (or a new unique value, after
this change).

Removing autogeneration from GracePeriod ids ended up being likely not
strictly necessary for this change (it was a consequence of an earlier
iteration).  However, it does alleviate the problem of mutation of an
immutable object after creation and is more in line with how we've decided to
allocate other identifiers.

* Changed needed after rebase.
2020-10-26 13:38:14 -04:00
sarahcaseybot
576c05ff5f Add certificate checks to RegistrarSettingsAction (#843)
* Add certificate checks to RegistrarSettingsAction

* Add some comments

* Add more functionality to CertificateChecker and update call sites

* Small code cleanups

* Small format fix
2020-10-23 15:46:57 -04:00
gbrodman
f52e887db5 Create SQL schema for RdeRevision (#835)
* Create SQL schema for RdeRevision

* Split RdeRevision IDs into three separate DB fields as unified pkey

* Rename variable

* Merge remote-tracking branch 'origin/master' into rdeRevision

* Rename variable in one other location

* Implement no-op toDatastore/Sql for RdeRevision

* Responses to CR

* Merge remote-tracking branch 'origin/master' into rdeRevision

* Use a date for the date column

* Fix exception messages in tests

* Regen diagram to fix the test

* Use assignment in static factory methods

* Merge remote-tracking branch 'origin/master' into rdeRevision
2020-10-23 13:14:07 -04:00
Weimin Yu
6ed286e3bc Upgrade error-prone to 3.3.4 (#848)
* Upgrade error-prone to 3.3.4

This would fix the failure with openjdk 11.0.9 in
3.3.3.

Fixed new antipatterns raised by the new version:
- Replaced unnecessary lambdas with methods.
- Switched wait/sleep calls to equivalent methods using java.time types
- Types inheriting Object.toString() should not be assigned to string
parameter in logging statements.
2020-10-23 11:17:57 -04:00
236 changed files with 15055 additions and 6527 deletions

View File

@@ -1,25 +1,28 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.github.ben-manes.caffeine:caffeine:2.7.0
com.github.kevinstern:software-and-algorithms:1.0
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.google.auto.value:auto-value:1.6.3
com.google.auto:auto-common:0.10
com.google.code.findbugs:jFormatString:3.0.0
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotation:2.3.3
com.google.errorprone:error_prone_annotations:2.3.3
com.google.errorprone:error_prone_check_api:2.3.3
com.google.errorprone:error_prone_core:2.3.3
com.google.errorprone:error_prone_type_annotations:2.3.3
com.google.errorprone:error_prone_annotation:2.3.4
com.google.errorprone:error_prone_annotations:2.3.4
com.google.errorprone:error_prone_check_api:2.3.4
com.google.errorprone:error_prone_core:2.3.4
com.google.errorprone:error_prone_type_annotations:2.3.4
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:27.0.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.1
com.google.protobuf:protobuf-java:3.4.0
com.googlecode.java-diff-utils:diffutils:1.3.0
org.checkerframework:checker-qual:2.5.3
org.checkerframework:dataflow:2.5.3
org.checkerframework:javacutil:2.5.3
org.checkerframework:checker-qual:3.0.0
org.checkerframework:dataflow:3.0.0
org.checkerframework:javacutil:3.0.0
org.codehaus.mojo:animal-sniffer-annotations:1.17
org.pcollections:pcollections:2.1.2
org.plumelib:plume-util:1.0.6
org.plumelib:reflection-util:0.0.2
org.plumelib:require-javadoc:0.1.0

View File

@@ -1,24 +1,27 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.github.ben-manes.caffeine:caffeine:2.7.0
com.github.kevinstern:software-and-algorithms:1.0
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.google.auto:auto-common:0.10
com.google.code.findbugs:jFormatString:3.0.0
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotation:2.3.3
com.google.errorprone:error_prone_annotations:2.3.3
com.google.errorprone:error_prone_check_api:2.3.3
com.google.errorprone:error_prone_core:2.3.3
com.google.errorprone:error_prone_type_annotations:2.3.3
com.google.errorprone:error_prone_annotation:2.3.4
com.google.errorprone:error_prone_annotations:2.3.4
com.google.errorprone:error_prone_check_api:2.3.4
com.google.errorprone:error_prone_core:2.3.4
com.google.errorprone:error_prone_type_annotations:2.3.4
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:27.0.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.1
com.google.protobuf:protobuf-java:3.4.0
com.googlecode.java-diff-utils:diffutils:1.3.0
org.checkerframework:checker-qual:2.5.3
org.checkerframework:dataflow:2.5.3
org.checkerframework:javacutil:2.5.3
org.checkerframework:checker-qual:3.0.0
org.checkerframework:dataflow:3.0.0
org.checkerframework:javacutil:3.0.0
org.codehaus.mojo:animal-sniffer-annotations:1.17
org.pcollections:pcollections:2.1.2
org.plumelib:plume-util:1.0.6
org.plumelib:reflection-util:0.0.2
org.plumelib:require-javadoc:0.1.0

View File

@@ -1,24 +1,27 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.github.ben-manes.caffeine:caffeine:2.7.0
com.github.kevinstern:software-and-algorithms:1.0
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.google.auto:auto-common:0.10
com.google.code.findbugs:jFormatString:3.0.0
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotation:2.3.3
com.google.errorprone:error_prone_annotations:2.3.3
com.google.errorprone:error_prone_check_api:2.3.3
com.google.errorprone:error_prone_core:2.3.3
com.google.errorprone:error_prone_type_annotations:2.3.3
com.google.errorprone:error_prone_annotation:2.3.4
com.google.errorprone:error_prone_annotations:2.3.4
com.google.errorprone:error_prone_check_api:2.3.4
com.google.errorprone:error_prone_core:2.3.4
com.google.errorprone:error_prone_type_annotations:2.3.4
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:27.0.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.1
com.google.protobuf:protobuf-java:3.4.0
com.googlecode.java-diff-utils:diffutils:1.3.0
org.checkerframework:checker-qual:2.5.3
org.checkerframework:dataflow:2.5.3
org.checkerframework:javacutil:2.5.3
org.checkerframework:checker-qual:3.0.0
org.checkerframework:dataflow:3.0.0
org.checkerframework:javacutil:3.0.0
org.codehaus.mojo:animal-sniffer-annotations:1.17
org.pcollections:pcollections:2.1.2
org.plumelib:plume-util:1.0.6
org.plumelib:reflection-util:0.0.2
org.plumelib:require-javadoc:0.1.0

View File

@@ -1,24 +1,27 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.github.ben-manes.caffeine:caffeine:2.7.0
com.github.kevinstern:software-and-algorithms:1.0
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.google.auto:auto-common:0.10
com.google.code.findbugs:jFormatString:3.0.0
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotation:2.3.3
com.google.errorprone:error_prone_annotations:2.3.3
com.google.errorprone:error_prone_check_api:2.3.3
com.google.errorprone:error_prone_core:2.3.3
com.google.errorprone:error_prone_type_annotations:2.3.3
com.google.errorprone:error_prone_annotation:2.3.4
com.google.errorprone:error_prone_annotations:2.3.4
com.google.errorprone:error_prone_check_api:2.3.4
com.google.errorprone:error_prone_core:2.3.4
com.google.errorprone:error_prone_type_annotations:2.3.4
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:27.0.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.1
com.google.protobuf:protobuf-java:3.4.0
com.googlecode.java-diff-utils:diffutils:1.3.0
org.checkerframework:checker-qual:2.5.3
org.checkerframework:dataflow:2.5.3
org.checkerframework:javacutil:2.5.3
org.checkerframework:checker-qual:3.0.0
org.checkerframework:dataflow:3.0.0
org.checkerframework:javacutil:3.0.0
org.codehaus.mojo:animal-sniffer-annotations:1.17
org.pcollections:pcollections:2.1.2
org.plumelib:plume-util:1.0.6
org.plumelib:reflection-util:0.0.2
org.plumelib:require-javadoc:0.1.0

View File

@@ -1,24 +1,27 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.github.ben-manes.caffeine:caffeine:2.7.0
com.github.kevinstern:software-and-algorithms:1.0
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.google.auto:auto-common:0.10
com.google.code.findbugs:jFormatString:3.0.0
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotation:2.3.3
com.google.errorprone:error_prone_annotations:2.3.3
com.google.errorprone:error_prone_check_api:2.3.3
com.google.errorprone:error_prone_core:2.3.3
com.google.errorprone:error_prone_type_annotations:2.3.3
com.google.errorprone:error_prone_annotation:2.3.4
com.google.errorprone:error_prone_annotations:2.3.4
com.google.errorprone:error_prone_check_api:2.3.4
com.google.errorprone:error_prone_core:2.3.4
com.google.errorprone:error_prone_type_annotations:2.3.4
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:27.0.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.1
com.google.protobuf:protobuf-java:3.4.0
com.googlecode.java-diff-utils:diffutils:1.3.0
org.checkerframework:checker-qual:2.5.3
org.checkerframework:dataflow:2.5.3
org.checkerframework:javacutil:2.5.3
org.checkerframework:checker-qual:3.0.0
org.checkerframework:dataflow:3.0.0
org.checkerframework:javacutil:3.0.0
org.codehaus.mojo:animal-sniffer-annotations:1.17
org.pcollections:pcollections:2.1.2
org.plumelib:plume-util:1.0.6
org.plumelib:reflection-util:0.0.2
org.plumelib:require-javadoc:0.1.0

View File

@@ -1,24 +1,27 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.github.ben-manes.caffeine:caffeine:2.7.0
com.github.kevinstern:software-and-algorithms:1.0
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.google.auto:auto-common:0.10
com.google.code.findbugs:jFormatString:3.0.0
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotation:2.3.3
com.google.errorprone:error_prone_annotations:2.3.3
com.google.errorprone:error_prone_check_api:2.3.3
com.google.errorprone:error_prone_core:2.3.3
com.google.errorprone:error_prone_type_annotations:2.3.3
com.google.errorprone:error_prone_annotation:2.3.4
com.google.errorprone:error_prone_annotations:2.3.4
com.google.errorprone:error_prone_check_api:2.3.4
com.google.errorprone:error_prone_core:2.3.4
com.google.errorprone:error_prone_type_annotations:2.3.4
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:27.0.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.1
com.google.protobuf:protobuf-java:3.4.0
com.googlecode.java-diff-utils:diffutils:1.3.0
org.checkerframework:checker-qual:2.5.3
org.checkerframework:dataflow:2.5.3
org.checkerframework:javacutil:2.5.3
org.checkerframework:checker-qual:3.0.0
org.checkerframework:dataflow:3.0.0
org.checkerframework:javacutil:3.0.0
org.codehaus.mojo:animal-sniffer-annotations:1.17
org.pcollections:pcollections:2.1.2
org.plumelib:plume-util:1.0.6
org.plumelib:reflection-util:0.0.2
org.plumelib:require-javadoc:0.1.0

View File

@@ -18,7 +18,6 @@ import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.util.concurrent.Uninterruptibles;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
import org.joda.time.ReadableDuration;
@@ -41,6 +40,6 @@ public final class SystemSleeper implements Sleeper, Serializable {
@Override
public void sleepUninterruptibly(ReadableDuration duration) {
checkArgument(duration.getMillis() >= 0);
Uninterruptibles.sleepUninterruptibly(duration.getMillis(), TimeUnit.MILLISECONDS);
Uninterruptibles.sleepUninterruptibly(java.time.Duration.ofMillis(duration.getMillis()));
}
}

View File

@@ -220,6 +220,7 @@ dependencies {
compile deps['com.googlecode.json-simple:json-simple']
compile deps['com.jcraft:jsch']
testCompile deps['com.thoughtworks.qdox:qdox']
compile deps['com.zaxxer:HikariCP']
compile deps['dnsjava:dnsjava']
runtime deps['guru.nidi:graphviz-java-all-j2v8']
testCompile deps['io.github.classgraph:classgraph']
@@ -257,6 +258,7 @@ dependencies {
testCompile deps['com.fasterxml.jackson.core:jackson-databind']
runtime deps['org.glassfish.jaxb:jaxb-runtime']
compile deps['org.hibernate:hibernate-core']
compile deps['org.hibernate:hibernate-hikaricp']
compile deps['org.joda:joda-money']
compile deps['org.json:json']
compile deps['org.jsoup:jsoup']
@@ -310,7 +312,6 @@ dependencies {
testCompile deps['org.hamcrest:hamcrest-all']
testCompile deps['org.hamcrest:hamcrest-core']
testCompile deps['org.hamcrest:hamcrest-library']
compile deps['org.hibernate:hibernate-hikaricp']
testCompile deps['junit:junit']
testCompile deps['org.junit.jupiter:junit-jupiter-api']
testCompile deps['org.junit.jupiter:junit-jupiter-engine']

View File

@@ -1,8 +1,8 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.github.ben-manes.caffeine:caffeine:2.7.0
com.github.kevinstern:software-and-algorithms:1.0
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.google.auto.value:auto-value:1.6.3
com.google.auto:auto-common:0.10
com.google.code.findbugs:jFormatString:3.0.0
@@ -11,11 +11,11 @@ com.google.dagger:dagger-compiler:2.28
com.google.dagger:dagger-producers:2.28
com.google.dagger:dagger-spi:2.28
com.google.dagger:dagger:2.28
com.google.errorprone:error_prone_annotation:2.3.3
com.google.errorprone:error_prone_annotation:2.3.4
com.google.errorprone:error_prone_annotations:2.3.4
com.google.errorprone:error_prone_check_api:2.3.3
com.google.errorprone:error_prone_core:2.3.3
com.google.errorprone:error_prone_type_annotations:2.3.3
com.google.errorprone:error_prone_check_api:2.3.4
com.google.errorprone:error_prone_core:2.3.4
com.google.errorprone:error_prone_type_annotations:2.3.4
com.google.errorprone:javac-shaded:9-dev-r4023-3
com.google.googlejavaformat:google-java-format:1.5
com.google.guava:failureaccess:1.0.1
@@ -30,11 +30,14 @@ javax.inject:javax.inject:1
javax.persistence:javax.persistence-api:2.2
net.ltgt.gradle.incap:incap:0.2
org.checkerframework:checker-compat-qual:2.5.3
org.checkerframework:checker-qual:2.11.1
org.checkerframework:dataflow:2.5.3
org.checkerframework:javacutil:2.5.3
org.checkerframework:checker-qual:3.0.0
org.checkerframework:dataflow:3.0.0
org.checkerframework:javacutil:3.0.0
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61
org.jetbrains.kotlin:kotlin-stdlib:1.3.61
org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.1.0
org.jetbrains:annotations:13.0
org.pcollections:pcollections:2.1.2
org.plumelib:plume-util:1.0.6
org.plumelib:reflection-util:0.0.2
org.plumelib:require-javadoc:0.1.0

View File

@@ -125,7 +125,7 @@ com.squareup.okio:okio:1.13.0
com.sun.istack:istack-commons-runtime:3.0.7
com.sun.xml.fastinfoset:FastInfoset:1.2.15
com.thoughtworks.paranamer:paranamer:2.7
com.zaxxer:HikariCP:3.2.0
com.zaxxer:HikariCP:3.4.5
commons-codec:commons-codec:1.13
commons-logging:commons-logging:1.2
dnsjava:dnsjava:2.1.7
@@ -176,7 +176,7 @@ javax.xml.bind:jaxb-api:2.3.1
jline:jline:1.0
joda-time:joda-time:2.10.5
junit:junit:4.12
net.bytebuddy:byte-buddy:1.10.10
net.bytebuddy:byte-buddy:1.10.17
net.java.dev.jna:jna-platform:5.5.0
net.java.dev.jna:jna:5.5.0
org.apache.avro:avro:1.8.2
@@ -205,7 +205,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
org.checkerframework:checker-qual:3.5.0
org.codehaus.jackson:jackson-core-asl:1.9.13
org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
@@ -216,11 +216,11 @@ org.flywaydb:flyway-core:5.2.4
org.glassfish.jaxb:jaxb-runtime:2.3.1
org.glassfish.jaxb:txw2:2.3.1
org.hamcrest:hamcrest-core:1.3
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
org.hibernate:hibernate-core:5.4.17.Final
org.hibernate:hibernate-hikaricp:5.4.17.Final
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
org.hibernate:hibernate-core:5.4.23.Final
org.hibernate:hibernate-hikaricp:5.4.23.Final
org.javassist:javassist:3.24.0-GA
org.jboss.logging:jboss-logging:3.3.2.Final
org.jboss.logging:jboss-logging:3.4.1.Final
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
org.jboss:jandex:2.1.3.Final
org.jetbrains:annotations:19.0.0
@@ -236,7 +236,7 @@ org.ow2.asm:asm-commons:7.1
org.ow2.asm:asm-tree:8.0.1
org.ow2.asm:asm-util:8.0.1
org.ow2.asm:asm:8.0.1
org.postgresql:postgresql:42.2.14
org.postgresql:postgresql:42.2.18
org.rnorth.duct-tape:duct-tape:1.0.8
org.rnorth.visible-assertions:visible-assertions:2.1.2
org.rnorth:tcp-unix-socket-proxy:1.0.2

View File

@@ -124,7 +124,7 @@ com.squareup.okio:okio:1.13.0
com.sun.istack:istack-commons-runtime:3.0.7
com.sun.xml.fastinfoset:FastInfoset:1.2.15
com.thoughtworks.paranamer:paranamer:2.7
com.zaxxer:HikariCP:3.2.0
com.zaxxer:HikariCP:3.4.5
commons-codec:commons-codec:1.13
commons-logging:commons-logging:1.2
dnsjava:dnsjava:2.1.7
@@ -174,7 +174,7 @@ javax.validation:validation-api:1.0.0.GA
javax.xml.bind:jaxb-api:2.3.1
jline:jline:1.0
joda-time:joda-time:2.10.5
net.bytebuddy:byte-buddy:1.10.10
net.bytebuddy:byte-buddy:1.10.17
net.java.dev.jna:jna-platform:5.5.0
net.java.dev.jna:jna:5.5.0
org.apache.avro:avro:1.8.2
@@ -213,11 +213,11 @@ org.easymock:easymock:3.0
org.flywaydb:flyway-core:5.2.4
org.glassfish.jaxb:jaxb-runtime:2.3.1
org.glassfish.jaxb:txw2:2.3.1
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
org.hibernate:hibernate-core:5.4.17.Final
org.hibernate:hibernate-hikaricp:5.4.17.Final
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
org.hibernate:hibernate-core:5.4.23.Final
org.hibernate:hibernate-hikaricp:5.4.23.Final
org.javassist:javassist:3.24.0-GA
org.jboss.logging:jboss-logging:3.3.2.Final
org.jboss.logging:jboss-logging:3.4.1.Final
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
org.jboss:jandex:2.1.3.Final
org.jetbrains:annotations:19.0.0
@@ -233,7 +233,7 @@ org.ow2.asm:asm-commons:7.1
org.ow2.asm:asm-tree:8.0.1
org.ow2.asm:asm-util:8.0.1
org.ow2.asm:asm:8.0.1
org.postgresql:postgresql:42.2.14
org.postgresql:postgresql:42.2.18
org.rnorth.duct-tape:duct-tape:1.0.8
org.rnorth.visible-assertions:visible-assertions:2.1.2
org.rnorth:tcp-unix-socket-proxy:1.0.2

View File

@@ -130,7 +130,7 @@ com.squareup.okio:okio:1.13.0
com.sun.istack:istack-commons-runtime:3.0.7
com.sun.xml.fastinfoset:FastInfoset:1.2.15
com.thoughtworks.paranamer:paranamer:2.7
com.zaxxer:HikariCP:3.2.0
com.zaxxer:HikariCP:3.4.5
commons-codec:commons-codec:1.13
commons-logging:commons-logging:1.2
dnsjava:dnsjava:2.1.7
@@ -185,7 +185,7 @@ jline:jline:1.0
joda-time:joda-time:2.10.5
junit:junit:4.12
net.arnx:nashorn-promise:0.1.1
net.bytebuddy:byte-buddy:1.10.10
net.bytebuddy:byte-buddy:1.10.17
net.java.dev.jna:jna-platform:5.5.0
net.java.dev.jna:jna:5.5.0
org.apache.avro:avro:1.8.2
@@ -216,7 +216,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
org.checkerframework:checker-qual:3.5.0
org.codehaus.jackson:jackson-core-asl:1.9.13
org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
@@ -227,11 +227,11 @@ org.flywaydb:flyway-core:5.2.4
org.glassfish.jaxb:jaxb-runtime:2.3.1
org.glassfish.jaxb:txw2:2.3.1
org.hamcrest:hamcrest-core:1.3
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
org.hibernate:hibernate-core:5.4.17.Final
org.hibernate:hibernate-hikaricp:5.4.17.Final
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
org.hibernate:hibernate-core:5.4.23.Final
org.hibernate:hibernate-hikaricp:5.4.23.Final
org.javassist:javassist:3.24.0-GA
org.jboss.logging:jboss-logging:3.3.2.Final
org.jboss.logging:jboss-logging:3.4.1.Final
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
org.jboss:jandex:2.1.3.Final
org.jetbrains:annotations:19.0.0
@@ -247,7 +247,7 @@ org.ow2.asm:asm-commons:7.1
org.ow2.asm:asm-tree:8.0.1
org.ow2.asm:asm-util:8.0.1
org.ow2.asm:asm:8.0.1
org.postgresql:postgresql:42.2.14
org.postgresql:postgresql:42.2.18
org.rnorth.duct-tape:duct-tape:1.0.8
org.rnorth.visible-assertions:visible-assertions:2.1.2
org.rnorth:tcp-unix-socket-proxy:1.0.2

View File

@@ -130,7 +130,7 @@ com.squareup.okio:okio:1.13.0
com.sun.istack:istack-commons-runtime:3.0.7
com.sun.xml.fastinfoset:FastInfoset:1.2.15
com.thoughtworks.paranamer:paranamer:2.7
com.zaxxer:HikariCP:3.2.0
com.zaxxer:HikariCP:3.4.5
commons-codec:commons-codec:1.13
commons-logging:commons-logging:1.2
dnsjava:dnsjava:2.1.7
@@ -184,7 +184,7 @@ javax.xml.bind:jaxb-api:2.3.1
jline:jline:1.0
joda-time:joda-time:2.10.5
net.arnx:nashorn-promise:0.1.1
net.bytebuddy:byte-buddy:1.10.10
net.bytebuddy:byte-buddy:1.10.17
net.java.dev.jna:jna-platform:5.5.0
net.java.dev.jna:jna:5.5.0
org.apache.avro:avro:1.8.2
@@ -215,7 +215,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
org.checkerframework:checker-qual:3.5.0
org.codehaus.jackson:jackson-core-asl:1.9.13
org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
@@ -225,11 +225,11 @@ org.easymock:easymock:3.0
org.flywaydb:flyway-core:5.2.4
org.glassfish.jaxb:jaxb-runtime:2.3.1
org.glassfish.jaxb:txw2:2.3.1
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
org.hibernate:hibernate-core:5.4.17.Final
org.hibernate:hibernate-hikaricp:5.4.17.Final
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
org.hibernate:hibernate-core:5.4.23.Final
org.hibernate:hibernate-hikaricp:5.4.23.Final
org.javassist:javassist:3.24.0-GA
org.jboss.logging:jboss-logging:3.3.2.Final
org.jboss.logging:jboss-logging:3.4.1.Final
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
org.jboss:jandex:2.1.3.Final
org.jetbrains:annotations:19.0.0
@@ -245,7 +245,7 @@ org.ow2.asm:asm-commons:7.1
org.ow2.asm:asm-tree:8.0.1
org.ow2.asm:asm-util:8.0.1
org.ow2.asm:asm:8.0.1
org.postgresql:postgresql:42.2.14
org.postgresql:postgresql:42.2.18
org.rnorth.duct-tape:duct-tape:1.0.8
org.rnorth.visible-assertions:visible-assertions:2.1.2
org.rnorth:tcp-unix-socket-proxy:1.0.2

View File

@@ -1,24 +1,27 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.github.ben-manes.caffeine:caffeine:2.7.0
com.github.kevinstern:software-and-algorithms:1.0
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.google.auto:auto-common:0.10
com.google.code.findbugs:jFormatString:3.0.0
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotation:2.3.3
com.google.errorprone:error_prone_annotations:2.3.3
com.google.errorprone:error_prone_check_api:2.3.3
com.google.errorprone:error_prone_core:2.3.3
com.google.errorprone:error_prone_type_annotations:2.3.3
com.google.errorprone:error_prone_annotation:2.3.4
com.google.errorprone:error_prone_annotations:2.3.4
com.google.errorprone:error_prone_check_api:2.3.4
com.google.errorprone:error_prone_core:2.3.4
com.google.errorprone:error_prone_type_annotations:2.3.4
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:27.0.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.1
com.google.protobuf:protobuf-java:3.4.0
com.googlecode.java-diff-utils:diffutils:1.3.0
org.checkerframework:checker-qual:2.5.3
org.checkerframework:dataflow:2.5.3
org.checkerframework:javacutil:2.5.3
org.checkerframework:checker-qual:3.0.0
org.checkerframework:dataflow:3.0.0
org.checkerframework:javacutil:3.0.0
org.codehaus.mojo:animal-sniffer-annotations:1.17
org.pcollections:pcollections:2.1.2
org.plumelib:plume-util:1.0.6
org.plumelib:reflection-util:0.0.2
org.plumelib:require-javadoc:0.1.0

View File

@@ -1,24 +1,27 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.github.ben-manes.caffeine:caffeine:2.7.0
com.github.kevinstern:software-and-algorithms:1.0
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.google.auto:auto-common:0.10
com.google.code.findbugs:jFormatString:3.0.0
com.google.code.findbugs:jsr305:3.0.2
com.google.errorprone:error_prone_annotation:2.3.3
com.google.errorprone:error_prone_annotations:2.3.3
com.google.errorprone:error_prone_check_api:2.3.3
com.google.errorprone:error_prone_core:2.3.3
com.google.errorprone:error_prone_type_annotations:2.3.3
com.google.errorprone:error_prone_annotation:2.3.4
com.google.errorprone:error_prone_annotations:2.3.4
com.google.errorprone:error_prone_check_api:2.3.4
com.google.errorprone:error_prone_core:2.3.4
com.google.errorprone:error_prone_type_annotations:2.3.4
com.google.guava:failureaccess:1.0.1
com.google.guava:guava:27.0.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.j2objc:j2objc-annotations:1.1
com.google.protobuf:protobuf-java:3.4.0
com.googlecode.java-diff-utils:diffutils:1.3.0
org.checkerframework:checker-qual:2.5.3
org.checkerframework:dataflow:2.5.3
org.checkerframework:javacutil:2.5.3
org.checkerframework:checker-qual:3.0.0
org.checkerframework:dataflow:3.0.0
org.checkerframework:javacutil:3.0.0
org.codehaus.mojo:animal-sniffer-annotations:1.17
org.pcollections:pcollections:2.1.2
org.plumelib:plume-util:1.0.6
org.plumelib:reflection-util:0.0.2
org.plumelib:require-javadoc:0.1.0

View File

@@ -125,7 +125,7 @@ com.squareup.okio:okio:1.13.0
com.sun.istack:istack-commons-runtime:3.0.7
com.sun.xml.fastinfoset:FastInfoset:1.2.15
com.thoughtworks.paranamer:paranamer:2.7
com.zaxxer:HikariCP:3.2.0
com.zaxxer:HikariCP:3.4.5
commons-codec:commons-codec:1.13
commons-logging:commons-logging:1.2
dnsjava:dnsjava:2.1.7
@@ -176,7 +176,7 @@ javax.xml.bind:jaxb-api:2.3.1
jline:jline:1.0
joda-time:joda-time:2.10.5
junit:junit:4.12
net.bytebuddy:byte-buddy:1.10.10
net.bytebuddy:byte-buddy:1.10.17
net.java.dev.jna:jna-platform:5.5.0
net.java.dev.jna:jna:5.5.0
org.apache.avro:avro:1.8.2
@@ -205,7 +205,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
org.checkerframework:checker-qual:3.5.0
org.codehaus.jackson:jackson-core-asl:1.9.13
org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
@@ -216,11 +216,11 @@ org.flywaydb:flyway-core:5.2.4
org.glassfish.jaxb:jaxb-runtime:2.3.1
org.glassfish.jaxb:txw2:2.3.1
org.hamcrest:hamcrest-core:1.3
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
org.hibernate:hibernate-core:5.4.17.Final
org.hibernate:hibernate-hikaricp:5.4.17.Final
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
org.hibernate:hibernate-core:5.4.23.Final
org.hibernate:hibernate-hikaricp:5.4.23.Final
org.javassist:javassist:3.24.0-GA
org.jboss.logging:jboss-logging:3.3.2.Final
org.jboss.logging:jboss-logging:3.4.1.Final
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
org.jboss:jandex:2.1.3.Final
org.jetbrains:annotations:19.0.0
@@ -236,7 +236,7 @@ org.ow2.asm:asm-commons:7.1
org.ow2.asm:asm-tree:8.0.1
org.ow2.asm:asm-util:8.0.1
org.ow2.asm:asm:8.0.1
org.postgresql:postgresql:42.2.14
org.postgresql:postgresql:42.2.18
org.rnorth.duct-tape:duct-tape:1.0.8
org.rnorth.visible-assertions:visible-assertions:2.1.2
org.rnorth:tcp-unix-socket-proxy:1.0.2

View File

@@ -124,7 +124,7 @@ com.squareup.okio:okio:1.13.0
com.sun.istack:istack-commons-runtime:3.0.7
com.sun.xml.fastinfoset:FastInfoset:1.2.15
com.thoughtworks.paranamer:paranamer:2.7
com.zaxxer:HikariCP:3.2.0
com.zaxxer:HikariCP:3.4.5
commons-codec:commons-codec:1.13
commons-logging:commons-logging:1.2
dnsjava:dnsjava:2.1.7
@@ -175,7 +175,7 @@ javax.xml.bind:jaxb-api:2.3.1
jline:jline:1.0
joda-time:joda-time:2.10.5
junit:junit:4.12
net.bytebuddy:byte-buddy:1.10.10
net.bytebuddy:byte-buddy:1.10.17
net.java.dev.jna:jna-platform:5.5.0
net.java.dev.jna:jna:5.5.0
org.apache.avro:avro:1.8.2
@@ -215,11 +215,11 @@ org.flywaydb:flyway-core:5.2.4
org.glassfish.jaxb:jaxb-runtime:2.3.1
org.glassfish.jaxb:txw2:2.3.1
org.hamcrest:hamcrest-core:1.3
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
org.hibernate:hibernate-core:5.4.17.Final
org.hibernate:hibernate-hikaricp:5.4.17.Final
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
org.hibernate:hibernate-core:5.4.23.Final
org.hibernate:hibernate-hikaricp:5.4.23.Final
org.javassist:javassist:3.24.0-GA
org.jboss.logging:jboss-logging:3.3.2.Final
org.jboss.logging:jboss-logging:3.4.1.Final
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
org.jboss:jandex:2.1.3.Final
org.jetbrains:annotations:19.0.0
@@ -235,7 +235,7 @@ org.ow2.asm:asm-commons:7.1
org.ow2.asm:asm-tree:8.0.1
org.ow2.asm:asm-util:8.0.1
org.ow2.asm:asm:8.0.1
org.postgresql:postgresql:42.2.14
org.postgresql:postgresql:42.2.18
org.rnorth.duct-tape:duct-tape:1.0.8
org.rnorth.visible-assertions:visible-assertions:2.1.2
org.rnorth:tcp-unix-socket-proxy:1.0.2

View File

@@ -129,7 +129,7 @@ com.squareup.okio:okio:1.13.0
com.sun.istack:istack-commons-runtime:3.0.7
com.sun.xml.fastinfoset:FastInfoset:1.2.15
com.thoughtworks.paranamer:paranamer:2.7
com.zaxxer:HikariCP:3.2.0
com.zaxxer:HikariCP:3.4.5
commons-codec:commons-codec:1.13
commons-logging:commons-logging:1.2
dnsjava:dnsjava:2.1.7
@@ -184,7 +184,7 @@ jline:jline:1.0
joda-time:joda-time:2.10.5
junit:junit:4.12
net.arnx:nashorn-promise:0.1.1
net.bytebuddy:byte-buddy:1.10.10
net.bytebuddy:byte-buddy:1.10.17
net.java.dev.jna:jna-platform:5.5.0
net.java.dev.jna:jna:5.5.0
org.apache.avro:avro:1.8.2
@@ -215,7 +215,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
org.checkerframework:checker-qual:3.5.0
org.codehaus.jackson:jackson-core-asl:1.9.13
org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
@@ -226,11 +226,11 @@ org.flywaydb:flyway-core:5.2.4
org.glassfish.jaxb:jaxb-runtime:2.3.1
org.glassfish.jaxb:txw2:2.3.1
org.hamcrest:hamcrest-core:1.3
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
org.hibernate:hibernate-core:5.4.17.Final
org.hibernate:hibernate-hikaricp:5.4.17.Final
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
org.hibernate:hibernate-core:5.4.23.Final
org.hibernate:hibernate-hikaricp:5.4.23.Final
org.javassist:javassist:3.24.0-GA
org.jboss.logging:jboss-logging:3.3.2.Final
org.jboss.logging:jboss-logging:3.4.1.Final
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
org.jboss:jandex:2.1.3.Final
org.jetbrains:annotations:19.0.0
@@ -246,7 +246,7 @@ org.ow2.asm:asm-commons:7.1
org.ow2.asm:asm-tree:8.0.1
org.ow2.asm:asm-util:8.0.1
org.ow2.asm:asm:8.0.1
org.postgresql:postgresql:42.2.14
org.postgresql:postgresql:42.2.18
org.rnorth.duct-tape:duct-tape:1.0.8
org.rnorth.visible-assertions:visible-assertions:2.1.2
org.rnorth:tcp-unix-socket-proxy:1.0.2

View File

@@ -129,7 +129,7 @@ com.squareup.okio:okio:1.13.0
com.sun.istack:istack-commons-runtime:3.0.7
com.sun.xml.fastinfoset:FastInfoset:1.2.15
com.thoughtworks.paranamer:paranamer:2.7
com.zaxxer:HikariCP:3.2.0
com.zaxxer:HikariCP:3.4.5
commons-codec:commons-codec:1.13
commons-logging:commons-logging:1.2
dnsjava:dnsjava:2.1.7
@@ -184,7 +184,7 @@ jline:jline:1.0
joda-time:joda-time:2.10.5
junit:junit:4.12
net.arnx:nashorn-promise:0.1.1
net.bytebuddy:byte-buddy:1.10.10
net.bytebuddy:byte-buddy:1.10.17
net.java.dev.jna:jna-platform:5.5.0
net.java.dev.jna:jna:5.5.0
org.apache.avro:avro:1.8.2
@@ -215,7 +215,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
org.checkerframework:checker-qual:3.5.0
org.codehaus.jackson:jackson-core-asl:1.9.13
org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
@@ -226,11 +226,11 @@ org.flywaydb:flyway-core:5.2.4
org.glassfish.jaxb:jaxb-runtime:2.3.1
org.glassfish.jaxb:txw2:2.3.1
org.hamcrest:hamcrest-core:1.3
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
org.hibernate:hibernate-core:5.4.17.Final
org.hibernate:hibernate-hikaricp:5.4.17.Final
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
org.hibernate:hibernate-core:5.4.23.Final
org.hibernate:hibernate-hikaricp:5.4.23.Final
org.javassist:javassist:3.24.0-GA
org.jboss.logging:jboss-logging:3.3.2.Final
org.jboss.logging:jboss-logging:3.4.1.Final
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
org.jboss:jandex:2.1.3.Final
org.jetbrains:annotations:19.0.0
@@ -246,7 +246,7 @@ org.ow2.asm:asm-commons:7.1
org.ow2.asm:asm-tree:8.0.1
org.ow2.asm:asm-util:8.0.1
org.ow2.asm:asm:8.0.1
org.postgresql:postgresql:42.2.14
org.postgresql:postgresql:42.2.18
org.rnorth.duct-tape:duct-tape:1.0.8
org.rnorth.visible-assertions:visible-assertions:2.1.2
org.rnorth:tcp-unix-socket-proxy:1.0.2

View File

@@ -129,7 +129,7 @@ com.squareup.okio:okio:1.13.0
com.sun.istack:istack-commons-runtime:3.0.7
com.sun.xml.fastinfoset:FastInfoset:1.2.15
com.thoughtworks.paranamer:paranamer:2.7
com.zaxxer:HikariCP:3.2.0
com.zaxxer:HikariCP:3.4.5
commons-codec:commons-codec:1.13
commons-logging:commons-logging:1.2
dnsjava:dnsjava:2.1.7
@@ -184,7 +184,7 @@ jline:jline:1.0
joda-time:joda-time:2.10.5
junit:junit:4.12
net.arnx:nashorn-promise:0.1.1
net.bytebuddy:byte-buddy:1.10.10
net.bytebuddy:byte-buddy:1.10.17
net.java.dev.jna:jna-platform:5.5.0
net.java.dev.jna:jna:5.5.0
org.apache.avro:avro:1.8.2
@@ -215,7 +215,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
org.checkerframework:checker-qual:3.5.0
org.codehaus.jackson:jackson-core-asl:1.9.13
org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
@@ -226,11 +226,11 @@ org.flywaydb:flyway-core:5.2.4
org.glassfish.jaxb:jaxb-runtime:2.3.1
org.glassfish.jaxb:txw2:2.3.1
org.hamcrest:hamcrest-core:1.3
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
org.hibernate:hibernate-core:5.4.17.Final
org.hibernate:hibernate-hikaricp:5.4.17.Final
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
org.hibernate:hibernate-core:5.4.23.Final
org.hibernate:hibernate-hikaricp:5.4.23.Final
org.javassist:javassist:3.24.0-GA
org.jboss.logging:jboss-logging:3.3.2.Final
org.jboss.logging:jboss-logging:3.4.1.Final
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
org.jboss:jandex:2.1.3.Final
org.jetbrains:annotations:19.0.0
@@ -246,7 +246,7 @@ org.ow2.asm:asm-commons:7.1
org.ow2.asm:asm-tree:8.0.1
org.ow2.asm:asm-util:8.0.1
org.ow2.asm:asm:8.0.1
org.postgresql:postgresql:42.2.14
org.postgresql:postgresql:42.2.18
org.rnorth.duct-tape:duct-tape:1.0.8
org.rnorth.visible-assertions:visible-assertions:2.1.2
org.rnorth:tcp-unix-socket-proxy:1.0.2

View File

@@ -130,7 +130,7 @@ com.squareup.okio:okio:1.13.0
com.sun.istack:istack-commons-runtime:3.0.7
com.sun.xml.fastinfoset:FastInfoset:1.2.15
com.thoughtworks.paranamer:paranamer:2.7
com.zaxxer:HikariCP:3.2.0
com.zaxxer:HikariCP:3.4.5
commons-codec:commons-codec:1.13
commons-logging:commons-logging:1.2
dnsjava:dnsjava:2.1.7
@@ -184,7 +184,7 @@ javax.xml.bind:jaxb-api:2.3.1
jline:jline:1.0
joda-time:joda-time:2.10.5
net.arnx:nashorn-promise:0.1.1
net.bytebuddy:byte-buddy:1.10.10
net.bytebuddy:byte-buddy:1.10.17
net.java.dev.jna:jna-platform:5.5.0
net.java.dev.jna:jna:5.5.0
org.apache.avro:avro:1.8.2
@@ -215,7 +215,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
org.checkerframework:checker-qual:3.5.0
org.codehaus.jackson:jackson-core-asl:1.9.13
org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
@@ -225,11 +225,11 @@ org.easymock:easymock:3.0
org.flywaydb:flyway-core:5.2.4
org.glassfish.jaxb:jaxb-runtime:2.3.1
org.glassfish.jaxb:txw2:2.3.1
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
org.hibernate:hibernate-core:5.4.17.Final
org.hibernate:hibernate-hikaricp:5.4.17.Final
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
org.hibernate:hibernate-core:5.4.23.Final
org.hibernate:hibernate-hikaricp:5.4.23.Final
org.javassist:javassist:3.24.0-GA
org.jboss.logging:jboss-logging:3.3.2.Final
org.jboss.logging:jboss-logging:3.4.1.Final
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
org.jboss:jandex:2.1.3.Final
org.jetbrains:annotations:19.0.0
@@ -245,7 +245,7 @@ org.ow2.asm:asm-commons:7.1
org.ow2.asm:asm-tree:8.0.1
org.ow2.asm:asm-util:8.0.1
org.ow2.asm:asm:8.0.1
org.postgresql:postgresql:42.2.14
org.postgresql:postgresql:42.2.18
org.rnorth.duct-tape:duct-tape:1.0.8
org.rnorth.visible-assertions:visible-assertions:2.1.2
org.rnorth:tcp-unix-socket-proxy:1.0.2

View File

@@ -1,8 +1,8 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.github.ben-manes.caffeine:caffeine:2.7.0
com.github.kevinstern:software-and-algorithms:1.0
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.google.auto.value:auto-value:1.6.3
com.google.auto:auto-common:0.10
com.google.code.findbugs:jFormatString:3.0.0
@@ -11,11 +11,11 @@ com.google.dagger:dagger-compiler:2.28
com.google.dagger:dagger-producers:2.28
com.google.dagger:dagger-spi:2.28
com.google.dagger:dagger:2.28
com.google.errorprone:error_prone_annotation:2.3.3
com.google.errorprone:error_prone_annotation:2.3.4
com.google.errorprone:error_prone_annotations:2.3.4
com.google.errorprone:error_prone_check_api:2.3.3
com.google.errorprone:error_prone_core:2.3.3
com.google.errorprone:error_prone_type_annotations:2.3.3
com.google.errorprone:error_prone_check_api:2.3.4
com.google.errorprone:error_prone_core:2.3.4
com.google.errorprone:error_prone_type_annotations:2.3.4
com.google.errorprone:javac-shaded:9-dev-r4023-3
com.google.googlejavaformat:google-java-format:1.5
com.google.guava:failureaccess:1.0.1
@@ -30,11 +30,14 @@ javax.inject:javax.inject:1
javax.persistence:javax.persistence-api:2.2
net.ltgt.gradle.incap:incap:0.2
org.checkerframework:checker-compat-qual:2.5.3
org.checkerframework:checker-qual:2.11.1
org.checkerframework:dataflow:2.5.3
org.checkerframework:javacutil:2.5.3
org.checkerframework:checker-qual:3.0.0
org.checkerframework:dataflow:3.0.0
org.checkerframework:javacutil:3.0.0
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.61
org.jetbrains.kotlin:kotlin-stdlib:1.3.61
org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.1.0
org.jetbrains:annotations:13.0
org.pcollections:pcollections:2.1.2
org.plumelib:plume-util:1.0.6
org.plumelib:reflection-util:0.0.2
org.plumelib:require-javadoc:0.1.0

View File

@@ -133,7 +133,7 @@ com.sun.istack:istack-commons-runtime:3.0.7
com.sun.xml.fastinfoset:FastInfoset:1.2.15
com.thoughtworks.paranamer:paranamer:2.7
com.thoughtworks.qdox:qdox:1.12.1
com.zaxxer:HikariCP:3.2.0
com.zaxxer:HikariCP:3.4.5
commons-codec:commons-codec:1.13
commons-logging:commons-logging:1.2
dnsjava:dnsjava:2.1.7
@@ -185,7 +185,7 @@ jline:jline:1.0
joda-time:joda-time:2.10.5
junit:junit:4.13
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.10
net.bytebuddy:byte-buddy:1.10.17
net.java.dev.jna:jna-platform:5.5.0
net.java.dev.jna:jna:5.5.0
org.apache.avro:avro:1.8.2
@@ -225,7 +225,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
org.checkerframework:checker-qual:3.5.0
org.codehaus.jackson:jackson-core-asl:1.9.13
org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
@@ -238,11 +238,11 @@ org.glassfish.jaxb:txw2:2.3.1
org.hamcrest:hamcrest-all:1.3
org.hamcrest:hamcrest-core:1.3
org.hamcrest:hamcrest-library:1.3
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
org.hibernate:hibernate-core:5.4.17.Final
org.hibernate:hibernate-hikaricp:5.4.17.Final
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
org.hibernate:hibernate-core:5.4.23.Final
org.hibernate:hibernate-hikaricp:5.4.23.Final
org.javassist:javassist:3.24.0-GA
org.jboss.logging:jboss-logging:3.3.2.Final
org.jboss.logging:jboss-logging:3.4.1.Final
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
org.jboss:jandex:2.1.3.Final
org.jetbrains:annotations:19.0.0
@@ -272,7 +272,7 @@ org.ow2.asm:asm-commons:7.1
org.ow2.asm:asm-tree:8.0.1
org.ow2.asm:asm-util:8.0.1
org.ow2.asm:asm:8.0.1
org.postgresql:postgresql:42.2.14
org.postgresql:postgresql:42.2.18
org.rnorth.duct-tape:duct-tape:1.0.8
org.rnorth.visible-assertions:visible-assertions:2.1.2
org.rnorth:tcp-unix-socket-proxy:1.0.2

View File

@@ -132,7 +132,7 @@ com.sun.istack:istack-commons-runtime:3.0.7
com.sun.xml.fastinfoset:FastInfoset:1.2.15
com.thoughtworks.paranamer:paranamer:2.7
com.thoughtworks.qdox:qdox:1.12.1
com.zaxxer:HikariCP:3.2.0
com.zaxxer:HikariCP:3.4.5
commons-codec:commons-codec:1.13
commons-logging:commons-logging:1.2
dnsjava:dnsjava:2.1.7
@@ -184,7 +184,7 @@ jline:jline:1.0
joda-time:joda-time:2.10.5
junit:junit:4.13
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.10
net.bytebuddy:byte-buddy:1.10.17
net.java.dev.jna:jna-platform:5.5.0
net.java.dev.jna:jna:5.5.0
org.apache.avro:avro:1.8.2
@@ -237,11 +237,11 @@ org.glassfish.jaxb:txw2:2.3.1
org.hamcrest:hamcrest-all:1.3
org.hamcrest:hamcrest-core:1.3
org.hamcrest:hamcrest-library:1.3
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
org.hibernate:hibernate-core:5.4.17.Final
org.hibernate:hibernate-hikaricp:5.4.17.Final
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
org.hibernate:hibernate-core:5.4.23.Final
org.hibernate:hibernate-hikaricp:5.4.23.Final
org.javassist:javassist:3.24.0-GA
org.jboss.logging:jboss-logging:3.3.2.Final
org.jboss.logging:jboss-logging:3.4.1.Final
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
org.jboss:jandex:2.1.3.Final
org.jetbrains:annotations:19.0.0
@@ -271,7 +271,7 @@ org.ow2.asm:asm-commons:7.1
org.ow2.asm:asm-tree:8.0.1
org.ow2.asm:asm-util:8.0.1
org.ow2.asm:asm:8.0.1
org.postgresql:postgresql:42.2.14
org.postgresql:postgresql:42.2.18
org.rnorth.duct-tape:duct-tape:1.0.8
org.rnorth.visible-assertions:visible-assertions:2.1.2
org.rnorth:tcp-unix-socket-proxy:1.0.2

View File

@@ -138,7 +138,7 @@ com.sun.istack:istack-commons-runtime:3.0.7
com.sun.xml.fastinfoset:FastInfoset:1.2.15
com.thoughtworks.paranamer:paranamer:2.7
com.thoughtworks.qdox:qdox:1.12.1
com.zaxxer:HikariCP:3.2.0
com.zaxxer:HikariCP:3.4.5
commons-codec:commons-codec:1.13
commons-logging:commons-logging:1.2
dnsjava:dnsjava:2.1.7
@@ -195,7 +195,7 @@ joda-time:joda-time:2.10.5
junit:junit:4.13
net.arnx:nashorn-promise:0.1.1
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.10
net.bytebuddy:byte-buddy:1.10.17
net.java.dev.jna:jna-platform:5.5.0
net.java.dev.jna:jna:5.5.0
org.apache.avro:avro:1.8.2
@@ -236,7 +236,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
org.checkerframework:checker-qual:3.5.0
org.codehaus.jackson:jackson-core-asl:1.9.13
org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
@@ -250,11 +250,11 @@ org.glassfish.jaxb:txw2:2.3.1
org.hamcrest:hamcrest-all:1.3
org.hamcrest:hamcrest-core:1.3
org.hamcrest:hamcrest-library:1.3
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
org.hibernate:hibernate-core:5.4.17.Final
org.hibernate:hibernate-hikaricp:5.4.17.Final
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
org.hibernate:hibernate-core:5.4.23.Final
org.hibernate:hibernate-hikaricp:5.4.23.Final
org.javassist:javassist:3.24.0-GA
org.jboss.logging:jboss-logging:3.3.2.Final
org.jboss.logging:jboss-logging:3.4.1.Final
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
org.jboss:jandex:2.1.3.Final
org.jetbrains:annotations:19.0.0
@@ -284,7 +284,7 @@ org.ow2.asm:asm-commons:7.1
org.ow2.asm:asm-tree:8.0.1
org.ow2.asm:asm-util:8.0.1
org.ow2.asm:asm:8.0.1
org.postgresql:postgresql:42.2.14
org.postgresql:postgresql:42.2.18
org.rnorth.duct-tape:duct-tape:1.0.8
org.rnorth.visible-assertions:visible-assertions:2.1.2
org.rnorth:tcp-unix-socket-proxy:1.0.2

View File

@@ -138,7 +138,7 @@ com.sun.istack:istack-commons-runtime:3.0.7
com.sun.xml.fastinfoset:FastInfoset:1.2.15
com.thoughtworks.paranamer:paranamer:2.7
com.thoughtworks.qdox:qdox:1.12.1
com.zaxxer:HikariCP:3.2.0
com.zaxxer:HikariCP:3.4.5
commons-codec:commons-codec:1.13
commons-logging:commons-logging:1.2
dnsjava:dnsjava:2.1.7
@@ -195,7 +195,7 @@ joda-time:joda-time:2.10.5
junit:junit:4.13
net.arnx:nashorn-promise:0.1.1
net.bytebuddy:byte-buddy-agent:1.10.5
net.bytebuddy:byte-buddy:1.10.10
net.bytebuddy:byte-buddy:1.10.17
net.java.dev.jna:jna-platform:5.5.0
net.java.dev.jna:jna:5.5.0
org.apache.avro:avro:1.8.2
@@ -236,7 +236,7 @@ org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcpkix-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-compat-qual:2.5.5
org.checkerframework:checker-qual:2.11.1
org.checkerframework:checker-qual:3.5.0
org.codehaus.jackson:jackson-core-asl:1.9.13
org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
@@ -250,11 +250,11 @@ org.glassfish.jaxb:txw2:2.3.1
org.hamcrest:hamcrest-all:1.3
org.hamcrest:hamcrest-core:1.3
org.hamcrest:hamcrest-library:1.3
org.hibernate.common:hibernate-commons-annotations:5.1.0.Final
org.hibernate:hibernate-core:5.4.17.Final
org.hibernate:hibernate-hikaricp:5.4.17.Final
org.hibernate.common:hibernate-commons-annotations:5.1.2.Final
org.hibernate:hibernate-core:5.4.23.Final
org.hibernate:hibernate-hikaricp:5.4.23.Final
org.javassist:javassist:3.24.0-GA
org.jboss.logging:jboss-logging:3.3.2.Final
org.jboss.logging:jboss-logging:3.4.1.Final
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.1.1.Final
org.jboss:jandex:2.1.3.Final
org.jetbrains:annotations:19.0.0
@@ -284,7 +284,7 @@ org.ow2.asm:asm-commons:7.1
org.ow2.asm:asm-tree:8.0.1
org.ow2.asm:asm-util:8.0.1
org.ow2.asm:asm:8.0.1
org.postgresql:postgresql:42.2.14
org.postgresql:postgresql:42.2.18
org.rnorth.duct-tape:duct-tape:1.0.8
org.rnorth.visible-assertions:visible-assertions:2.1.2
org.rnorth:tcp-unix-socket-proxy:1.0.2

View File

@@ -92,9 +92,8 @@ public class InitSqlPipeline implements Serializable {
* Datastore kinds to be written to the SQL database before the cleansed version of {@link
* DomainBase}.
*/
// TODO(weiminyu): include Registry.class when it is modeled in JPA.
private static final ImmutableList<Class<?>> PHASE_ONE_ORDERED =
ImmutableList.of(Registrar.class, ContactResource.class);
ImmutableList.of(Registry.class, Registrar.class, ContactResource.class);
/**
* Datastore kinds to be written to the SQL database after the cleansed version of {@link

View File

@@ -1,44 +0,0 @@
// Copyright 2020 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.config;
import com.google.common.collect.ImmutableSortedMap;
import dagger.Module;
import dagger.Provides;
import google.registry.config.RegistryConfig.Config;
import google.registry.util.CertificateChecker;
import google.registry.util.Clock;
import javax.inject.Singleton;
import org.joda.time.DateTime;
/** Dagger module that provides the {@link CertificateChecker} used in the application. */
// TODO(sarahbot@): Move this module to a better location. Possibly flows/. If we decide to move
// CertificateChecker.java to core/ delete this file and inject the CertificateChecker constructor
// instead.
@Module
public abstract class CertificateCheckerModule {
@Provides
@Singleton
static CertificateChecker provideCertificateChecker(
@Config("maxValidityDaysSchedule") ImmutableSortedMap<DateTime, Integer> validityDaysMap,
@Config("expirationWarningDays") int daysToExpiration,
@Config("minimumRsaKeyLength") int minimumRsaKeyLength,
Clock clock) {
return new CertificateChecker(validityDaysMap, daysToExpiration, minimumRsaKeyLength, clock);
}
private CertificateCheckerModule() {}
}

View File

@@ -1377,6 +1377,12 @@ public final class RegistryConfig {
public static int provideMinimumRsaKeyLength(RegistryConfigSettings config) {
return config.sslCertificateValidation.minimumRsaKeyLength;
}
@Provides
@Config("allowedEcdsaCurves")
public static ImmutableSet<String> provideAllowedEcdsaCurves(RegistryConfigSettings config) {
return ImmutableSet.copyOf(config.sslCertificateValidation.allowedEcdsaCurves);
}
}
/** Returns the App Engine project ID, which is based off the environment name. */

View File

@@ -16,6 +16,7 @@ package google.registry.config;
import java.util.List;
import java.util.Map;
import java.util.Set;
/** The POJO that YAML config files are deserialized into. */
public class RegistryConfigSettings {
@@ -226,5 +227,6 @@ public class RegistryConfigSettings {
public Map<String, Integer> maxValidityDaysSchedule;
public int expirationWarningDays;
public int minimumRsaKeyLength;
public Set<String> allowedEcdsaCurves;
}
}

View File

@@ -458,5 +458,9 @@ sslCertificateValidation:
# The number of days before a certificate expires that indicates the
# certificate is nearing expiration and warnings should be sent.
expirationWarningDays: 30
# The minimum number of bits an RSA key must contain
# The minimum number of bits an RSA key must contain.
minimumRsaKeyLength: 2048
# The ECDSA curves that are allowed for public keys.
allowedEcdsaCurves:
- secp256r1
- secp384r1

View File

@@ -12,17 +12,30 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
package google.registry.flows.certs;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import google.registry.config.RegistryConfig.Config;
import google.registry.util.Clock;
import google.registry.util.DateTimeUtils;
import java.io.ByteArrayInputStream;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Date;
import java.util.stream.Collectors;
import javax.inject.Inject;
import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.joda.time.DateTime;
import org.joda.time.Days;
@@ -33,6 +46,7 @@ public class CertificateChecker {
private final int daysToExpiration;
private final int minimumRsaKeyLength;
private final Clock clock;
private final ImmutableSet<String> allowedEcdsaCurves;
/**
* Constructs a CertificateChecker instance with the specified configuration parameters.
@@ -53,23 +67,42 @@ public class CertificateChecker {
* );
* </pre>
*/
@Inject
public CertificateChecker(
ImmutableSortedMap<DateTime, Integer> maxValidityLengthSchedule,
int daysToExpiration,
int minimumRsaKeyLength,
@Config("maxValidityDaysSchedule")
ImmutableSortedMap<DateTime, Integer> maxValidityDaysSchedule,
@Config("expirationWarningDays") int expirationWarningDays,
@Config("minimumRsaKeyLength") int minimumRsaKeyLength,
@Config("allowedEcdsaCurves") ImmutableSet<String> allowedEcdsaCurves,
Clock clock) {
checkArgument(
maxValidityLengthSchedule.containsKey(START_OF_TIME),
maxValidityDaysSchedule.containsKey(START_OF_TIME),
"Max validity length schedule must contain an entry for START_OF_TIME");
this.maxValidityLengthSchedule = maxValidityLengthSchedule;
this.daysToExpiration = daysToExpiration;
this.maxValidityLengthSchedule = maxValidityDaysSchedule;
this.daysToExpiration = expirationWarningDays;
this.minimumRsaKeyLength = minimumRsaKeyLength;
this.allowedEcdsaCurves = allowedEcdsaCurves;
this.clock = clock;
}
/**
* Checks a certificate for violations and returns a list of all the violations the certificate
* has.
* Checks the given certificate string for violations and throws an exception if any violations
* exist.
*/
public void validateCertificate(String certificateString) {
ImmutableSet<CertificateViolation> violations = checkCertificate(certificateString);
if (!violations.isEmpty()) {
String displayMessages =
violations.stream()
.map(violation -> getViolationDisplayMessage(violation))
.collect(Collectors.joining("\n"));
throw new IllegalArgumentException(displayMessages);
}
}
/**
* Checks a given certificate for violations and returns a list of all the violations the
* certificate has.
*/
public ImmutableSet<CertificateViolation> checkCertificate(X509Certificate certificate) {
ImmutableSet.Builder<CertificateViolation> violations = new ImmutableSet.Builder<>();
@@ -97,13 +130,34 @@ public class CertificateChecker {
violations.add(CertificateViolation.RSA_KEY_LENGTH_TOO_SHORT);
}
} else if (key.getAlgorithm().equals("EC")) {
// TODO(sarahbot): Add verification of ECDSA curves
if (!checkCurveName(key, allowedEcdsaCurves)) {
violations.add(CertificateViolation.INVALID_ECDSA_CURVE);
}
} else {
violations.add(CertificateViolation.ALGORITHM_CONSTRAINED);
}
return violations.build();
}
/**
* Converts a given string to a certificate and checks it for violations, returning a list of all
* the violations the certificate has.
*/
public ImmutableSet<CertificateViolation> checkCertificate(String certificateString) {
X509Certificate certificate;
try {
certificate =
(X509Certificate)
CertificateFactory.getInstance("X509")
.generateCertificate(new ByteArrayInputStream(certificateString.getBytes(UTF_8)));
} catch (CertificateException e) {
throw new IllegalArgumentException("Unable to read given certificate.");
}
return checkCertificate(certificate);
}
/**
* Returns whether the certificate is nearing expiration.
*
@@ -126,6 +180,33 @@ public class CertificateChecker {
return Days.daysBetween(start.withTimeAtStartOfDay(), end.withTimeAtStartOfDay()).getDays();
}
/** Checks if the curve used for a public key is in the list of acceptable curves. */
private static boolean checkCurveName(PublicKey key, ImmutableSet<String> allowedEcdsaCurves) {
org.bouncycastle.jce.spec.ECParameterSpec params;
// These 2 different instances of PublicKey need to be handled separately since their OIDs are
// encoded differently. More details on this can be found at
// https://stackoverflow.com/questions/49895713/how-to-find-the-matching-curve-name-from-an-ecpublickey.
if (key instanceof ECPublicKey) {
ECPublicKey ecKey = (ECPublicKey) key;
params = EC5Util.convertSpec(ecKey.getParams(), false);
} else if (key instanceof org.bouncycastle.jce.interfaces.ECPublicKey) {
org.bouncycastle.jce.interfaces.ECPublicKey ecKey =
(org.bouncycastle.jce.interfaces.ECPublicKey) key;
params = ecKey.getParameters();
} else {
throw new IllegalArgumentException("Unrecognized instance of PublicKey.");
}
return allowedEcdsaCurves.stream()
.anyMatch(
curve -> {
ECNamedCurveParameterSpec cParams = ECNamedCurveTable.getParameterSpec(curve);
return cParams.getN().equals(params.getN())
&& cParams.getH().equals(params.getH())
&& cParams.getCurve().equals(params.getCurve())
&& cParams.getG().equals(params.getG());
});
}
private String getViolationDisplayMessage(CertificateViolation certificateViolation) {
// Yes, we'd rather do this as an instance method on the CertificateViolation enum itself, but
// we can't because we need access to configuration (injected as instance variables) which you
@@ -145,6 +226,9 @@ public class CertificateChecker {
return String.format(
"Certificate validity period is too long; it must be less than or equal to %d days.",
this.maxValidityLengthSchedule.lastEntry().getValue());
case INVALID_ECDSA_CURVE:
return String.format(
"The ECDSA key must use one of these algorithms: %s", allowedEcdsaCurves);
default:
throw new IllegalArgumentException(
String.format(
@@ -161,7 +245,8 @@ public class CertificateChecker {
NOT_YET_VALID,
VALIDITY_LENGTH_TOO_LONG,
RSA_KEY_LENGTH_TOO_SHORT,
ALGORITHM_CONSTRAINED;
ALGORITHM_CONSTRAINED,
INVALID_ECDSA_CURVE;
/**
* Gets a suitable end-user-facing display message for this particular certificate violation.

View File

@@ -24,7 +24,6 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import static google.registry.util.CollectionUtils.nullToEmpty;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.cache.CacheBuilder;
@@ -387,7 +386,7 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
private static LoadingCache<VKey<? extends EppResource>, EppResource> createEppResourcesCache(
Duration expiry) {
return CacheBuilder.newBuilder()
.expireAfterWrite(expiry.getMillis(), MILLISECONDS)
.expireAfterWrite(java.time.Duration.ofMillis(expiry.getMillis()))
.maximumSize(getEppResourceMaxCachedEntries())
.build(CACHE_LOADER);
}

View File

@@ -61,7 +61,17 @@ public abstract class ImmutableObject implements Cloneable {
private boolean equalsImmutableObject(ImmutableObject other) {
return getClass().equals(other.getClass())
&& hashCode() == other.hashCode()
&& ModelUtils.getFieldValues(this).equals(ModelUtils.getFieldValues(other));
&& getSignificantFields().equals(other.getSignificantFields());
}
/**
* Returns the map of significant fields (fields that we care about for purposes of comparison and
* display).
*
* <p>Isolated into a method so that derived classes can override it.
*/
protected Map<Field, Object> getSignificantFields() {
return ModelUtils.getFieldValues(this);
}
@Override
@@ -72,7 +82,7 @@ public abstract class ImmutableObject implements Cloneable {
@Override
public int hashCode() {
if (hashCode == null) {
hashCode = Arrays.hashCode(ModelUtils.getFieldValues(this).values().toArray());
hashCode = Arrays.hashCode(getSignificantFields().values().toArray());
}
return hashCode;
}
@@ -111,7 +121,7 @@ public abstract class ImmutableObject implements Cloneable {
@Override
public String toString() {
NavigableMap<String, Object> sortedFields = new TreeMap<>();
for (Entry<Field, Object> entry : ModelUtils.getFieldValues(this).entrySet()) {
for (Entry<Field, Object> entry : getSignificantFields().entrySet()) {
sortedFields.put(entry.getKey().getName(), entry.getValue());
}
return toStringHelper(sortedFields);
@@ -121,7 +131,7 @@ public abstract class ImmutableObject implements Cloneable {
public String toHydratedString() {
// We can't use ImmutableSortedMap because we need to allow null values.
NavigableMap<String, Object> sortedFields = new TreeMap<>();
for (Entry<Field, Object> entry : ModelUtils.getFieldValues(this).entrySet()) {
for (Entry<Field, Object> entry : getSignificantFields().entrySet()) {
Field field = entry.getKey();
Object value = entry.getValue();
sortedFields.put(
@@ -161,7 +171,7 @@ public abstract class ImmutableObject implements Cloneable {
// LinkedHashMap to preserve field ordering and because ImmutableMap forbids null
// values.
Map<String, Object> result = new LinkedHashMap<>();
for (Entry<Field, Object> entry : ModelUtils.getFieldValues(o).entrySet()) {
for (Entry<Field, Object> entry : ((ImmutableObject) o).getSignificantFields().entrySet()) {
Field field = entry.getKey();
if (!field.isAnnotationPresent(IgnoredInDiffableMap.class)) {
result.put(field.getName(), toMapRecursive(entry.getValue()));

View File

@@ -194,7 +194,7 @@ public class ModelUtils {
* returned map in its implementation of {@link ImmutableObject#toString} and {@link
* ImmutableObject#equals}, which work by comparing and printing these maps.
*/
static Map<Field, Object> getFieldValues(Object instance) {
public static Map<Field, Object> getFieldValues(Object instance) {
// Don't make this ImmutableMap because field values can be null.
Map<Field, Object> values = new LinkedHashMap<>();
for (Field field : getAllFields(instance.getClass()).values()) {

View File

@@ -59,25 +59,11 @@ public class OteStats {
private OteStats() {}
private static final Predicate<EppInput> HAS_CLAIMS_NOTICE =
eppInput -> {
Optional<LaunchCreateExtension> launchCreate =
eppInput.getSingleExtension(LaunchCreateExtension.class);
return launchCreate.isPresent() && launchCreate.get().getNotice() != null;
};
private static final Predicate<EppInput> HAS_SEC_DNS =
eppInput ->
eppInput.getSingleExtension(SecDnsCreateExtension.class).isPresent()
|| eppInput.getSingleExtension(SecDnsUpdateExtension.class).isPresent();
private static final Predicate<EppInput> IS_SUNRISE =
eppInput -> {
Optional<LaunchCreateExtension> launchCreate =
eppInput.getSingleExtension(LaunchCreateExtension.class);
return launchCreate.isPresent() && !isNullOrEmpty(launchCreate.get().getSignedMarks());
};
private static final Predicate<EppInput> IS_IDN =
eppInput ->
((DomainCommand.Create)
@@ -94,6 +80,18 @@ public class OteStats {
.getResourceCommand())
.getInetAddresses());
private static boolean hasClaimsNotice(EppInput eppInput) {
Optional<LaunchCreateExtension> launchCreate =
eppInput.getSingleExtension(LaunchCreateExtension.class);
return launchCreate.isPresent() && launchCreate.get().getNotice() != null;
}
private static boolean isSunrise(EppInput eppInput) {
Optional<LaunchCreateExtension> launchCreate =
eppInput.getSingleExtension(LaunchCreateExtension.class);
return launchCreate.isPresent() && !isNullOrEmpty(launchCreate.get().getSignedMarks());
}
/** Enum defining the distinct statistics (types of registrar actions) to record. */
public enum StatType {
CONTACT_CREATES(0, equalTo(Type.CONTACT_CREATE)),
@@ -107,8 +105,8 @@ public class OteStats {
DOMAIN_CREATES(0, equalTo(Type.DOMAIN_CREATE)),
DOMAIN_CREATES_ASCII(1, equalTo(Type.DOMAIN_CREATE), IS_IDN.negate()),
DOMAIN_CREATES_IDN(1, equalTo(Type.DOMAIN_CREATE), IS_IDN),
DOMAIN_CREATES_START_DATE_SUNRISE(1, equalTo(Type.DOMAIN_CREATE), IS_SUNRISE),
DOMAIN_CREATES_WITH_CLAIMS_NOTICE(1, equalTo(Type.DOMAIN_CREATE), HAS_CLAIMS_NOTICE),
DOMAIN_CREATES_START_DATE_SUNRISE(1, equalTo(Type.DOMAIN_CREATE), OteStats::isSunrise),
DOMAIN_CREATES_WITH_CLAIMS_NOTICE(1, equalTo(Type.DOMAIN_CREATE), OteStats::hasClaimsNotice),
DOMAIN_CREATES_WITH_FEE(
1,
equalTo(Type.DOMAIN_CREATE),

View File

@@ -24,6 +24,7 @@ import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
@@ -49,6 +50,8 @@ import google.registry.model.transfer.TransferData.TransferServerApproveEntity;
import google.registry.persistence.VKey;
import google.registry.persistence.WithLongVKey;
import google.registry.schema.replay.DatastoreAndSqlEntity;
import google.registry.schema.replay.DatastoreEntity;
import google.registry.schema.replay.SqlEntity;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
@@ -118,13 +121,11 @@ public abstract class BillingEvent extends ImmutableObject
String clientId;
/** Revision id of the entry in DomainHistory table that ths bill belongs to. */
// TODO(shicong): Add foreign key constraint when DomainHistory table is generated
@Ignore
@Column(nullable = false)
Long domainHistoryRevisionId;
/** ID of the EPP resource that the bill is for. */
// TODO(shicong): Add foreign key constraint when we expand DatastoreHelp for Postgresql
@Ignore
@Column(nullable = false)
String domainRepoId;
@@ -288,7 +289,7 @@ public abstract class BillingEvent extends ImmutableObject
@javax.persistence.Index(columnList = "eventTime"),
@javax.persistence.Index(columnList = "billingTime"),
@javax.persistence.Index(columnList = "syntheticCreationTime"),
@javax.persistence.Index(columnList = "allocation_token_id")
@javax.persistence.Index(columnList = "allocationToken")
})
@AttributeOverride(name = "id", column = @Column(name = "billing_event_id"))
@WithLongVKey
@@ -331,10 +332,7 @@ public abstract class BillingEvent extends ImmutableObject
/**
* The {@link AllocationToken} used in the creation of this event, or null if one was not used.
*
* <p>TODO(shicong): Add foreign key constraint when AllocationToken schema is generated
*/
@Column(name = "allocation_token_id")
@Index
@Nullable
VKey<AllocationToken> allocationToken;
@@ -685,7 +683,7 @@ public abstract class BillingEvent extends ImmutableObject
@ReportedOn
@Entity
@WithLongVKey
public static class Modification extends BillingEvent implements DatastoreAndSqlEntity {
public static class Modification extends BillingEvent implements DatastoreEntity {
/** The change in cost that should be applied to the original billing event. */
Money cost;
@@ -747,6 +745,11 @@ public abstract class BillingEvent extends ImmutableObject
.build();
}
@Override
public ImmutableList<SqlEntity> toSqlEntities() {
return ImmutableList.of(); // not persisted in SQL
}
/** A builder for {@link Modification} since it is immutable. */
public static class Builder extends BillingEvent.Builder<Modification, Builder> {

View File

@@ -20,15 +20,16 @@ import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Parent;
import google.registry.model.ImmutableObject;
import javax.persistence.MappedSuperclass;
import javax.persistence.Transient;
/** A singleton entity in Datastore. */
@MappedSuperclass
public abstract class CrossTldSingleton extends ImmutableObject {
public static final long SINGLETON_ID = 1; // There is always exactly one of these.
public static final long SINGLETON_ID = 1; // There is always exactly one of these.
@Id
long id = SINGLETON_ID;
@Id @Transient long id = SINGLETON_ID;
@Parent
Key<EntityGroupRoot> parent = getCrossTldKey();
@Transient @Parent Key<EntityGroupRoot> parent = getCrossTldKey();
}

View File

@@ -60,7 +60,9 @@ public class ContactHistory extends HistoryEntry implements SqlEntity {
@Id
@Access(AccessType.PROPERTY)
public String getContactRepoId() {
return parent.getName();
// We need to handle null case here because Hibernate sometimes accesses this method before
// parent gets initialized
return parent == null ? null : parent.getName();
}
/** This method is private because it is only used by Hibernate. */

View File

@@ -303,14 +303,13 @@ public class DomainContent extends EppResource
allContacts.stream().map(DesignatedContact::reconstitute).collect(toImmutableSet());
setContactFields(allContacts, true);
// We have to return the cloned object here because the original object's
// hashcode is not correct due to the change to its domainRepoId. The cloned
// object will have a null hashcode so that it can get a recalculated hashcode
// when its hashCode() is invoked.
// We have to return the cloned object here because the original object's hashcode is not
// correct due to the change to its domainRepoId and history ids. The cloned object will have a
// null hashcode so that it can get a recalculated hashcode when its hashCode() is invoked.
// TODO(b/162739503): Remove this after fully migrating to Cloud SQL.
gracePeriods =
nullToEmptyImmutableCopy(gracePeriods).stream()
.map(gracePeriod -> gracePeriod.cloneWithDomainRepoId(getRepoId()))
.map(gracePeriod -> gracePeriod.cloneAfterOfyLoad(getRepoId()))
.collect(toImmutableSet());
// Restore history record ids.

View File

@@ -14,14 +14,17 @@
package google.registry.model.domain;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.EntitySubclass;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.ImmutableObject;
import google.registry.model.domain.DomainHistory.DomainHistoryId;
import google.registry.model.domain.secdns.DomainDsDataHistory;
import google.registry.model.host.HostResource;
import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.HistoryEntry;
@@ -40,10 +43,12 @@ import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.JoinTable;
import javax.persistence.OneToMany;
import javax.persistence.PostLoad;
@@ -75,7 +80,9 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
@Id
@Access(AccessType.PROPERTY)
public String getDomainRepoId() {
return parent.getName();
// We need to handle null case here because Hibernate sometimes accesses this method before
// parent gets initialized
return parent == null ? null : parent.getName();
}
/** This method is private because it is only used by Hibernate. */
@@ -93,6 +100,24 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
@Column(name = "host_repo_id")
Set<VKey<HostResource>> nsHosts;
@OneToMany(
cascade = {CascadeType.ALL},
fetch = FetchType.EAGER,
orphanRemoval = true)
@JoinColumns({
@JoinColumn(
name = "domainHistoryRevisionId",
referencedColumnName = "historyRevisionId",
insertable = false,
updatable = false),
@JoinColumn(
name = "domainRepoId",
referencedColumnName = "domainRepoId",
insertable = false,
updatable = false)
})
Set<DomainDsDataHistory> dsDataHistories;
@Override
@Nullable
@Access(AccessType.PROPERTY)
@@ -127,14 +152,24 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
*
* <p>This will be empty for any DomainHistory/HistoryEntry generated before this field was added,
* mid-2017, as well as any action that does not generate billable events (e.g. updates).
*
* <p>This method is dedicated for Hibernate, external caller should use {@link
* #getDomainTransactionRecords()}.
*/
@Access(AccessType.PROPERTY)
@OneToMany(cascade = {CascadeType.ALL})
@JoinColumn(name = "historyRevisionId", referencedColumnName = "historyRevisionId")
@JoinColumn(name = "domainRepoId", referencedColumnName = "domainRepoId")
@Override
public Set<DomainTransactionRecord> getDomainTransactionRecords() {
return super.getDomainTransactionRecords();
@SuppressWarnings("unused")
private Set<DomainTransactionRecord> getInternalDomainTransactionRecords() {
return domainTransactionRecords;
}
/** Sets the domain transaction records. This method is dedicated for Hibernate. */
@SuppressWarnings("unused")
private void setInternalDomainTransactionRecords(
Set<DomainTransactionRecord> domainTransactionRecords) {
this.domainTransactionRecords = domainTransactionRecords;
}
@Id
@@ -150,6 +185,11 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
return nsHosts;
}
/** Returns the collection of {@link DomainDsDataHistory} instances. */
public ImmutableSet<DomainDsDataHistory> getDsDataHistories() {
return nullToEmptyImmutableCopy(dsDataHistories);
}
/**
* The values of all the fields on the {@link DomainContent} object after the action represented
* by this history object was executed.
@@ -266,9 +306,6 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
public Builder setDomainContent(DomainContent domainContent) {
getInstance().domainContent = domainContent;
if (domainContent != null) {
getInstance().nsHosts = nullToEmptyImmutableCopy(domainContent.nsHosts);
}
return this;
}
@@ -276,5 +313,22 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
getInstance().parent = Key.create(DomainBase.class, domainRepoId);
return this;
}
@Override
public DomainHistory build() {
DomainHistory instance = super.build();
// TODO(b/171990736): Assert instance.domainContent is not null after database migration.
// Note that we cannot assert that instance.domainContent is not null here because this
// builder is also used to convert legacy HistoryEntry objects to DomainHistory, when
// domainContent is not available.
if (instance.domainContent != null) {
instance.nsHosts = nullToEmptyImmutableCopy(instance.domainContent.nsHosts);
instance.dsDataHistories =
nullToEmptyImmutableCopy(instance.domainContent.getDsData()).stream()
.map(dsData -> DomainDsDataHistory.createFrom(instance.id, dsData))
.collect(toImmutableSet());
}
return instance;
}
}
}

View File

@@ -21,6 +21,7 @@ import com.googlecode.objectify.annotation.Embed;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.ofy.ObjectifyService;
import google.registry.persistence.VKey;
import google.registry.schema.replay.DatastoreAndSqlEntity;
import javax.annotation.Nullable;
@@ -53,12 +54,15 @@ public class GracePeriod extends GracePeriodBase implements DatastoreAndSqlEntit
(billingEventRecurring != null) == GracePeriodStatus.AUTO_RENEW.equals(type),
"Recurring billing events must be present on (and only on) autorenew grace periods");
GracePeriod instance = new GracePeriod();
instance.id = ObjectifyService.allocateId();
instance.type = checkArgumentNotNull(type);
instance.domainRepoId = checkArgumentNotNull(domainRepoId);
instance.expirationTime = checkArgumentNotNull(expirationTime);
instance.clientId = checkArgumentNotNull(clientId);
instance.billingEventOneTime = billingEventOneTime;
instance.billingEventOneTimeHistoryId = DomainBase.getHistoryId(billingEventOneTime);
instance.billingEventRecurring = billingEventRecurring;
instance.billingEventRecurringHistoryId = DomainBase.getHistoryId(billingEventRecurring);
return instance;
}
@@ -108,14 +112,28 @@ public class GracePeriod extends GracePeriodBase implements DatastoreAndSqlEntit
}
/**
* Returns a clone of this {@link GracePeriod} with {@link #domainRepoId} set to the given value.
* Returns a clone of this {@link GracePeriod} with {@link #domainRepoId} set to the given value
* and reconstructed history ids.
*
* <p>TODO(b/162739503): Remove this function after fully migrating to Cloud SQL.
*/
public GracePeriod cloneWithDomainRepoId(String domainRepoId) {
public GracePeriod cloneAfterOfyLoad(String domainRepoId) {
GracePeriod clone = clone(this);
clone.id = ObjectifyService.allocateId();
clone.domainRepoId = checkArgumentNotNull(domainRepoId);
clone.restoreHistoryIds();
return clone;
}
/**
* Returns a clone of this {@link GracePeriod} with {@link #billingEventRecurring} set to the
* given value.
*
* <p>TODO(b/162231099): Remove this function after duplicate id issue is solved.
*/
public GracePeriod cloneWithRecurringBillingEvent(VKey<BillingEvent.Recurring> recurring) {
GracePeriod clone = clone(this);
clone.billingEventRecurring = recurring;
return clone;
}
}

View File

@@ -14,18 +14,21 @@
package google.registry.model.domain;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.ImmutableObject;
import google.registry.model.ModelUtils;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.OneTime;
import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.persistence.VKey;
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.persistence.Column;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.MappedSuperclass;
import org.joda.time.DateTime;
@@ -36,7 +39,6 @@ public class GracePeriodBase extends ImmutableObject {
/** Unique id required for hibernate representation. */
@javax.persistence.Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Ignore
Long id;
@@ -67,6 +69,10 @@ public class GracePeriodBase extends ImmutableObject {
@Column(name = "billing_event_id")
VKey<OneTime> billingEventOneTime = null;
@Ignore
@Column(name = "billing_event_history_id")
Long billingEventOneTimeHistoryId;
/**
* The recurring billing event corresponding to the action that triggered this grace period, if
* applicable - i.e. if the action was an autorenew - or null in all other cases.
@@ -75,6 +81,14 @@ public class GracePeriodBase extends ImmutableObject {
@Column(name = "billing_recurrence_id")
VKey<BillingEvent.Recurring> billingEventRecurring = null;
@Ignore
@Column(name = "billing_recurrence_history_id")
Long billingEventRecurringHistoryId;
public long getId() {
return id;
}
public GracePeriodStatus getType() {
return type;
}
@@ -101,6 +115,7 @@ public class GracePeriodBase extends ImmutableObject {
* period is not AUTO_RENEW.
*/
public VKey<BillingEvent.OneTime> getOneTimeBillingEvent() {
restoreOfyKeys();
return billingEventOneTime;
}
@@ -109,6 +124,63 @@ public class GracePeriodBase extends ImmutableObject {
* period is AUTO_RENEW.
*/
public VKey<BillingEvent.Recurring> getRecurringBillingEvent() {
restoreOfyKeys();
return billingEventRecurring;
}
/**
* Restores history ids for composite VKeys after a load from datastore.
*
* <p>For use by DomainContent.load() ONLY.
*/
protected void restoreHistoryIds() {
billingEventOneTimeHistoryId = DomainBase.getHistoryId(billingEventOneTime);
billingEventRecurringHistoryId = DomainBase.getHistoryId(billingEventRecurring);
}
/**
* Override {@link ImmutableObject#getSignificantFields()} to exclude "id", which breaks equality
* testing in the unit tests.
*/
@Override
protected Map<Field, Object> getSignificantFields() {
restoreOfyKeys();
// Can't use streams or ImmutableMap because we can have null values.
Map<Field, Object> result = new LinkedHashMap();
for (Map.Entry<Field, Object> entry : ModelUtils.getFieldValues(this).entrySet()) {
if (!entry.getKey().getName().equals("id")) {
result.put(entry.getKey(), entry.getValue());
}
}
return result;
}
/**
* Restores Ofy keys in the billing events.
*
* <p>This must be called by all methods that access the one time or recurring billing event keys.
* When the billing event keys are loaded from SQL, they are loaded as asymmetric keys because the
* database columns that we load them from do not contain all of the information necessary to
* reconsitute the Ofy side of the key. In other cases, we restore the Ofy key during the
* hibernate {@link javax.persistence.PostLoad} method from the other fields of the object, but we
* have been unable to make this work with hibernate's internal persistence model in this case
* because the {@link GracePeriod}'s hash code is evaluated prior to these calls, and would be
* invalidated by changing the fields.
*/
private final synchronized void restoreOfyKeys() {
if (billingEventOneTime != null && !billingEventOneTime.maybeGetOfyKey().isPresent()) {
billingEventOneTime =
DomainBase.restoreOfyFrom(
Key.create(DomainBase.class, domainRepoId),
billingEventOneTime,
billingEventOneTimeHistoryId);
}
if (billingEventRecurring != null && !billingEventRecurring.maybeGetOfyKey().isPresent()) {
billingEventRecurring =
DomainBase.restoreOfyFrom(
Key.create(DomainBase.class, domainRepoId),
billingEventRecurring,
billingEventRecurringHistoryId);
}
}
}

View File

@@ -17,89 +17,68 @@ package google.registry.model.domain.secdns;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.ImmutableObject;
import google.registry.model.domain.secdns.DelegationSignerData.DelegationSignerDataId;
import google.registry.schema.replay.DatastoreAndSqlEntity;
import google.registry.model.domain.secdns.DelegationSignerData.DomainDsDataId;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Index;
import javax.persistence.Table;
import javax.xml.bind.DatatypeConverter;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
/**
* Holds the data necessary to construct a single Delegation Signer (DS) record for a domain.
*
* @see <a href="http://tools.ietf.org/html/rfc5910">RFC 5910</a>
* @see <a href="http://tools.ietf.org/html/rfc4034">RFC 4034</a>
* <p>TODO(shicong): Rename this class to DomainDsData.
*/
@Embed
@XmlType(name = "dsData")
@Entity
@IdClass(DomainDsDataId.class)
@Table(indexes = @Index(columnList = "domainRepoId"))
@IdClass(DelegationSignerDataId.class)
public class DelegationSignerData extends ImmutableObject implements DatastoreAndSqlEntity {
public class DelegationSignerData extends DomainDsDataBase {
private DelegationSignerData() {}
@Ignore @XmlTransient @javax.persistence.Id String domainRepoId;
/** The identifier for this particular key in the domain. */
@javax.persistence.Id
@Column(nullable = false)
int keyTag;
/**
* The algorithm used by this key.
*
* @see <a href="http://tools.ietf.org/html/rfc4034#appendix-A.1">RFC 4034 Appendix A.1</a>
*/
@Column(nullable = false)
@XmlElement(name = "alg")
int algorithm;
/**
* The algorithm used to generate the digest.
*
* @see <a href="http://tools.ietf.org/html/rfc4034#appendix-A.2">RFC 4034 Appendix A.2</a>
*/
@Column(nullable = false)
int digestType;
/**
* The hexBinary digest of the public key.
*
* @see <a href="http://tools.ietf.org/html/rfc4034#section-5.1.4">RFC 4034 Section 5.1.4</a>
*/
@Column(nullable = false)
@XmlJavaTypeAdapter(HexBinaryAdapter.class)
byte[] digest;
@Override
@Id
@Access(AccessType.PROPERTY)
public String getDomainRepoId() {
return super.getDomainRepoId();
}
@Override
@Id
@Access(AccessType.PROPERTY)
public int getKeyTag() {
return keyTag;
return super.getKeyTag();
}
@Override
@Id
@Access(AccessType.PROPERTY)
public int getAlgorithm() {
return algorithm;
return super.getAlgorithm();
}
@Override
@Id
@Access(AccessType.PROPERTY)
public int getDigestType() {
return digestType;
return super.getDigestType();
}
@Override
@Id
@Access(AccessType.PROPERTY)
public byte[] getDigest() {
return digest;
}
public String getDigestAsString() {
return digest == null ? "" : DatatypeConverter.printHexBinary(digest);
return super.getDigest();
}
public DelegationSignerData cloneWithDomainRepoId(String domainRepoId) {
@@ -135,30 +114,135 @@ public class DelegationSignerData extends ImmutableObject implements DatastoreAn
return create(keyTag, algorithm, digestType, DatatypeConverter.parseHexBinary(digestAsHex));
}
/**
* Returns the presentation format of this DS record.
*
* @see <a href="https://tools.ietf.org/html/rfc4034#section-5.3">RFC 4034 Section 5.3</a>
*/
public String toRrData() {
return String.format(
"%d %d %d %s",
this.keyTag, this.algorithm, this.digestType, DatatypeConverter.printHexBinary(digest));
}
/** Class to represent the composite primary key of {@link DelegationSignerData} entity. */
static class DomainDsDataId extends ImmutableObject implements Serializable {
static class DelegationSignerDataId extends ImmutableObject implements Serializable {
String domainRepoId;
int keyTag;
private DelegationSignerDataId() {}
int algorithm;
private DelegationSignerDataId(String domainRepoId, int keyTag) {
int digestType;
byte[] digest;
/** Hibernate requires this default constructor. */
private DomainDsDataId() {}
/** Constructs a {link DomainDsDataId} instance. */
DomainDsDataId(String domainRepoId, int keyTag, int algorithm, int digestType, byte[] digest) {
this.domainRepoId = domainRepoId;
this.keyTag = keyTag;
this.algorithm = algorithm;
this.digestType = digestType;
this.digest = digest;
}
/**
* Returns the domain repository ID.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private String getDomainRepoId() {
return domainRepoId;
}
/**
* Returns the key tag.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private int getKeyTag() {
return keyTag;
}
/**
* Returns the algorithm.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private int getAlgorithm() {
return algorithm;
}
/**
* Returns the digest type.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private int getDigestType() {
return digestType;
}
/**
* Returns the digest.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private byte[] getDigest() {
return digest;
}
/**
* Sets the domain repository ID.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private void setDomainRepoId(String domainRepoId) {
this.domainRepoId = domainRepoId;
}
/**
* Sets the key tag.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private void setKeyTag(int keyTag) {
this.keyTag = keyTag;
}
public static DelegationSignerDataId create(String domainRepoId, int keyTag) {
return new DelegationSignerDataId(checkArgumentNotNull(domainRepoId), keyTag);
/**
* Sets the algorithm.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private void setAlgorithm(int algorithm) {
this.algorithm = algorithm;
}
/**
* Sets the digest type.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private void setDigestType(int digestType) {
this.digestType = digestType;
}
/**
* Sets the digest.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private void setDigest(byte[] digest) {
this.digest = digest;
}
public static DomainDsDataId create(
String domainRepoId, int keyTag, int algorithm, int digestType, byte[] digest) {
return new DomainDsDataId(
domainRepoId, keyTag, algorithm, digestType, checkArgumentNotNull(digest));
}
}
}

View File

@@ -0,0 +1,150 @@
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.model.domain.secdns;
import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.ImmutableObject;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.MappedSuperclass;
import javax.persistence.Transient;
import javax.xml.bind.DatatypeConverter;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
/** Base class for {@link DelegationSignerData} and {@link DomainDsDataHistory}. */
@Embed
@MappedSuperclass
@Access(AccessType.FIELD)
public abstract class DomainDsDataBase extends ImmutableObject {
@Ignore @XmlTransient @Transient String domainRepoId;
/** The identifier for this particular key in the domain. */
@Transient int keyTag;
/**
* The algorithm used by this key.
*
* @see <a href="http://tools.ietf.org/html/rfc4034#appendix-A.1">RFC 4034 Appendix A.1</a>
*/
@Transient
@XmlElement(name = "alg")
int algorithm;
/**
* The algorithm used to generate the digest.
*
* @see <a href="http://tools.ietf.org/html/rfc4034#appendix-A.2">RFC 4034 Appendix A.2</a>
*/
@Transient int digestType;
/**
* The hexBinary digest of the public key.
*
* @see <a href="http://tools.ietf.org/html/rfc4034#section-5.1.4">RFC 4034 Section 5.1.4</a>
*/
@Transient
@XmlJavaTypeAdapter(HexBinaryAdapter.class)
byte[] digest;
public String getDomainRepoId() {
return domainRepoId;
}
public int getKeyTag() {
return keyTag;
}
public int getAlgorithm() {
return algorithm;
}
public int getDigestType() {
return digestType;
}
public byte[] getDigest() {
return digest;
}
/**
* Sets the domain repository ID.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private void setDomainRepoId(String domainRepoId) {
this.domainRepoId = domainRepoId;
}
/**
* Sets the key tag.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private void setKeyTag(int keyTag) {
this.keyTag = keyTag;
}
/**
* Sets the algorithm.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private void setAlgorithm(int algorithm) {
this.algorithm = algorithm;
}
/**
* Sets the digest type.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private void setDigestType(int digestType) {
this.digestType = digestType;
}
/**
* Sets the digest.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private void setDigest(byte[] digest) {
this.digest = digest;
}
public String getDigestAsString() {
return digest == null ? "" : DatatypeConverter.printHexBinary(digest);
}
/**
* Returns the presentation format of this DS record.
*
* @see <a href="https://tools.ietf.org/html/rfc4034#section-5.3">RFC 4034 Section 5.3</a>
*/
public String toRrData() {
return String.format(
"%d %d %d %s",
this.keyTag, this.algorithm, this.digestType, DatatypeConverter.printHexBinary(digest));
}
}

View File

@@ -0,0 +1,92 @@
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.model.domain.secdns;
import com.google.common.collect.ImmutableList;
import google.registry.model.domain.DomainHistory;
import google.registry.model.ofy.ObjectifyService;
import google.registry.schema.replay.DatastoreEntity;
import google.registry.schema.replay.SqlEntity;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
/** Entity class to represent a historic {@link DelegationSignerData}. */
@Entity
public class DomainDsDataHistory extends DomainDsDataBase implements SqlEntity {
@Id Long dsDataHistoryRevisionId;
/** ID of the {@link DomainHistory} entity that this entity is associated with. */
@Column(nullable = false)
Long domainHistoryRevisionId;
private DomainDsDataHistory() {}
/**
* Creates a {@link DomainDsDataHistory} instance from given {@link #domainHistoryRevisionId} and
* {@link DelegationSignerData} instance.
*/
public static DomainDsDataHistory createFrom(
long domainHistoryRevisionId, DelegationSignerData dsData) {
DomainDsDataHistory instance = new DomainDsDataHistory();
instance.domainHistoryRevisionId = domainHistoryRevisionId;
instance.domainRepoId = dsData.domainRepoId;
instance.keyTag = dsData.getKeyTag();
instance.algorithm = dsData.getAlgorithm();
instance.digestType = dsData.getDigestType();
instance.digest = dsData.getDigest();
instance.dsDataHistoryRevisionId = ObjectifyService.allocateId();
return instance;
}
@Override
@Access(AccessType.PROPERTY)
public String getDomainRepoId() {
return super.getDomainRepoId();
}
@Override
@Access(AccessType.PROPERTY)
public int getKeyTag() {
return super.getKeyTag();
}
@Override
@Access(AccessType.PROPERTY)
public int getAlgorithm() {
return super.getAlgorithm();
}
@Override
@Access(AccessType.PROPERTY)
public int getDigestType() {
return super.getDigestType();
}
@Override
@Access(AccessType.PROPERTY)
@Column(nullable = false)
public byte[] getDigest() {
return super.getDigest();
}
@Override
public ImmutableList<DatastoreEntity> toDatastoreEntities() {
return ImmutableList.of(); // not persisted in Datastore
}
}

View File

@@ -61,7 +61,9 @@ public class HostHistory extends HistoryEntry implements SqlEntity {
@Id
@Access(AccessType.PROPERTY)
public String getHostRepoId() {
return parent.getName();
// We need to handle null case here because Hibernate sometimes accesses this method before
// parent gets initialized
return parent == null ? null : parent.getName();
}
/** This method is private because it is only used by Hibernate. */

View File

@@ -20,7 +20,6 @@ import static google.registry.config.RegistryConfig.getEppResourceMaxCachedEntri
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.TypeUtils.instantiate;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.cache.CacheBuilder;
@@ -244,7 +243,7 @@ public abstract class ForeignKeyIndex<E extends EppResource> extends BackupGroup
private static LoadingCache<Key<ForeignKeyIndex<?>>, Optional<ForeignKeyIndex<?>>>
createForeignKeyIndexesCache(Duration expiry) {
return CacheBuilder.newBuilder()
.expireAfterWrite(expiry.getMillis(), MILLISECONDS)
.expireAfterWrite(java.time.Duration.ofMillis(expiry.getMillis()))
.maximumSize(getEppResourceMaxCachedEntries())
.build(CACHE_LOADER);
}

View File

@@ -23,7 +23,9 @@ import com.google.common.base.Functions;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Streams;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.Result;
import google.registry.model.contact.ContactHistory;
import google.registry.model.host.HostHistory;
import google.registry.model.reporting.HistoryEntry;
@@ -102,12 +104,22 @@ public class DatastoreTransactionManager implements TransactionManager {
@Override
public void insert(Object entity) {
saveEntity(entity);
put(entity);
}
@Override
public void insertAll(ImmutableCollection<?> entities) {
getOfy().save().entities(entities);
putAll(entities);
}
@Override
public void insertWithoutBackup(Object entity) {
putWithoutBackup(entity);
}
@Override
public void insertAllWithoutBackup(ImmutableCollection<?> entities) {
putAllWithoutBackup(entities);
}
@Override
@@ -117,17 +129,37 @@ public class DatastoreTransactionManager implements TransactionManager {
@Override
public void putAll(ImmutableCollection<?> entities) {
getOfy().save().entities(entities);
syncIfTransactionless(getOfy().save().entities(entities));
}
@Override
public void putWithoutBackup(Object entity) {
syncIfTransactionless(getOfy().saveWithoutBackup().entities(entity));
}
@Override
public void putAllWithoutBackup(ImmutableCollection<?> entities) {
syncIfTransactionless(getOfy().saveWithoutBackup().entities(entities));
}
@Override
public void update(Object entity) {
saveEntity(entity);
put(entity);
}
@Override
public void updateAll(ImmutableCollection<?> entities) {
getOfy().save().entities(entities);
putAll(entities);
}
@Override
public void updateWithoutBackup(Object entity) {
putWithoutBackup(entity);
}
@Override
public void updateAllWithoutBackup(ImmutableCollection<?> entities) {
putAllWithoutBackup(entities);
}
@Override
@@ -158,6 +190,11 @@ public class DatastoreTransactionManager implements TransactionManager {
return result;
}
@Override
public <T> T load(T entity) {
return ofy().load().entity(entity).now();
}
@Override
public <T> ImmutableMap<VKey<? extends T>, T> load(Iterable<? extends VKey<? extends T>> keys) {
// Keep track of the Key -> VKey mapping so we can translate them back.
@@ -175,13 +212,17 @@ public class DatastoreTransactionManager implements TransactionManager {
@Override
public <T> ImmutableList<T> loadAll(Class<T> clazz) {
// We can do a ofy().load().type(clazz), but this doesn't work in a transaction.
throw new UnsupportedOperationException("Not available in the Datastore transaction manager");
return ImmutableList.copyOf(getOfy().load().type(clazz));
}
@Override
public <T> ImmutableList<T> loadAll(Iterable<T> entities) {
return ImmutableList.copyOf(getOfy().load().entities(entities).values());
}
@Override
public void delete(VKey<?> key) {
getOfy().delete().key(key.getOfyKey()).now();
syncIfTransactionless(getOfy().delete().key(key.getOfyKey()));
}
@Override
@@ -192,7 +233,35 @@ public class DatastoreTransactionManager implements TransactionManager {
StreamSupport.stream(vKeys.spliterator(), false)
.map(VKey::getOfyKey)
.collect(toImmutableList());
getOfy().delete().keys(list).now();
syncIfTransactionless(getOfy().delete().keys(list));
}
@Override
public void delete(Object entity) {
syncIfTransactionless(getOfy().delete().entity(entity));
}
@Override
public void deleteWithoutBackup(VKey<?> key) {
syncIfTransactionless(getOfy().deleteWithoutBackup().key(key.getOfyKey()));
}
@Override
public void deleteWithoutBackup(Iterable<? extends VKey<?>> keys) {
syncIfTransactionless(
getOfy()
.deleteWithoutBackup()
.keys(Streams.stream(keys).map(VKey::getOfyKey).collect(toImmutableList())));
}
@Override
public void deleteWithoutBackup(Object entity) {
syncIfTransactionless(getOfy().deleteWithoutBackup().entity(entity));
}
@Override
public void clearSessionCache() {
getOfy().clearSessionCache();
}
/**
@@ -209,7 +278,7 @@ public class DatastoreTransactionManager implements TransactionManager {
if (entity instanceof HistoryEntry) {
entity = ((HistoryEntry) entity).asHistoryEntry();
}
getOfy().save().entity(entity);
syncIfTransactionless(getOfy().save().entity(entity));
}
@SuppressWarnings("unchecked")
@@ -227,4 +296,19 @@ public class DatastoreTransactionManager implements TransactionManager {
private <T> T loadNullable(VKey<T> key) {
return toChildHistoryEntryIfPossible(getOfy().load().key(key.getOfyKey()).now());
}
/**
* Executes the given {@link Result} instance synchronously if not in a transaction.
*
* <p>The {@link Result} instance contains a task that will be executed by Objectify
* asynchronously. If it is in a transaction, we don't need to execute the task immediately
* because it is guaranteed to be done by the end of the transaction. However, if it is not in a
* transaction, we need to execute it in case the following code expects that happens before
* themselves.
*/
private void syncIfTransactionless(Result<?> result) {
if (!inTransaction()) {
result.now();
}
}
}

View File

@@ -15,17 +15,30 @@
package google.registry.model.rde;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Verify.verify;
import static com.google.common.base.Verify.verifyNotNull;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.rde.RdeNamingUtils.makePartialName;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.google.common.base.VerifyException;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.BackupGroupRoot;
import google.registry.model.ImmutableObject;
import google.registry.model.rde.RdeRevision.RdeRevisionId;
import google.registry.persistence.VKey;
import google.registry.persistence.converter.LocalDateConverter;
import google.registry.schema.replay.NonReplicatedEntity;
import java.io.Serializable;
import java.util.Optional;
import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.IdClass;
import javax.persistence.Transient;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
/**
* Datastore entity for tracking RDE revisions.
@@ -35,19 +48,41 @@ import org.joda.time.DateTime;
* flag is included in the generated XML.
*/
@Entity
public final class RdeRevision extends ImmutableObject {
@javax.persistence.Entity
@IdClass(RdeRevisionId.class)
public final class RdeRevision extends BackupGroupRoot implements NonReplicatedEntity {
/** String triplet of tld, date, and mode, e.g. {@code soy_2015-09-01_full}. */
@Id
String id;
@Id @Transient String id;
@javax.persistence.Id @Ignore String tld;
@javax.persistence.Id @Ignore LocalDate date;
@javax.persistence.Id @Ignore RdeMode mode;
/**
* Number of last revision successfully staged to GCS.
*
* <p>This values begins at zero upon object creation and thenceforth incremented transactionally.
*/
@Column(nullable = false)
int revision;
/** Hibernate requires an empty constructor. */
private RdeRevision() {}
public static RdeRevision create(
String id, String tld, LocalDate date, RdeMode mode, int revision) {
RdeRevision instance = new RdeRevision();
instance.id = id;
instance.tld = tld;
instance.date = date;
instance.mode = mode;
instance.revision = revision;
return instance;
}
public int getRevision() {
return revision;
}
@@ -58,9 +93,12 @@ public final class RdeRevision extends ImmutableObject {
* @return {@code 0} for first deposit generation and {@code >0} for resends
*/
public static int getNextRevision(String tld, DateTime date, RdeMode mode) {
RdeRevision object =
ofy().load().type(RdeRevision.class).id(makePartialName(tld, date, mode)).now();
return object == null ? 0 : object.revision + 1;
String id = makePartialName(tld, date, mode);
RdeRevisionId sqlKey = RdeRevisionId.create(tld, date.toLocalDate(), mode);
Key<RdeRevision> ofyKey = Key.create(RdeRevision.class, id);
Optional<RdeRevision> revisionOptional =
tm().maybeLoad(VKey.create(RdeRevision.class, sqlKey, ofyKey));
return revisionOptional.map(rdeRevision -> rdeRevision.revision + 1).orElse(0);
}
/**
@@ -76,17 +114,56 @@ public final class RdeRevision extends ImmutableObject {
checkArgument(revision >= 0, "Negative revision: %s", revision);
String triplet = makePartialName(tld, date, mode);
tm().assertInTransaction();
RdeRevision object = ofy().load().type(RdeRevision.class).id(triplet).now();
RdeRevisionId sqlKey = RdeRevisionId.create(tld, date.toLocalDate(), mode);
Key<RdeRevision> ofyKey = Key.create(RdeRevision.class, triplet);
Optional<RdeRevision> revisionOptional =
tm().maybeLoad(VKey.create(RdeRevision.class, sqlKey, ofyKey));
if (revision == 0) {
verify(object == null, "RdeRevision object already created: %s", object);
revisionOptional.ifPresent(
rdeRevision -> {
throw new IllegalArgumentException(
String.format(
"RdeRevision object already created and revision 0 specified: %s",
rdeRevision));
});
} else {
verifyNotNull(object, "RDE revision object missing for %s?! revision=%s", triplet, revision);
verify(object.revision == revision - 1,
"RDE revision object should be at %s but was: %s", revision - 1, object);
checkArgument(
revisionOptional.isPresent(),
"Couldn't find existing RDE revision %s when trying to save new revision %s",
triplet,
revision);
checkArgument(
revisionOptional.get().revision == revision - 1,
"RDE revision object should be at revision %s but was: %s",
revision - 1,
revisionOptional.get());
}
RdeRevision object = RdeRevision.create(triplet, tld, date.toLocalDate(), mode, revision);
tm().put(object);
}
/** Class to represent the composite primary key of {@link RdeRevision} entity. */
static class RdeRevisionId extends ImmutableObject implements Serializable {
String tld;
// Auto-conversion doesn't work for ID classes, we must specify @Column and @Convert
@Column(columnDefinition = "date")
@Convert(converter = LocalDateConverter.class)
LocalDate date;
@Enumerated(EnumType.STRING)
RdeMode mode;
/** Hibernate requires this default constructor. */
private RdeRevisionId() {}
static RdeRevisionId create(String tld, LocalDate date, RdeMode mode) {
RdeRevisionId instance = new RdeRevisionId();
instance.tld = tld;
instance.date = date;
instance.mode = mode;
return instance;
}
object = new RdeRevision();
object.id = triplet;
object.revision = revision;
ofy().save().entity(object);
}
}

View File

@@ -32,6 +32,7 @@ import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.registry.Registries.assertTldsExist;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableSortedCopy;
import static google.registry.util.PasswordUtils.SALT_SUPPLIER;
@@ -704,10 +705,18 @@ public class Registrar extends ImmutableObject
return new Builder(clone(this));
}
/** Creates a {@link VKey} for this instance. */
public VKey<Registrar> createVKey() {
return VKey.create(Registrar.class, clientIdentifier, Key.create(this));
}
/** Creates a {@link VKey} for the given {@code registrarId}. */
public static VKey<Registrar> createVKey(String registrarId) {
checkArgumentNotNull(registrarId, "registrarId must be specified");
return VKey.create(
Registrar.class, registrarId, Key.create(getCrossTldKey(), Registrar.class, registrarId));
}
/** A builder for constructing {@link Registrar}, since it is immutable. */
public static class Builder extends Buildable.Builder<Registrar> {
public Builder() {}
@@ -991,8 +1000,7 @@ public class Registrar extends ImmutableObject
/** Loads and returns a registrar entity by its client id directly from Datastore. */
public static Optional<Registrar> loadByClientId(String clientId) {
checkArgument(!Strings.isNullOrEmpty(clientId), "clientId must be specified");
return Optional.ofNullable(
ofy().load().type(Registrar.class).parent(getCrossTldKey()).id(clientId).now());
return transactIfJpaTm(() -> tm().maybeLoad(createVKey(clientId)));
}
/**

View File

@@ -25,6 +25,7 @@ import static google.registry.model.CacheUtils.memoizeWithShortExpiration;
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.persistence.transaction.TransactionManagerUtil.ofyOrJpaTm;
import static google.registry.util.CollectionUtils.entriesToImmutableMap;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
@@ -59,15 +60,21 @@ public final class Registries {
tm().doTransactionless(
() -> {
ImmutableSet<String> tlds =
ofy()
.load()
.type(Registry.class)
.ancestor(getCrossTldKey())
.keys()
.list()
.stream()
.map(Key::getName)
.collect(toImmutableSet());
ofyOrJpaTm(
() ->
ofy()
.load()
.type(Registry.class)
.ancestor(getCrossTldKey())
.keys()
.list()
.stream()
.map(Key::getName)
.collect(toImmutableSet()),
() ->
tm().loadAll(Registry.class).stream()
.map(Registry::getTldStr)
.collect(toImmutableSet()));
return Registry.getAll(tlds).stream()
.map(e -> Maps.immutableEntry(e.getTldStr(), e.getTldType()))
.collect(entriesToImmutableMap());

View File

@@ -22,13 +22,11 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Maps.toMap;
import static google.registry.config.RegistryConfig.getSingletonCacheRefreshDuration;
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.joda.money.CurrencyUnit.USD;
import com.google.common.annotations.VisibleForTesting;
@@ -62,6 +60,7 @@ import google.registry.model.domain.fee.BaseFee.FeeType;
import google.registry.model.domain.fee.Fee;
import google.registry.model.registry.label.PremiumList;
import google.registry.model.registry.label.ReservedList;
import google.registry.persistence.VKey;
import google.registry.schema.replay.DatastoreAndSqlEntity;
import google.registry.util.Idn;
import java.util.Map;
@@ -260,35 +259,32 @@ public class Registry extends ImmutableObject implements Buildable, DatastoreAnd
/** A cache that loads the {@link Registry} for a given tld. */
private static final LoadingCache<String, Optional<Registry>> CACHE =
CacheBuilder.newBuilder()
.expireAfterWrite(getSingletonCacheRefreshDuration().getMillis(), MILLISECONDS)
.expireAfterWrite(
java.time.Duration.ofMillis(getSingletonCacheRefreshDuration().getMillis()))
.build(
new CacheLoader<String, Optional<Registry>>() {
@Override
public Optional<Registry> load(final String tld) {
// Enter a transaction-less context briefly; we don't want to enroll every TLD in
// a transaction that might be wrapping this call.
return Optional.ofNullable(
tm().doTransactionless(
() ->
ofy()
.load()
.key(Key.create(getCrossTldKey(), Registry.class, tld))
.now()));
return tm().doTransactionless(() -> tm().maybeLoad(createVKey(tld)));
}
@Override
public Map<String, Optional<Registry>> loadAll(Iterable<? extends String> tlds) {
ImmutableMap<String, Key<Registry>> keysMap =
toMap(
ImmutableSet.copyOf(tlds),
tld -> Key.create(getCrossTldKey(), Registry.class, tld));
Map<Key<Registry>, Registry> entities =
tm().doTransactionless(() -> ofy().load().keys(keysMap.values()));
ImmutableMap<String, VKey<Registry>> keysMap =
toMap(ImmutableSet.copyOf(tlds), Registry::createVKey);
Map<VKey<? extends Registry>, Registry> entities =
tm().doTransactionless(() -> tm().load(keysMap.values()));
return Maps.transformEntries(
keysMap, (k, v) -> Optional.ofNullable(entities.getOrDefault(v, null)));
}
});
public static VKey<Registry> createVKey(String tld) {
return VKey.create(Registry.class, tld, Key.create(getCrossTldKey(), Registry.class, tld));
}
/**
* The name of the pricing engine that this TLD uses.
*
@@ -386,7 +382,7 @@ public class Registry extends ImmutableObject implements Buildable, DatastoreAnd
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
/** The set of reserved lists that are applicable to this registry. */
@Column(name = "reserved_list_names", nullable = false)
@Column(name = "reserved_list_names")
Set<Key<ReservedList>> reservedLists;
/** Retrieves an ImmutableSet of all ReservedLists associated with this tld. */

View File

@@ -25,7 +25,7 @@ import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.model.ofy.ObjectifyService.allocateId;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static google.registry.persistence.transaction.TransactionManagerUtil.ofyOrJpaTm;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Splitter;
@@ -46,8 +46,8 @@ import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
import google.registry.model.annotations.ReportedOn;
import google.registry.model.registry.Registry;
import google.registry.schema.replay.DatastoreAndSqlEntity;
import google.registry.schema.replay.DatastoreEntity;
import google.registry.schema.replay.NonReplicatedEntity;
import google.registry.schema.replay.SqlEntity;
import google.registry.schema.tld.PremiumListDao;
import google.registry.util.NonFinalForTesting;
@@ -56,6 +56,7 @@ import java.io.IOException;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
@@ -89,7 +90,7 @@ import org.joda.time.Duration;
@javax.persistence.Entity
@Table(indexes = {@Index(columnList = "name", name = "premiumlist_name_idx")})
public final class PremiumList extends BaseDomainLabelList<Money, PremiumList.PremiumListEntry>
implements DatastoreAndSqlEntity {
implements NonReplicatedEntity {
/** Stores the revision key for the set of currently used premium list entry entities. */
@Transient Key<PremiumListRevision> revisionKey;
@@ -197,7 +198,7 @@ public final class PremiumList extends BaseDomainLabelList<Money, PremiumList.Pr
@VisibleForTesting
static LoadingCache<String, PremiumList> createCachePremiumLists(Duration cachePersistDuration) {
return CacheBuilder.newBuilder()
.expireAfterWrite(cachePersistDuration.getMillis(), MILLISECONDS)
.expireAfterWrite(java.time.Duration.ofMillis(cachePersistDuration.getMillis()))
.build(
new CacheLoader<String, PremiumList>() {
@Override
@@ -208,7 +209,9 @@ public final class PremiumList extends BaseDomainLabelList<Money, PremiumList.Pr
}
private static PremiumList loadPremiumList(String name) {
return ofy().load().type(PremiumList.class).parent(getCrossTldKey()).id(name).now();
return ofyOrJpaTm(
() -> ofy().load().type(PremiumList.class).parent(getCrossTldKey()).id(name).now(),
() -> PremiumListDao.getLatestRevision(name).orElseThrow(NoSuchElementException::new));
}
/**
@@ -221,7 +224,8 @@ public final class PremiumList extends BaseDomainLabelList<Money, PremiumList.Pr
static final LoadingCache<Key<PremiumListRevision>, PremiumListRevision>
cachePremiumListRevisions =
CacheBuilder.newBuilder()
.expireAfterWrite(getSingletonCachePersistDuration().getMillis(), MILLISECONDS)
.expireAfterWrite(
java.time.Duration.ofMillis(getSingletonCachePersistDuration().getMillis()))
.build(
new CacheLoader<Key<PremiumListRevision>, PremiumListRevision>() {
@Override
@@ -260,14 +264,14 @@ public final class PremiumList extends BaseDomainLabelList<Money, PremiumList.Pr
static LoadingCache<Key<PremiumListEntry>, Optional<PremiumListEntry>>
createCachePremiumListEntries(Duration cachePersistDuration) {
return CacheBuilder.newBuilder()
.expireAfterWrite(cachePersistDuration.getMillis(), MILLISECONDS)
.expireAfterWrite(java.time.Duration.ofMillis(cachePersistDuration.getMillis()))
.maximumSize(getStaticPremiumListMaxCachedEntries())
.build(
new CacheLoader<Key<PremiumListEntry>, Optional<PremiumListEntry>>() {
@Override
public Optional<PremiumListEntry> load(final Key<PremiumListEntry> entryKey) {
return tm()
.doTransactionless(() -> Optional.ofNullable(ofy().load().key(entryKey).now()));
return tm().doTransactionless(
() -> Optional.ofNullable(ofy().load().key(entryKey).now()));
}
});
}

View File

@@ -19,8 +19,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.config.RegistryConfig.getDomainLabelListCacheDuration;
import static google.registry.model.registry.label.ReservationType.FULLY_BLOCKED;
import static google.registry.persistence.transaction.TransactionManagerUtil.ofyOrJpaTm;
import static google.registry.util.CollectionUtils.nullToEmpty;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.joda.time.DateTimeZone.UTC;
import com.google.common.base.Splitter;
@@ -38,7 +38,7 @@ import com.googlecode.objectify.mapper.Mapper;
import google.registry.model.Buildable;
import google.registry.model.registry.Registry;
import google.registry.model.registry.label.DomainLabelMetrics.MetricsReservedListMatch;
import google.registry.schema.replay.DatastoreAndSqlEntity;
import google.registry.schema.replay.NonReplicatedEntity;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -67,7 +67,7 @@ import org.joda.time.DateTime;
@Table(indexes = {@Index(columnList = "name", name = "reservedlist_name_idx")})
public final class ReservedList
extends BaseDomainLabelList<ReservationType, ReservedList.ReservedListEntry>
implements DatastoreAndSqlEntity {
implements NonReplicatedEntity {
@Mapify(ReservedListEntry.LabelMapper.class)
@ElementCollection
@@ -241,12 +241,15 @@ public final class ReservedList
private static LoadingCache<String, ReservedList> cache =
CacheBuilder.newBuilder()
.expireAfterWrite(getDomainLabelListCacheDuration().getMillis(), MILLISECONDS)
.expireAfterWrite(
java.time.Duration.ofMillis(getDomainLabelListCacheDuration().getMillis()))
.build(
new CacheLoader<String, ReservedList>() {
@Override
public ReservedList load(String listName) {
return ReservedListDualWriteDao.getLatestRevision(listName).orElse(null);
return ofyOrJpaTm(
() -> ReservedListDualWriteDao.getLatestRevision(listName).orElse(null),
() -> ReservedListSqlDao.getLatestRevision(listName).orElse(null));
}
});

View File

@@ -193,7 +193,7 @@ public class HistoryEntry extends ImmutableObject implements Buildable, Datastor
* transaction counts (such as contact or host mutations).
*/
@Transient // domain-specific
Set<DomainTransactionRecord> domainTransactionRecords;
protected Set<DomainTransactionRecord> domainTransactionRecords;
public long getId() {
// For some reason, Hibernate throws NPE during some initialization phase if we don't deal with
@@ -273,7 +273,8 @@ public class HistoryEntry extends ImmutableObject implements Buildable, Datastor
/** This method exists solely to satisfy Hibernate. Use the {@link Builder} instead. */
@SuppressWarnings("UnusedMethod")
private void setDomainTransactionRecords(Set<DomainTransactionRecord> domainTransactionRecords) {
this.domainTransactionRecords = ImmutableSet.copyOf(domainTransactionRecords);
this.domainTransactionRecords =
domainTransactionRecords == null ? null : ImmutableSet.copyOf(domainTransactionRecords);
}
public static VKey<HistoryEntry> createVKey(Key<HistoryEntry> key) {

View File

@@ -16,6 +16,7 @@ package google.registry.model.server;
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import com.google.common.collect.ImmutableList;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
@@ -23,11 +24,13 @@ import com.googlecode.objectify.annotation.Parent;
import google.registry.model.ImmutableObject;
import google.registry.model.annotations.ReportedOn;
import google.registry.model.common.EntityGroupRoot;
import google.registry.schema.replay.DatastoreEntity;
import google.registry.schema.replay.SqlEntity;
/** Pointer to the latest {@link KmsSecretRevision}. */
@Entity
@ReportedOn
public class KmsSecret extends ImmutableObject {
public class KmsSecret extends ImmutableObject implements DatastoreEntity {
/** The unique name of this {@link KmsSecret}. */
@Id String name;
@@ -45,6 +48,11 @@ public class KmsSecret extends ImmutableObject {
return latestRevision;
}
@Override
public ImmutableList<SqlEntity> toSqlEntities() {
return ImmutableList.of(); // not persisted in SQL
}
public static KmsSecret create(String name, KmsSecretRevision latestRevision) {
KmsSecret instance = new KmsSecret();
instance.name = name;

View File

@@ -20,11 +20,19 @@ import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.OnLoad;
import com.googlecode.objectify.annotation.Parent;
import google.registry.model.Buildable;
import google.registry.model.CreateAutoTimestamp;
import google.registry.model.ImmutableObject;
import google.registry.model.annotations.ReportedOn;
import google.registry.schema.replay.NonReplicatedEntity;
import javax.persistence.Column;
import javax.persistence.Index;
import javax.persistence.PostLoad;
import javax.persistence.Table;
import javax.persistence.Transient;
/**
* An encrypted value.
@@ -35,13 +43,22 @@ import google.registry.model.annotations.ReportedOn;
*
* <p>The value can be encrypted and decrypted using Cloud KMS.
*
* <p>Note that the primary key of this entity is {@link #revisionKey}, which is auto-generated by
* the database. So, if a retry of insertion happens after the previous attempt unexpectedly
* succeeds, we will end up with having two exact same revisions that differ only by revisionKey.
* This is fine though, because we only use the revision with the highest revisionKey.
*
* <p>TODO: remove Datastore-specific fields post-Registry-3.0-migration and rename to KmsSecret.
*
* @see <a href="https://cloud.google.com/kms/docs/">Google Cloud Key Management Service
* Documentation</a>
* @see google.registry.keyring.kms.KmsKeyring
*/
@Entity
@ReportedOn
public class KmsSecretRevision extends ImmutableObject {
@javax.persistence.Entity(name = "KmsSecret")
@Table(indexes = {@Index(columnList = "secretName")})
public class KmsSecretRevision extends ImmutableObject implements NonReplicatedEntity {
/**
* The maximum allowable secret size. Although Datastore allows entities up to 1 MB in size,
@@ -49,18 +66,31 @@ public class KmsSecretRevision extends ImmutableObject {
*/
private static final int MAX_SECRET_SIZE_BYTES = 64 * 1024 * 1024;
/** The revision of this secret. */
@Id long revisionKey;
/**
* The revision of this secret.
*
* <p>TODO: change name of the variable to revisionId once we're off Datastore
*/
@Id
@javax.persistence.Id
@Column(name = "revisionId")
long revisionKey;
/** The parent {@link KmsSecret} which contains metadata about this {@link KmsSecretRevision}. */
@Parent Key<KmsSecret> parent;
@Parent @Transient Key<KmsSecret> parent;
@Column(nullable = false)
@Ignore
String secretName;
/**
* The name of the {@code cryptoKeyVersion} associated with this {@link KmsSecretRevision}.
*
* <p>TODO: change name of the variable to cryptoKeyVersionName once we're off Datastore
*
* @see <a
* href="https://cloud.google.com/kms/docs/reference/rest/v1/projects.locations.keyRings.cryptoKeys.cryptoKeyVersions">projects.locations.keyRings.cryptoKeys.cryptoKeyVersions</a>
*/
@Column(nullable = false, name = "cryptoKeyVersionName")
String kmsCryptoKeyVersionName;
/**
@@ -70,9 +100,11 @@ public class KmsSecretRevision extends ImmutableObject {
* @see <a
* href="https://cloud.google.com/kms/docs/reference/rest/v1/projects.locations.keyRings.cryptoKeys/encrypt">projects.locations.keyRings.cryptoKeys.encrypt</a>
*/
@Column(nullable = false)
String encryptedValue;
/** An automatically managed creation timestamp. */
@Column(nullable = false)
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
public String getKmsCryptoKeyVersionName() {
@@ -83,6 +115,18 @@ public class KmsSecretRevision extends ImmutableObject {
return encryptedValue;
}
// When loading from SQL, fill out the Datastore-specific field
@PostLoad
void postLoad() {
parent = Key.create(getCrossTldKey(), KmsSecret.class, secretName);
}
// When loading from Datastore, fill out the SQL-specific field
@OnLoad
void onLoad() {
secretName = parent.getName();
}
/** A builder for constructing {@link KmsSecretRevision} entities, since they are immutable. */
public static class Builder extends Buildable.Builder<KmsSecretRevision> {
@@ -108,6 +152,7 @@ public class KmsSecretRevision extends ImmutableObject {
*/
public Builder setParent(String secretName) {
getInstance().parent = Key.create(getCrossTldKey(), KmsSecret.class, secretName);
getInstance().secretName = secretName;
return this;
}
}

View File

@@ -0,0 +1,54 @@
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.model.server;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.isNullOrEmpty;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import java.util.Optional;
/**
* A {@link KmsSecretRevision} DAO for Cloud SQL.
*
* <p>TODO: Rename this class to KmsSecretDao after migrating to Cloud SQL.
*/
public class KmsSecretRevisionSqlDao {
private KmsSecretRevisionSqlDao() {}
/** Saves the given KMS secret revision. */
public static void save(KmsSecretRevision kmsSecretRevision) {
checkArgumentNotNull(kmsSecretRevision, "kmsSecretRevision cannot be null");
jpaTm().assertInTransaction();
jpaTm().put(kmsSecretRevision);
}
/** Returns the latest revision for the secret name given, or absent if nonexistent. */
public static Optional<KmsSecretRevision> getLatestRevision(String secretName) {
checkArgument(!isNullOrEmpty(secretName), "secretName cannot be null or empty");
jpaTm().assertInTransaction();
return jpaTm()
.getEntityManager()
.createQuery(
"FROM KmsSecret ks WHERE ks.revisionKey IN (SELECT MAX(revisionKey) FROM "
+ "KmsSecret subKs WHERE subKs.secretName = :secretName)",
KmsSecretRevision.class)
.setParameter("secretName", secretName)
.getResultStream()
.findFirst();
}
}

View File

@@ -14,29 +14,43 @@
package google.registry.model.server;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Longs;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Ignore;
import com.googlecode.objectify.annotation.OnLoad;
import com.googlecode.objectify.annotation.Unindex;
import google.registry.model.annotations.NotBackedUp;
import google.registry.model.annotations.NotBackedUp.Reason;
import google.registry.model.common.CrossTldSingleton;
import google.registry.persistence.VKey;
import google.registry.schema.replay.DatastoreEntity;
import google.registry.schema.replay.SqlEntity;
import java.nio.ByteBuffer;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.PostLoad;
import javax.persistence.Transient;
/** A secret number used for generating tokens (such as XSRF tokens). */
@Entity
@javax.persistence.Entity
@Unindex
@NotBackedUp(reason = Reason.AUTO_GENERATED)
// TODO(b/27427316): Replace this with an entry in KMSKeyring
public class ServerSecret extends CrossTldSingleton {
public class ServerSecret extends CrossTldSingleton implements DatastoreEntity, SqlEntity {
/**
* Cache of the singleton ServerSecret instance that creates it if not present.
@@ -45,28 +59,34 @@ public class ServerSecret extends CrossTldSingleton {
* Supplier that can be reset for testing purposes.
*/
private static final LoadingCache<Class<ServerSecret>, ServerSecret> CACHE =
CacheBuilder.newBuilder().build(
new CacheLoader<Class<ServerSecret>, ServerSecret>() {
@Override
public ServerSecret load(Class<ServerSecret> unused) {
// Fast path - non-transactional load to hit memcache.
ServerSecret secret = ofy().load().entity(new ServerSecret()).now();
if (secret != null) {
return secret;
}
// Slow path - transactionally create a new ServerSecret (once per app setup).
return tm().transact(() -> {
// Check again for an existing secret within the transaction to avoid races.
ServerSecret secret1 = ofy().load().entity(new ServerSecret()).now();
if (secret1 == null) {
UUID uuid = UUID.randomUUID();
secret1 = create(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits());
ofy().saveWithoutBackup().entity(secret1).now();
CacheBuilder.newBuilder()
.build(
new CacheLoader<Class<ServerSecret>, ServerSecret>() {
@Override
public ServerSecret load(Class<ServerSecret> unused) {
return retrieveAndSaveSecret();
}
return secret1;
});
}
});
private static ServerSecret retrieveAndSaveSecret() {
VKey<ServerSecret> key =
VKey.create(
ServerSecret.class,
SINGLETON_ID,
Key.create(getCrossTldKey(), ServerSecret.class, SINGLETON_ID));
return tm().transact(
() -> {
// transactionally create a new ServerSecret (once per app setup) if necessary.
// return the ofy() result during Datastore-primary phase
ServerSecret secret =
ofyTm().maybeLoad(key).orElseGet(() -> create(UUID.randomUUID()));
// During a dual-write period, write it to both Datastore and SQL
// even if we didn't have to retrieve it from the DB
ofyTm().transact(() -> ofyTm().putWithoutBackup(secret));
jpaTm().transact(() -> jpaTm().putWithoutBackup(secret));
return secret;
});
}
/** Returns the global ServerSecret instance, creating it if one isn't already in Datastore. */
public static ServerSecret get() {
@@ -77,23 +97,38 @@ public class ServerSecret extends CrossTldSingleton {
}
}
/** Most significant 8 bytes of the UUID value. */
long mostSignificant;
/** Most significant 8 bytes of the UUID value (stored separately for legacy purposes). */
@Transient long mostSignificant;
/** Least significant 8 bytes of the UUID value. */
long leastSignificant;
/** Least significant 8 bytes of the UUID value (stored separately for legacy purposes). */
@Transient long leastSignificant;
@VisibleForTesting
static ServerSecret create(long mostSignificant, long leastSignificant) {
ServerSecret secret = new ServerSecret();
secret.mostSignificant = mostSignificant;
secret.leastSignificant = leastSignificant;
return secret;
/** The UUID value itself. */
@Id
@Column(columnDefinition = "uuid")
@Ignore
UUID secret;
/** Convert the Datastore representation to SQL. */
@OnLoad
void onLoad() {
secret = new UUID(mostSignificant, leastSignificant);
}
/** Returns the value of this ServerSecret as a UUID. */
public UUID asUuid() {
return new UUID(mostSignificant, leastSignificant);
/** Convert the SQL representation to Datastore. */
@PostLoad
void postLoad() {
mostSignificant = secret.getMostSignificantBits();
leastSignificant = secret.getLeastSignificantBits();
}
@VisibleForTesting
static ServerSecret create(UUID uuid) {
ServerSecret secret = new ServerSecret();
secret.mostSignificant = uuid.getMostSignificantBits();
secret.leastSignificant = uuid.getLeastSignificantBits();
secret.secret = uuid;
return secret;
}
/** Returns the value of this ServerSecret as a byte array. */
@@ -104,6 +139,16 @@ public class ServerSecret extends CrossTldSingleton {
.array();
}
@Override
public ImmutableList<SqlEntity> toSqlEntities() {
return ImmutableList.of(); // dually-written
}
@Override
public ImmutableList<DatastoreEntity> toDatastoreEntities() {
return ImmutableList.of(); // dually-written
}
@VisibleForTesting
static void resetCache() {
CACHE.invalidateAll();

View File

@@ -30,6 +30,9 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;
import com.google.common.flogger.FluentLogger;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.EmbedMap;
import com.googlecode.objectify.annotation.Entity;
@@ -41,8 +44,18 @@ import google.registry.model.ImmutableObject;
import google.registry.model.annotations.NotBackedUp;
import google.registry.model.annotations.NotBackedUp.Reason;
import google.registry.model.common.EntityGroupRoot;
import google.registry.schema.replay.NonReplicatedEntity;
import google.registry.util.CollectionUtils;
import java.util.Map;
import java.util.Optional;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.JoinColumn;
import javax.persistence.MapKeyColumn;
import javax.persistence.Transient;
import org.joda.time.DateTime;
/**
@@ -56,35 +69,49 @@ import org.joda.time.DateTime;
* order to avoid exceeding the one megabyte max entity size limit, we'll also be sharding that
* entity into multiple entities, each entity containing {@value #SHARD_SIZE} rows.
*
* <p>TODO: We can remove the sharding once we have converted entirely to Cloud SQL storage during
* the Registry 3.0 migration. Then, the entire table will be stored conceptually as one entity (in
* fact in SignedMarkRevocationList and SignedMarkRevocationEntry tables).
*
* @see google.registry.tmch.SmdrlCsvParser
* @see <a href="http://tools.ietf.org/html/draft-lozano-tmch-func-spec-08#section-6.2">
* TMCH functional specifications - SMD Revocation List</a>
* @see <a href="http://tools.ietf.org/html/draft-lozano-tmch-func-spec-08#section-6.2">TMCH
* functional specifications - SMD Revocation List</a>
*/
@Entity
@javax.persistence.Entity
@NotBackedUp(reason = Reason.EXTERNALLY_SOURCED)
public class SignedMarkRevocationList extends ImmutableObject {
public class SignedMarkRevocationList extends ImmutableObject implements NonReplicatedEntity {
@VisibleForTesting
static final int SHARD_SIZE = 10000;
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@VisibleForTesting static final int SHARD_SIZE = 10000;
/** Common ancestor for queries. */
@Parent
Key<EntityGroupRoot> parent = getCrossTldKey();
@Parent @Transient Key<EntityGroupRoot> parent = getCrossTldKey();
/** ID for the sharded entity. */
@Id
long id;
@Id @Transient long id;
@Ignore
@javax.persistence.Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long revisionId;
/** Time when this list was last updated, as specified in the first line of the CSV file. */
DateTime creationTime;
/** A map from SMD IDs to revocation time. */
@EmbedMap
@ElementCollection
@CollectionTable(
name = "SignedMarkRevocationEntry",
joinColumns = @JoinColumn(name = "revisionId", referencedColumnName = "revisionId"))
@MapKeyColumn(name = "smdId")
@Column(name = "revocationTime", nullable = false)
Map</*@MatchesPattern("[0-9]+-[0-9]+")*/ String, DateTime> revokes;
/** Indicates that this is a shard rather than a "full" list. */
@Ignore
boolean isShard;
@Ignore @Transient boolean isShard;
/**
* A cached supplier that fetches the SMDRL shards from Datastore and recombines them into a
@@ -92,32 +119,16 @@ public class SignedMarkRevocationList extends ImmutableObject {
*/
private static final Supplier<SignedMarkRevocationList> CACHE =
memoizeWithShortExpiration(
() ->
tm()
.transactNewReadOnly(
() -> {
Iterable<SignedMarkRevocationList> shards =
ofy()
.load()
.type(SignedMarkRevocationList.class)
.ancestor(getCrossTldKey());
DateTime creationTime =
isEmpty(shards)
? START_OF_TIME
: checkNotNull(
Iterables.get(shards, 0).creationTime, "creationTime");
ImmutableMap.Builder<String, DateTime> revokes =
new ImmutableMap.Builder<>();
for (SignedMarkRevocationList shard : shards) {
revokes.putAll(shard.revokes);
checkState(
creationTime.equals(shard.creationTime),
"Inconsistent creation times: %s vs. %s",
creationTime,
shard.creationTime);
}
return create(creationTime, revokes.build());
}));
() -> {
SignedMarkRevocationList datastoreList = loadFromDatastore();
// Also load the list from Cloud SQL, compare the two lists, and log if different.
try {
loadAndCompareCloudSqlList(datastoreList);
} catch (Throwable t) {
logger.atSevere().withCause(t).log("Error comparing signed mark revocation lists.");
}
return datastoreList;
});
/** Return a single logical instance that combines all Datastore shards. */
public static SignedMarkRevocationList get() {
@@ -149,10 +160,39 @@ public class SignedMarkRevocationList extends ImmutableObject {
return revokes.size();
}
/** Save this list to Datastore in sharded form. Returns {@code this}. */
/** Save this list to Datastore in sharded form and to Cloud SQL. Returns {@code this}. */
public SignedMarkRevocationList save() {
tm()
.transact(
saveToDatastore();
SignedMarkRevocationListDao.trySave(this);
return this;
}
/** Loads the shards from Datastore and combines them into one list. */
private static SignedMarkRevocationList loadFromDatastore() {
return tm().transactNewReadOnly(
() -> {
Iterable<SignedMarkRevocationList> shards =
ofy().load().type(SignedMarkRevocationList.class).ancestor(getCrossTldKey());
DateTime creationTime =
isEmpty(shards)
? START_OF_TIME
: checkNotNull(Iterables.get(shards, 0).creationTime, "creationTime");
ImmutableMap.Builder<String, DateTime> revokes = new ImmutableMap.Builder<>();
for (SignedMarkRevocationList shard : shards) {
revokes.putAll(shard.revokes);
checkState(
creationTime.equals(shard.creationTime),
"Inconsistent creation times: %s vs. %s",
creationTime,
shard.creationTime);
}
return create(creationTime, revokes.build());
});
}
/** Save this list to Datastore in sharded form. */
private SignedMarkRevocationList saveToDatastore() {
tm().transact(
() -> {
ofy()
.deleteWithoutBackup()
@@ -165,8 +205,7 @@ public class SignedMarkRevocationList extends ImmutableObject {
ofy()
.saveWithoutBackup()
.entities(
CollectionUtils.partitionMap(revokes, SHARD_SIZE)
.stream()
CollectionUtils.partitionMap(revokes, SHARD_SIZE).stream()
.map(
shardRevokes -> {
SignedMarkRevocationList shard = create(creationTime, shardRevokes);
@@ -180,6 +219,38 @@ public class SignedMarkRevocationList extends ImmutableObject {
return this;
}
private static void loadAndCompareCloudSqlList(SignedMarkRevocationList datastoreList) {
// Lifted with some modifications from ClaimsListShard
Optional<SignedMarkRevocationList> maybeCloudSqlList =
SignedMarkRevocationListDao.getLatestRevision();
if (maybeCloudSqlList.isPresent()) {
SignedMarkRevocationList cloudSqlList = maybeCloudSqlList.get();
MapDifference<String, DateTime> diff =
Maps.difference(datastoreList.revokes, cloudSqlList.revokes);
if (!diff.areEqual()) {
if (diff.entriesDiffering().size() > 10) {
logger.atWarning().log(
String.format(
"Unequal SM revocation lists detected, Cloud SQL list with revision id %d has %d"
+ " different records than the current Datastore list.",
cloudSqlList.revisionId, diff.entriesDiffering().size()));
} else {
StringBuilder diffMessage = new StringBuilder("Unequal SM revocation lists detected:\n");
diff.entriesDiffering()
.forEach(
(label, valueDiff) ->
diffMessage.append(
String.format(
"SMD %s has key %s in Datastore and key %s in Cloud SQL.\n",
label, valueDiff.leftValue(), valueDiff.rightValue())));
logger.atWarning().log(diffMessage.toString());
}
}
} else {
logger.atWarning().log("Signed mark revocation list in Cloud SQL is empty.");
}
}
/** As a safety mechanism, fail if someone tries to save this class directly. */
@OnSave
void disallowUnshardedSaves() {

View File

@@ -0,0 +1,76 @@
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.model.smd;
import static google.registry.model.CacheUtils.memoizeWithShortExpiration;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import com.google.common.base.Supplier;
import com.google.common.flogger.FluentLogger;
import java.util.Optional;
import javax.persistence.EntityManager;
public class SignedMarkRevocationListDao {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final Supplier<Optional<SignedMarkRevocationList>> CACHE =
memoizeWithShortExpiration(SignedMarkRevocationListDao::getLatestRevision);
/** Returns the most recent revision of the {@link SignedMarkRevocationList}, from cache. */
public static Optional<SignedMarkRevocationList> getLatestRevisionCached() {
return CACHE.get();
}
public static Optional<SignedMarkRevocationList> getLatestRevision() {
return jpaTm()
.transact(
() -> {
EntityManager em = jpaTm().getEntityManager();
Long revisionId =
em.createQuery("SELECT MAX(revisionId) FROM SignedMarkRevocationList", Long.class)
.getSingleResult();
return em.createQuery(
"FROM SignedMarkRevocationList smrl LEFT JOIN FETCH smrl.revokes "
+ "WHERE smrl.revisionId = :revisionId",
SignedMarkRevocationList.class)
.setParameter("revisionId", revisionId)
.getResultStream()
.findFirst();
});
}
/**
* Try to save the given {@link SignedMarkRevocationList} into Cloud SQL. If the save fails, the
* error will be logged but no exception will be thrown.
*
* <p>This method is used during the dual-write phase of database migration as Datastore is still
* the authoritative database.
*/
static void trySave(SignedMarkRevocationList signedMarkRevocationList) {
try {
SignedMarkRevocationListDao.save(signedMarkRevocationList);
logger.atInfo().log(
"Inserted %,d signed mark revocations into Cloud SQL",
signedMarkRevocationList.revokes.size());
} catch (Throwable e) {
logger.atSevere().withCause(e).log("Error inserting signed mark revocations into Cloud SQL");
}
}
private static void save(SignedMarkRevocationList signedMarkRevocationList) {
jpaTm().transact(() -> jpaTm().getEntityManager().persist(signedMarkRevocationList));
}
}

View File

@@ -57,7 +57,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
import javax.annotation.Nullable;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
@@ -146,8 +145,7 @@ public class ClaimsListShard extends ImmutableObject implements DatastoreAndSqlE
private static final Retrier LOADER_RETRIER = new Retrier(new SystemSleeper(), 2);
private static final Callable<ClaimsListShard> LOADER_CALLABLE =
() -> {
private static ClaimsListShard loadClaimsListShard() {
// Find the most recent revision.
Key<ClaimsListRevision> revisionKey = getCurrentRevision();
@@ -246,7 +244,9 @@ public class ClaimsListShard extends ImmutableObject implements DatastoreAndSqlE
*/
private static final Supplier<ClaimsListShard> CACHE =
memoizeWithShortExpiration(
() -> LOADER_RETRIER.callWithRetry(LOADER_CALLABLE, IllegalStateException.class));
() ->
LOADER_RETRIER.callWithRetry(
ClaimsListShard::loadClaimsListShard, IllegalStateException.class));
/** Returns the revision id of this claims list, or throws exception if it is null. */
public Long getRevisionId() {

View File

@@ -15,31 +15,50 @@
package google.registry.model.tmch;
import static com.google.common.base.Preconditions.checkNotNull;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.google.common.collect.ImmutableList;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import google.registry.model.annotations.NotBackedUp;
import google.registry.model.annotations.NotBackedUp.Reason;
import google.registry.model.common.CrossTldSingleton;
import javax.annotation.Nullable;
import google.registry.model.tmch.TmchCrl.TmchCrlId;
import google.registry.persistence.VKey;
import google.registry.schema.replay.DatastoreEntity;
import google.registry.schema.replay.SqlEntity;
import java.io.Serializable;
import java.util.Optional;
import javax.annotation.concurrent.Immutable;
import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.IdClass;
import org.joda.time.DateTime;
/** Datastore singleton for ICANN's TMCH CA certificate revocation list (CRL). */
@Entity
@javax.persistence.Entity
@Immutable
@NotBackedUp(reason = Reason.EXTERNALLY_SOURCED)
public final class TmchCrl extends CrossTldSingleton {
@IdClass(TmchCrlId.class)
public final class TmchCrl extends CrossTldSingleton implements DatastoreEntity, SqlEntity {
String crl;
DateTime updated;
String url;
@Id String crl;
@Id DateTime updated;
@Id String url;
/** Returns the singleton instance of this entity, without memoization. */
@Nullable
public static TmchCrl get() {
return ofy().load().entity(new TmchCrl()).now();
public static Optional<TmchCrl> get() {
VKey<TmchCrl> key =
VKey.create(
TmchCrl.class, SINGLETON_ID, Key.create(getCrossTldKey(), TmchCrl.class, SINGLETON_ID));
// return the ofy() result during Datastore-primary phase
return ofyTm().transact(() -> ofyTm().maybeLoad(key));
}
/**
@@ -47,16 +66,18 @@ public final class TmchCrl extends CrossTldSingleton {
*
* <p>Please do not call this function unless your CRL is properly formatted, signed by the root,
* and actually newer than the one currently in Datastore.
*
* <p>During the dual-write period, we write to both Datastore and SQL
*/
public static void set(final String crl, final String url) {
tm()
.transactNew(
tm().transact(
() -> {
TmchCrl tmchCrl = new TmchCrl();
tmchCrl.updated = tm().getTransactionTime();
tmchCrl.crl = checkNotNull(crl, "crl");
tmchCrl.url = checkNotNull(url, "url");
ofy().saveWithoutBackup().entity(tmchCrl);
ofyTm().transactNew(() -> ofyTm().putWithoutBackup(tmchCrl));
jpaTm().transactNew(() -> jpaTm().putWithoutBackup(tmchCrl));
});
}
@@ -74,4 +95,36 @@ public final class TmchCrl extends CrossTldSingleton {
public final DateTime getUpdated() {
return updated;
}
@Override
public ImmutableList<SqlEntity> toSqlEntities() {
return ImmutableList.of(); // dually-written
}
@Override
public ImmutableList<DatastoreEntity> toDatastoreEntities() {
return ImmutableList.of(); // dually-written
}
static class TmchCrlId implements Serializable {
@Column(name = "certificateRevocations")
String crl;
@Column(name = "updateTimestamp")
DateTime updated;
String url;
/** Hibernate requires this default constructor. */
private TmchCrlId() {}
static TmchCrlId create(String crl, DateTime updated, String url) {
TmchCrlId result = new TmchCrlId();
result.crl = crl;
result.updated = updated;
result.url = url;
return result;
}
}
}

View File

@@ -144,16 +144,19 @@ public class DomainTransferData extends TransferData<DomainTransferData.Builder>
rootKey, serverApproveAutorenewPollMessage, serverApproveAutorenewPollMessageHistoryId);
}
@SuppressWarnings("unused") // For Hibernate.
private void loadServerApproveBillingEventHistoryId(
@AlsoLoad("serverApproveBillingEvent") VKey<BillingEvent.OneTime> val) {
serverApproveBillingEventHistoryId = DomainBase.getHistoryId(val);
}
@SuppressWarnings("unused") // For Hibernate.
private void loadServerApproveAutorenewEventHistoryId(
@AlsoLoad("serverApproveAutorenewEvent") VKey<BillingEvent.Recurring> val) {
serverApproveAutorenewEventHistoryId = DomainBase.getHistoryId(val);
}
@SuppressWarnings("unused") // For Hibernate.
private void loadServerApproveAutorenewPollMessageHistoryId(
@AlsoLoad("serverApproveAutorenewPollMessage") VKey<PollMessage.Autorenew> val) {
serverApproveAutorenewPollMessageHistoryId = DomainBase.getHistoryId(val);

View File

@@ -22,7 +22,6 @@ import google.registry.request.RequestHandler;
import google.registry.util.SystemClock;
import java.io.IOException;
import java.security.Security;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
@@ -51,13 +50,16 @@ public class ServletBase extends HttpServlet {
// etc), we log the error but keep the main thread running. Also the shutdown hook will only be
// registered if metric reporter starts up correctly.
try {
metricReporter.get().startAsync().awaitRunning(10, TimeUnit.SECONDS);
metricReporter.get().startAsync().awaitRunning(java.time.Duration.ofSeconds(10));
logger.atInfo().log("Started up MetricReporter");
LifecycleManager.getInstance()
.setShutdownHook(
() -> {
try {
metricReporter.get().stopAsync().awaitTerminated(10, TimeUnit.SECONDS);
metricReporter
.get()
.stopAsync()
.awaitTerminated(java.time.Duration.ofSeconds(10));
logger.atInfo().log("Shut down MetricReporter");
} catch (TimeoutException e) {
logger.atSevere().withCause(e).log("Failed to stop MetricReporter.");

View File

@@ -15,6 +15,7 @@
package google.registry.persistence.transaction;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
@@ -25,6 +26,7 @@ import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import google.registry.config.RegistryConfig;
import google.registry.persistence.JpaRetries;
@@ -238,6 +240,16 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
entities.forEach(this::insert);
}
@Override
public void insertWithoutBackup(Object entity) {
insert(entity);
}
@Override
public void insertAllWithoutBackup(ImmutableCollection<?> entities) {
insertAll(entities);
}
@Override
public void put(Object entity) {
checkArgumentNotNull(entity, "entity must be specified");
@@ -253,6 +265,16 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
entities.forEach(this::put);
}
@Override
public void putWithoutBackup(Object entity) {
put(entity);
}
@Override
public void putAllWithoutBackup(ImmutableCollection<?> entities) {
putAll(entities);
}
@Override
public void update(Object entity) {
checkArgumentNotNull(entity, "entity must be specified");
@@ -269,6 +291,16 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
entities.forEach(this::update);
}
@Override
public void updateWithoutBackup(Object entity) {
update(entity);
}
@Override
public void updateAllWithoutBackup(ImmutableCollection<?> entities) {
updateAll(entities);
}
@Override
public <T> boolean exists(VKey<T> key) {
checkArgumentNotNull(key, "key must be specified");
@@ -315,6 +347,14 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
return result;
}
@Override
public <T> T load(T entity) {
checkArgumentNotNull(entity, "entity must be specified");
assertInTransaction();
return (T)
load(VKey.createSql(entity.getClass(), emf.getPersistenceUnitUtil().getIdentifier(entity)));
}
@Override
public <T> ImmutableMap<VKey<? extends T>, T> load(Iterable<? extends VKey<? extends T>> keys) {
checkArgumentNotNull(keys, "keys must be specified");
@@ -342,6 +382,11 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
.getResultList());
}
@Override
public <T> ImmutableList<T> loadAll(Iterable<T> entities) {
return Streams.stream(entities).map(this::load).collect(toImmutableList());
}
private int internalDelete(VKey<?> key) {
checkArgumentNotNull(key, "key must be specified");
assertInTransaction();
@@ -366,6 +411,37 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
vKeys.forEach(this::internalDelete);
}
@Override
public void delete(Object entity) {
checkArgumentNotNull(entity, "entity must be specified");
assertInTransaction();
Object managedEntity = entity;
if (!getEntityManager().contains(entity)) {
managedEntity = getEntityManager().merge(entity);
}
getEntityManager().remove(managedEntity);
}
@Override
public void deleteWithoutBackup(VKey<?> key) {
delete(key);
}
@Override
public void deleteWithoutBackup(Iterable<? extends VKey<?>> keys) {
delete(keys);
}
@Override
public void deleteWithoutBackup(Object entity) {
delete(entity);
}
@Override
public void clearSessionCache() {
// This is an intended no-op method as there is no session cache in Postgresql.
}
@Override
public <T> void assertDelete(VKey<T> key) {
if (internalDelete(key) != 1) {

View File

@@ -91,18 +91,90 @@ public interface TransactionManager {
/** Persists all new entities in the database, throws exception if any entity already exists. */
void insertAll(ImmutableCollection<?> entities);
/**
* Persists a new entity in the database without writing any backup if the underlying database is
* Datastore.
*
* <p>This method is for the sake of keeping a single code path when replacing ofy() with tm() in
* the application code. When the method is invoked with Datastore, it won't write the commit log
* backup; when invoked with Cloud SQL, it behaves the same as the method which doesn't have
* "WithoutBackup" in its method name because it is not necessary to have the backup mechanism in
* SQL.
*/
void insertWithoutBackup(Object entity);
/**
* Persists all new entities in the database without writing any backup if the underlying database
* is Datastore.
*
* <p>This method is for the sake of keeping a single code path when replacing ofy() with tm() in
* the application code. When the method is invoked with Datastore, it won't write the commit log
* backup; when invoked with Cloud SQL, it behaves the same as the method which doesn't have
* "WithoutBackup" in its method name because it is not necessary to have the backup mechanism in
* SQL.
*/
void insertAllWithoutBackup(ImmutableCollection<?> entities);
/** Persists a new entity or update the existing entity in the database. */
void put(Object entity);
/** Persists all new entities or update the existing entities in the database. */
void putAll(ImmutableCollection<?> entities);
/**
* Persists a new entity or update the existing entity in the database without writing any backup
* if the underlying database is Datastore.
*
* <p>This method is for the sake of keeping a single code path when replacing ofy() with tm() in
* the application code. When the method is invoked with Datastore, it won't write the commit log
* backup; when invoked with Cloud SQL, it behaves the same as the method which doesn't have
* "WithoutBackup" in its method name because it is not necessary to have the backup mechanism in
* SQL.
*/
void putWithoutBackup(Object entity);
/**
* Persists all new entities or update the existing entities in the database without writing any
* backup if the underlying database is Datastore.
*
* <p>This method is for the sake of keeping a single code path when replacing ofy() with tm() in
* the application code. When the method is invoked with Datastore, it won't write the commit log
* backup; when invoked with Cloud SQL, it behaves the same as the method which doesn't have
* "WithoutBackup" in its method name because it is not necessary to have the backup mechanism in
* SQL.
*/
void putAllWithoutBackup(ImmutableCollection<?> entities);
/** Updates an entity in the database, throws exception if the entity does not exist. */
void update(Object entity);
/** Updates all entities in the database, throws exception if any entity does not exist. */
void updateAll(ImmutableCollection<?> entities);
/**
* Updates an entity in the database without writing any backup if the underlying database is
* Datastore.
*
* <p>This method is for the sake of keeping a single code path when replacing ofy() with tm() in
* the application code. When the method is invoked with Datastore, it won't write the commit log
* backup; when invoked with Cloud SQL, it behaves the same as the method which doesn't have
* "WithoutBackup" in its method name because it is not necessary to have the backup mechanism in
* SQL.
*/
void updateWithoutBackup(Object entity);
/**
* Updates all entities in the database without writing any backup if the underlying database is
* Datastore.
*
* <p>This method is for the sake of keeping a single code path when replacing ofy() with tm() in
* the application code. When the method is invoked with Datastore, it won't write the commit log
* backup; when invoked with Cloud SQL, it behaves the same as the method which doesn't have
* "WithoutBackup" in its method name because it is not necessary to have the backup mechanism in
* SQL.
*/
void updateAllWithoutBackup(ImmutableCollection<?> entities);
/** Returns whether the given entity with same ID exists. */
boolean exists(Object entity);
@@ -115,6 +187,11 @@ public interface TransactionManager {
/** Loads the entity by its id, throws NoSuchElementException if it doesn't exist. */
<T> T load(VKey<T> key);
/**
* Loads the given entity from the database, throws NoSuchElementException if it doesn't exist.
*/
<T> T load(T entity);
/**
* Loads the set of entities by their key id.
*
@@ -125,9 +202,38 @@ public interface TransactionManager {
/** Loads all entities of the given type, returns empty if there is no such entity. */
<T> ImmutableList<T> loadAll(Class<T> clazz);
/**
* Loads all given entities from the database, throws NoSuchElementException if it doesn't exist.
*/
<T> ImmutableList<T> loadAll(Iterable<T> entities);
/** Deletes the entity by its id. */
void delete(VKey<?> key);
/** Deletes the set of entities by their key id. */
void delete(Iterable<? extends VKey<?>> keys);
/** Deletes the given entity from the database. */
void delete(Object entity);
/**
* Deletes the entity by its id without writing any backup if the underlying database is
* Datastore.
*/
void deleteWithoutBackup(VKey<?> key);
/**
* Deletes the set of entities by their key id without writing any backup if the underlying
* database is Datastore.
*/
void deleteWithoutBackup(Iterable<? extends VKey<?>> keys);
/**
* Deletes the given entity from the database without writing any backup if the underlying
* database is Datastore.
*/
void deleteWithoutBackup(Object entity);
/** Clears the session cache if the underlying database is Datastore, otherwise it is a no-op. */
void clearSessionCache();
}

View File

@@ -0,0 +1,110 @@
// Copyright 2020 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.tm;
import google.registry.model.ofy.DatastoreTransactionManager;
import java.util.function.Supplier;
/** Utility class that provides supplementary methods for {@link TransactionManager}. */
public class TransactionManagerUtil {
/**
* Returns the result of the given {@link Supplier}.
*
* <p>If {@link TransactionManagerFactory#tm()} returns a {@link JpaTransactionManager} instance,
* the {@link Supplier} is executed in a transaction.
*/
public static <T> T transactIfJpaTm(Supplier<T> supplier) {
if (tm() instanceof JpaTransactionManager) {
return tm().transact(supplier);
} else {
return supplier.get();
}
}
/**
* Executes the given {@link Runnable}.
*
* <p>If {@link TransactionManagerFactory#tm()} returns a {@link JpaTransactionManager} instance,
* the {@link Runnable} is executed in a transaction.
*/
public static void transactIfJpaTm(Runnable runnable) {
transactIfJpaTm(
() -> {
runnable.run();
return null;
});
}
/**
* Executes either {@code ofyRunnable} if {@link TransactionManagerFactory#tm()} returns a {@link
* JpaTransactionManager} instance, or {@code jpaRunnable} if {@link
* TransactionManagerFactory#tm()} returns a {@link DatastoreTransactionManager} instance.
*/
public static void ofyOrJpaTm(Runnable ofyRunnable, Runnable jpaRunnable) {
ofyOrJpaTm(
() -> {
ofyRunnable.run();
return null;
},
() -> {
jpaRunnable.run();
return null;
});
}
/**
* Returns the result from either {@code ofySupplier} if {@link TransactionManagerFactory#tm()}
* returns a {@link JpaTransactionManager} instance, or {@code jpaSupplier} if {@link
* TransactionManagerFactory#tm()} returns a {@link DatastoreTransactionManager} instance.
*/
public static <T> T ofyOrJpaTm(Supplier<T> ofySupplier, Supplier<T> jpaSupplier) {
if (tm() instanceof DatastoreTransactionManager) {
return ofySupplier.get();
} else if (tm() instanceof JpaTransactionManager) {
return jpaSupplier.get();
} else {
throw new IllegalStateException(
"Expected tm() to be DatastoreTransactionManager or JpaTransactionManager, but got "
+ tm().getClass());
}
}
/**
* Executes the given {@link Runnable} if {@link TransactionManagerFactory#tm()} returns a {@link
* DatastoreTransactionManager} instance, otherwise does nothing.
*/
public static void ofyTmOrDoNothing(Runnable ofyRunnable) {
if (tm() instanceof DatastoreTransactionManager) {
ofyRunnable.run();
}
}
/**
* Returns the result from the given {@link Supplier} if {@link TransactionManagerFactory#tm()}
* returns a {@link DatastoreTransactionManager} instance, otherwise returns null.
*/
public static <T> T ofyTmOrDoNothing(Supplier<T> ofySupplier) {
if (tm() instanceof DatastoreTransactionManager) {
return ofySupplier.get();
} else {
return null;
}
}
private TransactionManagerUtil() {}
}

View File

@@ -0,0 +1,35 @@
// Copyright 2020 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 com.google.common.collect.ImmutableList;
/**
* Represents an entity that should not participate in asynchronous replication.
*
* <p>We expect that this is a result of the entity being dually-written.
*/
public interface NonReplicatedEntity extends DatastoreEntity, SqlEntity {
@Override
default ImmutableList<DatastoreEntity> toDatastoreEntities() {
return ImmutableList.of();
}
@Override
default ImmutableList<SqlEntity> toSqlEntities() {
return ImmutableList.of();
}
}

View File

@@ -18,7 +18,6 @@ import static google.registry.config.RegistryConfig.getDomainLabelListCacheDurat
import static google.registry.config.RegistryConfig.getSingletonCachePersistDuration;
import static google.registry.config.RegistryConfig.getStaticPremiumListMaxCachedEntries;
import static google.registry.schema.tld.PremiumListDao.getPriceForLabel;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
@@ -48,7 +47,7 @@ class PremiumListCache {
static LoadingCache<String, Optional<PremiumList>> createCachePremiumLists(
Duration cachePersistDuration) {
return CacheBuilder.newBuilder()
.expireAfterWrite(cachePersistDuration.getMillis(), MILLISECONDS)
.expireAfterWrite(java.time.Duration.ofMillis(cachePersistDuration.getMillis()))
.build(
new CacheLoader<String, Optional<PremiumList>>() {
@Override
@@ -81,7 +80,7 @@ class PremiumListCache {
static LoadingCache<RevisionIdAndLabel, Optional<BigDecimal>> createCachePremiumEntries(
Duration cachePersistDuration) {
return CacheBuilder.newBuilder()
.expireAfterWrite(cachePersistDuration.getMillis(), MILLISECONDS)
.expireAfterWrite(java.time.Duration.ofMillis(cachePersistDuration.getMillis()))
.maximumSize(getStaticPremiumListMaxCachedEntries())
.build(
new CacheLoader<RevisionIdAndLabel, Optional<BigDecimal>>() {

View File

@@ -18,7 +18,6 @@ import static google.registry.config.RegistryConfig.ConfigModule.TmchCaMode.PILO
import static google.registry.config.RegistryConfig.ConfigModule.TmchCaMode.PRODUCTION;
import static google.registry.config.RegistryConfig.getSingletonCacheRefreshDuration;
import static google.registry.util.ResourceUtils.readResourceUtf8;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
@@ -33,6 +32,7 @@ import java.security.GeneralSecurityException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.Optional;
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
@@ -77,19 +77,21 @@ public final class TmchCertificateAuthority {
*/
private static final LoadingCache<TmchCaMode, X509CRL> CRL_CACHE =
CacheBuilder.newBuilder()
.expireAfterWrite(getSingletonCacheRefreshDuration().getMillis(), MILLISECONDS)
.expireAfterWrite(
java.time.Duration.ofMillis(getSingletonCacheRefreshDuration().getMillis()))
.build(
new CacheLoader<TmchCaMode, X509CRL>() {
@Override
public X509CRL load(final TmchCaMode tmchCaMode) throws GeneralSecurityException {
TmchCrl storedCrl = TmchCrl.get();
String crlContents;
if (storedCrl == null) {
String file = (tmchCaMode == PILOT) ? CRL_PILOT_FILE : CRL_FILE;
crlContents = readResourceUtf8(TmchCertificateAuthority.class, file);
} else {
crlContents = storedCrl.getCrl();
}
Optional<TmchCrl> storedCrl = TmchCrl.get();
String crlContents =
storedCrl
.map(TmchCrl::getCrl)
.orElseGet(
() -> {
String file = (tmchCaMode == PILOT) ? CRL_PILOT_FILE : CRL_FILE;
return readResourceUtf8(TmchCertificateAuthority.class, file);
});
X509CRL crl = X509Utils.loadCrl(crlContents);
crl.verify(ROOT_CERTS.get(tmchCaMode).getPublicKey());
return crl;

View File

@@ -18,7 +18,6 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;
import java.io.File;
import java.util.function.Predicate;
/**
* Compares two Datastore backups in V3 format on local file system. This is for use in tests and
@@ -30,8 +29,10 @@ import java.util.function.Predicate;
*/
class CompareDbBackups {
private static final String DS_V3_BACKUP_FILE_PREFIX = "output-";
private static final Predicate<File> DATA_FILE_MATCHER =
file -> file.isFile() && file.getName().startsWith(DS_V3_BACKUP_FILE_PREFIX);
private static boolean isDatastoreV3File(File file) {
return file.isFile() && file.getName().startsWith(DS_V3_BACKUP_FILE_PREFIX);
}
public static void main(String[] args) {
if (args.length != 2) {
@@ -40,9 +41,11 @@ class CompareDbBackups {
}
ImmutableSet<EntityWrapper> entities1 =
RecordAccumulator.readDirectory(new File(args[0]), DATA_FILE_MATCHER).getEntityWrapperSet();
RecordAccumulator.readDirectory(new File(args[0]), CompareDbBackups::isDatastoreV3File)
.getEntityWrapperSet();
ImmutableSet<EntityWrapper> entities2 =
RecordAccumulator.readDirectory(new File(args[1]), DATA_FILE_MATCHER).getEntityWrapperSet();
RecordAccumulator.readDirectory(new File(args[1]), CompareDbBackups::isDatastoreV3File)
.getEntityWrapperSet();
// Calculate the entities added and removed.
SetView<EntityWrapper> added = Sets.difference(entities2, entities1);

View File

@@ -22,7 +22,6 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
import static google.registry.util.RegistrarUtils.normalizeRegistrarName;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.joda.time.DateTimeZone.UTC;
import com.beust.jcommander.Parameter;
@@ -31,6 +30,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
import google.registry.flows.certs.CertificateChecker;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarAddress;
import google.registry.model.registry.Registry;
@@ -39,15 +39,9 @@ import google.registry.tools.params.OptionalLongParameter;
import google.registry.tools.params.OptionalPhoneNumberParameter;
import google.registry.tools.params.OptionalStringParameter;
import google.registry.tools.params.PathParameter;
import google.registry.util.CertificateChecker;
import google.registry.util.CertificateChecker.CertificateViolation;
import google.registry.util.CidrAddressBlock;
import java.io.ByteArrayInputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
@@ -55,7 +49,6 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import javax.inject.Inject;
import org.joda.money.CurrencyUnit;
@@ -369,7 +362,7 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
// existing certificate without providing a replacement. An uploaded empty certificate file
// will prevent the registrar from being able to establish EPP connections.
if (!asciiCert.equals("")) {
verifyCertificate(asciiCert);
certificateChecker.validateCertificate(asciiCert);
}
builder.setClientCertificate(asciiCert, now);
}
@@ -378,7 +371,7 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
String asciiCert =
new String(Files.readAllBytes(failoverClientCertificateFilename), US_ASCII);
if (!asciiCert.equals("")) {
verifyCertificate(asciiCert);
certificateChecker.validateCertificate(asciiCert);
}
builder.setFailoverClientCertificate(asciiCert, now);
}
@@ -482,22 +475,6 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
}
}
private void verifyCertificate(String certificateString) throws CertificateException {
X509Certificate certificate =
(X509Certificate)
CertificateFactory.getInstance("X509")
.generateCertificate(new ByteArrayInputStream(certificateString.getBytes(UTF_8)));
ImmutableSet<CertificateViolation> violations =
certificateChecker.checkCertificate(certificate);
if (!violations.isEmpty()) {
String displayMessages =
violations.stream()
.map(violation -> violation.getDisplayMessage(certificateChecker))
.collect(Collectors.joining("\n"));
throw new CertificateException(displayMessages);
}
}
@Override
protected String execute() throws Exception {
// Save registrar to Datastore and output its response

View File

@@ -19,42 +19,22 @@ import static google.registry.model.ofy.ObjectifyService.ofy;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.common.base.Splitter;
import com.google.common.io.CharStreams;
import com.google.common.io.Files;
import com.googlecode.objectify.Key;
import google.registry.model.ImmutableObject;
import google.registry.model.billing.BillingEvent;
import google.registry.model.domain.DomainBase;
import google.registry.util.NonFinalForTesting;
import google.registry.util.TypeUtils.TypeInstantiator;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
/**
* Command to resave entities with a unique id.
*
* <p>This command is used to address the duplicate id issue we found for certain {@link
* BillingEvent.OneTime} entities. The command reassigns an application wide unique id to the
* problematic entity and resaves it, it also resaves the entity having reference to the problematic
* entity with the updated id.
*
* <p>To use this command, you will need to provide the path to a file containing a list of strings
* representing the literal of Objectify key for the problematic entities. An example key literal
* is:
*
* <pre>
* "DomainBase", "111111-TEST", "HistoryEntry", 2222222, "OneTime", 3333333
* </pre>
*
* <p>Note that the double quotes are part of the key literal. The key literal can be retrieved from
* the column <code>__key__.path</code> in BigQuery.
*/
@Parameters(separators = " =", commandDescription = "Resave entities with a unique id.")
public class ResaveEntitiesWithUniqueIdCommand extends MutatingCommand {
/** Base Command to dedupe entities with duplicate IDs. */
abstract class DedupeEntityIdsCommand<T> extends MutatingCommand {
@Parameter(
names = "--key_paths_file",
@@ -66,7 +46,9 @@ public class ResaveEntitiesWithUniqueIdCommand extends MutatingCommand {
@NonFinalForTesting private static InputStream stdin = System.in;
private String keyChangeMessage;
private StringBuilder changeMessage = new StringBuilder();
abstract void dedupe(T entity);
@Override
protected void init() throws Exception {
@@ -85,8 +67,9 @@ public class ResaveEntitiesWithUniqueIdCommand extends MutatingCommand {
keyPathsFile == null ? "STDIN" : "File " + keyPathsFile.getAbsolutePath()));
continue;
}
if (entity instanceof BillingEvent.OneTime) {
resaveBillingEvent((BillingEvent.OneTime) entity);
Class<T> clazz = new TypeInstantiator<T>(getClass()) {}.getExactType();
if (clazz.isInstance(entity)) {
dedupe((T) entity);
} else {
throw new IllegalArgumentException("Unsupported entity key: " + untypedKey);
}
@@ -96,37 +79,19 @@ public class ResaveEntitiesWithUniqueIdCommand extends MutatingCommand {
@Override
protected void postBatchExecute() {
System.out.println(keyChangeMessage);
System.out.println(changeMessage);
}
private void deleteOldAndSaveNewEntity(ImmutableObject oldEntity, ImmutableObject newEntity) {
void stageEntityKeyChange(ImmutableObject oldEntity, ImmutableObject newEntity) {
stageEntityChange(oldEntity, null);
stageEntityChange(null, newEntity);
appendChangeMessage(
String.format(
"Changed entity key from: %s to: %s", Key.create(oldEntity), Key.create(newEntity)));
}
private void resaveBillingEvent(BillingEvent.OneTime billingEvent) {
Key<BillingEvent> key = Key.create(billingEvent);
Key<DomainBase> domainKey = getGrandParentAsDomain(key);
DomainBase domain = ofy().load().key(domainKey).now();
// The BillingEvent.OneTime entity to be resaved should be the billing event created a few
// years ago, so they should not be referenced from TransferData and GracePeriod in the domain.
assertNotInDomainTransferData(domain, key);
domain
.getGracePeriods()
.forEach(
gracePeriod ->
checkState(
!gracePeriod.getOneTimeBillingEvent().getOfyKey().equals(key),
"Entity %s is referenced by a grace period in domain %s",
key,
domainKey));
// By setting id to 0L, Buildable.build() will assign an application wide unique id to it.
BillingEvent.OneTime uniqIdBillingEvent = billingEvent.asBuilder().setId(0L).build();
deleteOldAndSaveNewEntity(billingEvent, uniqIdBillingEvent);
keyChangeMessage =
String.format("Old Entity Key: %s New Entity Key: %s", key, Key.create(uniqIdBillingEvent));
void appendChangeMessage(String message) {
changeMessage.append(message);
}
private static boolean isKind(Key<?> key, Class<?> clazz) {
@@ -165,7 +130,7 @@ public class ResaveEntitiesWithUniqueIdCommand extends MutatingCommand {
return literal.substring(1, literal.length() - 1);
}
private static Key<DomainBase> getGrandParentAsDomain(Key<?> key) {
static Key<DomainBase> getGrandParentAsDomain(Key<?> key) {
Key<?> grandParent;
try {
grandParent = key.getParent().getParent();
@@ -178,19 +143,4 @@ public class ResaveEntitiesWithUniqueIdCommand extends MutatingCommand {
}
return (Key<DomainBase>) grandParent;
}
private static void assertNotInDomainTransferData(DomainBase domainBase, Key<?> key) {
if (!domainBase.getTransferData().isEmpty()) {
domainBase
.getTransferData()
.getServerApproveEntities()
.forEach(
entityKey ->
checkState(
!entityKey.getOfyKey().equals(key),
"Entity %s is referenced by the transfer data in domain %s",
key,
domainBase.createVKey().getOfyKey()));
}
}
}

View File

@@ -0,0 +1,88 @@
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.tools;
import static com.google.common.base.Preconditions.checkState;
import static google.registry.model.ofy.ObjectifyService.ofy;
import com.beust.jcommander.Parameters;
import com.googlecode.objectify.Key;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.OneTime;
import google.registry.model.domain.DomainBase;
/**
* Command to dedupe {@link BillingEvent.OneTime} entities having duplicate IDs.
*
* <p>This command is used to address the duplicate id issue we found for certain {@link
* BillingEvent.OneTime} entities. The command reassigns an application wide unique id to the
* problematic entity and resaves it, it also resaves the entity having reference to the problematic
* entity with the updated id.
*
* <p>To use this command, you will need to provide the path to a file containing a list of strings
* representing the literal of Objectify key for the problematic entities. An example key literal
* is:
*
* <pre>
* "DomainBase", "111111-TEST", "HistoryEntry", 2222222, "OneTime", 3333333
* </pre>
*
* <p>Note that the double quotes are part of the key literal. The key literal can be retrieved from
* the column <code>__key__.path</code> in BigQuery.
*/
@Parameters(
separators = " =",
commandDescription = "Dedupe BillingEvent.OneTime entities with duplicate IDs.")
public class DedupeOneTimeBillingEventIdsCommand extends DedupeEntityIdsCommand<OneTime> {
@Override
void dedupe(OneTime entity) {
Key<BillingEvent> key = Key.create(entity);
Key<DomainBase> domainKey = getGrandParentAsDomain(key);
DomainBase domain = ofy().load().key(domainKey).now();
// The BillingEvent.OneTime entity to be resaved should be the billing event created a few
// years ago, so they should not be referenced from TransferData and GracePeriod in the domain.
assertNotInDomainTransferData(domain, key);
domain
.getGracePeriods()
.forEach(
gracePeriod ->
checkState(
!gracePeriod.getOneTimeBillingEvent().getOfyKey().equals(key),
"Entity %s is referenced by a grace period in domain %s",
key,
domainKey));
// By setting id to 0L, Buildable.build() will assign an application wide unique id to it.
BillingEvent.OneTime uniqIdBillingEvent = entity.asBuilder().setId(0L).build();
stageEntityKeyChange(entity, uniqIdBillingEvent);
}
private static void assertNotInDomainTransferData(DomainBase domainBase, Key<?> key) {
if (!domainBase.getTransferData().isEmpty()) {
domainBase
.getTransferData()
.getServerApproveEntities()
.forEach(
entityKey ->
checkState(
!entityKey.getOfyKey().equals(key),
"Entity %s is referenced by the transfer data in domain %s",
key,
domainBase.createVKey().getOfyKey()));
}
}
}

View File

@@ -0,0 +1,192 @@
// Copyright 2020 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.tools;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.model.ofy.ObjectifyService.ofy;
import com.beust.jcommander.Parameters;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.googlecode.objectify.Key;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.OneTime;
import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.GracePeriod;
import google.registry.model.transfer.DomainTransferData;
import google.registry.model.transfer.TransferData.TransferServerApproveEntity;
import google.registry.persistence.VKey;
import java.util.List;
import java.util.Set;
/**
* A command that re-saves the problematic {@link BillingEvent.Recurring} entities with unique IDs.
*
* <p>This command is used to address the duplicate id issue we found for certain {@link
* BillingEvent.Recurring} entities. The command reassigns an application wide unique id to the
* problematic entity and resaves it, it also resaves the entity having reference to the problematic
* entity with the updated id.
*
* <p>To use this command, you will need to provide the path to a file containing a list of strings
* representing the literal of Objectify key for the problematic entities. An example key literal
* is:
*
* <pre>
* "DomainBase", "111111-TEST", "HistoryEntry", 2222222, "Recurring", 3333333
* </pre>
*
* <p>Note that the double quotes are part of the key literal. The key literal can be retrieved from
* the column <code>__key__.path</code> in BigQuery.
*/
@Parameters(
separators = " =",
commandDescription = "Dedupe BillingEvent.Recurring entities with duplicate IDs.")
public class DedupeRecurringBillingEventIdsCommand
extends DedupeEntityIdsCommand<BillingEvent.Recurring> {
@Override
void dedupe(Recurring recurring) {
// Loads the associated DomainBase and BillingEvent.OneTime entities that
// may have reference to this BillingEvent.Recurring entity.
Key<DomainBase> domainKey = getGrandParentAsDomain(Key.create(recurring));
DomainBase domain = ofy().load().key(domainKey).now();
List<BillingEvent.OneTime> oneTimes =
ofy().load().type(BillingEvent.OneTime.class).ancestor(domainKey).list();
VKey<Recurring> oldRecurringVKey = recurring.createVKey();
// By setting id to 0L, Buildable.build() will assign an application wide unique id to it.
Recurring uniqIdRecurring = recurring.asBuilder().setId(0L).build();
VKey<Recurring> newRecurringVKey = uniqIdRecurring.createVKey();
// After having the unique id for the BillingEvent.Recurring entity, we also need to
// update the references in other entities to point to the new BillingEvent.Recurring
// entity.
updateReferenceInOneTimeBillingEvent(oneTimes, oldRecurringVKey, newRecurringVKey);
updateReferenceInDomain(domain, oldRecurringVKey, newRecurringVKey);
stageEntityKeyChange(recurring, uniqIdRecurring);
}
/**
* Resaves {@link BillingEvent.OneTime} entities with updated {@link
* BillingEvent.OneTime#cancellationMatchingBillingEvent}.
*
* <p>{@link BillingEvent.OneTime#cancellationMatchingBillingEvent} is a {@link VKey} to a {@link
* BillingEvent.Recurring} entity. So, if the {@link BillingEvent.Recurring} entity gets a new key
* by changing its id, we need to update {@link
* BillingEvent.OneTime#cancellationMatchingBillingEvent} as well.
*/
private void updateReferenceInOneTimeBillingEvent(
List<OneTime> oneTimes, VKey<Recurring> oldRecurringVKey, VKey<Recurring> newRecurringVKey) {
oneTimes.forEach(
oneTime -> {
if (oneTime.getCancellationMatchingBillingEvent() != null
&& oneTime.getCancellationMatchingBillingEvent().equals(oldRecurringVKey)) {
BillingEvent.OneTime updatedOneTime =
oneTime.asBuilder().setCancellationMatchingBillingEvent(newRecurringVKey).build();
stageEntityChange(oneTime, updatedOneTime);
appendChangeMessage(
String.format(
"Changed cancellationMatchingBillingEvent in entity %s from %s to %s\n",
oneTime.createVKey().getOfyKey(),
oneTime.getCancellationMatchingBillingEvent().getOfyKey(),
updatedOneTime.getCancellationMatchingBillingEvent().getOfyKey()));
}
});
}
/**
* Resaves {@link DomainBase} entity with updated references to {@link BillingEvent.Recurring}
* entity.
*
* <p>The following 4 fields in the domain entity can be or have a reference to this
* BillingEvent.Recurring entity, so we need to check them and replace them with the new entity
* when necessary:
*
* <ol>
* <li>domain.autorenewBillingEvent, see {@link DomainBase#autorenewBillingEvent}
* <li>domain.transferData.serverApproveAutorenewEvent, see {@link
* DomainTransferData#serverApproveAutorenewEvent}
* <li>domain.transferData.serverApproveEntities, see {@link
* DomainTransferData#serverApproveEntities}
* <li>domain.gracePeriods.billingEventRecurring, see {@link GracePeriod#billingEventRecurring}
* </ol>
*/
private void updateReferenceInDomain(
DomainBase domain, VKey<Recurring> oldRecurringVKey, VKey<Recurring> newRecurringVKey) {
DomainBase.Builder domainBuilder = domain.asBuilder();
StringBuilder domainChange =
new StringBuilder(
String.format(
"Resaved domain %s with following changes:\n", domain.createVKey().getOfyKey()));
if (domain.getAutorenewBillingEvent() != null
&& domain.getAutorenewBillingEvent().equals(oldRecurringVKey)) {
domainBuilder.setAutorenewBillingEvent(newRecurringVKey);
domainChange.append(
String.format(
" Changed autorenewBillingEvent from %s to %s.\n",
oldRecurringVKey, newRecurringVKey));
}
if (domain.getTransferData().getServerApproveAutorenewEvent() != null
&& domain.getTransferData().getServerApproveAutorenewEvent().equals(oldRecurringVKey)) {
Set<VKey<? extends TransferServerApproveEntity>> serverApproveEntities =
Sets.union(
Sets.difference(
domain.getTransferData().getServerApproveEntities(),
ImmutableSet.of(oldRecurringVKey)),
ImmutableSet.of(newRecurringVKey));
domainBuilder.setTransferData(
domain
.getTransferData()
.asBuilder()
.setServerApproveEntities(ImmutableSet.copyOf(serverApproveEntities))
.setServerApproveAutorenewEvent(newRecurringVKey)
.build());
domainChange.append(
String.format(
" Changed transferData.serverApproveAutoRenewEvent from %s to %s.\n",
oldRecurringVKey, newRecurringVKey));
domainChange.append(
String.format(
" Changed transferData.serverApproveEntities to remove %s and add %s.\n",
oldRecurringVKey, newRecurringVKey));
}
ImmutableSet<GracePeriod> updatedGracePeriod =
domain.getGracePeriods().stream()
.map(
gracePeriod ->
gracePeriod.getRecurringBillingEvent().equals(oldRecurringVKey)
? gracePeriod.cloneWithRecurringBillingEvent(newRecurringVKey)
: gracePeriod)
.collect(toImmutableSet());
if (!updatedGracePeriod.equals(domain.getGracePeriods())) {
domainBuilder.setGracePeriods(updatedGracePeriod);
domainChange.append(
String.format(
" Changed gracePeriods to remove %s and add %s.\n",
oldRecurringVKey, newRecurringVKey));
}
DomainBase updatedDomain = domainBuilder.build();
if (!updatedDomain.equals(domain)) {
stageEntityChange(domain, updatedDomain);
appendChangeMessage(domainChange.toString());
}
}
}

View File

@@ -48,6 +48,8 @@ public final class RegistryTool {
.put("create_reserved_list", CreateReservedListCommand.class)
.put("create_tld", CreateTldCommand.class)
.put("curl", CurlCommand.class)
.put("dedupe_one_time_billing_event_ids", DedupeOneTimeBillingEventIdsCommand.class)
.put("dedupe_recurring_billing_event_ids", DedupeRecurringBillingEventIdsCommand.class)
.put("delete_allocation_tokens", DeleteAllocationTokensCommand.class)
.put("delete_domain", DeleteDomainCommand.class)
.put("delete_host", DeleteHostCommand.class)
@@ -99,7 +101,6 @@ public final class RegistryTool {
.put("remove_ip_address", RemoveIpAddressCommand.class)
.put("renew_domain", RenewDomainCommand.class)
.put("resave_entities", ResaveEntitiesCommand.class)
.put("resave_entities_with_unique_id", ResaveEntitiesWithUniqueIdCommand.class)
.put("resave_environment_entities", ResaveEnvironmentEntitiesCommand.class)
.put("resave_epp_resource", ResaveEppResourceCommand.class)
.put("send_escrow_report_to_icann", SendEscrowReportToIcannCommand.class)

View File

@@ -20,7 +20,6 @@ import dagger.Lazy;
import google.registry.batch.BatchModule;
import google.registry.beam.initsql.BeamJpaModule;
import google.registry.bigquery.BigqueryModule;
import google.registry.config.CertificateCheckerModule;
import google.registry.config.CredentialModule.LocalCredentialJson;
import google.registry.config.RegistryConfig.Config;
import google.registry.config.RegistryConfig.ConfigModule;
@@ -61,7 +60,6 @@ import javax.inject.Singleton;
BatchModule.class,
BeamJpaModule.class,
BigqueryModule.class,
CertificateCheckerModule.class,
ConfigModule.class,
CloudDnsWriterModule.class,
DatastoreAdminModule.class,

View File

@@ -38,6 +38,7 @@ import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import google.registry.config.RegistryEnvironment;
import google.registry.flows.certs.CertificateChecker;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarContact;
import google.registry.model.registrar.RegistrarContact.Type;
@@ -64,7 +65,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import javax.inject.Inject;
import org.joda.time.DateTime;
@@ -93,11 +93,13 @@ public class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonA
@Inject SendEmailUtils sendEmailUtils;
@Inject AuthenticatedRegistrarAccessor registrarAccessor;
@Inject AuthResult authResult;
@Inject CertificateChecker certificateChecker;
@Inject RegistrarSettingsAction() {}
private static final Predicate<RegistrarContact> HAS_PHONE =
contact -> contact.getPhoneNumber() != null;
private static boolean hasPhone(RegistrarContact contact) {
return contact.getPhoneNumber() != null;
}
@Override
public void run() {
@@ -306,19 +308,40 @@ public class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonA
RegistrarFormFields.IP_ADDRESS_ALLOW_LIST_FIELD
.extractUntyped(args)
.orElse(ImmutableList.of()));
RegistrarFormFields.CLIENT_CERTIFICATE_FIELD
.extractUntyped(args)
.ifPresent(
certificate -> builder.setClientCertificate(certificate, tm().getTransactionTime()));
RegistrarFormFields.FAILOVER_CLIENT_CERTIFICATE_FIELD
.extractUntyped(args)
.ifPresent(
certificate ->
builder.setFailoverClientCertificate(certificate, tm().getTransactionTime()));
Optional<String> certificateString =
RegistrarFormFields.CLIENT_CERTIFICATE_FIELD.extractUntyped(args);
if (certificateString.isPresent()) {
if (validateCertificate(initialRegistrar.getClientCertificate(), certificateString.get())) {
builder.setClientCertificate(certificateString.get(), tm().getTransactionTime());
}
}
Optional<String> failoverCertificateString =
RegistrarFormFields.FAILOVER_CLIENT_CERTIFICATE_FIELD.extractUntyped(args);
if (failoverCertificateString.isPresent()) {
if (validateCertificate(
initialRegistrar.getFailoverClientCertificate(), failoverCertificateString.get())) {
builder.setFailoverClientCertificate(
failoverCertificateString.get(), tm().getTransactionTime());
}
}
return checkNotChangedUnlessAllowed(builder, initialRegistrar, Role.OWNER);
}
/**
* Returns true if the registrar should accept the new certificate. Returns false if the
* certificate is already the one stored for the registrar.
*/
private boolean validateCertificate(String existingCertificate, String certificateString) {
if ((existingCertificate == null) || !existingCertificate.equals(certificateString)) {
certificateChecker.validateCertificate(certificateString);
return true;
}
return false;
}
/**
* Updates a registrar with the ADMIN-controlled args from the http request.
*
@@ -512,8 +535,8 @@ public class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonA
Multimap<Type, RegistrarContact> newContactsByType,
Type... types) {
for (Type type : types) {
if (oldContactsByType.get(type).stream().anyMatch(HAS_PHONE)
&& newContactsByType.get(type).stream().noneMatch(HAS_PHONE)) {
if (oldContactsByType.get(type).stream().anyMatch(RegistrarSettingsAction::hasPhone)
&& newContactsByType.get(type).stream().noneMatch(RegistrarSettingsAction::hasPhone)) {
throw new ContactRequirementException(
String.format(
"Please provide a phone number for at least one %s contact",

View File

@@ -47,12 +47,14 @@
<class>google.registry.model.domain.DomainHistory</class>
<class>google.registry.model.domain.GracePeriod</class>
<class>google.registry.model.domain.secdns.DelegationSignerData</class>
<class>google.registry.model.domain.secdns.DomainDsDataHistory</class>
<class>google.registry.model.domain.token.AllocationToken</class>
<class>google.registry.model.host.HostHistory</class>
<class>google.registry.model.host.HostResource</class>
<class>google.registry.model.poll.PollMessage</class>
<class>google.registry.model.poll.PollMessage$OneTime</class>
<class>google.registry.model.poll.PollMessage$Autorenew</class>
<class>google.registry.model.rde.RdeRevision</class>
<class>google.registry.model.registrar.Registrar</class>
<class>google.registry.model.registrar.RegistrarContact</class>
<class>google.registry.model.registry.label.PremiumList</class>
@@ -60,7 +62,11 @@
<class>google.registry.model.registry.Registry</class>
<class>google.registry.model.reporting.DomainTransactionRecord</class>
<class>google.registry.model.reporting.Spec11ThreatMatch</class>
<class>google.registry.model.server.KmsSecretRevision</class>
<class>google.registry.model.server.ServerSecret</class>
<class>google.registry.model.smd.SignedMarkRevocationList</class>
<class>google.registry.model.tmch.ClaimsListShard</class>
<class>google.registry.model.tmch.TmchCrl</class>
<class>google.registry.persistence.transaction.TransactionEntity</class>
<class>google.registry.schema.cursor.Cursor</class>
<class>google.registry.schema.domain.RegistryLock</class>

View File

@@ -480,8 +480,7 @@ public class ExpandRecurringBillingEventsActionTest
.setSyntheticCreationTime(testTime)
.build());
}
assertBillingEventsForResource(
domain, Iterables.toArray(expectedEvents, BillingEvent.class));
assertBillingEventsForResource(domain, Iterables.toArray(expectedEvents, BillingEvent.class));
assertCursorAt(testTime);
}

View File

@@ -143,6 +143,7 @@ public class TestPipelineExtension extends Pipeline
// Null until the pipeline has been run
@Nullable private List<TransformHierarchy.Node> runVisitedNodes;
@SuppressWarnings("UnnecessaryLambda") // Stay true to the original class.
private final Predicate<Node> isPAssertNode =
node ->
node.getTransform() instanceof PAssert.GroupThenAssert

View File

@@ -217,7 +217,7 @@ class InitSqlPipelineTest {
GracePeriodStatus.ADD,
"4-COM",
fakeClock.nowUtc().plusDays(1),
"registrar",
"TheRegistrar",
null))
.build());
exportDir = store.export(exportRootDir.getAbsolutePath(), ALL_KINDS, ImmutableSet.of());

View File

@@ -805,30 +805,35 @@ class EppLifecycleDomainTest extends EppTestCase {
// As the losing registrar, read the request poll message, and then ack it.
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
String messageId = "1-C-EXAMPLE-20-26-2001";
assertThatCommand("poll.xml")
.atTime("2001-01-01T00:01:00Z")
.hasResponse("poll_response_domain_transfer_request.xml");
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-C-EXAMPLE-17-23-2001"))
.hasResponse("poll_response_domain_transfer_request.xml", ImmutableMap.of("ID", messageId));
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", messageId))
.atTime("2001-01-01T00:01:00Z")
.hasResponse("poll_ack_response_empty.xml");
// Five days in the future, expect a server approval poll message to the loser, and ack it.
messageId = "1-C-EXAMPLE-20-25-2001";
assertThatCommand("poll.xml")
.atTime("2001-01-06T00:01:00Z")
.hasResponse("poll_response_domain_transfer_server_approve_loser.xml");
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-C-EXAMPLE-17-22-2001"))
.hasResponse(
"poll_response_domain_transfer_server_approve_loser.xml",
ImmutableMap.of("ID", messageId));
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", messageId))
.atTime("2001-01-06T00:01:00Z")
.hasResponse("poll_ack_response_empty.xml");
assertThatLogoutSucceeds();
// Also expect a server approval poll message to the winner, with the transfer request trid.
messageId = "1-C-EXAMPLE-20-24-2001";
assertThatLoginSucceeds("TheRegistrar", "password2");
assertThatCommand("poll.xml")
.atTime("2001-01-06T00:02:00Z")
.hasResponse(
"poll_response_domain_transfer_server_approve_winner.xml",
ImmutableMap.of("SERVER_TRID", transferRequestTrid));
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", "1-C-EXAMPLE-17-21-2001"))
ImmutableMap.of("SERVER_TRID", transferRequestTrid, "ID", messageId));
assertThatCommand("poll_ack.xml", ImmutableMap.of("ID", messageId))
.atTime("2001-01-06T00:02:00Z")
.hasResponse("poll_ack_response_empty.xml");
assertThatLogoutSucceeds();

View File

@@ -12,21 +12,30 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.util;
package google.registry.flows.certs;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.util.CertificateChecker.CertificateViolation.ALGORITHM_CONSTRAINED;
import static google.registry.util.CertificateChecker.CertificateViolation.EXPIRED;
import static google.registry.util.CertificateChecker.CertificateViolation.NOT_YET_VALID;
import static google.registry.util.CertificateChecker.CertificateViolation.RSA_KEY_LENGTH_TOO_SHORT;
import static google.registry.util.CertificateChecker.CertificateViolation.VALIDITY_LENGTH_TOO_LONG;
import static google.registry.flows.certs.CertificateChecker.CertificateViolation.ALGORITHM_CONSTRAINED;
import static google.registry.flows.certs.CertificateChecker.CertificateViolation.EXPIRED;
import static google.registry.flows.certs.CertificateChecker.CertificateViolation.INVALID_ECDSA_CURVE;
import static google.registry.flows.certs.CertificateChecker.CertificateViolation.NOT_YET_VALID;
import static google.registry.flows.certs.CertificateChecker.CertificateViolation.RSA_KEY_LENGTH_TOO_SHORT;
import static google.registry.flows.certs.CertificateChecker.CertificateViolation.VALIDITY_LENGTH_TOO_LONG;
import static google.registry.testing.CertificateSamples.SAMPLE_CERT;
import static google.registry.testing.CertificateSamples.SAMPLE_CERT3;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import google.registry.testing.FakeClock;
import google.registry.util.SelfSignedCaCertificate;
import java.security.AlgorithmParameters;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECParameterSpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.joda.time.DateTime;
import org.junit.jupiter.api.Test;
@@ -42,6 +51,7 @@ class CertificateCheckerTest {
ImmutableSortedMap.of(START_OF_TIME, 825, DateTime.parse("2020-09-01T00:00:00Z"), 398),
30,
2048,
ImmutableSet.of("secp256r1", "secp384r1"),
fakeClock);
@Test
@@ -189,6 +199,24 @@ class CertificateCheckerTest {
.containsExactly(ALGORITHM_CONSTRAINED);
}
@Test
void test_checkCertificate_validCertificateString() throws Exception {
fakeClock.setTo(DateTime.parse("2020-11-01T00:00:00Z"));
assertThat(certificateChecker.checkCertificate(SAMPLE_CERT3)).isEmpty();
assertThat(certificateChecker.checkCertificate(SAMPLE_CERT))
.containsExactly(VALIDITY_LENGTH_TOO_LONG);
}
@Test
void test_checkCertificate_invalidCertificateString() throws Exception {
fakeClock.setTo(DateTime.parse("2020-11-01T00:00:00Z"));
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> certificateChecker.checkCertificate("bad certificate string"));
assertThat(thrown).hasMessageThat().isEqualTo("Unable to read given certificate.");
}
@Test
void test_isNearingExpiration_yesItIs() throws Exception {
fakeClock.setTo(DateTime.parse("2021-09-20T00:00:00Z"));
@@ -225,4 +253,62 @@ class CertificateCheckerTest {
.isEqualTo(
"Certificate validity period is too long; it must be less than or equal to 398 days.");
}
@Test
void test_checkCurveName_invalidCurve_returnsViolation() throws Exception {
fakeClock.setTo(DateTime.parse("2020-10-01T00:00:00Z"));
// Invalid curve
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
AlgorithmParameters apParam = AlgorithmParameters.getInstance("EC");
apParam.init(new ECGenParameterSpec("secp128r1"));
ECParameterSpec spec = apParam.getParameterSpec(ECParameterSpec.class);
keyGen.initialize(spec, new SecureRandom());
X509Certificate certificate =
SelfSignedCaCertificate.create(
keyGen.generateKeyPair(),
SSL_HOST,
DateTime.parse("2020-09-02T00:00:00Z"),
DateTime.parse("2021-10-01T00:00:00Z"))
.cert();
assertThat(certificateChecker.checkCertificate(certificate))
.containsExactly(INVALID_ECDSA_CURVE);
}
@Test
void test_checkCurveName_p256Curve_returnsNoViolations() throws Exception {
fakeClock.setTo(DateTime.parse("2020-10-01T00:00:00Z"));
// valid P-256 curve
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
AlgorithmParameters apParam = AlgorithmParameters.getInstance("EC");
apParam.init(new ECGenParameterSpec("secp256r1"));
ECParameterSpec spec = apParam.getParameterSpec(ECParameterSpec.class);
keyGen.initialize(spec, new SecureRandom());
X509Certificate certificate =
SelfSignedCaCertificate.create(
keyGen.generateKeyPair(),
SSL_HOST,
DateTime.parse("2020-09-02T00:00:00Z"),
DateTime.parse("2021-10-01T00:00:00Z"))
.cert();
assertThat(certificateChecker.checkCertificate(certificate)).isEmpty();
}
@Test
void test_checkCurveName_p384Curve_returnsNoViolations() throws Exception {
fakeClock.setTo(DateTime.parse("2020-10-01T00:00:00Z"));
// valid P-384 curve
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
AlgorithmParameters apParam = AlgorithmParameters.getInstance("EC");
apParam.init(new ECGenParameterSpec("secp384r1"));
ECParameterSpec spec = apParam.getParameterSpec(ECParameterSpec.class);
keyGen.initialize(spec, new SecureRandom());
X509Certificate certificate =
SelfSignedCaCertificate.create(
keyGen.generateKeyPair(),
SSL_HOST,
DateTime.parse("2020-09-02T00:00:00Z"),
DateTime.parse("2021-10-01T00:00:00Z"))
.cert();
assertThat(certificateChecker.checkCertificate(certificate)).isEmpty();
}
}

View File

@@ -32,9 +32,11 @@ import com.googlecode.objectify.annotation.Id;
import google.registry.schema.replay.EntityTest.EntityForTesting;
import google.registry.testing.AppEngineExtension;
import google.registry.util.CidrAddressBlock;
import java.lang.reflect.Field;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -320,4 +322,40 @@ public class ImmutableObjectTest {
root.set = ImmutableSet.of(Key.create(persistResource(ValueObject.create(1, "foo"))));
assertThat(root.toHydratedString()).contains("foo");
}
@Test
void testInsignificantFields() {
HasInsignificantFields instance1 =
HasInsignificantFields.create("significant", "insignificant");
HasInsignificantFields instance2 = HasInsignificantFields.create("significant", "other");
assertThat(instance1).isEqualTo(instance2);
// The hash code test test is implicit in "equals", it is added here just for clarity.
assertThat(instance1.hashCode()).isEqualTo(instance2.hashCode());
assertThat(instance1.toString()).matches(
"(?s)HasInsignificantFields (.*): \\{\\s*significant=significant\\s*\\}\\s*");
}
static class HasInsignificantFields extends ImmutableObject {
String significant;
String insignificant;
static HasInsignificantFields create(String significant, String insignificant) {
HasInsignificantFields instance = new HasInsignificantFields();
instance.significant = significant;
instance.insignificant = insignificant;
return instance;
}
@Override
protected Map<Field, Object> getSignificantFields() {
Map<Field, Object> result = new LinkedHashMap();
for (Map.Entry<Field, Object> entry : ModelUtils.getFieldValues(this).entrySet()) {
if (!entry.getKey().getName().equals("insignificant")) {
result.put(entry.getKey(), entry.getValue());
}
}
return result;
}
}
}

View File

@@ -17,11 +17,12 @@ package google.registry.model.billing;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
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.ofyTmOrDoNothing;
import static google.registry.persistence.transaction.TransactionManagerUtil.transactIfJpaTm;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.persistActiveDomain;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.SqlHelper.saveRegistrar;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static org.joda.money.CurrencyUnit.USD;
import static org.joda.time.DateTimeZone.UTC;
@@ -39,13 +40,17 @@ import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.domain.token.AllocationToken.TokenStatus;
import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import google.registry.util.DateTimeUtils;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link BillingEvent}. */
@DualDatabaseTest
public class BillingEventTest extends EntityTestCase {
private final DateTime now = DateTime.now(UTC);
@@ -67,16 +72,26 @@ public class BillingEventTest extends EntityTestCase {
void setUp() {
createTld("tld");
domain = persistActiveDomain("foo.tld");
historyEntry = persistResource(
new HistoryEntry.Builder()
.setParent(domain)
.setModificationTime(now)
.build());
historyEntry2 = persistResource(
new HistoryEntry.Builder()
.setParent(domain)
.setModificationTime(now.plusDays(1))
.build());
historyEntry =
persistResource(
new HistoryEntry.Builder()
.setParent(domain)
.setModificationTime(now)
.setRequestedByRegistrar(false)
.setType(HistoryEntry.Type.DOMAIN_CREATE)
.setXmlBytes(new byte[0])
.build()
.toChildHistoryEntity());
historyEntry2 =
persistResource(
new HistoryEntry.Builder()
.setParent(domain)
.setModificationTime(now.plusDays(1))
.setRequestedByRegistrar(false)
.setType(HistoryEntry.Type.DOMAIN_CREATE)
.setXmlBytes(new byte[0])
.build()
.toChildHistoryEntity());
AllocationToken allocationToken =
persistResource(
@@ -149,74 +164,38 @@ public class BillingEventTest extends EntityTestCase {
.setEventTime(now.plusDays(1))
.setBillingTime(now.plusYears(1).plusDays(45))
.setRecurringEventKey(recurring.createVKey())));
modification = persistResource(commonInit(
new BillingEvent.Modification.Builder()
.setParent(historyEntry2)
.setReason(Reason.CREATE)
.setCost(Money.of(USD, 1))
.setDescription("Something happened")
.setEventTime(now.plusDays(1))
.setEventKey(Key.create(oneTime))));
modification =
ofyTmOrDoNothing(
() ->
persistResource(
commonInit(
new BillingEvent.Modification.Builder()
.setParent(historyEntry2)
.setReason(Reason.CREATE)
.setCost(Money.of(USD, 1))
.setDescription("Something happened")
.setEventTime(now.plusDays(1))
.setEventKey(Key.create(oneTime)))));
}
private <E extends BillingEvent, B extends BillingEvent.Builder<E, B>> E commonInit(B builder) {
return builder
.setClientId("a registrar")
.setTargetId("foo.tld")
.build();
return builder.setClientId("TheRegistrar").setTargetId("foo.tld").build();
}
private void saveNewBillingEvent(BillingEvent billingEvent) {
jpaTm().transact(() -> jpaTm().insert(billingEvent));
}
@Test
void testCloudSqlPersistence_OneTime() {
saveRegistrar("a registrar");
saveNewBillingEvent(oneTime);
BillingEvent.OneTime persisted = jpaTm().transact(() -> jpaTm().load(oneTime.createVKey()));
// TODO(b/168325240): Remove this fix after VKeyConverter generates symmetric key for
// AllocationToken.
BillingEvent.OneTime fixed =
persisted.asBuilder().setAllocationToken(oneTime.getAllocationToken().get()).build();
assertThat(fixed).isEqualTo(oneTime);
}
@Test
void testCloudSqlPersistence_Cancellation() {
saveRegistrar("a registrar");
saveNewBillingEvent(oneTime);
saveNewBillingEvent(cancellationOneTime);
BillingEvent.Cancellation persisted =
jpaTm().transact(() -> jpaTm().load(cancellationOneTime.createVKey()));
// TODO(b/168537779): Remove this fix after VKey<OneTime> can be reconstructed correctly.
BillingEvent.Cancellation fixed =
persisted.asBuilder().setOneTimeEventKey(oneTime.createVKey()).build();
assertThat(fixed).isEqualTo(cancellationOneTime);
}
@Test
void testCloudSqlPersistence_Recurring() {
saveRegistrar("a registrar");
saveNewBillingEvent(recurring);
BillingEvent.Recurring persisted = jpaTm().transact(() -> jpaTm().load(recurring.createVKey()));
assertThat(persisted).isEqualTo(recurring);
}
@Test
@TestOfyAndSql
void testPersistence() {
assertThat(ofy().load().entity(oneTime).now()).isEqualTo(oneTime);
assertThat(ofy().load().entity(oneTimeSynthetic).now()).isEqualTo(oneTimeSynthetic);
assertThat(ofy().load().entity(recurring).now()).isEqualTo(recurring);
assertThat(ofy().load().entity(cancellationOneTime).now()).isEqualTo(cancellationOneTime);
assertThat(ofy().load().entity(cancellationRecurring).now()).isEqualTo(cancellationRecurring);
assertThat(ofy().load().entity(modification).now()).isEqualTo(modification);
assertThat(transactIfJpaTm(() -> tm().load(oneTime))).isEqualTo(oneTime);
assertThat(transactIfJpaTm(() -> tm().load(oneTimeSynthetic))).isEqualTo(oneTimeSynthetic);
assertThat(transactIfJpaTm(() -> tm().load(recurring))).isEqualTo(recurring);
assertThat(transactIfJpaTm(() -> tm().load(cancellationOneTime)))
.isEqualTo(cancellationOneTime);
assertThat(transactIfJpaTm(() -> tm().load(cancellationRecurring)))
.isEqualTo(cancellationRecurring);
ofyTmOrDoNothing(() -> assertThat(tm().load(modification)).isEqualTo(modification));
}
@Test
@TestOfyOnly
void testParenting() {
// Note that these are all tested separately because BillingEvent is an abstract base class that
// lacks the @Entity annotation, and thus we cannot call .type(BillingEvent.class)
@@ -238,19 +217,14 @@ public class BillingEventTest extends EntityTestCase {
.containsExactly(modification);
}
@Test
@TestOfyAndSql
void testCancellationMatching() {
Key<?> recurringKey =
ofy()
.load()
.entity(oneTimeSynthetic)
.now()
.getCancellationMatchingBillingEvent()
.getOfyKey();
assertThat(ofy().load().key(recurringKey).now()).isEqualTo(recurring);
VKey<?> recurringKey =
transactIfJpaTm(() -> tm().load(oneTimeSynthetic).getCancellationMatchingBillingEvent());
assertThat(transactIfJpaTm(() -> tm().load(recurringKey))).isEqualTo(recurring);
}
@Test
@TestOfyOnly
void testIndexing() throws Exception {
verifyIndexing(
oneTime,
@@ -272,7 +246,7 @@ public class BillingEventTest extends EntityTestCase {
verifyIndexing(modification, "clientId", "eventTime");
}
@Test
@TestOfyAndSql
void testFailure_syntheticFlagWithoutCreationTime() {
IllegalStateException thrown =
assertThrows(
@@ -288,7 +262,7 @@ public class BillingEventTest extends EntityTestCase {
.contains("Synthetic creation time must be set if and only if the SYNTHETIC flag is set.");
}
@Test
@TestOfyAndSql
void testFailure_syntheticCreationTimeWithoutFlag() {
IllegalStateException thrown =
assertThrows(
@@ -299,7 +273,7 @@ public class BillingEventTest extends EntityTestCase {
.contains("Synthetic creation time must be set if and only if the SYNTHETIC flag is set");
}
@Test
@TestOfyAndSql
void testFailure_syntheticFlagWithoutCancellationMatchingKey() {
IllegalStateException thrown =
assertThrows(
@@ -317,7 +291,7 @@ public class BillingEventTest extends EntityTestCase {
+ "if and only if the SYNTHETIC flag is set");
}
@Test
@TestOfyAndSql
void testFailure_cancellationMatchingKeyWithoutFlag() {
IllegalStateException thrown =
assertThrows(
@@ -334,7 +308,7 @@ public class BillingEventTest extends EntityTestCase {
+ "if and only if the SYNTHETIC flag is set");
}
@Test
@TestOfyAndSql
void testSuccess_cancellation_forGracePeriod_withOneTime() {
BillingEvent.Cancellation newCancellation =
BillingEvent.Cancellation.forGracePeriod(
@@ -343,10 +317,15 @@ public class BillingEventTest extends EntityTestCase {
"foo.tld");
// Set ID to be the same to ignore for the purposes of comparison.
newCancellation = newCancellation.asBuilder().setId(cancellationOneTime.getId()).build();
assertThat(newCancellation).isEqualTo(cancellationOneTime);
// TODO(b/168537779): Remove setRecurringEventKey after symmetric VKey can be reconstructed
// correctly.
assertThat(newCancellation)
.isEqualTo(
cancellationOneTime.asBuilder().setOneTimeEventKey(oneTime.createVKey()).build());
}
@Test
@TestOfyAndSql
void testSuccess_cancellation_forGracePeriod_withRecurring() {
BillingEvent.Cancellation newCancellation =
BillingEvent.Cancellation.forGracePeriod(
@@ -354,16 +333,21 @@ public class BillingEventTest extends EntityTestCase {
GracePeriodStatus.AUTO_RENEW,
domain.getRepoId(),
now.plusYears(1).plusDays(45),
"a registrar",
"TheRegistrar",
recurring.createVKey()),
historyEntry2,
"foo.tld");
// Set ID to be the same to ignore for the purposes of comparison.
newCancellation = newCancellation.asBuilder().setId(cancellationRecurring.getId()).build();
assertThat(newCancellation).isEqualTo(cancellationRecurring);
// TODO(b/168537779): Remove setRecurringEventKey after symmetric VKey can be reconstructed
// correctly.
assertThat(newCancellation)
.isEqualTo(
cancellationRecurring.asBuilder().setRecurringEventKey(recurring.createVKey()).build());
}
@Test
@TestOfyAndSql
void testFailure_cancellation_forGracePeriodWithoutBillingEvent() {
IllegalArgumentException thrown =
assertThrows(
@@ -380,7 +364,7 @@ public class BillingEventTest extends EntityTestCase {
assertThat(thrown).hasMessageThat().contains("grace period without billing event");
}
@Test
@TestOfyAndSql
void testFailure_cancellationWithNoBillingEvent() {
IllegalStateException thrown =
assertThrows(
@@ -394,7 +378,7 @@ public class BillingEventTest extends EntityTestCase {
assertThat(thrown).hasMessageThat().contains("exactly one billing event");
}
@Test
@TestOfyAndSql
void testFailure_cancellationWithBothBillingEvents() {
IllegalStateException thrown =
assertThrows(
@@ -408,7 +392,7 @@ public class BillingEventTest extends EntityTestCase {
assertThat(thrown).hasMessageThat().contains("exactly one billing event");
}
@Test
@TestOfyAndSql
void testDeadCodeThatDeletedScrapCommandsReference() {
assertThat(recurring.getParentKey()).isEqualTo(Key.create(historyEntry));
new BillingEvent.OneTime.Builder().setParent(Key.create(historyEntry));

View File

@@ -17,6 +17,7 @@ package google.registry.model.domain;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.SqlHelper.assertThrowForeignKeyViolation;
import static google.registry.testing.SqlHelper.saveRegistrar;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
@@ -45,30 +46,29 @@ import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.ContactTransferData;
import google.registry.model.transfer.DomainTransferData;
import google.registry.persistence.VKey;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageExtension;
import google.registry.testing.DatastoreEntityExtension;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.FakeClock;
import google.registry.testing.TestSqlOnly;
import java.util.Arrays;
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;
/** Verify that we can store/retrieve DomainBase objects from a SQL database. */
@DualDatabaseTest
public class DomainBaseSqlTest {
protected FakeClock fakeClock = new FakeClock(DateTime.now(UTC));
@RegisterExtension
@Order(value = 1)
DatastoreEntityExtension datastoreEntityExtension = new DatastoreEntityExtension();
@RegisterExtension
JpaIntegrationWithCoverageExtension jpa =
new JpaTestRules.Builder().withClock(fakeClock).buildIntegrationWithCoverageExtension();
public final AppEngineExtension appEngine =
AppEngineExtension.builder()
.withDatastoreAndCloudSql()
.enableJpaEntityCoverageCheck(true)
.withClock(fakeClock)
.build();
private DomainBase domain;
private DomainHistory historyEntry;
@@ -78,6 +78,7 @@ public class DomainBaseSqlTest {
private HostResource host;
private ContactResource contact;
private ContactResource contact2;
private ImmutableSet<GracePeriod> gracePeriods;
@BeforeEach
void setUp() {
@@ -131,7 +132,7 @@ public class DomainBaseSqlTest {
contact2 = makeContact("contact_id2");
}
@Test
@TestSqlOnly
void testDomainBasePersistence() {
persistDomain();
@@ -143,7 +144,7 @@ public class DomainBaseSqlTest {
});
}
@Test
@TestSqlOnly
void testHostForeignKeyConstraints() {
assertThrowForeignKeyViolation(
() ->
@@ -157,7 +158,7 @@ public class DomainBaseSqlTest {
}));
}
@Test
@TestSqlOnly
void testContactForeignKeyConstraints() {
assertThrowForeignKeyViolation(
() ->
@@ -170,7 +171,7 @@ public class DomainBaseSqlTest {
}));
}
@Test
@TestSqlOnly
void testResaveDomain_succeeds() {
persistDomain();
jpaTm()
@@ -188,7 +189,7 @@ public class DomainBaseSqlTest {
});
}
@Test
@TestSqlOnly
void testModifyGracePeriod_setEmptyCollectionSuccessfully() {
persistDomain();
jpaTm()
@@ -208,7 +209,7 @@ public class DomainBaseSqlTest {
});
}
@Test
@TestSqlOnly
void testModifyGracePeriod_setNullCollectionSuccessfully() {
persistDomain();
jpaTm()
@@ -227,7 +228,7 @@ public class DomainBaseSqlTest {
});
}
@Test
@TestSqlOnly
void testModifyGracePeriod_addThenRemoveSuccessfully() {
persistDomain();
jpaTm()
@@ -304,7 +305,7 @@ public class DomainBaseSqlTest {
});
}
@Test
@TestSqlOnly
void testModifyGracePeriod_removeThenAddSuccessfully() {
persistDomain();
jpaTm()
@@ -346,7 +347,7 @@ public class DomainBaseSqlTest {
});
}
@Test
@TestSqlOnly
void testModifyDsData_addThenRemoveSuccessfully() {
persistDomain();
DelegationSignerData extraDsData =
@@ -390,8 +391,9 @@ public class DomainBaseSqlTest {
});
}
@Test
@TestSqlOnly
void testUpdates() {
createTld("com");
jpaTm()
.transact(
() -> {
@@ -422,6 +424,7 @@ public class DomainBaseSqlTest {
}
private void persistDomain() {
createTld("com");
jpaTm()
.transact(
() -> {
@@ -441,8 +444,9 @@ public class DomainBaseSqlTest {
});
}
@Test
@TestSqlOnly
void persistDomainWithCompositeVKeys() {
createTld("com");
jpaTm()
.transact(
() -> {
@@ -506,6 +510,20 @@ public class DomainBaseSqlTest {
.setServerApproveAutorenewEvent(billEvent.createVKey())
.setServerApproveAutorenewPollMessage(autorenewPollMessage.createVKey())
.build();
gracePeriods =
ImmutableSet.of(
GracePeriod.create(
GracePeriodStatus.ADD,
"4-COM",
END_OF_TIME,
"registrar1",
oneTimeBillingEvent.createVKey()),
GracePeriod.createForRecurring(
GracePeriodStatus.AUTO_RENEW,
"4-COM",
END_OF_TIME,
"registrar1",
billEvent.createVKey()));
jpaTm().insert(contact);
jpaTm().insert(contact2);
@@ -517,6 +535,7 @@ public class DomainBaseSqlTest {
.setAutorenewPollMessage(autorenewPollMessage.createVKey())
.setDeletePollMessage(deletePollMessage.createVKey())
.setTransferData(transferData)
.setGracePeriods(gracePeriods)
.build();
historyEntry = historyEntry.asBuilder().setDomainContent(domain).build();
jpaTm().insert(historyEntry);
@@ -553,10 +572,12 @@ public class DomainBaseSqlTest {
.isEqualTo(originalTransferData.getServerApproveAutorenewEvent());
assertThat(persistedTransferData.getServerApproveAutorenewPollMessage())
.isEqualTo(originalTransferData.getServerApproveAutorenewPollMessage());
assertThat(persisted.getGracePeriods()).isEqualTo(gracePeriods);
}
@Test
@TestSqlOnly
void persistDomainWithLegacyVKeys() {
createTld("com");
jpaTm()
.transact(
() -> {
@@ -624,6 +645,21 @@ public class DomainBaseSqlTest {
createLegacyVKey(
PollMessage.Autorenew.class, autorenewPollMessage.getId()))
.build();
gracePeriods =
ImmutableSet.of(
GracePeriod.create(
GracePeriodStatus.ADD,
"4-COM",
END_OF_TIME,
"registrar1",
createLegacyVKey(
BillingEvent.OneTime.class, oneTimeBillingEvent.getId())),
GracePeriod.createForRecurring(
GracePeriodStatus.AUTO_RENEW,
"4-COM",
END_OF_TIME,
"registrar1",
createLegacyVKey(BillingEvent.Recurring.class, billEvent.getId())));
jpaTm().insert(contact);
jpaTm().insert(contact2);
@@ -639,6 +675,7 @@ public class DomainBaseSqlTest {
.setDeletePollMessage(
createLegacyVKey(PollMessage.OneTime.class, deletePollMessage.getId()))
.setTransferData(transferData)
.setGracePeriods(gracePeriods)
.build();
historyEntry = historyEntry.asBuilder().setDomainContent(domain).build();
jpaTm().insert(historyEntry);
@@ -675,6 +712,7 @@ public class DomainBaseSqlTest {
.isEqualTo(originalTransferData.getServerApproveAutorenewEvent());
assertThat(persistedTransferData.getServerApproveAutorenewPollMessage())
.isEqualTo(originalTransferData.getServerApproveAutorenewPollMessage());
assertThat(domain.getGracePeriods()).isEqualTo(gracePeriods);
}
private <T> VKey<T> createLegacyVKey(Class<T> clazz, long id) {

View File

@@ -38,6 +38,7 @@ import com.google.common.collect.Ordering;
import com.google.common.collect.Streams;
import com.googlecode.objectify.Key;
import google.registry.model.EntityTestCase;
import google.registry.model.ImmutableObject;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.contact.ContactResource;
@@ -830,4 +831,63 @@ public class DomainBaseTest extends EntityTestCase {
assertThat(getOnlyElement(clone.getGracePeriods()).getType())
.isEqualTo(GracePeriodStatus.TRANSFER);
}
@Test
void testHistoryIdRestoration() {
// Verify that history ids for billing events are restored during load from datastore. History
// ids are not used by business code or persisted in datastore, but only to reconstruct
// objectify keys when loading from SQL.
DateTime now = fakeClock.nowUtc();
domain =
persistResource(
domain
.asBuilder()
.setRegistrationExpirationTime(now.plusYears(1))
.setGracePeriods(
ImmutableSet.of(
GracePeriod.createForRecurring(
GracePeriodStatus.AUTO_RENEW,
domain.getRepoId(),
now.plusDays(1),
"NewRegistrar",
recurringBillKey),
GracePeriod.create(
GracePeriodStatus.RENEW,
domain.getRepoId(),
now.plusDays(1),
"NewRegistrar",
oneTimeBillKey)))
.build());
ImmutableSet<BillEventInfo> historyIds =
domain.getGracePeriods().stream()
.map(
gp ->
new BillEventInfo(
gp.getRecurringBillingEvent(), gp.billingEventRecurringHistoryId,
gp.getOneTimeBillingEvent(), gp.billingEventOneTimeHistoryId))
.collect(toImmutableSet());
assertThat(historyIds)
.isEqualTo(
ImmutableSet.of(
new BillEventInfo(null, null, oneTimeBillKey, historyEntryKey.getId()),
new BillEventInfo(recurringBillKey, historyEntryKey.getId(), null, null)));
}
static class BillEventInfo extends ImmutableObject {
VKey<BillingEvent.Recurring> billingEventRecurring;
Long billingEventRecurringHistoryId;
VKey<BillingEvent.OneTime> billingEventOneTime;
Long billingEventOneTimeHistoryId;
BillEventInfo(
VKey<BillingEvent.Recurring> billingEventRecurring,
Long billingEventRecurringHistoryId,
VKey<BillingEvent.OneTime> billingEventOneTime,
Long billingEventOneTimeHistoryId) {
this.billingEventRecurring = billingEventRecurring;
this.billingEventRecurringHistoryId = billingEventRecurringHistoryId;
this.billingEventOneTime = billingEventOneTime;
this.billingEventOneTimeHistoryId = billingEventOneTimeHistoryId;
}
}
}

View File

@@ -44,6 +44,7 @@ public class GracePeriodTest {
private final DateTime now = DateTime.now(UTC);
private BillingEvent.OneTime onetime;
private VKey<BillingEvent.Recurring> recurringKey;
@BeforeEach
void before() {
@@ -59,6 +60,14 @@ public class GracePeriodTest {
.setPeriodYears(1)
.setTargetId("foo.google")
.build();
recurringKey =
VKey.create(
Recurring.class,
12345,
Key.create(
Key.create(Key.create(DomainBase.class, "1-TEST"), HistoryEntry.class, 343L),
Recurring.class,
12345));
}
@Test
@@ -71,6 +80,24 @@ public class GracePeriodTest {
assertThat(gracePeriod.getClientId()).isEqualTo("TheRegistrar");
assertThat(gracePeriod.getExpirationTime()).isEqualTo(now.plusDays(1));
assertThat(gracePeriod.hasBillingEvent()).isTrue();
assertThat(gracePeriod.billingEventOneTimeHistoryId).isEqualTo(12345L);
assertThat(gracePeriod.billingEventRecurringHistoryId).isNull();
}
@Test
void testSuccess_forRecurringEvent() {
GracePeriod gracePeriod =
GracePeriod.createForRecurring(
GracePeriodStatus.AUTO_RENEW, "1-TEST", now.plusDays(1), "TheRegistrar", recurringKey);
assertThat(gracePeriod.getType()).isEqualTo(GracePeriodStatus.AUTO_RENEW);
assertThat(gracePeriod.getDomainRepoId()).isEqualTo("1-TEST");
assertThat(gracePeriod.getOneTimeBillingEvent()).isNull();
assertThat(gracePeriod.getRecurringBillingEvent()).isEqualTo(recurringKey);
assertThat(gracePeriod.getClientId()).isEqualTo("TheRegistrar");
assertThat(gracePeriod.getExpirationTime()).isEqualTo(now.plusDays(1));
assertThat(gracePeriod.hasBillingEvent()).isTrue();
assertThat(gracePeriod.billingEventOneTimeHistoryId).isNull();
assertThat(gracePeriod.billingEventRecurringHistoryId).isEqualTo(343L);
}
@Test
@@ -98,11 +125,6 @@ public class GracePeriodTest {
@Test
void testFailure_createForRecurring_notAutoRenew() {
Key<Recurring> recurringKey =
Key.create(
Key.create(Key.create(DomainBase.class, "1-TEST"), HistoryEntry.class, 343L),
Recurring.class,
12345);
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
@@ -112,7 +134,7 @@ public class GracePeriodTest {
"1-TEST",
now.plusDays(1),
"TheRegistrar",
VKey.create(Recurring.class, 12345, recurringKey)));
recurringKey));
assertThat(thrown).hasMessageThat().contains("autorenew");
}
}

View File

@@ -20,10 +20,10 @@ import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableO
import static google.registry.model.ImmutableObjectSubject.immutableObjectCorrespondence;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.newContactResourceWithRoid;
import static google.registry.testing.DatastoreHelper.newDomainBase;
import static google.registry.testing.DatastoreHelper.newHostResourceWithRoid;
import static google.registry.testing.SqlHelper.saveRegistrar;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableSet;
@@ -34,28 +34,26 @@ import google.registry.model.domain.DomainBase;
import google.registry.model.domain.DomainContent;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.Period;
import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.eppcommon.Trid;
import google.registry.model.host.HostResource;
import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyOnly;
import google.registry.testing.TestSqlOnly;
/** Tests for {@link DomainHistory}. */
@DualDatabaseTest
public class DomainHistoryTest extends EntityTestCase {
DomainHistoryTest() {
super(JpaEntityCoverageCheck.ENABLED);
}
@BeforeEach
void beforeEach() {
saveRegistrar("TheRegistrar");
}
@Test
@TestSqlOnly
void testPersistence() {
DomainBase domain = createDomainWithContactsAndHosts();
DomainHistory domainHistory = createDomainHistory(domain);
@@ -70,7 +68,7 @@ public class DomainHistoryTest extends EntityTestCase {
});
}
@Test
@TestSqlOnly
void testLegacyPersistence_nullResource() {
DomainBase domain = createDomainWithContactsAndHosts();
DomainHistory domainHistory =
@@ -91,7 +89,7 @@ public class DomainHistoryTest extends EntityTestCase {
});
}
@Test
@TestOfyOnly
void testOfyPersistence() {
HostResource host = newHostResourceWithRoid("ns1.example.com", "host1");
ContactResource contact = newContactResourceWithRoid("contactId", "contact1");
@@ -127,6 +125,7 @@ public class DomainHistoryTest extends EntityTestCase {
}
static DomainBase createDomainWithContactsAndHosts() {
createTld("tld");
HostResource host = newHostResourceWithRoid("ns1.example.com", "host1");
ContactResource contact = newContactResourceWithRoid("contactId", "contact1");
@@ -141,6 +140,7 @@ public class DomainHistoryTest extends EntityTestCase {
newDomainBase("example.tld", "domainRepoId", contact)
.asBuilder()
.setNameservers(host.createVKey())
.setDsData(ImmutableSet.of(DelegationSignerData.create(1, 2, 3, new byte[] {0, 1, 2})))
.build();
jpaTm().transact(() -> jpaTm().insert(domain));
return domain;

View File

@@ -17,10 +17,10 @@ package google.registry.model.history;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.newContactResourceWithRoid;
import static google.registry.testing.DatastoreHelper.newHostResourceWithRoid;
import static google.registry.testing.SqlHelper.saveRegistrar;
import static google.registry.util.CollectionUtils.nullToEmpty;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -40,34 +40,32 @@ import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestSqlOnly;
/** Tests to check {@link HistoryEntry} + its subclasses' transitions to/from Datastore/SQL. */
@DualDatabaseTest
public class LegacyHistoryObjectTest extends EntityTestCase {
public LegacyHistoryObjectTest() {
super(JpaEntityCoverageCheck.ENABLED);
}
@BeforeEach
void beforeEach() {
saveRegistrar("TheRegistrar");
}
@Test
@TestSqlOnly
void testFullConversion_contact() {
// Create+save an old contact HistoryEntry, reload it, and verify it's a proper ContactHistory
ContactResource contact = newContactResourceWithRoid("contactId", "contact1");
HistoryEntry legacyHistoryEntry = historyEntryBuilderFor(contact).build();
tm().transact(() -> tm().insert(legacyHistoryEntry));
ofyTm().transact(() -> ofyTm().insert(legacyHistoryEntry));
// In Datastore, we will save it as HistoryEntry but retrieve it as ContactHistory
long historyEntryId = legacyHistoryEntry.getId();
HistoryEntry fromObjectify =
tm().transact(
ofyTm()
.transact(
() ->
tm().load(
ofyTm()
.load(
VKey.create(
HistoryEntry.class,
historyEntryId,
@@ -96,19 +94,22 @@ public class LegacyHistoryObjectTest extends EntityTestCase {
.isEqualTo(legacyHistoryFromSql.getParentVKey().getSqlKey());
}
@Test
@TestSqlOnly
void testFullConversion_domain() {
createTld("foobar");
// Create+save an old domain HistoryEntry, reload it, and verify it's a proper DomainHistory
DomainBase domain = DomainHistoryTest.createDomainWithContactsAndHosts();
HistoryEntry legacyHistoryEntry = historyEntryForDomain(domain);
tm().transact(() -> tm().insert(legacyHistoryEntry));
ofyTm().transact(() -> ofyTm().insert(legacyHistoryEntry));
// In Datastore, we will save it as HistoryEntry but retrieve it as DomainHistory
long historyEntryId = legacyHistoryEntry.getId();
HistoryEntry fromObjectify =
tm().transact(
ofyTm()
.transact(
() ->
tm().load(
ofyTm()
.load(
VKey.create(
HistoryEntry.class,
historyEntryId,
@@ -116,7 +117,8 @@ public class LegacyHistoryObjectTest extends EntityTestCase {
// The objects will be mostly the same, but the DomainHistory object has a couple extra fields
assertAboutImmutableObjects()
.that(legacyHistoryEntry)
.isEqualExceptFields(fromObjectify, "domainContent", "domainRepoId", "nsHosts");
.isEqualExceptFields(
fromObjectify, "domainContent", "domainRepoId", "nsHosts", "dsDataHistories");
assertThat(fromObjectify instanceof DomainHistory).isTrue();
DomainHistory legacyDomainHistory = (DomainHistory) fromObjectify;
@@ -129,24 +131,31 @@ public class LegacyHistoryObjectTest extends EntityTestCase {
.that(legacyDomainHistory)
.isEqualExceptFields(
// NB: period, transaction records, and other client ID are added in #794
legacyHistoryFromSql, "period", "domainTransactionRecords", "otherClientId", "nsHosts");
legacyHistoryFromSql,
"period",
"domainTransactionRecords",
"otherClientId",
"nsHosts",
"dsDataHistories");
assertThat(nullToEmpty(legacyDomainHistory.getNsHosts()))
.isEqualTo(nullToEmpty(legacyHistoryFromSql.getNsHosts()));
}
@Test
@TestSqlOnly
void testFullConversion_host() {
// Create+save an old host HistoryEntry, reload it, and verify it's a proper HostHistory
HostResource host = newHostResourceWithRoid("hs1.example.com", "host1");
HistoryEntry legacyHistoryEntry = historyEntryBuilderFor(host).build();
tm().transact(() -> tm().insert(legacyHistoryEntry));
ofyTm().transact(() -> ofyTm().insert(legacyHistoryEntry));
// In Datastore, we will save it as HistoryEntry but retrieve it as HostHistory
long historyEntryId = legacyHistoryEntry.getId();
HistoryEntry fromObjectify =
tm().transact(
ofyTm()
.transact(
() ->
tm().load(
ofyTm()
.load(
VKey.create(
HistoryEntry.class,
historyEntryId,

View File

@@ -27,7 +27,6 @@ import static google.registry.testing.DatastoreHelper.persistActiveContact;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.fail;
@@ -225,7 +224,7 @@ public class OfyTest {
if (firstAttemptTime == null) {
// Sleep a bit to ensure that the next attempt is at a new millisecond.
firstAttemptTime = tm().getTransactionTime();
sleepUninterruptibly(10, MILLISECONDS);
sleepUninterruptibly(java.time.Duration.ofMillis(10));
throw new ConcurrentModificationException();
}
assertThat(tm().getTransactionTime()).isGreaterThan(firstAttemptTime);

View File

@@ -15,13 +15,12 @@
package google.registry.model.poll;
import static com.google.common.truth.Truth.assertThat;
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.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.newDomainBase;
import static google.registry.testing.DatastoreHelper.persistActiveContact;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.testing.SqlHelper.saveRegistrar;
import static java.nio.charset.StandardCharsets.UTF_8;
import google.registry.model.EntityTestCase;
@@ -31,10 +30,14 @@ import google.registry.model.domain.Period;
import google.registry.model.eppcommon.Trid;
import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import google.registry.testing.TestSqlOnly;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link PollMessage}. */
@DualDatabaseTest
public class PollMessageTest extends EntityTestCase {
private DomainBase domain;
@@ -64,7 +67,8 @@ public class PollMessageTest extends EntityTestCase {
.setBySuperuser(false)
.setReason("reason")
.setRequestedByRegistrar(false)
.build());
.build()
.toChildHistoryEntity());
oneTime =
new PollMessage.OneTime.Builder()
.setId(100L)
@@ -83,35 +87,9 @@ public class PollMessageTest extends EntityTestCase {
.setAutorenewEndTime(fakeClock.nowUtc().plusDays(365))
.setTargetId("foobar.foo")
.build();
jpaTm()
.transact(
() -> {
saveRegistrar("TheRegistrar");
jpaTm().insert(contact);
jpaTm().insert(domain);
jpaTm().insert(historyEntry.toChildHistoryEntity());
});
}
@Test
void testCloudSqlPersistenceOneTime() {
jpaTm().transact(() -> jpaTm().insert(oneTime));
PollMessage.OneTime persisted =
jpaTm().transact(() -> jpaTm().load(VKey.createSql(PollMessage.OneTime.class, oneTime.id)));
assertThat(persisted).isEqualTo(oneTime);
}
@Test
void testCloudSqlPersistenceAutorenew() {
jpaTm().transact(() -> jpaTm().insert(autoRenew));
PollMessage.Autorenew persisted =
jpaTm()
.transact(
() -> jpaTm().load(VKey.createSql(PollMessage.Autorenew.class, autoRenew.id)));
assertThat(persisted).isEqualTo(autoRenew);
}
@Test
@TestSqlOnly
void testCloudSqlSupportForPolymorphicVKey() {
jpaTm().transact(() -> jpaTm().insert(oneTime));
PollMessage persistedOneTime =
@@ -126,7 +104,7 @@ public class PollMessageTest extends EntityTestCase {
assertThat(persistedAutoRenew).isEqualTo(autoRenew);
}
@Test
@TestOfyAndSql
void testPersistenceOneTime() {
PollMessage.OneTime pollMessage =
persistResource(
@@ -136,10 +114,10 @@ public class PollMessageTest extends EntityTestCase {
.setMsg("Test poll message")
.setParent(historyEntry)
.build());
assertThat(ofy().load().entity(pollMessage).now()).isEqualTo(pollMessage);
assertThat(tm().transact(() -> tm().load(pollMessage))).isEqualTo(pollMessage);
}
@Test
@TestOfyAndSql
void testPersistenceAutorenew() {
PollMessage.Autorenew pollMessage =
persistResource(
@@ -151,10 +129,10 @@ public class PollMessageTest extends EntityTestCase {
.setAutorenewEndTime(fakeClock.nowUtc().plusDays(365))
.setTargetId("foobar.foo")
.build());
assertThat(ofy().load().entity(pollMessage).now()).isEqualTo(pollMessage);
assertThat(tm().transact(() -> tm().load(pollMessage))).isEqualTo(pollMessage);
}
@Test
@TestOfyOnly
void testIndexingAutorenew() throws Exception {
PollMessage.Autorenew pollMessage =
persistResource(

View File

@@ -15,118 +15,116 @@
package google.registry.model.rde;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.rde.RdeMode.FULL;
import static google.registry.model.rde.RdeRevision.getNextRevision;
import static google.registry.model.rde.RdeRevision.saveRevision;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.base.VerifyException;
import google.registry.testing.AppEngineExtension;
import google.registry.model.EntityTestCase;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import org.joda.time.DateTime;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.BeforeEach;
/** Unit tests for {@link RdeRevision}. */
public class RdeRevisionTest {
@DualDatabaseTest
public class RdeRevisionTest extends EntityTestCase {
@RegisterExtension
final AppEngineExtension appEngine =
AppEngineExtension.builder().withDatastoreAndCloudSql().build();
public RdeRevisionTest() {
super(JpaEntityCoverageCheck.ENABLED);
}
@Test
@BeforeEach
void beforeEach() {
fakeClock.setTo(DateTime.parse("1984-12-18TZ"));
}
@TestOfyAndSql
void testGetNextRevision_objectDoesntExist_returnsZero() {
assertThat(getNextRevision("torment", DateTime.parse("1984-12-18TZ"), FULL)).isEqualTo(0);
tm().transact(
() -> assertThat(getNextRevision("torment", fakeClock.nowUtc(), FULL)).isEqualTo(0));
}
@Test
@TestOfyAndSql
void testGetNextRevision_objectExistsAtZero_returnsOne() {
save("sorrow", DateTime.parse("1984-12-18TZ"), FULL, 0);
assertThat(getNextRevision("sorrow", DateTime.parse("1984-12-18TZ"), FULL)).isEqualTo(1);
save("sorrow", fakeClock.nowUtc(), FULL, 0);
tm().transact(
() -> assertThat(getNextRevision("sorrow", fakeClock.nowUtc(), FULL)).isEqualTo(1));
}
@Test
@TestOfyAndSql
void testSaveRevision_objectDoesntExist_newRevisionIsZero_nextRevIsOne() {
tm().transact(() -> saveRevision("despondency", DateTime.parse("1984-12-18TZ"), FULL, 0));
tm().transact(() -> saveRevision("despondency", fakeClock.nowUtc(), FULL, 0));
tm().transact(
() ->
assertThat(getNextRevision("despondency", DateTime.parse("1984-12-18TZ"), FULL))
.isEqualTo(1));
assertThat(getNextRevision("despondency", fakeClock.nowUtc(), FULL)).isEqualTo(1));
}
@Test
@TestOfyAndSql
void testSaveRevision_objectDoesntExist_newRevisionIsOne_throwsVe() {
VerifyException thrown =
IllegalArgumentException thrown =
assertThrows(
VerifyException.class,
() ->
tm().transact(
() ->
saveRevision("despondency", DateTime.parse("1984-12-18TZ"), FULL, 1)));
assertThat(thrown).hasMessageThat().contains("object missing");
IllegalArgumentException.class,
() -> tm().transact(() -> saveRevision("despondency", fakeClock.nowUtc(), FULL, 1)));
assertThat(thrown)
.hasMessageThat()
.isEqualTo(
"Couldn't find existing RDE revision despondency_1984-12-18_full "
+ "when trying to save new revision 1");
}
@Test
@TestOfyAndSql
void testSaveRevision_objectExistsAtZero_newRevisionIsZero_throwsVe() {
save("melancholy", DateTime.parse("1984-12-18TZ"), FULL, 0);
VerifyException thrown =
save("melancholy", fakeClock.nowUtc(), FULL, 0);
IllegalArgumentException thrown =
assertThrows(
VerifyException.class,
() ->
tm().transact(
() -> saveRevision("melancholy", DateTime.parse("1984-12-18TZ"), FULL, 0)));
IllegalArgumentException.class,
() -> tm().transact(() -> saveRevision("melancholy", fakeClock.nowUtc(), FULL, 0)));
assertThat(thrown).hasMessageThat().contains("object already created");
}
@Test
@TestOfyAndSql
void testSaveRevision_objectExistsAtZero_newRevisionIsOne_nextRevIsTwo() {
save("melancholy", DateTime.parse("1984-12-18TZ"), FULL, 0);
tm().transact(() -> saveRevision("melancholy", DateTime.parse("1984-12-18TZ"), FULL, 1));
tm().transact(
() ->
assertThat(getNextRevision("melancholy", DateTime.parse("1984-12-18TZ"), FULL))
.isEqualTo(2));
DateTime startOfDay = fakeClock.nowUtc().withTimeAtStartOfDay();
save("melancholy", startOfDay, FULL, 0);
fakeClock.advanceOneMilli();
tm().transact(() -> saveRevision("melancholy", startOfDay, FULL, 1));
tm().transact(() -> assertThat(getNextRevision("melancholy", startOfDay, FULL)).isEqualTo(2));
}
@Test
@TestOfyAndSql
void testSaveRevision_objectExistsAtZero_newRevisionIsTwo_throwsVe() {
save("melancholy", DateTime.parse("1984-12-18TZ"), FULL, 0);
VerifyException thrown =
save("melancholy", fakeClock.nowUtc(), FULL, 0);
IllegalArgumentException thrown =
assertThrows(
VerifyException.class,
() ->
tm().transact(
() -> saveRevision("melancholy", DateTime.parse("1984-12-18TZ"), FULL, 2)));
assertThat(thrown).hasMessageThat().contains("should be at 1 ");
IllegalArgumentException.class,
() -> tm().transact(() -> saveRevision("melancholy", fakeClock.nowUtc(), FULL, 2)));
assertThat(thrown)
.hasMessageThat()
.contains("RDE revision object should be at revision 1 but was");
}
@Test
@TestOfyAndSql
void testSaveRevision_negativeRevision_throwsIae() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
tm().transact(
() ->
saveRevision("melancholy", DateTime.parse("1984-12-18TZ"), FULL, -1)));
() -> tm().transact(() -> saveRevision("melancholy", fakeClock.nowUtc(), FULL, -1)));
assertThat(thrown).hasMessageThat().contains("Negative revision");
}
@Test
@TestOfyAndSql
void testSaveRevision_callerNotInTransaction_throwsIse() {
IllegalStateException thrown =
assertThrows(
IllegalStateException.class,
() -> saveRevision("frenzy", DateTime.parse("1984-12-18TZ"), FULL, 1));
IllegalStateException.class, () -> saveRevision("frenzy", fakeClock.nowUtc(), FULL, 1));
assertThat(thrown).hasMessageThat().contains("transaction");
}
public static void save(String tld, DateTime date, RdeMode mode, int revision) {
String triplet = RdeNamingUtils.makePartialName(tld, date, mode);
RdeRevision object = new RdeRevision();
object.id = triplet;
object.revision = revision;
ofy().saveWithoutBackup().entity(object).now();
RdeRevision object = RdeRevision.create(triplet, tld, date.toLocalDate(), mode, revision);
tm().transact(() -> tm().put(object));
}
}

View File

@@ -24,10 +24,12 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.net.InternetDomainName;
import google.registry.model.registry.Registry.TldType;
import google.registry.testing.AppEngineExtension;
import org.junit.jupiter.api.Test;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link Registries}. */
@DualDatabaseTest
class RegistriesTest {
@RegisterExtension
@@ -38,13 +40,13 @@ class RegistriesTest {
createTlds("foo", "a.b.c"); // Test a multipart tld.
}
@Test
@TestOfyAndSql
void testGetTlds() {
initTestTlds();
assertThat(Registries.getTlds()).containsExactly("foo", "a.b.c");
}
@Test
@TestOfyAndSql
void test_getTldEntities() {
initTestTlds();
persistResource(newRegistry("testtld", "TESTTLD").asBuilder().setTldType(TldType.TEST).build());
@@ -54,25 +56,25 @@ class RegistriesTest {
.containsExactly(Registry.get("testtld"));
}
@Test
@TestOfyAndSql
void testGetTlds_withNoRegistriesPersisted_returnsEmptySet() {
assertThat(Registries.getTlds()).isEmpty();
}
@Test
@TestOfyAndSql
void testAssertTldExists_doesExist() {
initTestTlds();
Registries.assertTldExists("foo");
Registries.assertTldExists("a.b.c");
}
@Test
@TestOfyAndSql
void testAssertTldExists_doesntExist() {
initTestTlds();
assertThrows(IllegalArgumentException.class, () -> Registries.assertTldExists("baz"));
}
@Test
@TestOfyAndSql
void testFindTldForName() {
initTestTlds();
assertThat(Registries.findTldForName(InternetDomainName.from("example.foo")).get().toString())

View File

@@ -17,13 +17,11 @@ package google.registry.model.registry;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.Truth8.assertThat;
import static google.registry.model.common.EntityGroupRoot.getCrossTldKey;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.registry.Registry.TldState.GENERAL_AVAILABILITY;
import static google.registry.model.registry.Registry.TldState.PREDELEGATION;
import static google.registry.model.registry.Registry.TldState.QUIET_PERIOD;
import static google.registry.model.registry.Registry.TldState.START_DATE_SUNRISE;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.newRegistry;
import static google.registry.testing.DatastoreHelper.persistPremiumList;
@@ -44,59 +42,57 @@ import google.registry.model.registry.Registry.RegistryNotFoundException;
import google.registry.model.registry.Registry.TldState;
import google.registry.model.registry.label.PremiumList;
import google.registry.model.registry.label.ReservedList;
import google.registry.persistence.VKey;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageExtension;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestOfyOnly;
import java.math.BigDecimal;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link Registry}. */
@DualDatabaseTest
public class RegistryTest extends EntityTestCase {
RegistryTest() {
super(JpaEntityCoverageCheck.ENABLED);
}
@BeforeEach
void beforeEach() {
createTld("tld");
}
@RegisterExtension
JpaIntegrationWithCoverageExtension jpa =
new JpaTestRules.Builder().withClock(fakeClock).buildIntegrationWithCoverageExtension();
@Test
public void testCloudSqlPersistence() {
@TestOfyAndSql
public void testPersistence_updateReservedAndPremiumListSuccessfully() {
ReservedList rl15 = persistReservedList("tld-reserved15", "potato,FULLY_BLOCKED");
PremiumList pl = persistPremiumList("tld2", "lol,USD 50", "cat,USD 700");
Registry registry =
Registry.get("tld").asBuilder().setReservedLists(rl15).setPremiumList(pl).build();
jpaTm().transact(() -> jpaTm().insert(registry));
Registry persisted =
jpaTm().transact(() -> jpaTm().load(VKey.createSql(Registry.class, registry.tldStrId)));
tm().transact(() -> tm().put(registry));
Registry persisted = tm().transact(() -> tm().load(Registry.createVKey(registry.tldStrId)));
assertThat(persisted).isEqualTo(registry);
}
@Test
@TestOfyAndSql
void testPersistence() {
assertWithMessage("Registry not found").that(Registry.get("tld")).isNotNull();
assertThat(ofy().load().type(Registry.class).parent(getCrossTldKey()).id("tld").now())
assertThat(tm().transact(() -> tm().load(Registry.createVKey("tld"))))
.isEqualTo(Registry.get("tld"));
}
@Test
@TestOfyAndSql
void testFailure_registryNotFound() {
createTld("foo");
assertThrows(RegistryNotFoundException.class, () -> Registry.get("baz"));
}
@Test
@TestOfyOnly
void testIndexing() throws Exception {
verifyIndexing(Registry.get("tld"));
}
@Test
@TestOfyAndSql
void testSettingEscrowEnabled_null() {
assertThat(Registry.get("tld").asBuilder().setEscrowEnabled(true).build().getEscrowEnabled())
.isTrue();
@@ -104,7 +100,7 @@ public class RegistryTest extends EntityTestCase {
.isFalse();
}
@Test
@TestOfyAndSql
void testSettingCreateBillingCost() {
Registry registry =
Registry.get("tld").asBuilder().setCreateBillingCost(Money.of(USD, 42)).build();
@@ -113,7 +109,7 @@ public class RegistryTest extends EntityTestCase {
assertThat(registry.getStandardRestoreCost()).isEqualTo(Money.of(USD, 17));
}
@Test
@TestOfyAndSql
void testSettingRestoreBillingCost() {
Registry registry =
Registry.get("tld").asBuilder().setRestoreBillingCost(Money.of(USD, 42)).build();
@@ -122,19 +118,19 @@ public class RegistryTest extends EntityTestCase {
assertThat(registry.getStandardRestoreCost()).isEqualTo(Money.of(USD, 42));
}
@Test
@TestOfyAndSql
void testDefaultNumDnsPublishShards_equalToOne() {
Registry registry = Registry.get("tld").asBuilder().build();
assertThat(registry.getNumDnsPublishLocks()).isEqualTo(1);
}
@Test
@TestOfyAndSql
void testSettingNumDnsPublishShards() {
Registry registry = Registry.get("tld").asBuilder().setNumDnsPublishLocks(2).build();
assertThat(registry.getNumDnsPublishLocks()).isEqualTo(2);
}
@Test
@TestOfyAndSql
void testSetReservedList_doesntMutateExistingRegistry() {
ReservedList rl15 =
persistReservedList("tld-reserved15", "potato,FULLY_BLOCKED", "phone,FULLY_BLOCKED");
@@ -152,27 +148,27 @@ public class RegistryTest extends EntityTestCase {
assertThat(registry2.getReservedLists()).hasSize(2);
}
@Test
@TestOfyAndSql
void testGetReservedLists_doesntReturnNullWhenUninitialized() {
Registry registry = newRegistry("foo", "FOO");
assertThat(registry.getReservedLists()).isNotNull();
assertThat(registry.getReservedLists()).isEmpty();
}
@Test
@TestOfyAndSql
void testGetAll() {
createTld("foo");
assertThat(Registry.getAll(ImmutableSet.of("foo", "tld")))
.containsExactlyElementsIn(
ofy()
.load()
.keys(
Key.create(getCrossTldKey(), Registry.class, "foo"),
Key.create(getCrossTldKey(), Registry.class, "tld"))
tm().transact(
() ->
tm().load(
ImmutableSet.of(
Registry.createVKey("foo"), Registry.createVKey("tld"))))
.values());
}
@Test
@TestOfyAndSql
void testSetReservedLists() {
ReservedList rl5 =
persistReservedList("tld-reserved5", "lol,FULLY_BLOCKED", "cat,FULLY_BLOCKED");
@@ -186,7 +182,7 @@ public class RegistryTest extends EntityTestCase {
assertThat(r.getReservedLists()).isEmpty();
}
@Test
@TestOfyAndSql
void testSetReservedListsByName() {
persistReservedList("tld-reserved24", "lol,FULLY_BLOCKED", "cat,FULLY_BLOCKED");
persistReservedList("tld-reserved25", "mit,FULLY_BLOCKED", "tim,FULLY_BLOCKED");
@@ -201,7 +197,7 @@ public class RegistryTest extends EntityTestCase {
assertThat(r.getReservedLists()).isEmpty();
}
@Test
@TestOfyAndSql
void testSetPremiumList() {
PremiumList pl2 = persistPremiumList("tld2", "lol,USD 50", "cat,USD 700");
Registry registry = Registry.get("tld").asBuilder().setPremiumList(pl2).build();
@@ -211,20 +207,20 @@ public class RegistryTest extends EntityTestCase {
assertThat(stored.getName()).isEqualTo("tld2");
}
@Test
@TestOfyAndSql
void testSettingServerStatusChangeBillingCost() {
Registry registry =
Registry.get("tld").asBuilder().setServerStatusChangeBillingCost(Money.of(USD, 42)).build();
assertThat(registry.getServerStatusChangeCost()).isEqualTo(Money.of(USD, 42));
}
@Test
@TestOfyAndSql
void testSettingLordnUsername() {
Registry registry = Registry.get("tld").asBuilder().setLordnUsername("username").build();
assertThat(registry.getLordnUsername()).isEqualTo("username");
}
@Test
@TestOfyAndSql
void testSettingDnsWriters() {
Registry registry = Registry.get("tld");
assertThat(registry.getDnsWriters()).containsExactly(VoidDnsWriter.NAME);
@@ -232,7 +228,7 @@ public class RegistryTest extends EntityTestCase {
assertThat(registry.getDnsWriters()).containsExactly("baz", "bang");
}
@Test
@TestOfyAndSql
void testPdtLooksLikeGa() {
Registry registry =
Registry.get("tld")
@@ -242,7 +238,7 @@ public class RegistryTest extends EntityTestCase {
assertThat(registry.getTldState(START_OF_TIME)).isEqualTo(GENERAL_AVAILABILITY);
}
@Test
@TestOfyAndSql
void testTldStateTransitionTimes() {
Registry registry =
Registry.get("tld")
@@ -277,7 +273,7 @@ public class RegistryTest extends EntityTestCase {
assertThat(registry.getTldState(END_OF_TIME)).isEqualTo(GENERAL_AVAILABILITY);
}
@Test
@TestOfyAndSql
void testQuietPeriodCanAppearMultipleTimesAnywhere() {
Registry.get("tld")
.asBuilder()
@@ -292,7 +288,7 @@ public class RegistryTest extends EntityTestCase {
.build();
}
@Test
@TestOfyAndSql
void testRenewBillingCostTransitionTimes() {
Registry registry =
Registry.get("tld")
@@ -331,7 +327,7 @@ public class RegistryTest extends EntityTestCase {
assertThat(registry.getStandardRenewCost(END_OF_TIME)).isEqualTo(Money.of(USD, 3));
}
@Test
@TestOfyAndSql
void testRenewBillingCostNoTransitions() {
Registry registry = Registry.get("tld");
// The default value of 11 is set in createTld().
@@ -344,21 +340,21 @@ public class RegistryTest extends EntityTestCase {
assertThat(registry.getStandardRenewCost(END_OF_TIME)).isEqualTo(Money.of(USD, 11));
}
@Test
@TestOfyAndSql
void testFailure_tldNeverSet() {
IllegalArgumentException thrown =
assertThrows(IllegalArgumentException.class, () -> new Registry.Builder().build());
assertThat(thrown).hasMessageThat().contains("No registry TLD specified");
}
@Test
@TestOfyAndSql
void testFailure_setTldStr_null() {
IllegalArgumentException thrown =
assertThrows(IllegalArgumentException.class, () -> new Registry.Builder().setTldStr(null));
assertThat(thrown).hasMessageThat().contains("TLD must not be null");
}
@Test
@TestOfyAndSql
void testFailure_setTldStr_invalidTld() {
IllegalArgumentException thrown =
assertThrows(
@@ -368,7 +364,7 @@ public class RegistryTest extends EntityTestCase {
.contains("Cannot create registry for TLD that is not a valid, canonical domain name");
}
@Test
@TestOfyAndSql
void testFailure_setTldStr_nonCanonicalTld() {
IllegalArgumentException thrown =
assertThrows(
@@ -378,7 +374,7 @@ public class RegistryTest extends EntityTestCase {
.contains("Cannot create registry for TLD that is not a valid, canonical domain name");
}
@Test
@TestOfyAndSql
void testFailure_tldStatesOutOfOrder() {
assertThrows(
IllegalArgumentException.class,
@@ -392,7 +388,7 @@ public class RegistryTest extends EntityTestCase {
.build());
}
@Test
@TestOfyAndSql
void testFailure_duplicateTldState() {
assertThrows(
IllegalArgumentException.class,
@@ -406,7 +402,7 @@ public class RegistryTest extends EntityTestCase {
.build());
}
@Test
@TestOfyAndSql
void testFailure_pricingEngineIsRequired() {
IllegalArgumentException thrown =
assertThrows(
@@ -417,7 +413,7 @@ public class RegistryTest extends EntityTestCase {
.contains("All registries must have a configured pricing engine");
}
@Test
@TestOfyAndSql
void testFailure_negativeRenewBillingCostTransitionValue() {
IllegalArgumentException thrown =
assertThrows(
@@ -430,7 +426,7 @@ public class RegistryTest extends EntityTestCase {
assertThat(thrown).hasMessageThat().contains("billing cost cannot be negative");
}
@Test
@TestOfyAndSql
void testFailure_negativeCreateBillingCost() {
IllegalArgumentException thrown =
assertThrows(
@@ -439,7 +435,7 @@ public class RegistryTest extends EntityTestCase {
assertThat(thrown).hasMessageThat().contains("createBillingCost cannot be negative");
}
@Test
@TestOfyAndSql
void testFailure_negativeRestoreBillingCost() {
IllegalArgumentException thrown =
assertThrows(
@@ -448,7 +444,7 @@ public class RegistryTest extends EntityTestCase {
assertThat(thrown).hasMessageThat().contains("restoreBillingCost cannot be negative");
}
@Test
@TestOfyAndSql
void testFailure_nonPositiveNumDnsPublishLocks() {
IllegalArgumentException thrown =
assertThrows(
@@ -468,7 +464,7 @@ public class RegistryTest extends EntityTestCase {
"numDnsPublishLocks must be positive when set explicitly (use 1 for TLD-wide locks)");
}
@Test
@TestOfyAndSql
void testFailure_negativeServerStatusChangeBillingCost() {
IllegalArgumentException thrown =
assertThrows(
@@ -480,7 +476,7 @@ public class RegistryTest extends EntityTestCase {
assertThat(thrown).hasMessageThat().contains("billing cost cannot be negative");
}
@Test
@TestOfyAndSql
void testFailure_renewBillingCostTransitionValue_wrongCurrency() {
IllegalArgumentException thrown =
assertThrows(
@@ -494,7 +490,7 @@ public class RegistryTest extends EntityTestCase {
assertThat(thrown).hasMessageThat().contains("cost must be in the registry's currency");
}
@Test
@TestOfyAndSql
void testFailure_createBillingCost_wrongCurrency() {
IllegalArgumentException thrown =
assertThrows(
@@ -503,7 +499,7 @@ public class RegistryTest extends EntityTestCase {
assertThat(thrown).hasMessageThat().contains("cost must be in the registry's currency");
}
@Test
@TestOfyAndSql
void testFailure_restoreBillingCost_wrongCurrency() {
IllegalArgumentException thrown =
assertThrows(
@@ -512,7 +508,7 @@ public class RegistryTest extends EntityTestCase {
assertThat(thrown).hasMessageThat().contains("cost must be in the registry's currency");
}
@Test
@TestOfyAndSql
void testFailure_serverStatusChangeBillingCost_wrongCurrency() {
IllegalArgumentException thrown =
assertThrows(
@@ -525,13 +521,13 @@ public class RegistryTest extends EntityTestCase {
assertThat(thrown).hasMessageThat().contains("cost must be in the registry's currency");
}
@Test
@TestOfyAndSql
void testEapFee_undefined() {
assertThat(Registry.get("tld").getEapFeeFor(fakeClock.nowUtc()).getCost())
.isEqualTo(BigDecimal.ZERO.setScale(2, ROUND_UNNECESSARY));
}
@Test
@TestOfyAndSql
void testEapFee_specified() {
DateTime a = fakeClock.nowUtc().minusDays(1);
DateTime b = fakeClock.nowUtc().plusDays(1);
@@ -553,7 +549,7 @@ public class RegistryTest extends EntityTestCase {
.isEqualTo(new BigDecimal("50.00"));
}
@Test
@TestOfyAndSql
void testFailure_eapFee_wrongCurrency() {
IllegalArgumentException thrown =
assertThrows(
@@ -566,7 +562,7 @@ public class RegistryTest extends EntityTestCase {
assertThat(thrown).hasMessageThat().contains("All EAP fees must be in the registry's currency");
}
@Test
@TestOfyAndSql
void testFailure_roidSuffixTooLong() {
IllegalArgumentException e =
assertThrows(
@@ -575,14 +571,14 @@ public class RegistryTest extends EntityTestCase {
assertThat(e).hasMessageThat().isEqualTo("ROID suffix must be in format ^[A-Z0-9_]{1,8}$");
}
@Test
@TestOfyAndSql
void testFailure_roidSuffixNotUppercased() {
assertThrows(
IllegalArgumentException.class,
() -> Registry.get("tld").asBuilder().setRoidSuffix("abcd"));
}
@Test
@TestOfyAndSql
void testFailure_roidSuffixContainsInvalidCharacters() {
assertThrows(
IllegalArgumentException.class,

View File

@@ -17,23 +17,42 @@ package google.registry.model.reporting;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ImmutableObjectSubject.immutableObjectCorrespondence;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.testing.DatastoreHelper.createTlds;
import static google.registry.testing.DatastoreHelper.newDomainBase;
import static google.registry.testing.DatastoreHelper.persistActiveContact;
import static google.registry.testing.DatastoreHelper.persistResource;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import google.registry.model.EntityTestCase;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DomainBase;
import google.registry.model.reporting.Spec11ThreatMatch.ThreatType;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestSqlOnly;
import org.joda.time.LocalDate;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link Spec11ThreatMatchDao}. */
@DualDatabaseTest
public class Spec11ThreatMatchDaoTest extends EntityTestCase {
private static final LocalDate TODAY = new LocalDate(2020, 8, 4);
private static final LocalDate YESTERDAY = new LocalDate(2020, 8, 3);
private DomainBase todayComDomain;
private DomainBase todayOrgDomain;
private DomainBase yesterdayComDomain;
private DomainBase yesterdayOrgDomain;
@BeforeEach
void setUp() {
createTlds("com", "org");
ContactResource contact = persistActiveContact("jd1234");
todayComDomain = persistResource(newDomainBase("today.com", contact));
todayOrgDomain = persistResource(newDomainBase("today.org", contact));
yesterdayComDomain = persistResource(newDomainBase("yesterday.com", contact));
yesterdayOrgDomain = persistResource(newDomainBase("yesterday.org", contact));
jpaTm()
.transact(
() -> {
@@ -42,7 +61,7 @@ public class Spec11ThreatMatchDaoTest extends EntityTestCase {
});
}
@Test
@TestSqlOnly
void testDeleteEntriesByDate() {
// Verify that all entries with the date TODAY were removed
jpaTm()
@@ -66,7 +85,7 @@ public class Spec11ThreatMatchDaoTest extends EntityTestCase {
});
}
@Test
@TestSqlOnly
void testLoadEntriesByDate() {
jpaTm()
.transact(
@@ -82,23 +101,25 @@ public class Spec11ThreatMatchDaoTest extends EntityTestCase {
private ImmutableList<Spec11ThreatMatch> getThreatMatchesYesterday() {
return ImmutableList.of(
createThreatMatch("yesterday.com", YESTERDAY),
createThreatMatch("yesterday.org", YESTERDAY));
createThreatMatch("yesterday.com", yesterdayComDomain.getRepoId(), YESTERDAY),
createThreatMatch("yesterday.org", yesterdayOrgDomain.getRepoId(), YESTERDAY));
}
private ImmutableList<Spec11ThreatMatch> getThreatMatchesToday() {
return ImmutableList.of(
createThreatMatch("today.com", TODAY), createThreatMatch("today.org", TODAY));
createThreatMatch("today.com", todayComDomain.getRepoId(), TODAY),
createThreatMatch("today.org", todayOrgDomain.getRepoId(), TODAY));
}
private Spec11ThreatMatch createThreatMatch(String domainName, LocalDate date) {
private Spec11ThreatMatch createThreatMatch(
String domainName, String domainRepoId, LocalDate date) {
return new Spec11ThreatMatch()
.asBuilder()
.setThreatTypes(ImmutableSet.of(ThreatType.MALWARE))
.setCheckDate(date)
.setDomainName(domainName)
.setRegistrarId("Example Registrar")
.setDomainRepoId("1-COM")
.setRegistrarId("TheRegistrar")
.setDomainRepoId(domainRepoId)
.build();
}
}

View File

@@ -30,13 +30,16 @@ import google.registry.model.domain.DomainBase;
import google.registry.model.host.HostResource;
import google.registry.model.transfer.ContactTransferData;
import google.registry.persistence.VKey;
import google.registry.testing.DualDatabaseTest;
import google.registry.testing.TestOfyAndSql;
import google.registry.testing.TestSqlOnly;
import org.joda.time.LocalDate;
import org.joda.time.format.ISODateTimeFormat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link Spec11ThreatMatch}. */
@DualDatabaseTest
public class Spec11ThreatMatchTest extends EntityTestCase {
private static final String REGISTRAR_ID = "registrar";
@@ -100,8 +103,9 @@ public class Spec11ThreatMatchTest extends EntityTestCase {
.build();
}
@Test
@TestSqlOnly
void testPersistence() {
createTld("tld");
saveRegistrar(REGISTRAR_ID);
jpaTm()
@@ -121,7 +125,7 @@ public class Spec11ThreatMatchTest extends EntityTestCase {
assertThat(threat).isEqualTo(persistedThreat);
}
@Test
@TestSqlOnly
@Disabled("We can't rely on foreign keys until we've migrated to SQL")
void testThreatForeignKeyConstraints() {
assertThrowForeignKeyViolation(
@@ -152,7 +156,7 @@ public class Spec11ThreatMatchTest extends EntityTestCase {
});
}
@Test
@TestOfyAndSql
void testFailure_threatsWithInvalidFields() {
assertThrows(
IllegalArgumentException.class, () -> threat.asBuilder().setRegistrarId(null).build());

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