1
0
mirror of https://github.com/google/nomulus synced 2026-07-11 02:22:30 +00:00

Compare commits

...

24 Commits

Author SHA1 Message Date
Shicong Huang b9c40648d0 Add dual write for Registrar (#474)
* Add dual write for Registrar

* Use @AlsoLoad to set street field for Cloud SQL

* Change email body to use the new streetLine field

* Refactored the logic to handle street fields

* Simplify postLoad function

* Address comments

* Add a TODO to remove street

* Add test for onLoad and postLoad

* Rebase on master
2020-02-13 16:00:21 -05:00
sarahcaseybot 22a879e655 Add the schema and DAO for Locks in CloudSQL (#462)
* Add the schema and DAO for Locks in CloudSQL

* Addresses some comments

* Change number on flyway file

* Small changes

* More small changes

* Use checkArgumentNotNull instead of checkNotNull

* Address comments

* fix javadocs

* update persistence
2020-02-13 10:22:10 -05:00
Weimin Yu 62433c2238 Use JSON API for Maven Repo on GCS (#483)
* Use JSON API for Maven Repo on GCS

The url pattern https://storage.googleapis.com/{Bucket}/{Path}
uses the legacy XML API, which seems to be less robust than
the JSON API. We have observed connection resets after a few
thousand-file download bursts over 30 minutes.

This PR changes all urls to registry's Maven repo on GCS to
gcs://{Bucket}/{Path}. Gradle uses the JSON API for such urls.

TESTED=In Cloud Build with local change
2020-02-12 14:03:50 -05:00
Shicong Huang 90945bcc30 Add a test to verify persistence.xml (#482) 2020-02-12 12:39:03 -05:00
Weimin Yu f134c4bf37 Use dependency cache in all Gradle tasks in GCB (#481)
* Use dependency cache in all Gradle tasks in GCB

Make the initial test and the final publishing steps use the shared
dependency cache.

Also make the initial test use the registry's own maven repo instead
of Maven Central.
2020-02-11 14:50:22 -05:00
gbrodman 44921c29d6 Clean up RegistryLockPostAction (#470)
* Clean up RegistryLockPostAction

* pocId -> userEmail when appropriate

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

* Remove pocId
2020-02-11 08:43:44 -07:00
Weimin Yu ce80278ab7 Make Gradle dependency cache shareable in GCB (#479)
* Make Gradle dependency cache shareable in GCB

Make Gradle put its caches in the source tree so that
they can be preserved across steps. When left at their
default location, caches are lost after each step.
2020-02-10 11:20:11 -05:00
Shicong Huang 594ce30122 Auto-apply JPA converters for collection type (#469)
* Auto-apply JPA converters for collection type

* Extract common logic to a base class

* Remove extra lines

* Rebase on master
2020-02-10 10:33:43 -05:00
Weimin Yu 736f788eea Use CA cert in tests (#475)
* Use CA cert in tests

Our tests were using a self-signed cert created by a netty utility.
That cert is not CA, and cannot be used with newer JDK versions
(starting from u242 with Ubuntu openjdk).

Now we generate a unsafe cert for tests. The new cert,
SelfSignedCaCertificate, needs to be made test-only.
To do so we need to refactor both prober and proxy.
2020-02-07 10:23:24 -05:00
Shicong Huang d6f49f5c08 Add JPA annotations to RegistrarContact (#432) 2020-02-06 16:58:15 -05:00
Ben McIlwain 8b9139bc4c Always attempt to save the claims list to Cloud SQL (#472)
* Always attempt to save the claims list to Cloud SQL
2020-02-06 12:21:34 -05:00
Ben McIlwain b148102716 Always save reserved lists to Cloud SQL (#471)
* Always save reserved lists to Cloud SQL

Otherwise it's too easy to forget to run this additional option, and then things will
fall out of sync and start throwing errors.
2020-02-06 12:21:26 -05:00
Shicong Huang 9ea06ebf35 Use JPA @Enumerated to convert enum to string (#466) 2020-02-04 10:50:03 -05:00
gbrodman c0afb9aeee Use randomly-generated base-58 strings for RegistryLock verification codes (#464)
* Use randomly-generated strings for RegistryLock verification codes

We were using UUIDs before which are also fine, but unnecessarily long.
The RegistryLock class itself does not enforce any particular format for
the lock verification codes.
2020-02-03 13:50:54 -05:00
Michael Muller 76d8afe856 Implement dump_golden_schema command in devtool (#467)
* Implement dump_golden_schema command in devtool

Add a dump_golden_schema command so that we can generate the golden schema
in-place without having to do the test -> fail -> copy -> test dance.

Refactor the SQL container functionality from GenerateSqlCommand.  There is
some duplication of code between the dump command and SchemaTest which should
be dealt with in a subsequent PR.

* Reformatted and changes in response to review

* Fix getDockerTag() usage

* Fix "leaked resource"
2020-02-03 13:25:27 -05:00
gbrodman 36787c37fb Add a RegistryLockPostAction and tests (#459)
* Add a RegistryLockPostAction and tests

* Response to CR and more robust email content test

* Fix typo

* Run save + email in a JPA transaction

* Use action in subject
2020-02-03 11:34:01 -05:00
Weimin Yu 36e46097e3 Allow non-CA cert as trust anchor in tests (#468)
* Allow non-CA cert as trust anchor in tests

Stay with the legacy behavior to unblock release build.
We will update the test cert generation code later.

TESTED=reproduced and fixed test failures in builder container
2020-02-03 11:02:52 -05:00
Weimin Yu ce2f98f680 Work around Spinnaker issue wrt variables (#465)
* Work around Spinnaker issue wrt variables

Cloud Build variable reference need to stay from the  ${var} pattern
to prevent Spinnaker from trying to resolve it. In all files that
are used by Spinnaker, we change variable reference to the $var form.

We made the minimum amount of change possible, and will review this
issue after the permanent solution is available.
2020-01-30 13:28:36 -05:00
gbrodman daaf231d39 Add RegistryLockVerifyAction (#461)
* Add RegistryLockVerifyAction

The action takes two parameters
- isLock is a boolean, determining whether we're trying to lock or
unlock a domain
- lockVerificationCode is the UUID by which we'll look up the lock
object in question.

The lock in question must not be expired and must be in a valid lockable
/ unlockable state

* Some responses to CR

* Add slash and move test method

* Add more data and tests

* Fix screenshot
2020-01-29 16:36:39 -05:00
Shicong Huang 955c3d9aeb Fix money conversion for JPY in PremiumListDao (#463) 2020-01-29 13:10:58 -05:00
Shicong Huang d03cea2443 Add JPA annotations to class Registrar (#430)
* Add JPA annotations to class Registrar

* Use array for Java list

* Exclude parent field

* Use 3 columns for address and use text for enum

* Use EnumParameter and 3 properties in Address

* Rename columns and rebase on HEAD
2020-01-29 11:06:23 -05:00
Michael Muller e386bf5bd8 Get persistence of DomainBase actually working (#438)
* Get persistence of DomainBase actually working

Fix all of the existing problems with DomainBase persistence:
- Remove "final" keywords on getters that cause errors during startup.
- Remove Transient from creationTime (since there's a converter for
  CreateAutoTimestamp)
- Fix DesignatedContext persistence so that it only creates a single table.
  This is a lot more efficient given that these are many-to-one with their
  domains.
- Add a flyway script, update the golden schema.
- Create a unit test, add it to the integration test suite.

* Changes request in review

* Regenerated generated schema file.

* Changes for review

* Persist status value enum set

* Changes in response to review

* Changes requested in review

* Fixes for #456

* Rename Domain "status" column to "statuses"
2020-01-28 11:32:26 -05:00
sarahcaseybot 1912453f4e Fix IcannReportingUploadAction to use correct year in January (#452)
* Fix IcannReportingUploadAction to use correct year in January

* small fixes
2020-01-27 17:38:59 -05:00
Shicong Huang 43682aa7f4 Enable Cloud SQL for nomulus command implementing CommandWithRemoteApi (#460) 2020-01-27 10:28:38 -05:00
161 changed files with 4979 additions and 1359 deletions
+1
View File
@@ -97,6 +97,7 @@ nomulus.iws
!/gradle/wrapper/**/*.jar
.gradle/
**/build
cloudbuild-caches/
node_modules/**
/repos/
+1 -1
View File
@@ -22,7 +22,7 @@ import sys
import re
# We should never analyze any generated files
UNIVERSALLY_SKIPPED_PATTERNS = {"/build/", "/out/"}
UNIVERSALLY_SKIPPED_PATTERNS = {"/build/", "cloudbuild-caches", "/out/"}
# We can't rely on CI to have the Enum package installed so we do this instead.
FORBIDDEN = 1
REQUIRED = 2
+11 -2
View File
@@ -52,9 +52,11 @@ def outcastTestPatterns = [
def dockerIncompatibleTestPatterns = [
// The webdriver tests start headless Chrome in a Docker container,
// resulting in Docker-in-Docker complications. Likewise,
// GenerateSqlSchemaCommandTest launches postgresql in a docker container.
// GenerateSqlSchemaCommandTest and DumpGoldenSchemaCommandTest launch
// postgresql in a docker container.
"google/registry/webdriver/*",
"google/registry/tools/GenerateSqlSchemaCommandTest.*",
"google/registry/tools/DumpGoldenSchemaCommandTest.*",
// PathParameterTest includes tests which validate that file permissions are
// respected. However when running in Docker the user is root by default, so
// every file is read/write-able. There is no way to exclude specific test
@@ -93,7 +95,11 @@ sourceSets {
nonprod {
java {
compileClasspath += main.output
runtimeClasspath += main.output
// Add the DB runtime classpath to nonprod so we can load the flyway
// scripts.
runtimeClasspath += main.output +
rootProject.project(":db").sourceSets.main.runtimeClasspath
}
}
test {
@@ -321,6 +327,9 @@ dependencies {
// Tool dependencies. used for doc generation.
compile files("${System.properties['java.home']}/../lib/tools.jar")
// Flyway classes needed to generate the golden file.
compile deps['org.flywaydb:flyway-core']
closureCompiler deps['com.google.javascript:closure-compiler']
}
+15 -14
View File
@@ -10,11 +10,11 @@ com.fasterxml.jackson.core:jackson-annotations:2.9.10
com.fasterxml.jackson.core:jackson-core:2.9.10
com.fasterxml.jackson.core:jackson-databind:2.9.10
com.fasterxml:classmate:1.3.4
com.google.api-client:google-api-client-appengine:1.29.0
com.google.api-client:google-api-client-appengine:1.30.7
com.google.api-client:google-api-client-jackson2:1.27.0
com.google.api-client:google-api-client-java6:1.27.0
com.google.api-client:google-api-client-servlet:1.29.0
com.google.api-client:google-api-client:1.29.2
com.google.api-client:google-api-client-servlet:1.30.7
com.google.api-client:google-api-client:1.30.7
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.44.0
com.google.api.grpc:grpc-google-cloud-bigtable-admin-v2:0.38.0
com.google.api.grpc:grpc-google-cloud-bigtable-v2:0.38.0
@@ -84,22 +84,22 @@ com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.8.2
com.google.http-client:google-http-client-appengine:1.29.2
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client-appengine:1.34.0
com.google.http-client:google-http-client-jackson2:1.32.1
com.google.http-client:google-http-client-jackson:1.20.0
com.google.http-client:google-http-client-protobuf:1.20.0
com.google.http-client:google-http-client:1.30.1
com.google.http-client:google-http-client:1.34.0
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
com.google.jsinterop:jsinterop-annotations:1.0.2
com.google.monitoring-client:metrics:1.0.7
com.google.monitoring-client:stackdriver:1.0.7
com.google.oauth-client:google-oauth-client-appengine:1.29.0
com.google.oauth-client:google-oauth-client-appengine:1.30.5
com.google.oauth-client:google-oauth-client-java6:1.28.0
com.google.oauth-client:google-oauth-client-jetty:1.28.0
com.google.oauth-client:google-oauth-client-servlet:1.29.0
com.google.oauth-client:google-oauth-client:1.29.2
com.google.oauth-client:google-oauth-client-servlet:1.30.5
com.google.oauth-client:google-oauth-client:1.30.5
com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-5
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
@@ -124,7 +124,7 @@ io.dropwizard.metrics:metrics-core:3.1.2
io.grpc:grpc-all:1.17.1
io.grpc:grpc-alts:1.17.1
io.grpc:grpc-auth:1.17.1
io.grpc:grpc-context:1.19.0
io.grpc:grpc-context:1.22.1
io.grpc:grpc-core:1.17.1
io.grpc:grpc-grpclb:1.17.1
io.grpc:grpc-netty-shaded:1.17.1
@@ -146,10 +146,10 @@ io.netty:netty-handler:4.1.30.Final
io.netty:netty-resolver:4.1.30.Final
io.netty:netty-tcnative-boringssl-static:2.0.17.Final
io.netty:netty-transport:4.1.30.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-api:0.24.0
io.opencensus:opencensus-contrib-grpc-metrics:0.17.0
io.opencensus:opencensus-contrib-grpc-util:0.17.0
io.opencensus:opencensus-contrib-http-util:0.21.0
io.opencensus:opencensus-contrib-http-util:0.24.0
it.unimi.dsi:fastutil:6.5.16
javax.activation:activation:1.1
javax.activation:javax.activation-api:1.2.0
@@ -183,8 +183,8 @@ org.apache.beam:beam-vendor-grpc-1_21_0:0.1
org.apache.beam:beam-vendor-guava-26_0-jre:0.1
org.apache.commons:commons-compress:1.19
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.apache.httpcomponents:httpclient:4.5.10
org.apache.httpcomponents:httpcore:4.4.12
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-qual:2.8.1
@@ -193,6 +193,7 @@ org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
org.dom4j:dom4j:2.1.1
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.hamcrest:hamcrest-core:1.3
@@ -10,11 +10,11 @@ com.fasterxml.jackson.core:jackson-annotations:2.9.10
com.fasterxml.jackson.core:jackson-core:2.9.10
com.fasterxml.jackson.core:jackson-databind:2.9.10
com.fasterxml:classmate:1.3.4
com.google.api-client:google-api-client-appengine:1.29.0
com.google.api-client:google-api-client-appengine:1.30.7
com.google.api-client:google-api-client-jackson2:1.27.0
com.google.api-client:google-api-client-java6:1.27.0
com.google.api-client:google-api-client-servlet:1.29.0
com.google.api-client:google-api-client:1.29.2
com.google.api-client:google-api-client-servlet:1.30.7
com.google.api-client:google-api-client:1.30.7
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.44.0
com.google.api.grpc:grpc-google-cloud-bigtable-admin-v2:0.38.0
com.google.api.grpc:grpc-google-cloud-bigtable-v2:0.38.0
@@ -83,22 +83,22 @@ com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.8.2
com.google.http-client:google-http-client-appengine:1.29.2
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client-appengine:1.34.0
com.google.http-client:google-http-client-jackson2:1.32.1
com.google.http-client:google-http-client-jackson:1.20.0
com.google.http-client:google-http-client-protobuf:1.20.0
com.google.http-client:google-http-client:1.30.1
com.google.http-client:google-http-client:1.34.0
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
com.google.jsinterop:jsinterop-annotations:1.0.2
com.google.monitoring-client:metrics:1.0.7
com.google.monitoring-client:stackdriver:1.0.7
com.google.oauth-client:google-oauth-client-appengine:1.29.0
com.google.oauth-client:google-oauth-client-appengine:1.30.5
com.google.oauth-client:google-oauth-client-java6:1.28.0
com.google.oauth-client:google-oauth-client-jetty:1.28.0
com.google.oauth-client:google-oauth-client-servlet:1.29.0
com.google.oauth-client:google-oauth-client:1.29.2
com.google.oauth-client:google-oauth-client-servlet:1.30.5
com.google.oauth-client:google-oauth-client:1.30.5
com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-5
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
@@ -123,7 +123,7 @@ io.dropwizard.metrics:metrics-core:3.1.2
io.grpc:grpc-all:1.17.1
io.grpc:grpc-alts:1.17.1
io.grpc:grpc-auth:1.17.1
io.grpc:grpc-context:1.19.0
io.grpc:grpc-context:1.22.1
io.grpc:grpc-core:1.17.1
io.grpc:grpc-netty-shaded:1.17.1
io.grpc:grpc-netty:1.17.1
@@ -144,10 +144,10 @@ io.netty:netty-handler:4.1.30.Final
io.netty:netty-resolver:4.1.30.Final
io.netty:netty-tcnative-boringssl-static:2.0.17.Final
io.netty:netty-transport:4.1.30.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-api:0.24.0
io.opencensus:opencensus-contrib-grpc-metrics:0.17.0
io.opencensus:opencensus-contrib-grpc-util:0.17.0
io.opencensus:opencensus-contrib-http-util:0.21.0
io.opencensus:opencensus-contrib-http-util:0.24.0
it.unimi.dsi:fastutil:6.5.16
javax.activation:activation:1.1
javax.activation:javax.activation-api:1.2.0
@@ -180,8 +180,8 @@ org.apache.beam:beam-vendor-grpc-1_21_0:0.1
org.apache.beam:beam-vendor-guava-26_0-jre:0.1
org.apache.commons:commons-compress:1.19
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.apache.httpcomponents:httpclient:4.5.10
org.apache.httpcomponents:httpcore:4.4.12
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-qual:2.8.1
@@ -190,6 +190,7 @@ org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
org.dom4j:dom4j:2.1.1
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
+15 -14
View File
@@ -18,11 +18,11 @@ com.github.jnr:jnr-ffi:2.1.9
com.github.jnr:jnr-posix:3.0.47
com.github.jnr:jnr-unixsocket:0.21
com.github.jnr:jnr-x86asm:1.0.2
com.google.api-client:google-api-client-appengine:1.29.0
com.google.api-client:google-api-client-appengine:1.30.7
com.google.api-client:google-api-client-jackson2:1.27.0
com.google.api-client:google-api-client-java6:1.27.0
com.google.api-client:google-api-client-servlet:1.29.0
com.google.api-client:google-api-client:1.29.2
com.google.api-client:google-api-client-servlet:1.30.7
com.google.api-client:google-api-client:1.30.7
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.44.0
com.google.api.grpc:grpc-google-cloud-bigtable-admin-v2:0.38.0
com.google.api.grpc:grpc-google-cloud-bigtable-v2:0.38.0
@@ -95,22 +95,22 @@ com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.8.2
com.google.http-client:google-http-client-appengine:1.29.2
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client-appengine:1.34.0
com.google.http-client:google-http-client-jackson2:1.32.1
com.google.http-client:google-http-client-jackson:1.20.0
com.google.http-client:google-http-client-protobuf:1.20.0
com.google.http-client:google-http-client:1.30.1
com.google.http-client:google-http-client:1.34.0
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
com.google.jsinterop:jsinterop-annotations:1.0.2
com.google.monitoring-client:metrics:1.0.7
com.google.monitoring-client:stackdriver:1.0.7
com.google.oauth-client:google-oauth-client-appengine:1.29.0
com.google.oauth-client:google-oauth-client-appengine:1.30.5
com.google.oauth-client:google-oauth-client-java6:1.28.0
com.google.oauth-client:google-oauth-client-jetty:1.28.0
com.google.oauth-client:google-oauth-client-servlet:1.29.0
com.google.oauth-client:google-oauth-client:1.29.2
com.google.oauth-client:google-oauth-client-servlet:1.30.5
com.google.oauth-client:google-oauth-client:1.30.5
com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-5
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
@@ -135,7 +135,7 @@ io.dropwizard.metrics:metrics-core:3.1.2
io.grpc:grpc-all:1.17.1
io.grpc:grpc-alts:1.17.1
io.grpc:grpc-auth:1.17.1
io.grpc:grpc-context:1.19.0
io.grpc:grpc-context:1.22.1
io.grpc:grpc-core:1.17.1
io.grpc:grpc-grpclb:1.17.1
io.grpc:grpc-netty-shaded:1.17.1
@@ -157,10 +157,10 @@ io.netty:netty-handler:4.1.30.Final
io.netty:netty-resolver:4.1.30.Final
io.netty:netty-tcnative-boringssl-static:2.0.17.Final
io.netty:netty-transport:4.1.30.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-api:0.24.0
io.opencensus:opencensus-contrib-grpc-metrics:0.17.0
io.opencensus:opencensus-contrib-grpc-util:0.17.0
io.opencensus:opencensus-contrib-http-util:0.21.0
io.opencensus:opencensus-contrib-http-util:0.24.0
it.unimi.dsi:fastutil:6.5.16
javax.activation:activation:1.1
javax.activation:javax.activation-api:1.2.0
@@ -194,8 +194,8 @@ org.apache.beam:beam-vendor-grpc-1_21_0:0.1
org.apache.beam:beam-vendor-guava-26_0-jre:0.1
org.apache.commons:commons-compress:1.19
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.apache.httpcomponents:httpclient:4.5.10
org.apache.httpcomponents:httpcore:4.4.12
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-qual:2.8.1
@@ -204,6 +204,7 @@ org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
org.dom4j:dom4j:2.1.1
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.hamcrest:hamcrest-core:1.3
@@ -18,11 +18,11 @@ com.github.jnr:jnr-ffi:2.1.9
com.github.jnr:jnr-posix:3.0.47
com.github.jnr:jnr-unixsocket:0.21
com.github.jnr:jnr-x86asm:1.0.2
com.google.api-client:google-api-client-appengine:1.29.0
com.google.api-client:google-api-client-appengine:1.30.7
com.google.api-client:google-api-client-jackson2:1.27.0
com.google.api-client:google-api-client-java6:1.27.0
com.google.api-client:google-api-client-servlet:1.29.0
com.google.api-client:google-api-client:1.29.2
com.google.api-client:google-api-client-servlet:1.30.7
com.google.api-client:google-api-client:1.30.7
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.44.0
com.google.api.grpc:grpc-google-cloud-bigtable-admin-v2:0.38.0
com.google.api.grpc:grpc-google-cloud-bigtable-v2:0.38.0
@@ -95,22 +95,22 @@ com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.8.2
com.google.http-client:google-http-client-appengine:1.29.2
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client-appengine:1.34.0
com.google.http-client:google-http-client-jackson2:1.32.1
com.google.http-client:google-http-client-jackson:1.20.0
com.google.http-client:google-http-client-protobuf:1.20.0
com.google.http-client:google-http-client:1.30.1
com.google.http-client:google-http-client:1.34.0
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
com.google.jsinterop:jsinterop-annotations:1.0.2
com.google.monitoring-client:metrics:1.0.7
com.google.monitoring-client:stackdriver:1.0.7
com.google.oauth-client:google-oauth-client-appengine:1.29.0
com.google.oauth-client:google-oauth-client-appengine:1.30.5
com.google.oauth-client:google-oauth-client-java6:1.28.0
com.google.oauth-client:google-oauth-client-jetty:1.28.0
com.google.oauth-client:google-oauth-client-servlet:1.29.0
com.google.oauth-client:google-oauth-client:1.29.2
com.google.oauth-client:google-oauth-client-servlet:1.30.5
com.google.oauth-client:google-oauth-client:1.30.5
com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-5
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
@@ -135,7 +135,7 @@ io.dropwizard.metrics:metrics-core:3.1.2
io.grpc:grpc-all:1.17.1
io.grpc:grpc-alts:1.17.1
io.grpc:grpc-auth:1.17.1
io.grpc:grpc-context:1.19.0
io.grpc:grpc-context:1.22.1
io.grpc:grpc-core:1.17.1
io.grpc:grpc-grpclb:1.17.1
io.grpc:grpc-netty-shaded:1.17.1
@@ -157,10 +157,10 @@ io.netty:netty-handler:4.1.30.Final
io.netty:netty-resolver:4.1.30.Final
io.netty:netty-tcnative-boringssl-static:2.0.17.Final
io.netty:netty-transport:4.1.30.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-api:0.24.0
io.opencensus:opencensus-contrib-grpc-metrics:0.17.0
io.opencensus:opencensus-contrib-grpc-util:0.17.0
io.opencensus:opencensus-contrib-http-util:0.21.0
io.opencensus:opencensus-contrib-http-util:0.24.0
it.unimi.dsi:fastutil:6.5.16
javax.activation:activation:1.1
javax.activation:javax.activation-api:1.2.0
@@ -193,8 +193,8 @@ org.apache.beam:beam-vendor-grpc-1_21_0:0.1
org.apache.beam:beam-vendor-guava-26_0-jre:0.1
org.apache.commons:commons-compress:1.19
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.apache.httpcomponents:httpclient:4.5.10
org.apache.httpcomponents:httpcore:4.4.12
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-qual:2.8.1
@@ -203,6 +203,7 @@ org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
org.dom4j:dom4j:2.1.1
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
@@ -10,11 +10,11 @@ com.fasterxml.jackson.core:jackson-annotations:2.9.10
com.fasterxml.jackson.core:jackson-core:2.9.10
com.fasterxml.jackson.core:jackson-databind:2.9.10
com.fasterxml:classmate:1.3.4
com.google.api-client:google-api-client-appengine:1.29.0
com.google.api-client:google-api-client-appengine:1.30.7
com.google.api-client:google-api-client-jackson2:1.27.0
com.google.api-client:google-api-client-java6:1.27.0
com.google.api-client:google-api-client-servlet:1.29.0
com.google.api-client:google-api-client:1.29.2
com.google.api-client:google-api-client-servlet:1.30.7
com.google.api-client:google-api-client:1.30.7
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.44.0
com.google.api.grpc:grpc-google-cloud-bigtable-admin-v2:0.38.0
com.google.api.grpc:grpc-google-cloud-bigtable-v2:0.38.0
@@ -84,22 +84,22 @@ com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.8.2
com.google.http-client:google-http-client-appengine:1.29.2
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client-appengine:1.34.0
com.google.http-client:google-http-client-jackson2:1.32.1
com.google.http-client:google-http-client-jackson:1.20.0
com.google.http-client:google-http-client-protobuf:1.20.0
com.google.http-client:google-http-client:1.30.1
com.google.http-client:google-http-client:1.34.0
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
com.google.jsinterop:jsinterop-annotations:1.0.2
com.google.monitoring-client:metrics:1.0.7
com.google.monitoring-client:stackdriver:1.0.7
com.google.oauth-client:google-oauth-client-appengine:1.29.0
com.google.oauth-client:google-oauth-client-appengine:1.30.5
com.google.oauth-client:google-oauth-client-java6:1.28.0
com.google.oauth-client:google-oauth-client-jetty:1.28.0
com.google.oauth-client:google-oauth-client-servlet:1.29.0
com.google.oauth-client:google-oauth-client:1.29.2
com.google.oauth-client:google-oauth-client-servlet:1.30.5
com.google.oauth-client:google-oauth-client:1.30.5
com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-5
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
@@ -124,7 +124,7 @@ io.dropwizard.metrics:metrics-core:3.1.2
io.grpc:grpc-all:1.17.1
io.grpc:grpc-alts:1.17.1
io.grpc:grpc-auth:1.17.1
io.grpc:grpc-context:1.19.0
io.grpc:grpc-context:1.22.1
io.grpc:grpc-core:1.17.1
io.grpc:grpc-grpclb:1.17.1
io.grpc:grpc-netty-shaded:1.17.1
@@ -146,10 +146,10 @@ io.netty:netty-handler:4.1.30.Final
io.netty:netty-resolver:4.1.30.Final
io.netty:netty-tcnative-boringssl-static:2.0.17.Final
io.netty:netty-transport:4.1.30.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-api:0.24.0
io.opencensus:opencensus-contrib-grpc-metrics:0.17.0
io.opencensus:opencensus-contrib-grpc-util:0.17.0
io.opencensus:opencensus-contrib-http-util:0.21.0
io.opencensus:opencensus-contrib-http-util:0.24.0
it.unimi.dsi:fastutil:6.5.16
javax.activation:activation:1.1
javax.activation:javax.activation-api:1.2.0
@@ -183,8 +183,8 @@ org.apache.beam:beam-vendor-grpc-1_21_0:0.1
org.apache.beam:beam-vendor-guava-26_0-jre:0.1
org.apache.commons:commons-compress:1.19
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.apache.httpcomponents:httpclient:4.5.10
org.apache.httpcomponents:httpcore:4.4.12
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-qual:2.8.1
@@ -193,6 +193,7 @@ org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
org.dom4j:dom4j:2.1.1
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.hamcrest:hamcrest-core:1.3
@@ -10,11 +10,11 @@ com.fasterxml.jackson.core:jackson-annotations:2.9.10
com.fasterxml.jackson.core:jackson-core:2.9.10
com.fasterxml.jackson.core:jackson-databind:2.9.10
com.fasterxml:classmate:1.3.4
com.google.api-client:google-api-client-appengine:1.29.0
com.google.api-client:google-api-client-appengine:1.30.7
com.google.api-client:google-api-client-jackson2:1.27.0
com.google.api-client:google-api-client-java6:1.27.0
com.google.api-client:google-api-client-servlet:1.29.0
com.google.api-client:google-api-client:1.29.2
com.google.api-client:google-api-client-servlet:1.30.7
com.google.api-client:google-api-client:1.30.7
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.44.0
com.google.api.grpc:grpc-google-cloud-bigtable-admin-v2:0.38.0
com.google.api.grpc:grpc-google-cloud-bigtable-v2:0.38.0
@@ -83,22 +83,22 @@ com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.8.2
com.google.http-client:google-http-client-appengine:1.29.2
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client-appengine:1.34.0
com.google.http-client:google-http-client-jackson2:1.32.1
com.google.http-client:google-http-client-jackson:1.20.0
com.google.http-client:google-http-client-protobuf:1.20.0
com.google.http-client:google-http-client:1.30.1
com.google.http-client:google-http-client:1.34.0
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
com.google.jsinterop:jsinterop-annotations:1.0.2
com.google.monitoring-client:metrics:1.0.7
com.google.monitoring-client:stackdriver:1.0.7
com.google.oauth-client:google-oauth-client-appengine:1.29.0
com.google.oauth-client:google-oauth-client-appengine:1.30.5
com.google.oauth-client:google-oauth-client-java6:1.28.0
com.google.oauth-client:google-oauth-client-jetty:1.28.0
com.google.oauth-client:google-oauth-client-servlet:1.29.0
com.google.oauth-client:google-oauth-client:1.29.2
com.google.oauth-client:google-oauth-client-servlet:1.30.5
com.google.oauth-client:google-oauth-client:1.30.5
com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-5
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
@@ -123,7 +123,7 @@ io.dropwizard.metrics:metrics-core:3.1.2
io.grpc:grpc-all:1.17.1
io.grpc:grpc-alts:1.17.1
io.grpc:grpc-auth:1.17.1
io.grpc:grpc-context:1.19.0
io.grpc:grpc-context:1.22.1
io.grpc:grpc-core:1.17.1
io.grpc:grpc-netty-shaded:1.17.1
io.grpc:grpc-netty:1.17.1
@@ -144,10 +144,10 @@ io.netty:netty-handler:4.1.30.Final
io.netty:netty-resolver:4.1.30.Final
io.netty:netty-tcnative-boringssl-static:2.0.17.Final
io.netty:netty-transport:4.1.30.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-api:0.24.0
io.opencensus:opencensus-contrib-grpc-metrics:0.17.0
io.opencensus:opencensus-contrib-grpc-util:0.17.0
io.opencensus:opencensus-contrib-http-util:0.21.0
io.opencensus:opencensus-contrib-http-util:0.24.0
it.unimi.dsi:fastutil:6.5.16
javax.activation:activation:1.1
javax.activation:javax.activation-api:1.2.0
@@ -181,8 +181,8 @@ org.apache.beam:beam-vendor-grpc-1_21_0:0.1
org.apache.beam:beam-vendor-guava-26_0-jre:0.1
org.apache.commons:commons-compress:1.19
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.apache.httpcomponents:httpclient:4.5.10
org.apache.httpcomponents:httpcore:4.4.12
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-qual:2.8.1
@@ -191,6 +191,7 @@ org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
org.dom4j:dom4j:2.1.1
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.hamcrest:hamcrest-core:1.3
@@ -10,11 +10,11 @@ com.fasterxml.jackson.core:jackson-annotations:2.9.10
com.fasterxml.jackson.core:jackson-core:2.9.10
com.fasterxml.jackson.core:jackson-databind:2.9.10
com.fasterxml:classmate:1.3.4
com.google.api-client:google-api-client-appengine:1.29.0
com.google.api-client:google-api-client-appengine:1.30.7
com.google.api-client:google-api-client-jackson2:1.27.0
com.google.api-client:google-api-client-java6:1.27.0
com.google.api-client:google-api-client-servlet:1.29.0
com.google.api-client:google-api-client:1.29.2
com.google.api-client:google-api-client-servlet:1.30.7
com.google.api-client:google-api-client:1.30.7
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.44.0
com.google.api.grpc:grpc-google-cloud-bigtable-admin-v2:0.38.0
com.google.api.grpc:grpc-google-cloud-bigtable-v2:0.38.0
@@ -84,22 +84,22 @@ com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.8.2
com.google.http-client:google-http-client-appengine:1.29.2
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client-appengine:1.34.0
com.google.http-client:google-http-client-jackson2:1.32.1
com.google.http-client:google-http-client-jackson:1.20.0
com.google.http-client:google-http-client-protobuf:1.20.0
com.google.http-client:google-http-client:1.30.1
com.google.http-client:google-http-client:1.34.0
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
com.google.jsinterop:jsinterop-annotations:1.0.2
com.google.monitoring-client:metrics:1.0.7
com.google.monitoring-client:stackdriver:1.0.7
com.google.oauth-client:google-oauth-client-appengine:1.29.0
com.google.oauth-client:google-oauth-client-appengine:1.30.5
com.google.oauth-client:google-oauth-client-java6:1.28.0
com.google.oauth-client:google-oauth-client-jetty:1.28.0
com.google.oauth-client:google-oauth-client-servlet:1.29.0
com.google.oauth-client:google-oauth-client:1.29.2
com.google.oauth-client:google-oauth-client-servlet:1.30.5
com.google.oauth-client:google-oauth-client:1.30.5
com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-5
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
@@ -124,7 +124,7 @@ io.dropwizard.metrics:metrics-core:3.1.2
io.grpc:grpc-all:1.17.1
io.grpc:grpc-alts:1.17.1
io.grpc:grpc-auth:1.17.1
io.grpc:grpc-context:1.19.0
io.grpc:grpc-context:1.22.1
io.grpc:grpc-core:1.17.1
io.grpc:grpc-grpclb:1.17.1
io.grpc:grpc-netty-shaded:1.17.1
@@ -146,10 +146,10 @@ io.netty:netty-handler:4.1.30.Final
io.netty:netty-resolver:4.1.30.Final
io.netty:netty-tcnative-boringssl-static:2.0.17.Final
io.netty:netty-transport:4.1.30.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-api:0.24.0
io.opencensus:opencensus-contrib-grpc-metrics:0.17.0
io.opencensus:opencensus-contrib-grpc-util:0.17.0
io.opencensus:opencensus-contrib-http-util:0.21.0
io.opencensus:opencensus-contrib-http-util:0.24.0
it.unimi.dsi:fastutil:6.5.16
javax.activation:activation:1.1
javax.activation:javax.activation-api:1.2.0
@@ -183,8 +183,8 @@ org.apache.beam:beam-vendor-grpc-1_21_0:0.1
org.apache.beam:beam-vendor-guava-26_0-jre:0.1
org.apache.commons:commons-compress:1.19
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.apache.httpcomponents:httpclient:4.5.10
org.apache.httpcomponents:httpcore:4.4.12
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-qual:2.8.1
@@ -193,6 +193,7 @@ org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
org.dom4j:dom4j:2.1.1
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.hamcrest:hamcrest-core:1.3
@@ -10,11 +10,11 @@ com.fasterxml.jackson.core:jackson-annotations:2.9.10
com.fasterxml.jackson.core:jackson-core:2.9.10
com.fasterxml.jackson.core:jackson-databind:2.9.10
com.fasterxml:classmate:1.3.4
com.google.api-client:google-api-client-appengine:1.29.0
com.google.api-client:google-api-client-appengine:1.30.7
com.google.api-client:google-api-client-jackson2:1.27.0
com.google.api-client:google-api-client-java6:1.27.0
com.google.api-client:google-api-client-servlet:1.29.0
com.google.api-client:google-api-client:1.29.2
com.google.api-client:google-api-client-servlet:1.30.7
com.google.api-client:google-api-client:1.30.7
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.44.0
com.google.api.grpc:grpc-google-cloud-bigtable-admin-v2:0.38.0
com.google.api.grpc:grpc-google-cloud-bigtable-v2:0.38.0
@@ -84,22 +84,22 @@ com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.8.2
com.google.http-client:google-http-client-appengine:1.29.2
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client-appengine:1.34.0
com.google.http-client:google-http-client-jackson2:1.32.1
com.google.http-client:google-http-client-jackson:1.20.0
com.google.http-client:google-http-client-protobuf:1.20.0
com.google.http-client:google-http-client:1.30.1
com.google.http-client:google-http-client:1.34.0
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
com.google.jsinterop:jsinterop-annotations:1.0.2
com.google.monitoring-client:metrics:1.0.7
com.google.monitoring-client:stackdriver:1.0.7
com.google.oauth-client:google-oauth-client-appengine:1.29.0
com.google.oauth-client:google-oauth-client-appengine:1.30.5
com.google.oauth-client:google-oauth-client-java6:1.28.0
com.google.oauth-client:google-oauth-client-jetty:1.28.0
com.google.oauth-client:google-oauth-client-servlet:1.29.0
com.google.oauth-client:google-oauth-client:1.29.2
com.google.oauth-client:google-oauth-client-servlet:1.30.5
com.google.oauth-client:google-oauth-client:1.30.5
com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-5
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
@@ -124,7 +124,7 @@ io.dropwizard.metrics:metrics-core:3.1.2
io.grpc:grpc-all:1.17.1
io.grpc:grpc-alts:1.17.1
io.grpc:grpc-auth:1.17.1
io.grpc:grpc-context:1.19.0
io.grpc:grpc-context:1.22.1
io.grpc:grpc-core:1.17.1
io.grpc:grpc-grpclb:1.17.1
io.grpc:grpc-netty-shaded:1.17.1
@@ -146,10 +146,10 @@ io.netty:netty-handler:4.1.30.Final
io.netty:netty-resolver:4.1.30.Final
io.netty:netty-tcnative-boringssl-static:2.0.17.Final
io.netty:netty-transport:4.1.30.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-api:0.24.0
io.opencensus:opencensus-contrib-grpc-metrics:0.17.0
io.opencensus:opencensus-contrib-grpc-util:0.17.0
io.opencensus:opencensus-contrib-http-util:0.21.0
io.opencensus:opencensus-contrib-http-util:0.24.0
it.unimi.dsi:fastutil:6.5.16
javax.activation:activation:1.1
javax.activation:javax.activation-api:1.2.0
@@ -183,8 +183,8 @@ org.apache.beam:beam-vendor-grpc-1_21_0:0.1
org.apache.beam:beam-vendor-guava-26_0-jre:0.1
org.apache.commons:commons-compress:1.19
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.apache.httpcomponents:httpclient:4.5.10
org.apache.httpcomponents:httpcore:4.4.12
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-qual:2.8.1
@@ -193,6 +193,7 @@ org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
org.dom4j:dom4j:2.1.1
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.hamcrest:hamcrest-core:1.3
+15 -14
View File
@@ -10,11 +10,11 @@ com.fasterxml.jackson.core:jackson-annotations:2.9.10
com.fasterxml.jackson.core:jackson-core:2.9.10
com.fasterxml.jackson.core:jackson-databind:2.9.10
com.fasterxml:classmate:1.3.4
com.google.api-client:google-api-client-appengine:1.29.0
com.google.api-client:google-api-client-appengine:1.30.7
com.google.api-client:google-api-client-jackson2:1.27.0
com.google.api-client:google-api-client-java6:1.27.0
com.google.api-client:google-api-client-servlet:1.29.0
com.google.api-client:google-api-client:1.29.2
com.google.api-client:google-api-client-servlet:1.30.7
com.google.api-client:google-api-client:1.30.7
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.44.0
com.google.api.grpc:grpc-google-cloud-bigtable-admin-v2:0.38.0
com.google.api.grpc:grpc-google-cloud-bigtable-v2:0.38.0
@@ -84,22 +84,22 @@ com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.8.2
com.google.http-client:google-http-client-appengine:1.29.2
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client-appengine:1.34.0
com.google.http-client:google-http-client-jackson2:1.32.1
com.google.http-client:google-http-client-jackson:1.20.0
com.google.http-client:google-http-client-protobuf:1.20.0
com.google.http-client:google-http-client:1.30.1
com.google.http-client:google-http-client:1.34.0
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
com.google.jsinterop:jsinterop-annotations:1.0.2
com.google.monitoring-client:metrics:1.0.7
com.google.monitoring-client:stackdriver:1.0.7
com.google.oauth-client:google-oauth-client-appengine:1.29.0
com.google.oauth-client:google-oauth-client-appengine:1.30.5
com.google.oauth-client:google-oauth-client-java6:1.28.0
com.google.oauth-client:google-oauth-client-jetty:1.28.0
com.google.oauth-client:google-oauth-client-servlet:1.29.0
com.google.oauth-client:google-oauth-client:1.29.2
com.google.oauth-client:google-oauth-client-servlet:1.30.5
com.google.oauth-client:google-oauth-client:1.30.5
com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-5
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
@@ -124,7 +124,7 @@ io.dropwizard.metrics:metrics-core:3.1.2
io.grpc:grpc-all:1.17.1
io.grpc:grpc-alts:1.17.1
io.grpc:grpc-auth:1.17.1
io.grpc:grpc-context:1.19.0
io.grpc:grpc-context:1.22.1
io.grpc:grpc-core:1.17.1
io.grpc:grpc-grpclb:1.17.1
io.grpc:grpc-netty-shaded:1.17.1
@@ -146,10 +146,10 @@ io.netty:netty-handler:4.1.30.Final
io.netty:netty-resolver:4.1.30.Final
io.netty:netty-tcnative-boringssl-static:2.0.17.Final
io.netty:netty-transport:4.1.30.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-api:0.24.0
io.opencensus:opencensus-contrib-grpc-metrics:0.17.0
io.opencensus:opencensus-contrib-grpc-util:0.17.0
io.opencensus:opencensus-contrib-http-util:0.21.0
io.opencensus:opencensus-contrib-http-util:0.24.0
it.unimi.dsi:fastutil:6.5.16
javax.activation:activation:1.1
javax.activation:javax.activation-api:1.2.0
@@ -183,8 +183,8 @@ org.apache.beam:beam-vendor-grpc-1_21_0:0.1
org.apache.beam:beam-vendor-guava-26_0-jre:0.1
org.apache.commons:commons-compress:1.19
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.apache.httpcomponents:httpclient:4.5.10
org.apache.httpcomponents:httpcore:4.4.12
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-qual:2.8.1
@@ -193,6 +193,7 @@ org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
org.dom4j:dom4j:2.1.1
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.hamcrest:hamcrest-core:1.3
@@ -18,11 +18,11 @@ com.github.jnr:jnr-ffi:2.1.9
com.github.jnr:jnr-posix:3.0.47
com.github.jnr:jnr-unixsocket:0.21
com.github.jnr:jnr-x86asm:1.0.2
com.google.api-client:google-api-client-appengine:1.29.0
com.google.api-client:google-api-client-appengine:1.30.7
com.google.api-client:google-api-client-jackson2:1.27.0
com.google.api-client:google-api-client-java6:1.27.0
com.google.api-client:google-api-client-servlet:1.29.0
com.google.api-client:google-api-client:1.29.2
com.google.api-client:google-api-client-servlet:1.30.7
com.google.api-client:google-api-client:1.30.7
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.44.0
com.google.api.grpc:grpc-google-cloud-bigtable-admin-v2:0.38.0
com.google.api.grpc:grpc-google-cloud-bigtable-v2:0.38.0
@@ -95,22 +95,22 @@ com.google.guava:failureaccess:1.0.1
com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.8.2
com.google.http-client:google-http-client-appengine:1.29.2
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client-appengine:1.34.0
com.google.http-client:google-http-client-jackson2:1.32.1
com.google.http-client:google-http-client-jackson:1.20.0
com.google.http-client:google-http-client-protobuf:1.20.0
com.google.http-client:google-http-client:1.30.1
com.google.http-client:google-http-client:1.34.0
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
com.google.jsinterop:jsinterop-annotations:1.0.2
com.google.monitoring-client:metrics:1.0.7
com.google.monitoring-client:stackdriver:1.0.7
com.google.oauth-client:google-oauth-client-appengine:1.29.0
com.google.oauth-client:google-oauth-client-appengine:1.30.5
com.google.oauth-client:google-oauth-client-java6:1.28.0
com.google.oauth-client:google-oauth-client-jetty:1.28.0
com.google.oauth-client:google-oauth-client-servlet:1.29.0
com.google.oauth-client:google-oauth-client:1.29.2
com.google.oauth-client:google-oauth-client-servlet:1.30.5
com.google.oauth-client:google-oauth-client:1.30.5
com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-5
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
@@ -135,7 +135,7 @@ io.dropwizard.metrics:metrics-core:3.1.2
io.grpc:grpc-all:1.17.1
io.grpc:grpc-alts:1.17.1
io.grpc:grpc-auth:1.17.1
io.grpc:grpc-context:1.19.0
io.grpc:grpc-context:1.22.1
io.grpc:grpc-core:1.17.1
io.grpc:grpc-grpclb:1.17.1
io.grpc:grpc-netty-shaded:1.17.1
@@ -157,10 +157,10 @@ io.netty:netty-handler:4.1.30.Final
io.netty:netty-resolver:4.1.30.Final
io.netty:netty-tcnative-boringssl-static:2.0.17.Final
io.netty:netty-transport:4.1.30.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-api:0.24.0
io.opencensus:opencensus-contrib-grpc-metrics:0.17.0
io.opencensus:opencensus-contrib-grpc-util:0.17.0
io.opencensus:opencensus-contrib-http-util:0.21.0
io.opencensus:opencensus-contrib-http-util:0.24.0
it.unimi.dsi:fastutil:6.5.16
javax.activation:activation:1.1
javax.activation:javax.activation-api:1.2.0
@@ -193,8 +193,8 @@ org.apache.beam:beam-vendor-grpc-1_21_0:0.1
org.apache.beam:beam-vendor-guava-26_0-jre:0.1
org.apache.commons:commons-compress:1.19
org.apache.commons:commons-lang3:3.5
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.apache.httpcomponents:httpclient:4.5.10
org.apache.httpcomponents:httpcore:4.4.12
org.bouncycastle:bcpg-jdk15on:1.61
org.bouncycastle:bcprov-jdk15on:1.61
org.checkerframework:checker-qual:2.8.1
@@ -203,6 +203,7 @@ org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
org.dom4j:dom4j:2.1.1
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
@@ -10,11 +10,11 @@ com.fasterxml.jackson.core:jackson-annotations:2.9.10
com.fasterxml.jackson.core:jackson-core:2.9.10
com.fasterxml.jackson.core:jackson-databind:2.9.10
com.fasterxml:classmate:1.3.4
com.google.api-client:google-api-client-appengine:1.29.0
com.google.api-client:google-api-client-appengine:1.30.7
com.google.api-client:google-api-client-jackson2:1.27.0
com.google.api-client:google-api-client-java6:1.27.0
com.google.api-client:google-api-client-servlet:1.29.0
com.google.api-client:google-api-client:1.29.2
com.google.api-client:google-api-client-servlet:1.30.7
com.google.api-client:google-api-client:1.30.7
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.44.0
com.google.api.grpc:grpc-google-cloud-bigtable-admin-v2:0.38.0
com.google.api.grpc:grpc-google-cloud-bigtable-v2:0.38.0
@@ -86,11 +86,11 @@ com.google.guava:guava-testlib:28.1-jre
com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.8.2
com.google.http-client:google-http-client-appengine:1.29.2
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client-appengine:1.34.0
com.google.http-client:google-http-client-jackson2:1.32.1
com.google.http-client:google-http-client-jackson:1.20.0
com.google.http-client:google-http-client-protobuf:1.20.0
com.google.http-client:google-http-client:1.30.1
com.google.http-client:google-http-client:1.34.0
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
@@ -98,11 +98,11 @@ com.google.jsinterop:jsinterop-annotations:1.0.2
com.google.monitoring-client:contrib:1.0.7
com.google.monitoring-client:metrics:1.0.7
com.google.monitoring-client:stackdriver:1.0.7
com.google.oauth-client:google-oauth-client-appengine:1.29.0
com.google.oauth-client:google-oauth-client-appengine:1.30.5
com.google.oauth-client:google-oauth-client-java6:1.28.0
com.google.oauth-client:google-oauth-client-jetty:1.28.0
com.google.oauth-client:google-oauth-client-servlet:1.29.0
com.google.oauth-client:google-oauth-client:1.29.2
com.google.oauth-client:google-oauth-client-servlet:1.30.5
com.google.oauth-client:google-oauth-client:1.30.5
com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-5
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
@@ -133,7 +133,7 @@ io.github.classgraph:classgraph:4.8.52
io.grpc:grpc-all:1.17.1
io.grpc:grpc-alts:1.17.1
io.grpc:grpc-auth:1.17.1
io.grpc:grpc-context:1.19.0
io.grpc:grpc-context:1.22.1
io.grpc:grpc-core:1.17.1
io.grpc:grpc-grpclb:1.17.1
io.grpc:grpc-netty-shaded:1.17.1
@@ -155,10 +155,10 @@ io.netty:netty-handler:4.1.30.Final
io.netty:netty-resolver:4.1.30.Final
io.netty:netty-tcnative-boringssl-static:2.0.17.Final
io.netty:netty-transport:4.1.30.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-api:0.24.0
io.opencensus:opencensus-contrib-grpc-metrics:0.17.0
io.opencensus:opencensus-contrib-grpc-util:0.17.0
io.opencensus:opencensus-contrib-http-util:0.21.0
io.opencensus:opencensus-contrib-http-util:0.24.0
it.unimi.dsi:fastutil:6.5.16
javax.activation:activation:1.1
javax.activation:javax.activation-api:1.2.0
@@ -198,8 +198,8 @@ org.apache.commons:commons-lang3:3.8.1
org.apache.commons:commons-text:1.6
org.apache.ftpserver:ftplet-api:1.0.6
org.apache.ftpserver:ftpserver-core:1.0.6
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.apache.httpcomponents:httpclient:4.5.10
org.apache.httpcomponents:httpcore:4.4.12
org.apache.mina:mina-core:2.0.4
org.apache.sshd:sshd-core:2.0.0
org.apache.sshd:sshd-scp:2.0.0
@@ -215,6 +215,7 @@ org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
org.dom4j:dom4j:2.1.1
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.hamcrest:hamcrest-all:1.3
@@ -10,11 +10,11 @@ com.fasterxml.jackson.core:jackson-annotations:2.9.10
com.fasterxml.jackson.core:jackson-core:2.9.10
com.fasterxml.jackson.core:jackson-databind:2.9.10
com.fasterxml:classmate:1.3.4
com.google.api-client:google-api-client-appengine:1.29.0
com.google.api-client:google-api-client-appengine:1.30.7
com.google.api-client:google-api-client-jackson2:1.27.0
com.google.api-client:google-api-client-java6:1.27.0
com.google.api-client:google-api-client-servlet:1.29.0
com.google.api-client:google-api-client:1.29.2
com.google.api-client:google-api-client-servlet:1.30.7
com.google.api-client:google-api-client:1.30.7
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.44.0
com.google.api.grpc:grpc-google-cloud-bigtable-admin-v2:0.38.0
com.google.api.grpc:grpc-google-cloud-bigtable-v2:0.38.0
@@ -85,11 +85,11 @@ com.google.guava:guava-testlib:28.1-jre
com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.8.2
com.google.http-client:google-http-client-appengine:1.29.2
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client-appengine:1.34.0
com.google.http-client:google-http-client-jackson2:1.32.1
com.google.http-client:google-http-client-jackson:1.20.0
com.google.http-client:google-http-client-protobuf:1.20.0
com.google.http-client:google-http-client:1.30.1
com.google.http-client:google-http-client:1.34.0
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
@@ -97,11 +97,11 @@ com.google.jsinterop:jsinterop-annotations:1.0.2
com.google.monitoring-client:contrib:1.0.7
com.google.monitoring-client:metrics:1.0.7
com.google.monitoring-client:stackdriver:1.0.7
com.google.oauth-client:google-oauth-client-appengine:1.29.0
com.google.oauth-client:google-oauth-client-appengine:1.30.5
com.google.oauth-client:google-oauth-client-java6:1.28.0
com.google.oauth-client:google-oauth-client-jetty:1.28.0
com.google.oauth-client:google-oauth-client-servlet:1.29.0
com.google.oauth-client:google-oauth-client:1.29.2
com.google.oauth-client:google-oauth-client-servlet:1.30.5
com.google.oauth-client:google-oauth-client:1.30.5
com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-5
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
@@ -132,7 +132,7 @@ io.github.classgraph:classgraph:4.8.52
io.grpc:grpc-all:1.17.1
io.grpc:grpc-alts:1.17.1
io.grpc:grpc-auth:1.17.1
io.grpc:grpc-context:1.19.0
io.grpc:grpc-context:1.22.1
io.grpc:grpc-core:1.17.1
io.grpc:grpc-netty-shaded:1.17.1
io.grpc:grpc-netty:1.17.1
@@ -153,10 +153,10 @@ io.netty:netty-handler:4.1.30.Final
io.netty:netty-resolver:4.1.30.Final
io.netty:netty-tcnative-boringssl-static:2.0.17.Final
io.netty:netty-transport:4.1.30.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-api:0.24.0
io.opencensus:opencensus-contrib-grpc-metrics:0.17.0
io.opencensus:opencensus-contrib-grpc-util:0.17.0
io.opencensus:opencensus-contrib-http-util:0.21.0
io.opencensus:opencensus-contrib-http-util:0.24.0
it.unimi.dsi:fastutil:6.5.16
javax.activation:activation:1.1
javax.activation:javax.activation-api:1.2.0
@@ -196,8 +196,8 @@ org.apache.commons:commons-lang3:3.8.1
org.apache.commons:commons-text:1.6
org.apache.ftpserver:ftplet-api:1.0.6
org.apache.ftpserver:ftpserver-core:1.0.6
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.apache.httpcomponents:httpclient:4.5.10
org.apache.httpcomponents:httpcore:4.4.12
org.apache.mina:mina-core:2.0.4
org.apache.sshd:sshd-core:2.0.0
org.apache.sshd:sshd-scp:2.0.0
@@ -213,6 +213,7 @@ org.codehaus.jackson:jackson-mapper-asl:1.9.13
org.codehaus.mojo:animal-sniffer-annotations:1.18
org.dom4j:dom4j:2.1.1
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.hamcrest:hamcrest-all:1.3
@@ -18,11 +18,11 @@ com.github.jnr:jnr-ffi:2.1.9
com.github.jnr:jnr-posix:3.0.47
com.github.jnr:jnr-unixsocket:0.21
com.github.jnr:jnr-x86asm:1.0.2
com.google.api-client:google-api-client-appengine:1.29.0
com.google.api-client:google-api-client-appengine:1.30.7
com.google.api-client:google-api-client-jackson2:1.27.0
com.google.api-client:google-api-client-java6:1.27.0
com.google.api-client:google-api-client-servlet:1.29.0
com.google.api-client:google-api-client:1.29.2
com.google.api-client:google-api-client-servlet:1.30.7
com.google.api-client:google-api-client:1.30.7
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.44.0
com.google.api.grpc:grpc-google-cloud-bigtable-admin-v2:0.38.0
com.google.api.grpc:grpc-google-cloud-bigtable-v2:0.38.0
@@ -97,11 +97,11 @@ com.google.guava:guava-testlib:28.1-jre
com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.8.2
com.google.http-client:google-http-client-appengine:1.29.2
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client-appengine:1.34.0
com.google.http-client:google-http-client-jackson2:1.32.1
com.google.http-client:google-http-client-jackson:1.20.0
com.google.http-client:google-http-client-protobuf:1.20.0
com.google.http-client:google-http-client:1.30.1
com.google.http-client:google-http-client:1.34.0
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
@@ -109,11 +109,11 @@ com.google.jsinterop:jsinterop-annotations:1.0.2
com.google.monitoring-client:contrib:1.0.7
com.google.monitoring-client:metrics:1.0.7
com.google.monitoring-client:stackdriver:1.0.7
com.google.oauth-client:google-oauth-client-appengine:1.29.0
com.google.oauth-client:google-oauth-client-appengine:1.30.5
com.google.oauth-client:google-oauth-client-java6:1.28.0
com.google.oauth-client:google-oauth-client-jetty:1.28.0
com.google.oauth-client:google-oauth-client-servlet:1.29.0
com.google.oauth-client:google-oauth-client:1.29.2
com.google.oauth-client:google-oauth-client-servlet:1.30.5
com.google.oauth-client:google-oauth-client:1.30.5
com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-5
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
@@ -145,7 +145,7 @@ io.github.java-diff-utils:java-diff-utils:4.0
io.grpc:grpc-all:1.17.1
io.grpc:grpc-alts:1.17.1
io.grpc:grpc-auth:1.17.1
io.grpc:grpc-context:1.19.0
io.grpc:grpc-context:1.22.1
io.grpc:grpc-core:1.17.1
io.grpc:grpc-grpclb:1.17.1
io.grpc:grpc-netty-shaded:1.17.1
@@ -167,10 +167,10 @@ io.netty:netty-handler:4.1.30.Final
io.netty:netty-resolver:4.1.30.Final
io.netty:netty-tcnative-boringssl-static:2.0.17.Final
io.netty:netty-transport:4.1.30.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-api:0.24.0
io.opencensus:opencensus-contrib-grpc-metrics:0.17.0
io.opencensus:opencensus-contrib-grpc-util:0.17.0
io.opencensus:opencensus-contrib-http-util:0.21.0
io.opencensus:opencensus-contrib-http-util:0.24.0
it.unimi.dsi:fastutil:6.5.16
javax.activation:activation:1.1
javax.activation:javax.activation-api:1.2.0
@@ -210,8 +210,8 @@ org.apache.commons:commons-lang3:3.8.1
org.apache.commons:commons-text:1.6
org.apache.ftpserver:ftplet-api:1.0.6
org.apache.ftpserver:ftpserver-core:1.0.6
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.apache.httpcomponents:httpclient:4.5.10
org.apache.httpcomponents:httpcore:4.4.12
org.apache.mina:mina-core:2.0.4
org.apache.sshd:sshd-core:2.0.0
org.apache.sshd:sshd-scp:2.0.0
@@ -18,11 +18,11 @@ com.github.jnr:jnr-ffi:2.1.9
com.github.jnr:jnr-posix:3.0.47
com.github.jnr:jnr-unixsocket:0.21
com.github.jnr:jnr-x86asm:1.0.2
com.google.api-client:google-api-client-appengine:1.29.0
com.google.api-client:google-api-client-appengine:1.30.7
com.google.api-client:google-api-client-jackson2:1.27.0
com.google.api-client:google-api-client-java6:1.27.0
com.google.api-client:google-api-client-servlet:1.29.0
com.google.api-client:google-api-client:1.29.2
com.google.api-client:google-api-client-servlet:1.30.7
com.google.api-client:google-api-client:1.30.7
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.44.0
com.google.api.grpc:grpc-google-cloud-bigtable-admin-v2:0.38.0
com.google.api.grpc:grpc-google-cloud-bigtable-v2:0.38.0
@@ -97,11 +97,11 @@ com.google.guava:guava-testlib:28.1-jre
com.google.guava:guava:28.1-jre
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
com.google.gwt:gwt-user:2.8.2
com.google.http-client:google-http-client-appengine:1.29.2
com.google.http-client:google-http-client-jackson2:1.30.1
com.google.http-client:google-http-client-appengine:1.34.0
com.google.http-client:google-http-client-jackson2:1.32.1
com.google.http-client:google-http-client-jackson:1.20.0
com.google.http-client:google-http-client-protobuf:1.20.0
com.google.http-client:google-http-client:1.30.1
com.google.http-client:google-http-client:1.34.0
com.google.inject.extensions:guice-multibindings:4.1.0
com.google.inject:guice:4.1.0
com.google.j2objc:j2objc-annotations:1.3
@@ -109,11 +109,11 @@ com.google.jsinterop:jsinterop-annotations:1.0.2
com.google.monitoring-client:contrib:1.0.7
com.google.monitoring-client:metrics:1.0.7
com.google.monitoring-client:stackdriver:1.0.7
com.google.oauth-client:google-oauth-client-appengine:1.29.0
com.google.oauth-client:google-oauth-client-appengine:1.30.5
com.google.oauth-client:google-oauth-client-java6:1.28.0
com.google.oauth-client:google-oauth-client-jetty:1.28.0
com.google.oauth-client:google-oauth-client-servlet:1.29.0
com.google.oauth-client:google-oauth-client:1.29.2
com.google.oauth-client:google-oauth-client-servlet:1.30.5
com.google.oauth-client:google-oauth-client:1.30.5
com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-5
com.google.protobuf:protobuf-java-util:3.6.1
com.google.protobuf:protobuf-java:3.6.1
@@ -145,7 +145,7 @@ io.github.java-diff-utils:java-diff-utils:4.0
io.grpc:grpc-all:1.17.1
io.grpc:grpc-alts:1.17.1
io.grpc:grpc-auth:1.17.1
io.grpc:grpc-context:1.19.0
io.grpc:grpc-context:1.22.1
io.grpc:grpc-core:1.17.1
io.grpc:grpc-grpclb:1.17.1
io.grpc:grpc-netty-shaded:1.17.1
@@ -167,10 +167,10 @@ io.netty:netty-handler:4.1.30.Final
io.netty:netty-resolver:4.1.30.Final
io.netty:netty-tcnative-boringssl-static:2.0.17.Final
io.netty:netty-transport:4.1.30.Final
io.opencensus:opencensus-api:0.21.0
io.opencensus:opencensus-api:0.24.0
io.opencensus:opencensus-contrib-grpc-metrics:0.17.0
io.opencensus:opencensus-contrib-grpc-util:0.17.0
io.opencensus:opencensus-contrib-http-util:0.21.0
io.opencensus:opencensus-contrib-http-util:0.24.0
it.unimi.dsi:fastutil:6.5.16
javax.activation:activation:1.1
javax.activation:javax.activation-api:1.2.0
@@ -210,8 +210,8 @@ org.apache.commons:commons-lang3:3.8.1
org.apache.commons:commons-text:1.6
org.apache.ftpserver:ftplet-api:1.0.6
org.apache.ftpserver:ftpserver-core:1.0.6
org.apache.httpcomponents:httpclient:4.5.8
org.apache.httpcomponents:httpcore:4.4.11
org.apache.httpcomponents:httpclient:4.5.10
org.apache.httpcomponents:httpcore:4.4.12
org.apache.mina:mina-core:2.0.4
org.apache.sshd:sshd-core:2.0.0
org.apache.sshd:sshd-scp:2.0.0
@@ -61,6 +61,16 @@
<url-pattern>/registry-lock-get</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>frontend-servlet</servlet-name>
<url-pattern>/registry-lock-post</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>frontend-servlet</servlet-name>
<url-pattern>/registry-lock-verify</url-pattern>
</servlet-mapping>
<!-- Security config -->
<security-constraint>
<web-resource-collection>
@@ -50,6 +50,7 @@ import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.persistence.Transient;
import org.joda.time.DateTime;
@@ -87,7 +88,7 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
// Map the method to XML, not the field, because if we map the field (with an adaptor class) it
// will never be omitted from the xml even if the timestamp inside creationTime is null and we
// return null from the adaptor. (Instead it gets written as an empty tag.)
@Index @Transient CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
@Index CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
/**
* The time when this resource was or will be deleted.
@@ -105,7 +106,6 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
@Index
DateTime deletionTime;
/**
* The time that this resource was last updated.
*
@@ -116,7 +116,9 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
DateTime lastEppUpdateTime;
/** Status values associated with this resource. */
@Transient Set<StatusValue> status;
@Column(name = "statuses")
// TODO(mmuller): rename to "statuses" once we're off datastore.
Set<StatusValue> status;
/**
* Sorted map of {@link DateTime} keys (modified time) to {@link CommitLogManifest} entries.
@@ -126,6 +128,7 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
*
* @see google.registry.model.translators.CommitLogRevisionsTranslatorFactory
*/
@Transient
ImmutableSortedMap<DateTime, Key<CommitLogManifest>> revisions = ImmutableSortedMap.of();
public final String getRepoId() {
@@ -136,15 +139,15 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
return creationTime.getTimestamp();
}
public final String getCreationClientId() {
public String getCreationClientId() {
return creationClientId;
}
public final DateTime getLastEppUpdateTime() {
public DateTime getLastEppUpdateTime() {
return lastEppUpdateTime;
}
public final String getLastEppUpdateClientId() {
public String getLastEppUpdateClientId() {
return lastEppUpdateClientId;
}
@@ -162,7 +165,7 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
return nullToEmptyImmutableCopy(status);
}
public final DateTime getDeletionTime() {
public DateTime getDeletionTime() {
return deletionTime;
}
@@ -162,7 +162,10 @@ public abstract class ImmutableObject implements Cloneable {
// values.
Map<String, Object> result = new LinkedHashMap<>();
for (Entry<Field, Object> entry : ModelUtils.getFieldValues(o).entrySet()) {
result.put(entry.getKey().getName(), toMapRecursive(entry.getValue()));
Field field = entry.getKey();
if (!field.isAnnotationPresent(IgnoredInDiffableMap.class)) {
result.put(field.getName(), toMapRecursive(entry.getValue()));
}
}
return result;
} else if (o instanceof Map) {
@@ -191,6 +194,12 @@ public abstract class ImmutableObject implements Cloneable {
}
}
/** Marker to indicate that this filed should be ignored by {@link #toDiffableFieldMap}. */
@Documented
@Retention(RUNTIME)
@Target(FIELD)
protected @interface IgnoredInDiffableMap {}
/** Returns a map of all object fields (including sensitive data) that's used to produce diffs. */
@SuppressWarnings("unchecked")
public Map<String, Object> toDiffableFieldMap() {
@@ -21,7 +21,7 @@ import com.googlecode.objectify.annotation.Embed;
import com.googlecode.objectify.annotation.Index;
import google.registry.model.ImmutableObject;
import google.registry.model.contact.ContactResource;
import javax.persistence.Id;
import javax.persistence.Embeddable;
import javax.xml.bind.annotation.XmlEnumValue;
/**
@@ -40,7 +40,7 @@ import javax.xml.bind.annotation.XmlEnumValue;
* - Contact and Client Identifiers</a>
*/
@Embed
@javax.persistence.Entity
@Embeddable
public class DesignatedContact extends ImmutableObject {
/**
@@ -67,7 +67,7 @@ public class DesignatedContact extends ImmutableObject {
Type type;
@Index @Id Key<ContactResource> contact;
@Index Key<ContactResource> contact;
public Type getType() {
return type;
@@ -75,6 +75,7 @@ import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Embedded;
import javax.persistence.Transient;
import org.joda.time.DateTime;
import org.joda.time.Interval;
@@ -89,8 +90,16 @@ import org.joda.time.Interval;
*/
@ReportedOn
@Entity
@javax.persistence.Entity
@javax.persistence.Table(name = "Domain")
@javax.persistence.Entity(name = "Domain")
@javax.persistence.Table(
name = "Domain",
indexes = {
@javax.persistence.Index(columnList = "creationTime"),
@javax.persistence.Index(columnList = "currentSponsorClientId"),
@javax.persistence.Index(columnList = "deletionTime"),
@javax.persistence.Index(columnList = "fullyQualifiedDomainName"),
@javax.persistence.Index(columnList = "tld")
})
@ExternalMessagingName("domain")
public class DomainBase extends EppResource
implements ForeignKeyedEppResource, ResourceWithTransferData {
@@ -105,7 +114,7 @@ public class DomainBase extends EppResource
StatusValue.INACTIVE,
StatusValue.PENDING_DELETE,
StatusValue.SERVER_HOLD);
/**
* Fully qualified domain name (puny-coded), which serves as the foreign key for this domain.
*
@@ -115,22 +124,21 @@ public class DomainBase extends EppResource
*
* @invariant fullyQualifiedDomainName == fullyQualifiedDomainName.toLowerCase(Locale.ENGLISH)
*/
@Index
String fullyQualifiedDomainName;
@Index String fullyQualifiedDomainName;
/** The top level domain this is under, dernormalized from {@link #fullyQualifiedDomainName}. */
@Index
String tld;
/** References to hosts that are the nameservers for the domain. */
@Index @ElementCollection Set<Key<HostResource>> nsHosts;
@Index @ElementCollection @Transient Set<Key<HostResource>> nsHosts;
/**
* The union of the contacts visible via {@link #getContacts} and {@link #getRegistrant}.
*
* <p>These are stored in one field so that we can query across all contacts at once.
*/
@ElementCollection Set<DesignatedContact> allContacts;
@Transient Set<DesignatedContact> allContacts;
/** Authorization info (aka transfer secret) of the domain. */
@Embedded
@@ -146,7 +154,7 @@ public class DomainBase extends EppResource
* <p>This is {@literal @}XmlTransient because it needs to be returned under the "extension" tag
* of an info response rather than inside the "infData" tag.
*/
@ElementCollection Set<DelegationSignerData> dsData;
@Transient Set<DelegationSignerData> dsData;
/**
* The claims notice supplied when this application or domain was created, if there was one. It's
@@ -177,7 +185,7 @@ public class DomainBase extends EppResource
String idnTableName;
/** Fully qualified host names of this domain's active subordinate hosts. */
@ElementCollection Set<String> subordinateHosts;
Set<String> subordinateHosts;
/** When this domain's registration will expire. */
DateTime registrationExpirationTime;
@@ -189,7 +197,7 @@ public class DomainBase extends EppResource
* refer to a {@link PollMessage} timed to when the domain is fully deleted. If the domain is
* restored, the message should be deleted.
*/
Key<PollMessage.OneTime> deletePollMessage;
@Transient Key<PollMessage.OneTime> deletePollMessage;
/**
* The recurring billing event associated with this domain's autorenewals.
@@ -199,7 +207,7 @@ public class DomainBase extends EppResource
* {@link #registrationExpirationTime} is changed the recurrence should be closed, a new one
* should be created, and this field should be updated to point to the new one.
*/
Key<BillingEvent.Recurring> autorenewBillingEvent;
@Transient Key<BillingEvent.Recurring> autorenewBillingEvent;
/**
* The recurring poll message associated with this domain's autorenewals.
@@ -209,10 +217,10 @@ public class DomainBase extends EppResource
* {@link #registrationExpirationTime} is changed the recurrence should be closed, a new one
* should be created, and this field should be updated to point to the new one.
*/
Key<PollMessage.Autorenew> autorenewPollMessage;
@Transient Key<PollMessage.Autorenew> autorenewPollMessage;
/** The unexpired grace periods for this domain (some of which may not be active yet). */
@ElementCollection Set<GracePeriod> gracePeriods;
@Transient @ElementCollection Set<GracePeriod> gracePeriods;
/**
* The id of the signed mark that was used to create this domain in sunrise.
@@ -223,31 +231,7 @@ public class DomainBase extends EppResource
String smdId;
/** Data about any pending or past transfers on this domain. */
@Embedded
@AttributeOverrides({
@AttributeOverride(
name = "transferRequestTrid",
column = @Column(name = "transfer_data_request_trid")),
@AttributeOverride(
name = "transferPeriod",
column = @Column(name = "transfer_data_transfer_period")),
@AttributeOverride(
name = "transferredRegistrationExpirationTime",
column = @Column(name = "transfer_data_registration_expiration_time")),
@AttributeOverride(
name = "serverApproveEntities",
column = @Column(name = "transfer_data_server_approve_entities")),
@AttributeOverride(
name = "serverApproveBillingEvent",
column = @Column(name = "transfer_data_server_approve_billing_event")),
@AttributeOverride(
name = "serverApproveAutorenewEvent",
column = @Column(name = "transfer_data_server_approve_autorenrew_event")),
@AttributeOverride(
name = "serverApproveAutorenewPollMessage",
column = @Column(name = "transfer_data_server_approve_autorenrew_poll_message")),
})
TransferData transferData;
@Transient TransferData transferData;
/**
* The time that this resource was last transferred.
@@ -285,7 +269,7 @@ public class DomainBase extends EppResource
}
@Override
public final TransferData getTransferData() {
public TransferData getTransferData() {
return Optional.ofNullable(transferData).orElse(TransferData.EMPTY);
}
@@ -15,16 +15,26 @@
package google.registry.model.eppcommon;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.nullToEmpty;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.googlecode.objectify.annotation.AlsoLoad;
import com.googlecode.objectify.annotation.Ignore;
import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
import google.registry.model.JsonMapBuilder;
import google.registry.model.Jsonifiable;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;
import javax.persistence.Embeddable;
import javax.persistence.MappedSuperclass;
import javax.persistence.PostLoad;
import javax.persistence.Transient;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
@@ -42,12 +52,23 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
* @see google.registry.model.registrar.RegistrarAddress
*/
@XmlTransient
@Embeddable
@MappedSuperclass
public class Address extends ImmutableObject implements Jsonifiable {
/** The schema validation will enforce that this has 3 lines at most. */
// TODO(shicong): Remove this field after migration. We need to figure out how to generate same
// XML from streetLine[1,2,3].
@XmlJavaTypeAdapter(NormalizedStringAdapter.class)
@Transient
List<String> street;
@Ignore @XmlTransient @IgnoredInDiffableMap String streetLine1;
@Ignore @XmlTransient @IgnoredInDiffableMap String streetLine2;
@Ignore @XmlTransient @IgnoredInDiffableMap String streetLine3;
@XmlJavaTypeAdapter(NormalizedStringAdapter.class)
String city;
@@ -64,7 +85,11 @@ public class Address extends ImmutableObject implements Jsonifiable {
String countryCode;
public ImmutableList<String> getStreet() {
return nullToEmptyImmutableCopy(street);
if (street == null && streetLine1 != null) {
return ImmutableList.of(streetLine1, nullToEmpty(streetLine2), nullToEmpty(streetLine3));
} else {
return nullToEmptyImmutableCopy(street);
}
}
public String getCity() {
@@ -113,6 +138,9 @@ public class Address extends ImmutableObject implements Jsonifiable {
street == null || (!street.isEmpty() && street.size() <= 3),
"Street address must have [1-3] lines: %s", street);
getInstance().street = street;
getInstance().streetLine1 = street.get(0);
getInstance().streetLine2 = street.size() >= 2 ? street.get(1) : null;
getInstance().streetLine3 = street.size() == 3 ? street.get(2) : null;
return this;
}
@@ -139,4 +167,38 @@ public class Address extends ImmutableObject implements Jsonifiable {
return this;
}
}
/**
* Sets {@link #streetLine1}, {@link #streetLine2} and {@link #streetLine3} after loading the
* entity from Datastore.
*
* <p>This callback method is used by Objectify to set streetLine[1,2,3] fields as they are not
* persisted in the Datastore. TODO(shicong): Delete this method after database migration.
*/
void onLoad(@AlsoLoad("street") List<String> street) {
if (street == null || street.size() == 0) {
return;
}
streetLine1 = street.get(0);
streetLine2 = street.size() >= 2 ? street.get(1) : null;
streetLine3 = street.size() >= 3 ? street.get(2) : null;
}
/**
* Sets {@link #street} after loading the entity from Cloud SQL.
*
* <p>This callback method is used by Hibernate to set {@link #street} field as it is not
* persisted in Cloud SQL. We are doing this because the street list field is exposed by Address
* class and is used everywhere in our code base. Also, setting/reading a list of strings is more
* convenient.
*/
@PostLoad
void postLoad() {
street =
streetLine1 == null
? null
: Stream.of(streetLine1, streetLine2, streetLine3)
.filter(Objects::nonNull)
.collect(toImmutableList());
}
}
@@ -164,4 +164,5 @@ public enum StatusValue implements EppEnum {
public static StatusValue fromXmlName(String xmlName) {
return StatusValue.valueOf(LOWER_CAMEL.to(UPPER_UNDERSCORE, nullToEmpty(xmlName)));
}
}
@@ -85,12 +85,28 @@ import java.util.function.Predicate;
import javax.annotation.Nullable;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.joda.money.CurrencyUnit;
import org.joda.time.DateTime;
/** Information about a registrar. */
@ReportedOn
@Entity
@javax.persistence.Entity
@Table(
indexes = {
@javax.persistence.Index(columnList = "registrarName", name = "registrar_name_idx"),
@javax.persistence.Index(
columnList = "ianaIdentifier",
name = "registrar_iana_identifier_idx"),
})
public class Registrar extends ImmutableObject implements Buildable, Jsonifiable {
/** Represents the type of a registrar entity. */
@@ -208,14 +224,17 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
.doTransactionless(
() -> Maps.uniqueIndex(loadAll(), Registrar::getClientId)));
@Parent
Key<EntityGroupRoot> parent = getCrossTldKey();
@Parent @Transient Key<EntityGroupRoot> parent = getCrossTldKey();
/**
* Unique registrar client id. Must conform to "clIDType" as defined in RFC5730.
*
* @see <a href="http://tools.ietf.org/html/rfc5730#section-4.2">Shared Structure Schema</a>
* <p>TODO(shicong): Rename this field to clientId
*/
@Id
@javax.persistence.Id
@Column(name = "client_id", nullable = false)
String clientIdentifier;
/**
@@ -229,12 +248,17 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
* @see <a href="http://www.icann.org/registrar-reports/accredited-list.html">ICANN-Accredited
* Registrars</a>
*/
@Index String registrarName;
@Index
@Column(nullable = false)
String registrarName;
/** The type of this registrar. */
@Column(nullable = false)
@Enumerated(EnumType.STRING)
Type type;
/** The state of this registrar. */
@Enumerated(EnumType.STRING)
State state;
/** The set of TLDs which this registrar is allowed to access. */
@@ -277,6 +301,7 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
String passwordHash;
/** Randomly generated hash salt. */
@Column(name = "password_salt")
String salt;
// The following fields may appear redundant to the above, but are
@@ -287,6 +312,24 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
* unrestricted UTF-8.
*/
@IgnoreSave(IfNull.class)
@Embedded
@AttributeOverrides({
@AttributeOverride(
name = "streetLine1",
column = @Column(name = "localized_address_street_line1")),
@AttributeOverride(
name = "streetLine2",
column = @Column(name = "localized_address_street_line2")),
@AttributeOverride(
name = "streetLine3",
column = @Column(name = "localized_address_street_line3")),
@AttributeOverride(name = "city", column = @Column(name = "localized_address_city")),
@AttributeOverride(name = "state", column = @Column(name = "localized_address_state")),
@AttributeOverride(name = "zip", column = @Column(name = "localized_address_zip")),
@AttributeOverride(
name = "countryCode",
column = @Column(name = "localized_address_country_code"))
})
RegistrarAddress localizedAddress;
/**
@@ -294,6 +337,16 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
* representable in the 7-bit US-ASCII character set.
*/
@IgnoreSave(IfNull.class)
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "streetLine1", column = @Column(name = "i18n_address_street_line1")),
@AttributeOverride(name = "streetLine2", column = @Column(name = "i18n_address_street_line2")),
@AttributeOverride(name = "streetLine3", column = @Column(name = "i18n_address_street_line3")),
@AttributeOverride(name = "city", column = @Column(name = "i18n_address_city")),
@AttributeOverride(name = "state", column = @Column(name = "i18n_address_state")),
@AttributeOverride(name = "zip", column = @Column(name = "i18n_address_zip")),
@AttributeOverride(name = "countryCode", column = @Column(name = "i18n_address_country_code"))
})
RegistrarAddress internationalizedAddress;
/** Voice number. */
@@ -309,16 +362,17 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
/**
* Registrar identifier used for reporting to ICANN.
*
* <ul>
* <li>8 is used for Testing Registrar.
* <li>9997 is used by ICAAN for SLA monitoring.
* <li>9999 is used for cases when the registry operator acts as registrar.
* </ul>
* @see <a href="http://www.iana.org/assignments/registrar-ids/registrar-ids.txt">Registrar IDs</a>
*
* @see <a href="http://www.iana.org/assignments/registrar-ids/registrar-ids.txt">Registrar
* IDs</a>
*/
@Index
@Nullable
Long ianaIdentifier;
@Index @Nullable Long ianaIdentifier;
/** Identifier of registrar used in external billing system (e.g. Oracle). */
@Nullable
@@ -338,18 +392,19 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
*/
@Nullable
@Mapify(CurrencyMapper.class)
@org.hibernate.annotations.Type(type = "google.registry.persistence.CurrencyToBillingMapUserType")
Map<CurrencyUnit, BillingAccountEntry> billingAccountMap;
/** A billing account entry for this registrar, consisting of a currency and an account Id. */
@Embed
static class BillingAccountEntry extends ImmutableObject {
public static class BillingAccountEntry extends ImmutableObject {
CurrencyUnit currency;
String accountId;
BillingAccountEntry() {}
BillingAccountEntry(CurrencyUnit currency, String accountId) {
public BillingAccountEntry(CurrencyUnit currency, String accountId) {
this.accountId = accountId;
this.currency = currency;
}
@@ -366,6 +421,11 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
return billingAccountEntry.currency;
}
}
/** Returns the account id of this entry. */
public String getAccountId() {
return accountId;
}
}
/** URL of registrar's website. */
@@ -390,14 +450,10 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
/** An automatically managed last-saved timestamp. */
UpdateAutoTimestamp lastUpdateTime = UpdateAutoTimestamp.create(null);
/**
* The time that the certificate was last updated.
*/
/** The time that the certificate was last updated. */
DateTime lastCertificateUpdateTime;
/**
* Telephone support passcode (5-digit numeric)
*/
/** Telephone support passcode (5-digit numeric) */
String phonePasscode;
/**
@@ -20,6 +20,7 @@ import static google.registry.util.CollectionUtils.forceEmptyToNull;
import com.google.common.annotations.VisibleForTesting;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.eppcommon.Address;
import javax.persistence.Embeddable;
/**
* Registrar Address
@@ -29,6 +30,7 @@ import google.registry.model.eppcommon.Address;
* classes.
*/
@Embed
@Embeddable
public class RegistrarAddress extends Address {
@Override
@@ -45,6 +45,9 @@ import google.registry.model.annotations.ReportedOn;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Table;
import javax.persistence.Transient;
/**
* A contact for a Registrar. Note, equality, hashCode and comparable have been overridden to only
@@ -56,10 +59,16 @@ import java.util.Set;
*/
@ReportedOn
@Entity
@javax.persistence.Entity
@Table(
name = "RegistrarPoc",
indexes = {
@javax.persistence.Index(columnList = "gaeUserId", name = "registrarpoc_gae_user_id_idx")
})
// TODO(shicong): Rename the class name to RegistrarPoc after database migration
public class RegistrarContact extends ImmutableObject implements Jsonifiable {
@Parent
Key<Registrar> parent;
@Parent @Transient Key<Registrar> parent;
/**
* Registrar contacts types for partner communication tracking.
@@ -99,6 +108,8 @@ public class RegistrarContact extends ImmutableObject implements Jsonifiable {
/** The email address of the contact. */
@Id
@javax.persistence.Id
@Column(nullable = false)
String emailAddress;
/** The voice number of the contact. */
@@ -108,8 +119,8 @@ public class RegistrarContact extends ImmutableObject implements Jsonifiable {
String faxNumber;
/**
* Multiple types are used to associate the registrar contact with
* various mailing groups. This data is internal to the registry.
* Multiple types are used to associate the registrar contact with various mailing groups. This
* data is internal to the registry.
*/
Set<Type> types;
@@ -156,7 +156,7 @@ public class ClaimsListShard extends ImmutableObject {
try {
loadAndCompareCloudSqlList(datastoreList);
} catch (Throwable t) {
logger.atSevere().withCause(t).log("Error comparing reserved lists.");
logger.atSevere().withCause(t).log("Error comparing claims lists.");
}
return datastoreList;
};
@@ -31,6 +31,8 @@ import google.registry.ui.server.registrar.OteStatusAction;
import google.registry.ui.server.registrar.RegistrarConsoleModule;
import google.registry.ui.server.registrar.RegistrarSettingsAction;
import google.registry.ui.server.registrar.RegistryLockGetAction;
import google.registry.ui.server.registrar.RegistryLockPostAction;
import google.registry.ui.server.registrar.RegistryLockVerifyAction;
/** Dagger component with per-request lifetime for "default" App Engine module. */
@RequestScope
@@ -53,6 +55,10 @@ interface FrontendRequestComponent {
RegistryLockGetAction registryLockGetAction();
RegistryLockPostAction registryLockPostAction();
RegistryLockVerifyAction registryLockVerifyAction();
@Subcomponent.Builder
abstract class Builder implements RequestComponentBuilder<FrontendRequestComponent> {
@Override public abstract Builder requestModule(RequestModule requestModule);
@@ -0,0 +1,39 @@
// 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;
import google.registry.util.CidrAddressBlock;
import java.util.List;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
/**
* JPA {@link AttributeConverter} for storing/retrieving {@link List<CidrAddressBlock>} objects.
* TODO(shicong): Investigate if we can have one converter for any List type
*/
@Converter(autoApply = true)
public class CidrAddressBlockListConverter extends StringListConverterBase<CidrAddressBlock> {
@Override
String toString(CidrAddressBlock element) {
return element.toString();
}
@Override
CidrAddressBlock fromString(String value) {
return CidrAddressBlock.create(value);
}
}
@@ -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.persistence;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import google.registry.model.registrar.Registrar.BillingAccountEntry;
import java.util.Map;
import org.hibernate.usertype.UserType;
import org.joda.money.CurrencyUnit;
/**
* A custom {@link UserType} for storing/retrieving {@link Map<CurrencyUnit, BillingAccountEntry>}
* objects.
*/
public class CurrencyToBillingMapUserType extends MapUserType {
@Override
public Object toEntityTypeMap(Map<String, String> map) {
return map == null
? null
: map.entrySet().stream()
.collect(
toImmutableMap(
entry -> CurrencyUnit.of(entry.getKey()),
entry ->
new BillingAccountEntry(
CurrencyUnit.of(entry.getKey()), entry.getValue())));
}
@Override
public Map<String, String> toDbSupportedMap(Object map) {
return map == null
? null
: ((Map<CurrencyUnit, BillingAccountEntry>) map)
.entrySet().stream()
.collect(
toImmutableMap(
entry -> entry.getKey().getCode(),
entry -> entry.getValue().getAccountId()));
}
}
@@ -1,98 +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.persistence;
import java.sql.Array;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Collection;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
/** Generic Hibernate user type to store/retrieve Java collection as an array in Cloud SQL. */
public abstract class GenericCollectionUserType<T extends Collection> extends MutableUserType {
abstract T getNewCollection();
abstract ArrayColumnType getColumnType();
enum ArrayColumnType {
STRING(Types.ARRAY, "text");
final int typeCode;
final String typeName;
ArrayColumnType(int typeCode, String typeName) {
this.typeCode = typeCode;
this.typeName = typeName;
}
int getTypeCode() {
return typeCode;
}
String getTypeName() {
return typeName;
}
String getTypeDdlName() {
return typeName + "[]";
}
}
@Override
public int[] sqlTypes() {
return new int[] {getColumnType().getTypeCode()};
}
@Override
public Object nullSafeGet(
ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
throws HibernateException, SQLException {
if (rs.getArray(names[0]) != null) {
T result = getNewCollection();
for (Object element : (Object[]) rs.getArray(names[0]).getArray()) {
result.add(convertToElem(element));
}
return result;
}
return null;
}
@Override
public void nullSafeSet(
PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
throws HibernateException, SQLException {
if (value == null) {
st.setArray(index, null);
return;
}
T list = (T) value;
Array arr = st.getConnection().createArrayOf(getColumnType().getTypeName(), list.toArray());
st.setArray(index, arr);
}
/**
* Override this to convert an element value retrieved from the database to a different type.
*
* <p>This method is useful when encoding a java type to one of the types that can be used as an
* array element.
*/
protected Object convertToElem(Object columnValue) {
return columnValue;
}
}
@@ -45,13 +45,29 @@ public class MapUserType extends MutableUserType {
public Object nullSafeGet(
ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
throws HibernateException, SQLException {
return rs.getObject(names[0]);
return toEntityTypeMap((Map<String, String>) rs.getObject(names[0]));
}
@Override
public void nullSafeSet(
PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
throws HibernateException, SQLException {
st.setObject(index, value);
st.setObject(index, toDbSupportedMap(value));
}
/**
* Subclass can override this method to convert the {@link Map<String, String>} to a {@link Map}
* of specific type defined in the entity class.
*/
public Object toEntityTypeMap(Map<String, String> map) {
return map;
}
/**
* Subclass can override this method to convert the {@link Map} of specific type to a {@link
* Map<String, String>} that can be stored in the hstore type column.
*/
public Map<String, String> toDbSupportedMap(Object map) {
return (Map<String, String>) map;
}
}
@@ -13,9 +13,10 @@
// limitations under the License.
package google.registry.persistence;
import google.registry.persistence.GenericCollectionUserType.ArrayColumnType;
import java.sql.Types;
import org.hibernate.boot.model.TypeContributions;
import org.hibernate.dialect.PostgreSQL95Dialect;
import org.hibernate.service.ServiceRegistry;
/** Nomulus mapping rules for column types in Postgresql. */
public class NomulusPostgreSQLDialect extends PostgreSQL95Dialect {
@@ -25,8 +26,15 @@ public class NomulusPostgreSQLDialect extends PostgreSQL95Dialect {
registerColumnType(Types.TIMESTAMP_WITH_TIMEZONE, "timestamptz");
registerColumnType(Types.TIMESTAMP, "timestamptz");
registerColumnType(Types.OTHER, "hstore");
for (ArrayColumnType arrayType : ArrayColumnType.values()) {
registerColumnType(arrayType.getTypeCode(), arrayType.getTypeDdlName());
}
registerColumnType(
StringCollectionDescriptor.COLUMN_TYPE, StringCollectionDescriptor.COLUMN_DDL_NAME);
}
@Override
public void contributeTypes(
TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
super.contributeTypes(typeContributions, serviceRegistry);
typeContributions.contributeJavaTypeDescriptor(new StringCollectionDescriptor());
typeContributions.contributeSqlTypeDescriptor(new StringCollectionDescriptor());
}
}
@@ -14,24 +14,21 @@
package google.registry.persistence;
import java.util.HashSet;
import google.registry.model.registrar.RegistrarContact.Type;
import java.util.Set;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
/** Abstract Hibernate user type for storing/retrieving {@link Set<Enum<E>>}. */
public class EnumSetUserType<E extends Enum<E>> extends GenericCollectionUserType<Set<Enum<E>>> {
/** JPA {@link AttributeConverter} for storing/retrieving {@link Set<Type>}. */
@Converter(autoApply = true)
public class RegistrarPocSetConverter extends StringSetConverterBase<Type> {
@Override
Set<Enum<E>> getNewCollection() {
return new HashSet<Enum<E>>();
String toString(Type element) {
return element.name();
}
@Override
ArrayColumnType getColumnType() {
return ArrayColumnType.STRING;
}
@Override
public Class returnedClass() {
return Set.class;
Type fromString(String value) {
return Type.valueOf(value);
}
}
@@ -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.persistence;
import google.registry.model.eppcommon.StatusValue;
import java.util.Set;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
/** JPA {@link AttributeConverter} for storing/retrieving {@link Set<StatusValue>}. */
@Converter(autoApply = true)
public class StatusValueSetConverter extends StringSetConverterBase<StatusValue> {
@Override
String toString(StatusValue element) {
return element.name();
}
@Override
StatusValue fromString(String value) {
return StatusValue.valueOf(value);
}
}
@@ -0,0 +1,187 @@
// 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;
import static google.registry.persistence.StringCollectionDescriptor.StringCollection;
import com.google.common.collect.ImmutableList;
import java.sql.Array;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Collection;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.AbstractTypeDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.spi.JdbcRecommendedSqlTypeMappingContext;
import org.hibernate.type.descriptor.sql.BasicBinder;
import org.hibernate.type.descriptor.sql.BasicExtractor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
/**
* The {@link JavaTypeDescriptor} and {@link SqlTypeDescriptor} for {@link StringCollection}.
*
* <p>A {@link StringCollection} object is a simple wrapper for a {@link Collection<String>} which
* can be stored as a string array in the database. The {@link JavaTypeDescriptor} and {@link
* SqlTypeDescriptor} is used by JPA/Hibernate to map between the collection and {@link Array} which
* is the actual type that JDBC uses to read from and write to the database.
*
* @see <a
* href="https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#basic-jpa-convert">JPA
* 2.1 AttributeConverters</a>
*/
public class StringCollectionDescriptor extends AbstractTypeDescriptor<StringCollection>
implements SqlTypeDescriptor {
public static final int COLUMN_TYPE = Types.ARRAY;
public static final String COLUMN_NAME = "text";
public static final String COLUMN_DDL_NAME = COLUMN_NAME + "[]";
protected StringCollectionDescriptor() {
super(StringCollection.class);
}
@Override
public StringCollection fromString(String string) {
throw new UnsupportedOperationException(
"Constructing StringCollectionDescriptor from string is not allowed");
}
@Override
public <X> X unwrap(StringCollection value, Class<X> type, WrapperOptions options) {
if (value == null) {
return null;
}
if (Collection.class.isAssignableFrom(type)) {
return (X) value.getCollection();
}
throw unknownUnwrap(type);
}
@Override
public <X> StringCollection wrap(X value, WrapperOptions options) {
if (value == null) {
return null;
}
if (value instanceof Array) {
try {
String[] arr = (String[]) ((Array) value).getArray();
ImmutableList.Builder<String> builder = new ImmutableList.Builder<>();
for (String str : arr) {
builder.add(str);
}
return StringCollection.create(builder.build());
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
throw unknownWrap(value.getClass());
}
@Override
public SqlTypeDescriptor getJdbcRecommendedSqlType(JdbcRecommendedSqlTypeMappingContext context) {
return this;
}
@Override
public int getSqlType() {
return COLUMN_TYPE;
}
@Override
public boolean canBeRemapped() {
return false;
}
@Override
public <X> ValueBinder<X> getBinder(JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>(javaTypeDescriptor, this) {
@Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
throws SQLException {
if (value == null) {
st.setArray(index, null);
return;
}
if (value instanceof StringCollection) {
StringCollection stringCollection = (StringCollection) value;
if (stringCollection.getCollection() == null) {
st.setArray(index, null);
} else {
st.setArray(
index,
st.getConnection()
.createArrayOf(COLUMN_NAME, stringCollection.getCollection().toArray()));
}
} else {
throw new UnsupportedOperationException(
String.format(
"Binding type %s is not supported by StringCollectionDescriptor",
value.getClass().getName()));
}
}
@Override
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
throws SQLException {
// CallableStatement.setArray() doesn't have an overload version for setting array by its
// column name
throw new UnsupportedOperationException(
"Binding array by its column name is not supported");
}
};
}
@Override
public <X> ValueExtractor<X> getExtractor(JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>(javaTypeDescriptor, this) {
@Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap(rs.getArray(name), options);
}
@Override
protected X doExtract(CallableStatement statement, int index, WrapperOptions options)
throws SQLException {
return javaTypeDescriptor.wrap(statement.getArray(index), options);
}
@Override
protected X doExtract(CallableStatement statement, String name, WrapperOptions options)
throws SQLException {
return javaTypeDescriptor.wrap(statement.getArray(name), options);
}
};
}
public static class StringCollection {
private Collection<String> collection;
private StringCollection(Collection<String> collection) {
this.collection = collection;
}
public static StringCollection create(Collection<String> collection) {
return new StringCollection(collection);
}
public Collection<String> getCollection() {
return collection;
}
}
}
@@ -14,24 +14,21 @@
package google.registry.persistence;
import com.google.common.collect.Lists;
import java.util.List;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
/** Abstract Hibernate user type for storing/retrieving {@link List<String>}. */
public class StringListUserType extends GenericCollectionUserType<List<String>> {
/** JPA {@link AttributeConverter} for storing/retrieving {@link List<String>}. */
@Converter(autoApply = true)
public class StringListConverter extends StringListConverterBase<String> {
@Override
List<String> getNewCollection() {
return Lists.newArrayList();
String toString(String element) {
return element;
}
@Override
ArrayColumnType getColumnType() {
return ArrayColumnType.STRING;
}
@Override
public Class returnedClass() {
return List.class;
String fromString(String value) {
return value;
}
}
@@ -0,0 +1,48 @@
// 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;
import static com.google.common.collect.ImmutableList.toImmutableList;
import google.registry.persistence.StringCollectionDescriptor.StringCollection;
import java.util.List;
import javax.persistence.AttributeConverter;
/**
* Base JPA converter for {@link List} objects that are stored as an array of strings in the
* database.
*/
public abstract class StringListConverterBase<T>
implements AttributeConverter<List<T>, StringCollection> {
abstract String toString(T element);
abstract T fromString(String value);
@Override
public StringCollection convertToDatabaseColumn(List<T> attribute) {
return attribute == null
? null
: StringCollection.create(
attribute.stream().map(this::toString).collect(toImmutableList()));
}
@Override
public List<T> convertToEntityAttribute(StringCollection dbData) {
return dbData == null || dbData.getCollection() == null
? null
: dbData.getCollection().stream().map(this::fromString).collect(toImmutableList());
}
}
@@ -14,24 +14,21 @@
package google.registry.persistence;
import com.google.common.collect.Sets;
import java.util.Set;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
/** Abstract Hibernate user type for storing/retrieving {@link Set<String>}. */
public class StringSetUserType extends GenericCollectionUserType<Set<String>> {
/** JPA {@link AttributeConverter} for storing/retrieving {@link Set<String>}. */
@Converter(autoApply = true)
public class StringSetConverter extends StringSetConverterBase<String> {
@Override
Set<String> getNewCollection() {
return Sets.newHashSet();
String toString(String element) {
return element;
}
@Override
ArrayColumnType getColumnType() {
return ArrayColumnType.STRING;
}
@Override
public Class returnedClass() {
return Set.class;
String fromString(String value) {
return value;
}
}
@@ -0,0 +1,47 @@
// 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;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import google.registry.persistence.StringCollectionDescriptor.StringCollection;
import java.util.Set;
import javax.persistence.AttributeConverter;
/**
* Base JPA converter for {@link Set} objects that are stored as an array of strings in the
* database.
*/
public abstract class StringSetConverterBase<T>
implements AttributeConverter<Set<T>, StringCollection> {
abstract String toString(T element);
abstract T fromString(String value);
@Override
public StringCollection convertToDatabaseColumn(Set<T> attribute) {
return attribute == null
? null
: StringCollection.create(attribute.stream().map(this::toString).collect(toImmutableSet()));
}
@Override
public Set<T> convertToEntityAttribute(StringCollection dbData) {
return dbData == null || dbData.getCollection() == null
? null
: dbData.getCollection().stream().map(this::fromString).collect(toImmutableSet());
}
}
@@ -122,8 +122,7 @@ public final class IcannReportingStagingAction implements Runnable {
TaskOptions uploadTask =
TaskOptions.Builder.withUrl(IcannReportingUploadAction.PATH)
.method(Method.POST)
.countdownMillis(Duration.standardMinutes(2).getMillis())
.param(PARAM_SUBDIR, subdir);
.countdownMillis(Duration.standardMinutes(2).getMillis());
QueueFactory.getQueue(CRON_QUEUE).add(uploadTask);
return null;
},
@@ -20,7 +20,6 @@ import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
import static google.registry.model.common.Cursor.getCursorTimeOrStartOfTime;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.reporting.icann.IcannReportingModule.PARAM_SUBDIR;
import static google.registry.request.Action.Method.POST;
import static javax.servlet.http.HttpServletResponse.SC_OK;
@@ -40,7 +39,6 @@ import google.registry.model.registry.Registry;
import google.registry.model.registry.Registry.TldType;
import google.registry.request.Action;
import google.registry.request.HttpException.ServiceUnavailableException;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.request.lock.LockHandler;
@@ -91,8 +89,6 @@ public final class IcannReportingUploadAction implements Runnable {
@Config("reportingBucket")
String reportingBucket;
@Inject @Parameter(PARAM_SUBDIR) String subdir;
@Inject GcsUtils gcsUtils;
@Inject IcannHttpReporter icannReporter;
@Inject Retrier retrier;
@@ -145,7 +141,12 @@ public final class IcannReportingUploadAction implements Runnable {
CursorType cursorType,
String tldStr,
ImmutableMap.Builder<String, Boolean> reportSummaryBuilder) {
String reportBucketname = String.format("%s/%s", reportingBucket, subdir);
DateTime cursorTimeMinusMonth = cursorTime.withDayOfMonth(1).minusMonths(1);
String reportSubdir =
String.format(
"icann/monthly/%d-%02d",
cursorTimeMinusMonth.getYear(), cursorTimeMinusMonth.getMonthOfYear());
String reportBucketname = String.format("%s/%s", reportingBucket, reportSubdir);
String filename = getFileName(cursorType, cursorTime, tldStr);
final GcsFilename gcsFilename = new GcsFilename(reportBucketname, filename);
logger.atInfo().log("Reading ICANN report %s from bucket %s", filename, reportBucketname);
@@ -195,12 +196,13 @@ public final class IcannReportingUploadAction implements Runnable {
}
private String getFileName(CursorType cursorType, DateTime cursorTime, String tld) {
DateTime cursorTimeMinusMonth = cursorTime.withDayOfMonth(1).minusMonths(1);
return String.format(
"%s%s%d%02d.csv",
tld,
(cursorType.equals(CursorType.ICANN_UPLOAD_ACTIVITY) ? "-activity-" : "-transactions-"),
cursorTime.year().get(),
cursorTime.withDayOfMonth(1).minusMonths(1).monthOfYear().get());
cursorTimeMinusMonth.year().get(),
cursorTimeMinusMonth.monthOfYear().get());
}
/** Returns a map of each cursor to the CursorType and tld. */
@@ -0,0 +1,73 @@
// 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.registrar;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import google.registry.model.registrar.Registrar;
import java.util.Optional;
/** Data access object for {@link Registrar}. */
public class RegistrarDao {
private RegistrarDao() {}
/** Persists a new or updates an existing registrar in Cloud SQL. */
public static void saveNew(Registrar registrar) {
checkArgumentNotNull(registrar, "registrar must be specified");
jpaTm().transact(() -> jpaTm().getEntityManager().persist(registrar));
}
/** Updates an existing registrar in Cloud SQL, throws excpetion if it does not exist. */
public static void update(Registrar registrar) {
checkArgumentNotNull(registrar, "registrar must be specified");
jpaTm()
.transact(
() -> {
checkArgument(
checkExists(registrar.getClientId()),
"A registrar of this id does not exist: %s.",
registrar.getClientId());
jpaTm().getEntityManager().merge(registrar);
});
}
/** Returns whether the registrar of the given id exists. */
public static boolean checkExists(String clientId) {
checkArgumentNotNull(clientId, "clientId must be specified");
return jpaTm()
.transact(
() ->
jpaTm()
.getEntityManager()
.createQuery(
"SELECT 1 FROM Registrar WHERE clientIdentifier = :clientIdentifier",
Integer.class)
.setParameter("clientIdentifier", clientId)
.setMaxResults(1)
.getResultList()
.size()
> 0);
}
/** Loads the registrar by its id, returns empty if it doesn't exist. */
public static Optional<Registrar> load(String clientId) {
checkArgumentNotNull(clientId, "clientId must be specified");
return Optional.ofNullable(
jpaTm().transact(() -> jpaTm().getEntityManager().find(Registrar.class, clientId)));
}
}
@@ -0,0 +1,132 @@
// 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.server;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import google.registry.model.ImmutableObject;
import google.registry.schema.server.Lock.LockId;
import google.registry.util.DateTimeUtils;
import java.io.Serializable;
import java.time.ZonedDateTime;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Table;
import org.joda.time.DateTime;
import org.joda.time.Duration;
/**
* A lock on some shared resource.
*
* <p>Locks are either specific to a tld or global to the entire system, in which case a tld of
* {@link GLOBAL} is used.
*
* <p>This uses a compound primary key as defined in {@link LockId}.
*/
@Entity
@Table
@IdClass(LockId.class)
public class Lock {
/** The resource name used to create the lock. */
@Column(nullable = false)
@Id
String resourceName;
/** The tld used to create the lock. */
@Column(nullable = false)
@Id
String tld;
/**
* Unique log ID of the request that owns this lock.
*
* <p>When that request is no longer running (is finished), the lock can be considered implicitly
* released.
*
* <p>See {@link RequestStatusCheckerImpl#getLogId} for details about how it's created in
* practice.
*/
@Column(nullable = false)
String requestLogId;
/** When the lock was acquired. Used for logging. */
@Column(nullable = false)
ZonedDateTime acquiredTime;
/** When the lock can be considered implicitly released. */
@Column(nullable = false)
ZonedDateTime expirationTime;
/** The scope of a lock that is not specific to a single tld. */
static final String GLOBAL = "GLOBAL";
/**
* Validate input and create a new {@link Lock} for the given resource name in the specified tld.
*/
private Lock(
String resourceName,
String tld,
String requestLogId,
DateTime acquiredTime,
Duration leaseLength) {
this.resourceName = checkArgumentNotNull(resourceName, "The resource name cannot be null");
this.tld = checkArgumentNotNull(tld, "The tld cannot be null. For a global lock, use GLOBAL");
this.requestLogId =
checkArgumentNotNull(requestLogId, "The requestLogId of the lock cannot be null");
this.acquiredTime =
DateTimeUtils.toZonedDateTime(
checkArgumentNotNull(acquiredTime, "The acquired time of the lock cannot be null"));
checkArgumentNotNull(leaseLength, "The lease length of the lock cannot be null");
this.expirationTime = DateTimeUtils.toZonedDateTime(acquiredTime.plus(leaseLength));
}
// Hibernate requires a default constructor.
private Lock() {}
/** Constructs a {@link Lock} object. */
public static Lock create(
String resourceName,
String tld,
String requestLogId,
DateTime acquiredTime,
Duration leaseLength) {
checkArgumentNotNull(
tld, "The tld cannot be null. To create a global lock, use the createGlobal method");
return new Lock(resourceName, tld, requestLogId, acquiredTime, leaseLength);
}
/** Constructs a {@link Lock} object with a {@link GLOBAL} scope. */
public static Lock createGlobal(
String resourceName, String requestLogId, DateTime acquiredTime, Duration leaseLength) {
return new Lock(resourceName, GLOBAL, requestLogId, acquiredTime, leaseLength);
}
static class LockId extends ImmutableObject implements Serializable {
String resourceName;
String tld;
private LockId() {}
LockId(String resourceName, String tld) {
this.resourceName = checkArgumentNotNull(resourceName, "The resource name cannot be null");
this.tld = tld;
}
}
}
@@ -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.schema.server;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.schema.server.Lock.GLOBAL;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import google.registry.schema.server.Lock.LockId;
import java.util.Optional;
/** Data access object class for {@link Lock}. */
public class LockDao {
/** Saves the {@link Lock} object to Cloud SQL. */
public static void saveNew(Lock lock) {
jpaTm()
.transact(
() -> {
jpaTm().getEntityManager().persist(lock);
});
}
/**
* Loads and returns a {@link Lock} object with the given resourceName and tld from Cloud SQL if
* it exists, else empty.
*/
public static Optional<Lock> load(String resourceName, String tld) {
checkArgumentNotNull(resourceName, "The resource name of the lock to load cannot be null");
checkArgumentNotNull(tld, "The tld of the lock to load cannot be null");
return Optional.ofNullable(
jpaTm()
.transact(
() -> jpaTm().getEntityManager().find(Lock.class, new LockId(resourceName, tld))));
}
/**
* Loads a global {@link Lock} object with the given resourceName from Cloud SQL if it exists,
* else empty.
*/
public static Optional<Lock> load(String resourceName) {
checkArgumentNotNull(resourceName, "The resource name of the lock to load cannot be null");
return Optional.ofNullable(
jpaTm()
.transact(
() ->
jpaTm().getEntityManager().find(Lock.class, new LockId(resourceName, GLOBAL))));
}
/**
* Deletes the given {@link Lock} object from Cloud SQL. This method is idempotent and will simply
* return if the lock has already been deleted.
*/
public static void delete(Lock lock) {
jpaTm()
.transact(
() -> {
Optional<Lock> loadedLock = load(lock.resourceName, lock.tld);
if (loadedLock.isPresent()) {
jpaTm().getEntityManager().remove(loadedLock.get());
}
});
}
}
@@ -156,7 +156,11 @@ public class PremiumListDao {
RevisionIdAndLabel.create(premiumList.getRevisionId(), label);
try {
Optional<BigDecimal> price = PremiumListCache.cachePremiumEntries.get(revisionIdAndLabel);
return price.map(p -> Money.of(premiumList.getCurrency(), p));
return price.map(
p ->
Money.of(
premiumList.getCurrency(),
p.setScale(premiumList.getCurrency().getDecimalPlaces())));
} catch (InvalidCacheLoadException | ExecutionException e) {
throw new RuntimeException(
String.format(
@@ -1,23 +0,0 @@
// Copyright 2019 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;
/**
* Marker interface for commands that use Cloud Sql.
*
* <p>Just implementing this is sufficient to use Cloud Sql; {@link RegistryTool} will install it as
* needed.
*/
interface CommandWithCloudSql extends CommandWithRemoteApi {}
@@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Predicates.isNull;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
import static google.registry.util.RegistrarUtils.normalizeRegistrarName;
import static java.nio.charset.StandardCharsets.US_ASCII;
@@ -28,6 +29,7 @@ import com.google.common.base.Joiner;
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.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarAddress;
import google.registry.model.registry.Registry;
@@ -53,6 +55,8 @@ import org.joda.time.DateTime;
/** Shared base class for commands to create or update a {@link Registrar}. */
abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
static final FluentLogger logger = FluentLogger.forEnclosingClass();
@Parameter(
description = "Client identifier of the registrar account",
required = true)
@@ -458,4 +462,26 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
stageEntityChange(oldRegistrar, newRegistrar);
}
}
@Override
protected String execute() throws Exception {
// Save registrar to Datastore and output its response
logger.atInfo().log(super.execute());
String cloudSqlMessage;
try {
jpaTm()
.transact(
() ->
getChangedEntities().forEach(newEntity -> saveToCloudSql((Registrar) newEntity)));
cloudSqlMessage =
String.format("Updated %d entities in Cloud SQL.\n", getChangedEntities().size());
} catch (Throwable t) {
cloudSqlMessage = "Unexpected error saving registrar to Cloud SQL from nomulus tool command";
logger.atSevere().withCause(t).log(cloudSqlMessage);
}
return cloudSqlMessage;
}
abstract void saveToCloudSql(Registrar registrar);
}
@@ -62,12 +62,6 @@ public abstract class CreateOrUpdateReservedListCommand extends MutatingCommand
arity = 1)
Boolean shouldPublish;
@Parameter(
names = {"--also_cloud_sql"},
description =
"Persist reserved list to Cloud SQL in addition to Datastore; defaults to false.")
boolean alsoCloudSql;
google.registry.schema.tld.ReservedList cloudSqlReservedList;
abstract void saveToCloudSql();
@@ -78,23 +72,18 @@ public abstract class CreateOrUpdateReservedListCommand extends MutatingCommand
String output = super.execute();
logger.atInfo().log(output);
String cloudSqlMessage;
if (alsoCloudSql) {
String cloudSqlMessage =
String.format(
"Saved reserved list %s with %d entries",
name, cloudSqlReservedList.getLabelsToReservations().size());
try {
logger.atInfo().log("Saving reserved list to Cloud SQL for TLD %s", name);
saveToCloudSql();
logger.atInfo().log(cloudSqlMessage);
} catch (Throwable e) {
cloudSqlMessage =
String.format(
"Saved reserved list %s with %d entries",
name, cloudSqlReservedList.getLabelsToReservations().size());
try {
logger.atInfo().log("Saving reserved list to Cloud SQL for TLD %s", name);
saveToCloudSql();
logger.atInfo().log(cloudSqlMessage);
} catch (Throwable e) {
cloudSqlMessage =
"Unexpected error saving reserved list to Cloud SQL from nomulus tool command";
logger.atSevere().withCause(e).log(cloudSqlMessage);
}
} else {
cloudSqlMessage = "Persisting reserved list to Cloud SQL is not enabled";
"Unexpected error saving reserved list to Cloud SQL from nomulus tool command";
logger.atSevere().withCause(e).log(cloudSqlMessage);
}
return cloudSqlMessage;
}
@@ -32,6 +32,7 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;
import google.registry.config.RegistryEnvironment;
import google.registry.model.registrar.Registrar;
import google.registry.schema.registrar.RegistrarDao;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -69,6 +70,11 @@ final class CreateRegistrarCommand extends CreateOrUpdateRegistrarCommand
registrarState = Optional.ofNullable(registrarState).orElse(ACTIVE);
}
@Override
void saveToCloudSql(Registrar registrar) {
RegistrarDao.saveNew(registrar);
}
@Nullable
@Override
Registrar getOldRegistrar(final String clientId) {
@@ -67,11 +67,9 @@ final class CreateReservedListCommand extends CreateOrUpdateReservedListCommand
.setLastUpdateTime(now)
.build();
stageEntityChange(null, reservedList);
if (alsoCloudSql) {
cloudSqlReservedList =
google.registry.schema.tld.ReservedList.create(
name, shouldPublish, parseToReservationsByLabels(allLines));
}
cloudSqlReservedList =
google.registry.schema.tld.ReservedList.create(
name, shouldPublish, parseToReservationsByLabels(allLines));
}
@Override
@@ -32,12 +32,14 @@ import google.registry.model.registry.RegistryLockDao;
import google.registry.model.reporting.HistoryEntry;
import google.registry.schema.domain.RegistryLock;
import google.registry.util.Clock;
import google.registry.util.StringGenerator;
import java.util.Optional;
import java.util.UUID;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
/**
* Utility class for validating and applying {@link RegistryLock}s.
* Utility functions for validating and applying {@link RegistryLock}s.
*
* <p>For both locks and unlocks, a lock must be requested via the createRegistry*Requst methods
* then verified through the verifyAndApply* methods. These methods will verify that the domain in
@@ -45,9 +47,16 @@ import javax.annotation.Nullable;
*/
public final class DomainLockUtils {
private DomainLockUtils() {}
private static final int VERIFICATION_CODE_LENGTH = 32;
public static RegistryLock createRegistryLockRequest(
private final StringGenerator stringGenerator;
@Inject
public DomainLockUtils(@Named("base58StringGenerator") StringGenerator stringGenerator) {
this.stringGenerator = stringGenerator;
}
public RegistryLock createRegistryLockRequest(
String domainName,
String registrarId,
@Nullable String registrarPocId,
@@ -68,7 +77,7 @@ public final class DomainLockUtils {
RegistryLock lock =
new RegistryLock.Builder()
.setVerificationCode(UUID.randomUUID().toString())
.setVerificationCode(stringGenerator.createString(VERIFICATION_CODE_LENGTH))
.setDomainName(domainName)
.setRepoId(domainBase.getRepoId())
.setRegistrarId(registrarId)
@@ -78,7 +87,7 @@ public final class DomainLockUtils {
return RegistryLockDao.save(lock);
}
public static RegistryLock createRegistryUnlockRequest(
public RegistryLock createRegistryUnlockRequest(
String domainName, String registrarId, boolean isAdmin, Clock clock) {
DomainBase domainBase = getDomain(domainName, clock);
Optional<RegistryLock> lockOptional =
@@ -121,7 +130,7 @@ public final class DomainLockUtils {
}
RegistryLock newLock =
newLockBuilder
.setVerificationCode(UUID.randomUUID().toString())
.setVerificationCode(stringGenerator.createString(VERIFICATION_CODE_LENGTH))
.isSuperuser(isAdmin)
.setUnlockRequestTimestamp(clock.nowUtc())
.setRegistrarId(registrarId)
@@ -129,8 +138,7 @@ public final class DomainLockUtils {
return RegistryLockDao.save(newLock);
}
public static RegistryLock verifyAndApplyLock(
String verificationCode, boolean isAdmin, Clock clock) {
public RegistryLock verifyAndApplyLock(String verificationCode, boolean isAdmin, Clock clock) {
return jpaTm()
.transact(
() -> {
@@ -156,8 +164,7 @@ public final class DomainLockUtils {
});
}
public static RegistryLock verifyAndApplyUnlock(
String verificationCode, boolean isAdmin, Clock clock) {
public RegistryLock verifyAndApplyUnlock(String verificationCode, boolean isAdmin, Clock clock) {
return jpaTm()
.transact(
() -> {
@@ -60,11 +60,11 @@ public class LockDomainCommand extends LockOrUnlockDomainCommand {
@Override
protected RegistryLock createLock(String domain) {
return DomainLockUtils.createRegistryLockRequest(domain, clientId, null, true, clock);
return domainLockUtils.createRegistryLockRequest(domain, clientId, null, true, clock);
}
@Override
protected void finalizeLockOrUnlockRequest(RegistryLock lock) {
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true, clock);
domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true, clock);
}
}
@@ -34,11 +34,11 @@ import javax.inject.Inject;
/** Shared base class for commands to registry lock or unlock a domain via EPP. */
public abstract class LockOrUnlockDomainCommand extends ConfirmingCommand
implements CommandWithCloudSql {
implements CommandWithRemoteApi {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
static final ImmutableSet<StatusValue> REGISTRY_LOCK_STATUSES =
public static final ImmutableSet<StatusValue> REGISTRY_LOCK_STATUSES =
ImmutableSet.of(
SERVER_DELETE_PROHIBITED, SERVER_TRANSFER_PROHIBITED, SERVER_UPDATE_PROHIBITED);
@@ -57,6 +57,8 @@ public abstract class LockOrUnlockDomainCommand extends ConfirmingCommand
@Inject Clock clock;
@Inject DomainLockUtils domainLockUtils;
protected ImmutableSet<String> relevantDomains = ImmutableSet.of();
protected ImmutableSet<String> getDomains() {
@@ -20,6 +20,7 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Strings.emptyToNull;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DatastoreServiceUtils.getNameOrId;
@@ -45,9 +46,9 @@ import javax.annotation.Nullable;
public abstract class MutatingCommand extends ConfirmingCommand implements CommandWithRemoteApi {
/**
* A mutation of a specific entity, represented by an old and a new version of the entity.
* Storing the old version is necessary to enable checking that the existing entity has not been
* modified when applying a mutation that was created outside the same transaction.
* A mutation of a specific entity, represented by an old and a new version of the entity. Storing
* the old version is necessary to enable checking that the existing entity has not been modified
* when applying a mutation that was created outside the same transaction.
*/
private static class EntityChange {
@@ -169,8 +170,8 @@ public abstract class MutatingCommand extends ConfirmingCommand implements Comma
}
/**
* Returns a set of lists of EntityChange actions to commit. Each list should be executed in
* order inside a single transaction.
* Returns a set of lists of EntityChange actions to commit. Each list should be executed in order
* inside a single transaction.
*/
private ImmutableSet<ImmutableList<EntityChange>> getCollatedEntityChangeBatches() {
ImmutableSet.Builder<ImmutableList<EntityChange>> batches = new ImmutableSet.Builder<>();
@@ -221,4 +222,11 @@ public abstract class MutatingCommand extends ConfirmingCommand implements Comma
? "No entity changes to apply."
: changedEntitiesMap.values().stream().map(Object::toString).collect(joining("\n"));
}
/** Returns the collection of the new entity in the {@link EntityChange}. */
protected ImmutableList<ImmutableObject> getChangedEntities() {
return changedEntitiesMap.values().stream()
.map(entityChange -> entityChange.newEntity)
.collect(toImmutableList());
}
}
@@ -212,8 +212,6 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
}
// CommandWithRemoteApis need to have the remote api installed to work.
// CommandWithCloudSql extends CommandWithRemoteApi so the command will also get the remote
// api installed. This is because the DB password is stored in Datastore.
if (command instanceof CommandWithRemoteApi) {
if (installer == null) {
installer = new RemoteApiInstaller();
@@ -235,9 +233,10 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
// Make sure we start the command with a clean cache, so that any previous command won't
// interfere with this one.
ofy().clearSessionCache();
}
if (command instanceof CommandWithCloudSql) {
// Enable Cloud SQL for command that needs remote API as they will very likely use
// Cloud SQL after the database migration. Note that the DB password is stored in Datastore
// and it is already initialized above.
RegistryToolEnvironment.get().enableJpaTm();
}
@@ -60,11 +60,11 @@ public class UnlockDomainCommand extends LockOrUnlockDomainCommand {
@Override
protected RegistryLock createLock(String domain) {
return DomainLockUtils.createRegistryUnlockRequest(domain, clientId, true, clock);
return domainLockUtils.createRegistryUnlockRequest(domain, clientId, true, clock);
}
@Override
protected void finalizeLockOrUnlockRequest(RegistryLock lock) {
DomainLockUtils.verifyAndApplyUnlock(lock.getVerificationCode(), true, clock);
domainLockUtils.verifyAndApplyUnlock(lock.getVerificationCode(), true, clock);
}
}
@@ -29,7 +29,7 @@ import org.joda.time.DateTime;
/** Modifies {@link Cursor} timestamps used by locking rolling cursor tasks, like in RDE. */
@Parameters(separators = " =", commandDescription = "Modifies cursor timestamps used by LRC tasks")
final class UpdateCursorsCommand extends ConfirmingCommand implements CommandWithCloudSql {
final class UpdateCursorsCommand extends ConfirmingCommand implements CommandWithRemoteApi {
@Parameter(description = "TLDs on which to operate. Omit for global cursors.")
private List<String> tlds;
@@ -20,6 +20,7 @@ import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
import com.beust.jcommander.Parameters;
import google.registry.config.RegistryEnvironment;
import google.registry.model.registrar.Registrar;
import google.registry.schema.registrar.RegistrarDao;
import javax.annotation.Nullable;
/** Command to update a Registrar. */
@@ -49,4 +50,9 @@ final class UpdateRegistrarCommand extends CreateOrUpdateRegistrarCommand {
+ " contact.");
}
}
@Override
void saveToCloudSql(Registrar registrar) {
RegistrarDao.update(registrar);
}
}
@@ -50,11 +50,9 @@ final class UpdateReservedListCommand extends CreateOrUpdateReservedListCommand
.setLastUpdateTime(new SystemClock().nowUtc())
.setShouldPublish(shouldPublish);
stageEntityChange(existing.get(), updated.build());
if (alsoCloudSql) {
cloudSqlReservedList =
google.registry.schema.tld.ReservedList.create(
name, shouldPublish, parseToReservationsByLabels(allLines));
}
cloudSqlReservedList =
google.registry.schema.tld.ReservedList.create(
name, shouldPublish, parseToReservationsByLabels(allLines));
}
@Override
@@ -32,16 +32,11 @@ import java.util.List;
/** A command to upload a {@link ClaimsListShard}. */
@Parameters(separators = " =", commandDescription = "Manually upload a new claims list file")
final class UploadClaimsListCommand extends ConfirmingCommand implements CommandWithCloudSql {
final class UploadClaimsListCommand extends ConfirmingCommand implements CommandWithRemoteApi {
@Parameter(description = "Claims list filename")
private List<String> mainParameters = new ArrayList<>();
@Parameter(
names = {"--also_cloud_sql"},
description = "Persist claims list to Cloud SQL in addition to Datastore; defaults to false.")
boolean alsoCloudSql;
private String claimsListFilename;
private ClaimsList claimsList;
@@ -64,9 +59,7 @@ final class UploadClaimsListCommand extends ConfirmingCommand implements Command
@Override
public String execute() {
ClaimsListShard.create(claimsList.getTmdbGenerationTime(), claimsList.getLabelsToKeys()).save();
if (alsoCloudSql) {
ClaimsListDao.trySave(claimsList);
}
ClaimsListDao.trySave(claimsList);
return String.format("Successfully uploaded claims list %s", claimsListFilename);
}
}
@@ -14,7 +14,7 @@
package google.registry.ui.server.registrar;
import static google.registry.request.RequestParameters.extractBooleanParameter;
import static google.registry.request.RequestParameters.extractOptionalIntParameter;
import static google.registry.request.RequestParameters.extractOptionalParameter;
import static google.registry.request.RequestParameters.extractRequiredParameter;
@@ -144,4 +144,16 @@ public final class RegistrarConsoleModule {
static Optional<String> provideOptionalPasscode(HttpServletRequest req) {
return extractOptionalParameter(req, "passcode");
}
@Provides
@Parameter("lockVerificationCode")
static String provideLockVerificationCode(HttpServletRequest req) {
return extractRequiredParameter(req, "lockVerificationCode");
}
@Provides
@Parameter("isLock")
static Boolean provideIsLock(HttpServletRequest req) {
return extractBooleanParameter(req, "isLock");
}
}
@@ -119,8 +119,9 @@ public class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonA
// handler, registrar-settings really only supports read and update.
String op = Optional.ofNullable((String) input.get(OP_PARAM)).orElse("read");
@SuppressWarnings("unchecked")
Map<String, ?> args = (Map<String, Object>)
Optional.<Object>ofNullable(input.get(ARGS_PARAM)).orElse(ImmutableMap.of());
Map<String, ?> args =
(Map<String, Object>)
Optional.<Object>ofNullable(input.get(ARGS_PARAM)).orElse(ImmutableMap.of());
logger.atInfo().log("Received request '%s' on registrar '%s' with args %s", op, clientId, args);
String status = "SUCCESS";
@@ -296,8 +297,7 @@ public class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonA
.ifPresent(builder::setEmailAddress);
builder.setPhoneNumber(
RegistrarFormFields.PHONE_NUMBER_FIELD.extractUntyped(args).orElse(null));
builder.setFaxNumber(
RegistrarFormFields.FAX_NUMBER_FIELD.extractUntyped(args).orElse(null));
builder.setFaxNumber(RegistrarFormFields.FAX_NUMBER_FIELD.extractUntyped(args).orElse(null));
builder.setLocalizedAddress(
RegistrarFormFields.L10N_ADDRESS_FIELD.extractUntyped(args).orElse(null));
@@ -357,9 +357,7 @@ public class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonA
* <p>On success, returns {@code builder.build()}.
*/
private Registrar checkNotChangedUnlessAllowed(
Registrar.Builder builder,
Registrar originalRegistrar,
Role allowedRole) {
Registrar.Builder builder, Registrar originalRegistrar, Role allowedRole) {
Registrar updatedRegistrar = builder.build();
if (updatedRegistrar.equals(originalRegistrar)) {
return updatedRegistrar;
@@ -374,9 +372,7 @@ public class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonA
}
Map<?, ?> diffs =
DiffUtils.deepDiff(
originalRegistrar.toDiffableFieldMap(),
updatedRegistrar.toDiffableFieldMap(),
true);
originalRegistrar.toDiffableFieldMap(), updatedRegistrar.toDiffableFieldMap(), true);
throw new ForbiddenException(
String.format("Unauthorized: only %s can change fields %s", allowedRole, diffs.keySet()));
}
@@ -400,9 +396,10 @@ public class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonA
Set<String> emails = new HashSet<>();
for (RegistrarContact contact : updatedContacts) {
if (!emails.add(contact.getEmailAddress())) {
throw new ContactRequirementException(String.format(
"One email address (%s) cannot be used for multiple contacts",
contact.getEmailAddress()));
throw new ContactRequirementException(
String.format(
"One email address (%s) cannot be used for multiple contacts",
contact.getEmailAddress()));
}
}
// Check that required contacts don't go away, once they are set.
@@ -0,0 +1,216 @@
// 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.ui.server.registrar;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.security.JsonResponseHelper.Status.ERROR;
import static google.registry.security.JsonResponseHelper.Status.SUCCESS;
import static google.registry.ui.server.registrar.RegistrarConsoleModule.PARAM_CLIENT_ID;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
import com.google.gson.Gson;
import google.registry.config.RegistryConfig;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarContact;
import google.registry.request.Action;
import google.registry.request.Action.Method;
import google.registry.request.HttpException.ForbiddenException;
import google.registry.request.JsonActionRunner;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthResult;
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
import google.registry.request.auth.AuthenticatedRegistrarAccessor.RegistrarAccessDeniedException;
import google.registry.request.auth.UserAuthInfo;
import google.registry.schema.domain.RegistryLock;
import google.registry.security.JsonResponseHelper;
import google.registry.tools.DomainLockUtils;
import google.registry.util.Clock;
import google.registry.util.EmailMessage;
import google.registry.util.SendEmailService;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Map;
import java.util.Optional;
import javax.inject.Inject;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import org.apache.http.client.utils.URIBuilder;
/**
* UI action that allows for creating registry locks. Locks / unlocks must be verified separately
* before they are written permanently.
*
* <p>Note: at the moment we have no mechanism for JSON GET/POSTs in the same class or at the same
* URL, which is why this is distinct from the {@link RegistryLockGetAction}.
*/
@Action(
service = Action.Service.DEFAULT,
path = RegistryLockPostAction.PATH,
method = Method.POST,
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
public class RegistryLockPostAction implements Runnable, JsonActionRunner.JsonAction {
public static final String PATH = "/registry-lock-post";
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final Gson GSON = new Gson();
private static final URL URL_BASE = RegistryConfig.getDefaultServer();
private static final String VERIFICATION_EMAIL_TEMPLATE =
"Please click the link below to perform the lock / unlock action on domain %s. Note: "
+ "this code will expire in one hour.\n\n%s";
private final JsonActionRunner jsonActionRunner;
private final AuthResult authResult;
private final AuthenticatedRegistrarAccessor registrarAccessor;
private final SendEmailService sendEmailService;
private final Clock clock;
private final DomainLockUtils domainLockUtils;
private final InternetAddress gSuiteOutgoingEmailAddress;
@Inject
RegistryLockPostAction(
JsonActionRunner jsonActionRunner,
AuthResult authResult,
AuthenticatedRegistrarAccessor registrarAccessor,
SendEmailService sendEmailService,
Clock clock,
DomainLockUtils domainLockUtils,
@Config("gSuiteOutgoingEmailAddress") InternetAddress gSuiteOutgoingEmailAddress) {
this.jsonActionRunner = jsonActionRunner;
this.authResult = authResult;
this.registrarAccessor = registrarAccessor;
this.sendEmailService = sendEmailService;
this.clock = clock;
this.domainLockUtils = domainLockUtils;
this.gSuiteOutgoingEmailAddress = gSuiteOutgoingEmailAddress;
}
@Override
public void run() {
jsonActionRunner.run(this);
}
@Override
public Map<String, ?> handleJsonRequest(Map<String, ?> input) {
try {
checkArgumentNotNull(input, "Null JSON");
RegistryLockPostInput postInput =
GSON.fromJson(GSON.toJsonTree(input), RegistryLockPostInput.class);
checkArgument(
!Strings.isNullOrEmpty(postInput.clientId),
"Missing key for client: %s",
PARAM_CLIENT_ID);
checkArgument(
!Strings.isNullOrEmpty(postInput.fullyQualifiedDomainName),
"Missing key for fullyQualifiedDomainName");
checkNotNull(postInput.isLock, "Missing key for isLock");
UserAuthInfo userAuthInfo =
authResult
.userAuthInfo()
.orElseThrow(() -> new ForbiddenException("User is not logged in"));
boolean isAdmin = userAuthInfo.isUserAdmin();
String userEmail = userAuthInfo.user().getEmail();
if (!isAdmin) {
verifyRegistryLockPassword(postInput, userEmail);
}
jpaTm()
.transact(
() -> {
RegistryLock registryLock =
postInput.isLock
? domainLockUtils.createRegistryLockRequest(
postInput.fullyQualifiedDomainName,
postInput.clientId,
userEmail,
isAdmin,
clock)
: domainLockUtils.createRegistryUnlockRequest(
postInput.fullyQualifiedDomainName, postInput.clientId, isAdmin, clock);
sendVerificationEmail(registryLock, userEmail, postInput.isLock);
});
String action = postInput.isLock ? "lock" : "unlock";
return JsonResponseHelper.create(SUCCESS, String.format("Successful %s", action));
} catch (Throwable e) {
logger.atWarning().withCause(e).log("Failed to lock/unlock domain with input %s", input);
return JsonResponseHelper.create(
ERROR,
Optional.ofNullable(Throwables.getRootCause(e).getMessage()).orElse("Unspecified error"));
}
}
private void sendVerificationEmail(RegistryLock lock, String userEmail, boolean isLock) {
try {
String url =
new URIBuilder()
.setScheme("https")
.setHost(URL_BASE.getHost())
.setPath("registry-lock-verify")
.setParameter("lockVerificationCode", lock.getVerificationCode())
.setParameter("isLock", String.valueOf(isLock))
.build()
.toString();
String body = String.format(VERIFICATION_EMAIL_TEMPLATE, lock.getDomainName(), url);
ImmutableList<InternetAddress> recipients =
ImmutableList.of(new InternetAddress(userEmail, true));
String action = isLock ? "lock" : "unlock";
sendEmailService.sendEmail(
EmailMessage.newBuilder()
.setBody(body)
.setSubject(String.format("Registry %s verification", action))
.setRecipients(recipients)
.setFrom(gSuiteOutgoingEmailAddress)
.build());
} catch (AddressException | URISyntaxException e) {
throw new RuntimeException(e); // caught above -- this is so we can run in a transaction
}
}
private void verifyRegistryLockPassword(RegistryLockPostInput postInput, String userEmail)
throws RegistrarAccessDeniedException {
// Verify that the user can access the registrar and that the user has
// registry lock enabled and provided a correct password
Registrar registrar = registrarAccessor.getRegistrar(postInput.clientId);
checkArgument(
registrar.isRegistryLockAllowed(), "Registry lock not allowed for this registrar");
checkArgument(!Strings.isNullOrEmpty(postInput.password), "Missing key for password");
RegistrarContact registrarContact =
registrar.getContacts().stream()
.filter(contact -> contact.getEmailAddress().equals(userEmail))
.findFirst()
.orElseThrow(
() ->
new IllegalArgumentException(
String.format("Unknown user email %s", userEmail)));
checkArgument(
registrarContact.verifyRegistryLockPassword(postInput.password),
"Incorrect registry lock password for contact");
}
/** Value class that represents the expected input body from the UI request. */
private static class RegistryLockPostInput {
private String clientId;
private String fullyQualifiedDomainName;
private Boolean isLock;
private String password;
}
}
@@ -0,0 +1,100 @@
// 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.ui.server.registrar;
import static google.registry.ui.server.SoyTemplateUtils.CSS_RENAMING_MAP_SUPPLIER;
import com.google.common.base.Supplier;
import com.google.common.base.Throwables;
import com.google.common.flogger.FluentLogger;
import com.google.template.soy.tofu.SoyTofu;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.schema.domain.RegistryLock;
import google.registry.tools.DomainLockUtils;
import google.registry.ui.server.SoyTemplateUtils;
import google.registry.ui.soy.registrar.RegistryLockVerificationSoyInfo;
import google.registry.util.Clock;
import java.util.HashMap;
import javax.inject.Inject;
/** Action that allows for verification of registry lock / unlock requests */
@Action(
service = Action.Service.DEFAULT,
path = RegistryLockVerifyAction.PATH,
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
public final class RegistryLockVerifyAction extends HtmlAction {
public static final String PATH = "/registry-lock-verify";
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final Supplier<SoyTofu> TOFU_SUPPLIER =
SoyTemplateUtils.createTofuSupplier(
google.registry.ui.soy.ConsoleSoyInfo.getInstance(),
google.registry.ui.soy.AnalyticsSoyInfo.getInstance(),
google.registry.ui.soy.registrar.RegistryLockVerificationSoyInfo.getInstance());
private final Clock clock;
private final DomainLockUtils domainLockUtils;
private final String lockVerificationCode;
private final Boolean isLock;
@Inject
public RegistryLockVerifyAction(
Clock clock,
DomainLockUtils domainLockUtils,
@Parameter("lockVerificationCode") String lockVerificationCode,
@Parameter("isLock") Boolean isLock) {
this.clock = clock;
this.domainLockUtils = domainLockUtils;
this.lockVerificationCode = lockVerificationCode;
this.isLock = isLock;
}
@Override
public void runAfterLogin(HashMap<String, Object> data) {
try {
boolean isAdmin = authResult.userAuthInfo().get().isUserAdmin();
final RegistryLock resultLock;
if (isLock) {
resultLock = domainLockUtils.verifyAndApplyLock(lockVerificationCode, isAdmin, clock);
} else {
resultLock = domainLockUtils.verifyAndApplyUnlock(lockVerificationCode, isAdmin, clock);
}
data.put("isLock", isLock);
data.put("success", true);
data.put("fullyQualifiedDomainName", resultLock.getDomainName());
} catch (Throwable t) {
logger.atWarning().withCause(t).log(
"Error when verifying verification code %s", lockVerificationCode);
data.put("success", false);
data.put("errorMessage", Throwables.getRootCause(t).getMessage());
}
response.setPayload(
TOFU_SUPPLIER
.get()
.newRenderer(RegistryLockVerificationSoyInfo.VERIFICATION_PAGE)
.setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get())
.setData(data)
.render());
}
@Override
public String getPath() {
return PATH;
}
}
@@ -20,26 +20,28 @@
* Use Hibernate's ServiceRegistry for bootstrapping (not JPA-compliant)
-->
<class>google.registry.model.domain.DomainBase</class>
<class>google.registry.model.registrar.Registrar</class>
<class>google.registry.model.registrar.RegistrarContact</class>
<class>google.registry.schema.domain.RegistryLock</class>
<class>google.registry.schema.tmch.ClaimsList</class>
<class>google.registry.schema.cursor.Cursor</class>
<class>google.registry.model.transfer.BaseTransferObject</class>
<class>google.registry.schema.server.Lock</class>
<class>google.registry.schema.tld.PremiumList</class>
<class>google.registry.schema.tld.PremiumEntry</class>
<class>google.registry.schema.tld.ReservedList</class>
<class>google.registry.model.domain.secdns.DelegationSignerData</class>
<class>google.registry.model.domain.DesignatedContact</class>
<class>google.registry.model.domain.DomainBase</class>
<class>google.registry.model.domain.GracePeriod</class>
<class>org.joda.time.Period</class>
<class>google.registry.model.transfer.TransferData</class>
<class>google.registry.model.eppcommon.Trid</class>
<!-- Customized type converters -->
<class>google.registry.persistence.BloomFilterConverter</class>
<class>google.registry.persistence.CidrAddressBlockListConverter</class>
<class>google.registry.persistence.CreateAutoTimestampConverter</class>
<class>google.registry.persistence.CurrencyUnitConverter</class>
<class>google.registry.persistence.DateTimeConverter</class>
<class>google.registry.persistence.RegistrarPocSetConverter</class>
<class>google.registry.persistence.StatusValueSetConverter</class>
<class>google.registry.persistence.StringListConverter</class>
<class>google.registry.persistence.StringSetConverter</class>
<class>google.registry.persistence.UpdateAutoTimestampConverter</class>
<class>google.registry.persistence.ZonedDateTimeConverter</class>
@@ -0,0 +1,71 @@
// Copyright 2019 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.
{namespace registry.soy.registrar.registrylock.verification}
/**
* Results page for a registry lock/unlock verification.
*/
{template .verificationPage}
{@param username: string} /** Arbitrary username to display. */
{@param analyticsConfig: [googleAnalyticsId: string|null]}
{@param success: bool}
{@param? errorMessage: string}
{@param? isLock: bool}
{@param? fullyQualifiedDomainName: string}
{call registry.soy.console.header}
{param app: 'registrar' /}
{param subtitle: 'Verify Registry Lock' /}
{param analyticsConfig: $analyticsConfig /}
{/call}
{call registry.soy.console.googlebar data="all" /}
<div id="reg-content-and-footer">
<div id="reg-content">
<h1>Registry Lock Verification</h1>
{if $success}
{call .success data="all" /}
{else}
{call .failure data="all" /}
{/if}
<h3><a href="/registrar">Return to Registrar Console</a></h3>
</div>
{call registry.soy.console.footer /}
</div>
{/template}
/**
* Result page for failure, e.g. the UUID was invalid
*/
{template .failure}
{@param? errorMessage: string}
<h2 class="{css('kd-errormessage')}">Failed: {if isNonnull($errorMessage)}
{$errorMessage}
{else}
Undefined error message
{/if}
</h2>
{/template}
/**
* Result page for a successful lock / unlock.
*/
{template .success}
{@param? isLock: bool}
{@param? fullyQualifiedDomainName: string}
<h3>
Success: {if $isLock}lock{else}unlock{/if} has been applied to {$fullyQualifiedDomainName}
</h3>
{/template}
@@ -26,7 +26,9 @@ public class DevTool {
* any invocations in scripts (e.g. PDT, ICANN reporting).
*/
public static final ImmutableMap<String, Class<? extends Command>> COMMAND_MAP =
ImmutableMap.of("generate_sql_schema", GenerateSqlSchemaCommand.class);
ImmutableMap.of(
"dump_golden_schema", DumpGoldenSchemaCommand.class,
"generate_sql_schema", GenerateSqlSchemaCommand.class);
public static void main(String[] args) throws Exception {
RegistryToolEnvironment.parseFromArgs(args).setup();
@@ -0,0 +1,91 @@
// 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 com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import org.flywaydb.core.Flyway;
import org.testcontainers.containers.BindMode;
import org.testcontainers.containers.Container;
/**
* Generates a schema for JPA annotated classes using Hibernate.
*
* <p>Note that this isn't complete yet, as all of the persistent classes have not yet been
* converted. After converting a class, a call to "addAnnotatedClass()" for the new class must be
* added to the code below.
*/
@Parameters(separators = " =", commandDescription = "Dump golden schema.")
public class DumpGoldenSchemaCommand extends PostgresqlCommand {
// The mount point in the container.
private static final String CONTAINER_MOUNT_POINT = "/tmp/pg_dump.out";
@Parameter(
names = {"--output", "-o"},
description = "Output file",
required = true)
Path output;
@Override
void runCommand() throws IOException, InterruptedException {
Flyway flyway =
Flyway.configure()
.locations("sql/flyway")
.dataSource(
postgresContainer.getJdbcUrl(),
postgresContainer.getUsername(),
postgresContainer.getPassword())
.load();
flyway.migrate();
String userName = postgresContainer.getUsername();
String databaseName = postgresContainer.getDatabaseName();
Container.ExecResult result =
postgresContainer.execInContainer(getSchemaDumpCommand(userName, databaseName));
if (result.getExitCode() != 0) {
throw new RuntimeException(result.toString());
}
}
@Override
protected void onContainerCreate() throws IOException {
// open the output file for write so we can mount it.
new FileOutputStream(output.toFile()).close();
postgresContainer.withFileSystemBind(
output.toString(), CONTAINER_MOUNT_POINT, BindMode.READ_WRITE);
}
private static String[] getSchemaDumpCommand(String username, String dbName) {
return new String[] {
"pg_dump",
"-h",
"localhost",
"-U",
username,
"-f",
CONTAINER_MOUNT_POINT,
"--schema-only",
"--no-owner",
"--no-privileges",
"--exclude-table",
"flyway_schema_history",
dbName
};
}
}
@@ -0,0 +1,128 @@
// Copyright 2019 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.tools;
import com.beust.jcommander.Parameter;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.flogger.FluentLogger;
import google.registry.persistence.NomulusPostgreSql;
import org.testcontainers.containers.PostgreSQLContainer;
/** Base class for commands that need a PostgreSQL database. */
public abstract class PostgresqlCommand implements Command {
static final FluentLogger logger = FluentLogger.forEnclosingClass();
protected static final String DB_NAME = "postgres";
protected static final String DB_USERNAME = "postgres";
protected static final String DB_PASSWORD = "domain-registry";
@VisibleForTesting
public static final String DB_OPTIONS_CLASH =
"Database host and port may not be specified along with the option to start a "
+ "PostgreSQL container.";
@VisibleForTesting public static final int POSTGRESQL_PORT = 5432;
protected PostgreSQLContainer postgresContainer = null;
@Parameter(
names = {"-s", "--start_postgresql"},
description = "If specified, start PostgreSQL in a Docker container.")
boolean startPostgresql = false;
@Parameter(
names = {"-a", "--db_host"},
description = "Database host name.")
String databaseHost;
@Parameter(
names = {"-p", "--db_port"},
description = "Database port number. This defaults to the PostgreSQL default port.")
Integer databasePort;
/**
* Starts the database if appropriate.
*
* <p>Returns true if the database was successfully initialized, false if not.
*/
private boolean initializeDatabase() {
// Start PostgreSQL if requested.
if (startPostgresql) {
// Complain if the user has also specified either --db_host or --db_port.
if (databaseHost != null || databasePort != null) {
System.err.println(DB_OPTIONS_CLASH);
// TODO: it would be nice to exit(1) here, but this breaks testability.
return false;
}
// Start the container and store the address information.
postgresContainer =
new PostgreSQLContainer(NomulusPostgreSql.getDockerTag())
.withDatabaseName(DB_NAME)
.withUsername(DB_USERNAME)
.withPassword(DB_PASSWORD);
try {
onContainerCreate();
} catch (Exception e) {
logger.atSevere().withCause(e).log("Error in container callback hook.");
return false;
}
postgresContainer.start();
databaseHost = postgresContainer.getContainerIpAddress();
databasePort = postgresContainer.getMappedPort(POSTGRESQL_PORT);
} else if (databaseHost == null) {
System.err.println(
"You must specify either --start_postgresql to start a PostgreSQL database in a\n"
+ "docker instance, or specify --db_host (and, optionally, --db_port) to identify\n"
+ "the location of a running instance. To start a long-lived instance (suitable\n"
+ "for running this command multiple times) run this:\n\n"
+ " docker run --rm --name some-postgres -e POSTGRES_PASSWORD=domain-registry \\\n"
+ " -d "
+ NomulusPostgreSql.getDockerTag()
+ "\n\nCopy the container id output from the command, then run:\n\n"
+ " docker inspect <container-id> | grep IPAddress\n\n"
+ "To obtain the value for --db-host.\n");
// TODO(mmuller): need exit(1), see above.
return false;
}
// use the default port if non has been defined.
if (databasePort == null) {
databasePort = POSTGRESQL_PORT;
}
return true;
}
@Override
public void run() throws Exception {
if (!initializeDatabase()) {
return;
}
try {
runCommand();
} finally {
if (postgresContainer != null) {
postgresContainer.stop();
}
}
}
/** Called after the container has been created but before it has been started. */
protected void onContainerCreate() throws Exception {}
/** Command to be run while the database is running. */
abstract void runCommand() throws Exception;
}
@@ -0,0 +1,121 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.model.domain;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key;
import google.registry.model.EntityTestCase;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DesignatedContact.Type;
import google.registry.model.domain.launch.LaunchNotice;
import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
import google.registry.model.eppcommon.StatusValue;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageRule;
import javax.persistence.EntityManager;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Verify that we can store/retrieve DomainBase objects from a SQL database. */
@RunWith(JUnit4.class)
public class DomainBaseSqlTest extends EntityTestCase {
@Rule
public final JpaIntegrationWithCoverageRule jpaRule =
new JpaTestRules.Builder().buildIntegrationWithCoverageRule();
DomainBase domain;
Key<ContactResource> contactKey;
Key<ContactResource> contact2Key;
@Before
public void setUp() {
contactKey = Key.create(ContactResource.class, "contact_id1");
contact2Key = Key.create(ContactResource.class, "contact_id2");
domain =
new DomainBase.Builder()
.setFullyQualifiedDomainName("example.com")
.setRepoId("4-COM")
.setCreationClientId("a registrar")
.setLastEppUpdateTime(clock.nowUtc())
.setLastEppUpdateClientId("AnotherRegistrar")
.setLastTransferTime(clock.nowUtc())
.setStatusValues(
ImmutableSet.of(
StatusValue.CLIENT_DELETE_PROHIBITED,
StatusValue.SERVER_DELETE_PROHIBITED,
StatusValue.SERVER_TRANSFER_PROHIBITED,
StatusValue.SERVER_UPDATE_PROHIBITED,
StatusValue.SERVER_RENEW_PROHIBITED,
StatusValue.SERVER_HOLD))
.setRegistrant(contactKey)
.setContacts(ImmutableSet.of(DesignatedContact.create(Type.ADMIN, contact2Key)))
.setSubordinateHosts(ImmutableSet.of("ns1.example.com"))
.setPersistedCurrentSponsorClientId("losing")
.setRegistrationExpirationTime(clock.nowUtc().plusYears(1))
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("password")))
.setDsData(ImmutableSet.of(DelegationSignerData.create(1, 2, 3, new byte[] {0, 1, 2})))
.setLaunchNotice(
LaunchNotice.create("tcnid", "validatorId", START_OF_TIME, START_OF_TIME))
.setSmdId("smdid")
.build();
}
@Test
public void testDomainBasePersistence() {
jpaTm()
.transact(
() -> {
// Persist the domain.
EntityManager em = jpaTm().getEntityManager();
em.persist(domain);
});
jpaTm()
.transact(
() -> {
// Load the domain in its entirety.
EntityManager em = jpaTm().getEntityManager();
DomainBase result = em.find(DomainBase.class, "4-COM");
// Fix contacts, grace period and DS data, since we can't persist them yet.
result =
result
.asBuilder()
.setRegistrant(contactKey)
.setContacts(
ImmutableSet.of(DesignatedContact.create(Type.ADMIN, contact2Key)))
.setDsData(
ImmutableSet.of(
DelegationSignerData.create(1, 2, 3, new byte[] {0, 1, 2})))
.build();
// Fix the original creation timestamp (this gets initialized on first write)
DomainBase org = domain.asBuilder().setCreationTime(result.getCreationTime()).build();
// Note that the equality comparison forces a lazy load of all fields.
assertThat(result).isEqualTo(org);
});
}
}
@@ -0,0 +1,79 @@
// 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.eppcommon;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableList;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for {@link Address}. */
@RunWith(JUnit4.class)
public class AddressTest {
@Test
public void onLoad_setsIndividualStreetLinesSuccessfully() {
Address address = new Address();
address.onLoad(ImmutableList.of("line1", "line2", "line3"));
assertThat(address.streetLine1).isEqualTo("line1");
assertThat(address.streetLine2).isEqualTo("line2");
assertThat(address.streetLine3).isEqualTo("line3");
}
@Test
public void onLoad_setsOnlyNonNullStreetLines() {
Address address = new Address();
address.onLoad(ImmutableList.of("line1", "line2"));
assertThat(address.streetLine1).isEqualTo("line1");
assertThat(address.streetLine2).isEqualTo("line2");
assertThat(address.streetLine3).isNull();
}
@Test
public void onLoad_doNothingIfInputIsNull() {
Address address = new Address();
address.onLoad(null);
assertThat(address.streetLine1).isNull();
assertThat(address.streetLine2).isNull();
assertThat(address.streetLine3).isNull();
}
@Test
public void postLoad_setsStreetListSuccessfully() {
Address address = new Address();
address.streetLine1 = "line1";
address.streetLine2 = "line2";
address.streetLine3 = "line3";
address.postLoad();
assertThat(address.street).containsExactly("line1", "line2", "line3");
}
@Test
public void postLoad_setsOnlyNonNullStreetLines() {
Address address = new Address();
address.streetLine1 = "line1";
address.streetLine2 = "line2";
address.postLoad();
assertThat(address.street).containsExactly("line1", "line2");
}
@Test
public void postLoad_doNothingIfInputIsNull() {
Address address = new Address();
address.postLoad();
assertThat(address.street).isNull();
}
}
@@ -25,7 +25,6 @@ import google.registry.schema.domain.RegistryLock;
import google.registry.testing.AppEngineRule;
import google.registry.testing.FakeClock;
import java.util.Optional;
import java.util.UUID;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -201,7 +200,7 @@ public final class RegistryLockDaoTest {
.setRepoId("repoId")
.setDomainName("example.test")
.setRegistrarId("TheRegistrar")
.setVerificationCode(UUID.randomUUID().toString())
.setVerificationCode("123456789ABCDEFGHJKLMNPQRSTUVWXY")
.isSuperuser(true)
.build();
}
@@ -0,0 +1,69 @@
// 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;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import com.google.common.collect.ImmutableList;
import google.registry.model.ImmutableObject;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
import google.registry.util.CidrAddressBlock;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link CidrAddressBlockListConverter}. */
@RunWith(JUnit4.class)
public class CidrAddressBlockListUserTypeTest {
@Rule
public final JpaUnitTestRule jpaRule =
new JpaTestRules.Builder().withEntityClass(TestEntity.class).buildUnitTestRule();
@Test
public void roundTripConversion_returnsSameCidrAddressBlock() {
List<CidrAddressBlock> addresses =
ImmutableList.of(
CidrAddressBlock.create("0.0.0.0/32"),
CidrAddressBlock.create("255.255.255.254/31"),
CidrAddressBlock.create("::"),
CidrAddressBlock.create("8000::/1"),
CidrAddressBlock.create("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"));
TestEntity testEntity = new TestEntity(addresses);
jpaTm().transact(() -> jpaTm().getEntityManager().persist(testEntity));
TestEntity persisted =
jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, "id"));
assertThat(persisted.addresses).isEqualTo(addresses);
}
@Entity(name = "TestEntity") // Override entity name to avoid the nested class reference.
private static class TestEntity extends ImmutableObject {
@Id String name = "id";
List<CidrAddressBlock> addresses;
private TestEntity() {}
private TestEntity(List<CidrAddressBlock> addresses) {
this.addresses = addresses;
}
}
}
@@ -0,0 +1,74 @@
// 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;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import com.google.common.collect.ImmutableMap;
import google.registry.model.ImmutableObject;
import google.registry.model.registrar.Registrar.BillingAccountEntry;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
import java.util.Map;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.hibernate.annotations.Type;
import org.joda.money.CurrencyUnit;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link CurrencyToBillingMapUserType}. */
@RunWith(JUnit4.class)
public class CurrencyToBillingMapUserTypeTest {
@Rule
public final JpaUnitTestRule jpaRule =
new JpaTestRules.Builder()
.withInitScript("sql/flyway/V14__load_extension_for_hstore.sql")
.withEntityClass(TestEntity.class)
.buildUnitTestRule();
@Test
public void roundTripConversion_returnsSameCurrencyToBillingMap() {
ImmutableMap<CurrencyUnit, BillingAccountEntry> currencyToBilling =
ImmutableMap.of(
CurrencyUnit.of("USD"),
new BillingAccountEntry(CurrencyUnit.of("USD"), "accountId1"),
CurrencyUnit.of("CNY"),
new BillingAccountEntry(CurrencyUnit.of("CNY"), "accountId2"));
TestEntity testEntity = new TestEntity(currencyToBilling);
jpaTm().transact(() -> jpaTm().getEntityManager().persist(testEntity));
TestEntity persisted =
jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, "id"));
assertThat(persisted.currencyToBilling).containsExactlyEntriesIn(currencyToBilling);
}
@Entity(name = "TestEntity") // Override entity name to avoid the nested class reference.
private static class TestEntity extends ImmutableObject {
@Id String name = "id";
@Type(type = "google.registry.persistence.CurrencyToBillingMapUserType")
Map<CurrencyUnit, BillingAccountEntry> currencyToBilling;
private TestEntity() {}
private TestEntity(Map<CurrencyUnit, BillingAccountEntry> currencyToBilling) {
this.currencyToBilling = currencyToBilling;
}
}
}
@@ -0,0 +1,67 @@
// 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;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.truth.Expect;
import java.util.Collections;
import javax.persistence.AttributeConverter;
import javax.persistence.Entity;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests to verify persistence.xml is valid. */
@RunWith(JUnit4.class)
public class PersistenceXmlTest {
@ClassRule public static final Expect expect = Expect.create();
@Test
public void verifyClassTags_containOnlyRequiredClasses() {
ImmutableList<Class> managedClassed = PersistenceXmlUtility.getManagedClasses();
ImmutableList<Class> unnecessaryClasses =
managedClassed.stream()
.filter(
clazz ->
!clazz.isAnnotationPresent(Entity.class)
&& !AttributeConverter.class.isAssignableFrom(clazz))
.collect(toImmutableList());
ImmutableSet<Class> duplicateClasses =
managedClassed.stream()
.filter(clazz -> Collections.frequency(managedClassed, clazz) > 1)
.collect(toImmutableSet());
expect
.withMessage("Found duplicate <class> tags defined in persistence.xml.")
.that(duplicateClasses)
.isEmpty();
expect
.withMessage(
"Found unnecessary <class> tags defined in persistence.xml. Only entity class and"
+ " implementation of AttributeConverter are required to be added in"
+ " persistence.xml.")
.that(unnecessaryClasses)
.isEmpty();
}
}
@@ -18,29 +18,27 @@ import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import com.google.common.collect.ImmutableSet;
import google.registry.model.eppcommon.StatusValue;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.hibernate.annotations.Type;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link EnumSetUserType}. */
/** Unit tests for {@link StatusValueSetConverter}. */
@RunWith(JUnit4.class)
public class EnumSetUserTypeTest {
public class StatusValueSetConverterTest {
@Rule
public final JpaUnitTestRule jpaRule =
new JpaTestRules.Builder().withEntityClass(TestEntity.class).buildUnitTestRule();
public EnumSetUserTypeTest() {}
@Test
public void testRoundTrip() {
Set<TestEnum> enums = ImmutableSet.of(TestEnum.BAR, TestEnum.FOO);
Set<StatusValue> enums = ImmutableSet.of(StatusValue.INACTIVE, StatusValue.PENDING_DELETE);
TestEntity obj = new TestEntity("foo", enums);
jpaTm().transact(() -> jpaTm().getEntityManager().persist(obj));
@@ -49,29 +47,15 @@ public class EnumSetUserTypeTest {
assertThat(persisted.data).isEqualTo(enums);
}
enum TestEnum {
FOO,
BAR,
BAZ;
public static class TestEnumType extends EnumSetUserType<TestEnum> {
@Override
protected Object convertToElem(Object value) {
return TestEnum.valueOf((String) value);
}
}
}
@Entity(name = "TestEntity")
static class TestEntity {
@Id String name;
@Type(type = "google.registry.persistence.EnumSetUserTypeTest$TestEnum$TestEnumType")
Set<TestEnum> data;
Set<StatusValue> data;
TestEntity() {}
TestEntity(String name, Set<TestEnum> data) {
TestEntity(String name, Set<StatusValue> data) {
this.name = name;
this.data = data;
}
@@ -26,15 +26,14 @@ import java.util.List;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NoResultException;
import org.hibernate.annotations.Type;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link StringListUserType}. */
/** Unit tests for {@link StringListConverter}. */
@RunWith(JUnit4.class)
public class StringListUserTypeTest {
public class StringListConverterTest {
@Rule
public final JpaUnitTestRule jpaRule =
new JpaTestRules.Builder().withEntityClass(TestEntity.class).buildUnitTestRule();
@@ -123,7 +122,6 @@ public class StringListUserTypeTest {
@Id String name = "id";
@Type(type = "google.registry.persistence.StringListUserType")
List<String> tlds;
private TestEntity() {}
@@ -24,15 +24,14 @@ import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.hibernate.annotations.Type;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link StringSetUserType}. */
/** Unit tests for {@link StringSetConverter}. */
@RunWith(JUnit4.class)
public class StringSetUserTypeTest {
public class StringSetConverterTest {
@Rule
public final JpaUnitTestRule jpaRule =
new JpaTestRules.Builder().withEntityClass(TestEntity.class).buildUnitTestRule();
@@ -70,7 +69,6 @@ public class StringSetUserTypeTest {
@Id String name = "id";
@Type(type = "google.registry.persistence.StringSetUserType")
Set<String> tlds;
private TestEntity() {}
@@ -0,0 +1,80 @@
// 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;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
import google.registry.model.ImmutableObject;
import google.registry.model.registrar.Registrar.State;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaUnitTestRule;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link Enumerated} annotation. */
@RunWith(JUnit4.class)
public class StringValueEnumeratedTest {
@Rule
public final JpaUnitTestRule jpaRule =
new JpaTestRules.Builder().withEntityClass(TestEntity.class).buildUnitTestRule();
@Test
public void roundTripConversion_returnsSameEnum() {
TestEntity testEntity = new TestEntity(State.ACTIVE);
jpaTm().transact(() -> jpaTm().getEntityManager().persist(testEntity));
TestEntity persisted =
jpaTm().transact(() -> jpaTm().getEntityManager().find(TestEntity.class, "id"));
assertThat(persisted.state).isEqualTo(State.ACTIVE);
}
@Test
public void testNativeQuery_succeeds() {
TestEntity testEntity = new TestEntity(State.DISABLED);
jpaTm().transact(() -> jpaTm().getEntityManager().persist(testEntity));
assertThat(
jpaTm()
.transact(
() ->
jpaTm()
.getEntityManager()
.createNativeQuery("SELECT state FROM \"TestEntity\" WHERE name = 'id'")
.getSingleResult()))
.isEqualTo("DISABLED");
}
@Entity(name = "TestEntity") // Override entity name to avoid the nested class reference.
private static class TestEntity extends ImmutableObject {
@Id String name = "id";
@Enumerated(EnumType.STRING)
State state;
private TestEntity() {}
private TestEntity(State state) {
this.state = state;
}
}
}
@@ -81,12 +81,8 @@ public class IcannReportingStagingActionTest {
.thenReturn(ImmutableList.of("c", "d"));
}
private static void assertUploadTaskEnqueued(String subdir) {
TaskMatcher matcher =
new TaskMatcher()
.url("/_dr/task/icannReportingUpload")
.method("POST")
.param("subdir", subdir);
private static void assertUploadTaskEnqueued() {
TaskMatcher matcher = new TaskMatcher().url("/_dr/task/icannReportingUpload").method("POST");
assertTasksEnqueued("retryable-cron-tasks", matcher);
}
@@ -103,7 +99,7 @@ public class IcannReportingStagingActionTest {
"Completed staging the following 2 ICANN reports:\na\nb",
new InternetAddress("recipient@example.com"),
new InternetAddress("sender@example.com")));
assertUploadTaskEnqueued("default/dir");
assertUploadTaskEnqueued();
}
@Test
@@ -119,7 +115,7 @@ public class IcannReportingStagingActionTest {
"Completed staging the following 4 ICANN reports:\na\nb\nc\nd",
new InternetAddress("recipient@example.com"),
new InternetAddress("sender@example.com")));
assertUploadTaskEnqueued("default/dir");
assertUploadTaskEnqueued();
}
@Test
@@ -138,7 +134,7 @@ public class IcannReportingStagingActionTest {
"Completed staging the following 4 ICANN reports:\na\nb\nc\nd",
new InternetAddress("recipient@example.com"),
new InternetAddress("sender@example.com")));
assertUploadTaskEnqueued("default/dir");
assertUploadTaskEnqueued();
}
@Test
@@ -78,7 +78,6 @@ public class IcannReportingUploadActionTest {
action.icannReporter = mockReporter;
action.gcsUtils = new GcsUtils(gcsService, 1024);
action.retrier = new Retrier(new FakeSleeper(new FakeClock()), 3);
action.subdir = "icann/monthly/2006-06";
action.reportingBucket = "basin";
action.emailService = emailService;
action.sender = new InternetAddress("sender@example.com");
@@ -151,6 +150,43 @@ public class IcannReportingUploadActionTest {
new InternetAddress("sender@example.com")));
}
@Test
public void testSuccess_january() throws Exception {
clock.setTo(DateTime.parse("2006-01-22T00:30:00Z"));
persistResource(
Cursor.create(
CursorType.ICANN_UPLOAD_ACTIVITY, DateTime.parse("2006-01-01TZ"), Registry.get("tld")));
persistResource(
Cursor.create(
CursorType.ICANN_UPLOAD_TX, DateTime.parse("2006-01-01TZ"), Registry.get("tld")));
writeGcsFile(
gcsService,
new GcsFilename("basin/icann/monthly/2005-12", "tld-transactions-200512.csv"),
PAYLOAD_SUCCESS);
writeGcsFile(
gcsService,
new GcsFilename("basin/icann/monthly/2005-12", "tld-activity-200512.csv"),
PAYLOAD_SUCCESS);
when(mockReporter.send(PAYLOAD_SUCCESS, "tld-activity-200512.csv")).thenReturn(true);
when(mockReporter.send(PAYLOAD_SUCCESS, "tld-transactions-200512.csv")).thenReturn(true);
IcannReportingUploadAction action = createAction();
action.run();
verify(mockReporter).send(PAYLOAD_SUCCESS, "tld-activity-200512.csv");
verify(mockReporter).send(PAYLOAD_SUCCESS, "tld-transactions-200512.csv");
verifyNoMoreInteractions(mockReporter);
verify(emailService)
.sendEmail(
EmailMessage.create(
"ICANN Monthly report upload summary: 2/2 succeeded",
"Report Filename - Upload status:\n"
+ "tld-activity-200512.csv - SUCCESS\n"
+ "tld-transactions-200512.csv - SUCCESS",
new InternetAddress("recipient@example.com"),
new InternetAddress("sender@example.com")));
}
@Test
public void testSuccess_advancesCursor() throws Exception {
writeGcsFile(
@@ -280,15 +316,18 @@ public class IcannReportingUploadActionTest {
@Test
public void testFail_fileNotFound() throws Exception {
clock.setTo(DateTime.parse("2006-01-22T00:30:00Z"));
persistResource(
Cursor.create(
CursorType.ICANN_UPLOAD_ACTIVITY, DateTime.parse("2006-01-01TZ"), Registry.get("tld")));
IcannReportingUploadAction action = createAction();
action.subdir = "somewhere/else";
action.run();
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(
Level.SEVERE,
"Could not upload ICANN_UPLOAD_ACTIVITY report for foo because file"
+ " foo-activity-200606.csv did not exist");
"Could not upload ICANN_UPLOAD_ACTIVITY report for tld because file"
+ " tld-activity-200512.csv did not exist");
}
@Test
@@ -298,7 +337,6 @@ public class IcannReportingUploadActionTest {
CursorType.ICANN_UPLOAD_ACTIVITY, DateTime.parse("2006-08-01TZ"), Registry.get("foo")));
clock.setTo(DateTime.parse("2006-08-01T00:30:00Z"));
IcannReportingUploadAction action = createAction();
action.subdir = "icann/monthly/2006-07";
action.run();
assertAboutLogs()
.that(logHandler)
@@ -15,20 +15,26 @@
package google.registry.schema.integration;
import com.google.common.truth.Expect;
import google.registry.model.domain.DomainBaseSqlTest;
import google.registry.model.registry.RegistryLockDaoTest;
import google.registry.persistence.transaction.JpaEntityCoverage;
import google.registry.schema.cursor.CursorDaoTest;
import google.registry.schema.registrar.RegistrarDaoTest;
import google.registry.schema.server.LockDaoTest;
import google.registry.schema.tld.PremiumListDaoTest;
import google.registry.schema.tld.ReservedListDaoTest;
import google.registry.schema.tmch.ClaimsListDaoTest;
import google.registry.tools.CreateRegistrarCommandTest;
import google.registry.tools.CreateReservedListCommandTest;
import google.registry.tools.DomainLockUtilsTest;
import google.registry.tools.LockDomainCommandTest;
import google.registry.tools.UnlockDomainCommandTest;
import google.registry.tools.UpdateRegistrarCommandTest;
import google.registry.tools.UpdateReservedListCommandTest;
import google.registry.tools.server.CreatePremiumListActionTest;
import google.registry.tools.server.UpdatePremiumListActionTest;
import google.registry.ui.server.registrar.RegistryLockGetActionTest;
import google.registry.ui.server.registrar.RegistryLockVerifyActionTest;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.ClassRule;
@@ -51,16 +57,22 @@ import org.junit.runners.Suite.SuiteClasses;
@SuiteClasses({
ClaimsListDaoTest.class,
CreatePremiumListActionTest.class,
CreateRegistrarCommandTest.class,
CreateReservedListCommandTest.class,
CursorDaoTest.class,
DomainLockUtilsTest.class,
LockDaoTest.class,
LockDomainCommandTest.class,
DomainBaseSqlTest.class,
PremiumListDaoTest.class,
RegistrarDaoTest.class,
RegistryLockDaoTest.class,
RegistryLockGetActionTest.class,
RegistryLockVerifyActionTest.class,
ReservedListDaoTest.class,
UnlockDomainCommandTest.class,
UpdatePremiumListActionTest.class,
UpdateRegistrarCommandTest.class,
UpdateReservedListCommandTest.class
})
public class SqlIntegrationTestSuite {
@@ -0,0 +1,104 @@
// 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.registrar;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import com.google.common.collect.ImmutableList;
import google.registry.model.EntityTestCase;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarAddress;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageRule;
import google.registry.testing.FakeClock;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link RegistrarDao}. */
@RunWith(JUnit4.class)
public class RegistrarDaoTest extends EntityTestCase {
private final FakeClock fakeClock = new FakeClock();
@Rule
public final JpaIntegrationWithCoverageRule jpaRule =
new JpaTestRules.Builder().withClock(fakeClock).buildIntegrationWithCoverageRule();
private Registrar testRegistrar;
@Before
public void setUp() {
testRegistrar =
new Registrar.Builder()
.setType(Registrar.Type.TEST)
.setClientId("registrarId")
.setRegistrarName("registrarName")
.setLocalizedAddress(
new RegistrarAddress.Builder()
.setStreet(ImmutableList.of("123 Example Boulevard."))
.setCity("Williamsburg")
.setState("NY")
.setZip("11211")
.setCountryCode("US")
.build())
.build();
}
@Test
public void saveNew_worksSuccessfully() {
assertThat(RegistrarDao.checkExists("registrarId")).isFalse();
RegistrarDao.saveNew(testRegistrar);
assertThat(RegistrarDao.checkExists("registrarId")).isTrue();
}
@Test
public void update_worksSuccessfully() {
RegistrarDao.saveNew(testRegistrar);
Registrar persisted = RegistrarDao.load("registrarId").get();
assertThat(persisted.getRegistrarName()).isEqualTo("registrarName");
RegistrarDao.update(persisted.asBuilder().setRegistrarName("changedRegistrarName").build());
persisted = RegistrarDao.load("registrarId").get();
assertThat(persisted.getRegistrarName()).isEqualTo("changedRegistrarName");
}
@Test
public void update_throwsExceptionWhenEntityDoesNotExist() {
assertThat(RegistrarDao.checkExists("registrarId")).isFalse();
assertThrows(IllegalArgumentException.class, () -> RegistrarDao.update(testRegistrar));
}
@Test
public void load_worksSuccessfully() {
assertThat(RegistrarDao.checkExists("registrarId")).isFalse();
RegistrarDao.saveNew(testRegistrar);
Registrar persisted = RegistrarDao.load("registrarId").get();
assertThat(persisted.getClientId()).isEqualTo("registrarId");
assertThat(persisted.getRegistrarName()).isEqualTo("registrarName");
assertThat(persisted.getCreationTime()).isEqualTo(fakeClock.nowUtc());
assertThat(persisted.getLocalizedAddress())
.isEqualTo(
new RegistrarAddress.Builder()
.setStreet(ImmutableList.of("123 Example Boulevard."))
.setCity("Williamsburg")
.setState("NY")
.setZip("11211")
.setCountryCode("US")
.build());
}
}
@@ -0,0 +1,129 @@
// 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.server;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageRule;
import google.registry.testing.FakeClock;
import java.util.Optional;
import javax.persistence.RollbackException;
import org.joda.time.Duration;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link Lock}. */
@RunWith(JUnit4.class)
public class LockDaoTest {
private final FakeClock fakeClock = new FakeClock();
@Rule
public final JpaIntegrationWithCoverageRule jpaRule =
new JpaTestRules.Builder().withClock(fakeClock).buildIntegrationWithCoverageRule();
@Test
public void save_worksSuccessfully() {
Lock lock =
Lock.create("testResource", "tld", "testLogId", fakeClock.nowUtc(), Duration.millis(2));
LockDao.saveNew(lock);
Optional<Lock> returnedLock = LockDao.load("testResource", "tld");
assertThat(returnedLock.get().expirationTime).isEqualTo(lock.expirationTime);
assertThat(returnedLock.get().requestLogId).isEqualTo(lock.requestLogId);
}
@Test
public void save_failsWhenLockAlreadyExists() {
Lock lock =
Lock.create("testResource", "tld", "testLogId", fakeClock.nowUtc(), Duration.millis(2));
LockDao.saveNew(lock);
Lock lock2 =
Lock.create("testResource", "tld", "testLogId2", fakeClock.nowUtc(), Duration.millis(4));
RollbackException thrown = assertThrows(RollbackException.class, () -> LockDao.saveNew(lock2));
assertThat(thrown.getCause().getCause().getCause().getMessage())
.contains("duplicate key value violates unique constraint");
}
@Test
public void save_worksSuccesfullyGlobalLock() {
Lock lock =
Lock.createGlobal("testResource", "testLogId", fakeClock.nowUtc(), Duration.millis(2));
LockDao.saveNew(lock);
Optional<Lock> returnedLock = LockDao.load("testResource");
assertThat(returnedLock.get().expirationTime).isEqualTo(lock.expirationTime);
assertThat(returnedLock.get().requestLogId).isEqualTo(lock.requestLogId);
}
@Test
public void load_worksSuccessfully() {
Lock lock =
Lock.create("testResource", "tld", "testLogId", fakeClock.nowUtc(), Duration.millis(2));
LockDao.saveNew(lock);
Optional<Lock> returnedLock = LockDao.load("testResource", "tld");
assertThat(returnedLock.get().expirationTime).isEqualTo(lock.expirationTime);
assertThat(returnedLock.get().requestLogId).isEqualTo(lock.requestLogId);
}
@Test
public void load_worksSuccessfullyGlobalLock() {
Lock lock =
Lock.createGlobal("testResource", "testLogId", fakeClock.nowUtc(), Duration.millis(2));
LockDao.saveNew(lock);
Optional<Lock> returnedLock = LockDao.load("testResource");
assertThat(returnedLock.get().expirationTime).isEqualTo(lock.expirationTime);
assertThat(returnedLock.get().requestLogId).isEqualTo(lock.requestLogId);
}
@Test
public void load_worksSuccesfullyLockDoesNotExist() {
Optional<Lock> returnedLock = LockDao.load("testResource", "tld");
assertThat(returnedLock.isPresent()).isFalse();
}
@Test
public void delete_worksSuccesfully() {
Lock lock =
Lock.create("testResource", "tld", "testLogId", fakeClock.nowUtc(), Duration.millis(2));
LockDao.saveNew(lock);
Optional<Lock> returnedLock = LockDao.load("testResource", "tld");
assertThat(returnedLock.get().expirationTime).isEqualTo(lock.expirationTime);
LockDao.delete(lock);
returnedLock = LockDao.load("testResource", "tld");
assertThat(returnedLock.isPresent()).isFalse();
}
@Test
public void delete_worksSuccessfullyGlobalLock() {
Lock lock =
Lock.createGlobal("testResource", "testLogId", fakeClock.nowUtc(), Duration.millis(2));
LockDao.saveNew(lock);
Optional<Lock> returnedLock = LockDao.load("testResource");
assertThat(returnedLock.get().expirationTime).isEqualTo(lock.expirationTime);
LockDao.delete(lock);
returnedLock = LockDao.load("testResource");
assertThat(returnedLock.isPresent()).isFalse();
}
@Test
public void delete_succeedsLockDoesntExist() {
Lock lock =
Lock.createGlobal("testResource", "testLogId", fakeClock.nowUtc(), Duration.millis(2));
LockDao.delete(lock);
}
}
@@ -202,4 +202,38 @@ public class PremiumListDaoTest {
() -> PremiumListDao.getPremiumPrice("foobar", Registry.get("tld")));
assertThat(thrown).hasMessageThat().isEqualTo("Could not load premium list 'tld'");
}
@Test
public void testGetPremiumPrice_worksForJPY() {
persistResource(
newRegistry("foobar", "FOOBAR")
.asBuilder()
.setPremiumListKey(
Key.create(
getCrossTldKey(),
google.registry.model.registry.label.PremiumList.class,
"premlist"))
.build());
PremiumListDao.saveNew(
PremiumList.create(
"premlist",
JPY,
ImmutableMap.of(
"silver",
BigDecimal.valueOf(10.00),
"gold",
BigDecimal.valueOf(1000.0),
"palladium",
BigDecimal.valueOf(15000))));
assertThat(PremiumListDao.getPremiumPrice("silver", Registry.get("foobar")))
.hasValue(moneyOf(JPY, 10));
assertThat(PremiumListDao.getPremiumPrice("gold", Registry.get("foobar")))
.hasValue(moneyOf(JPY, 1000));
assertThat(PremiumListDao.getPremiumPrice("palladium", Registry.get("foobar")))
.hasValue(moneyOf(JPY, 15000));
}
private static Money moneyOf(CurrencyUnit unit, double amount) {
return Money.of(unit, BigDecimal.valueOf(amount).setScale(unit.getDecimalPlaces()));
}
}
@@ -83,7 +83,9 @@ public final class RegistryTestServer {
route("/registrar-ote-setup", FrontendServlet.class),
route("/registrar-ote-status", FrontendServlet.class),
route("/registrar-settings", FrontendServlet.class),
route("/registry-lock-get", FrontendServlet.class));
route("/registry-lock-get", FrontendServlet.class),
route("/registry-lock-post", FrontendServlet.class),
route("/registry-lock-verify", FrontendServlet.class));
private static final ImmutableList<Class<? extends Filter>> FILTERS = ImmutableList.of(
ObjectifyFilter.class,
@@ -33,12 +33,17 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Range;
import com.google.common.net.MediaType;
import google.registry.model.registrar.Registrar;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageRule;
import google.registry.schema.registrar.RegistrarDao;
import google.registry.testing.CertificateSamples;
import google.registry.testing.FakeClock;
import java.io.IOException;
import java.util.Optional;
import org.joda.money.CurrencyUnit;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
@@ -46,6 +51,12 @@ import org.mockito.Mock;
/** Unit tests for {@link CreateRegistrarCommand}. */
public class CreateRegistrarCommandTest extends CommandTestCase<CreateRegistrarCommand> {
private final FakeClock fakeClock = new FakeClock();
@Rule
public final JpaIntegrationWithCoverageRule jpaRule =
new JpaTestRules.Builder().withClock(fakeClock).buildIntegrationWithCoverageRule();
@Mock private AppEngineConnection connection;
@Before
@@ -100,6 +111,28 @@ public class CreateRegistrarCommandTest extends CommandTestCase<CreateRegistrarC
eq(new byte[0]));
}
@Test
public void testSuccess_alsoSaveToCloudSql() throws Exception {
runCommandForced(
"--name=blobio",
"--password=\"some_password\"",
"--registrar_type=REAL",
"--iana_id=8",
"--passcode=01234",
"--icann_referral_email=foo@bar.test",
"--street=\"123 Fake St\"",
"--city Fakington",
"--state MA",
"--zip 00351",
"--cc US",
"clientz");
Optional<Registrar> registrar = Registrar.loadByClientId("clientz");
assertThat(registrar).isPresent();
assertThat(registrar.get().verifyPassword("some_password")).isTrue();
assertThat(RegistrarDao.checkExists("clientz")).isTrue();
}
@Test
public void testSuccess_quotedPassword() throws Exception {
runCommandForced(
@@ -178,8 +178,7 @@ public class CreateReservedListCommandTest extends
@Test
public void testSaveToCloudSql_succeeds() throws Exception {
runCommandForced(
"--name=xn--q9jyb4c_common-reserved", "--input=" + reservedTermsPath, "--also_cloud_sql");
runCommandForced("--name=xn--q9jyb4c_common-reserved", "--input=" + reservedTermsPath);
verifyXnq9jyb4cInDatastore();
verifyXnq9jyb4cInCloudSql();
}
@@ -193,8 +192,7 @@ public class CreateReservedListCommandTest extends
"xn--q9jyb4c_common-reserved",
true,
ImmutableMap.of("testdomain", ReservedEntry.create(FULLY_BLOCKED, ""))));
runCommandForced(
"--name=xn--q9jyb4c_common-reserved", "--input=" + reservedTermsPath, "--also_cloud_sql");
runCommandForced("--name=xn--q9jyb4c_common-reserved", "--input=" + reservedTermsPath);
verifyXnq9jyb4cInDatastore();
}
@@ -38,8 +38,10 @@ import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCo
import google.registry.schema.domain.RegistryLock;
import google.registry.testing.AppEngineRule;
import google.registry.testing.DatastoreHelper;
import google.registry.testing.DeterministicStringGenerator;
import google.registry.testing.FakeClock;
import google.registry.testing.UserInfo;
import google.registry.util.StringGenerator.Alphabets;
import java.util.Set;
import java.util.stream.Collectors;
import org.joda.time.Duration;
@@ -57,6 +59,8 @@ public final class DomainLockUtilsTest {
private static final String POC_ID = "marla.singer@example.com";
private final FakeClock clock = new FakeClock();
private final DomainLockUtils domainLockUtils =
new DomainLockUtils(new DeterministicStringGenerator(Alphabets.BASE_58));
@Rule
public final AppEngineRule appEngineRule =
@@ -80,39 +84,39 @@ public final class DomainLockUtilsTest {
@Test
public void testSuccess_createLock() {
DomainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
domainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
}
@Test
public void testSuccess_createUnlock() {
RegistryLock lock =
DomainLockUtils.createRegistryLockRequest(
domainLockUtils.createRegistryLockRequest(
DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock);
DomainLockUtils.createRegistryUnlockRequest(DOMAIN_NAME, "TheRegistrar", false, clock);
domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock);
domainLockUtils.createRegistryUnlockRequest(DOMAIN_NAME, "TheRegistrar", false, clock);
}
@Test
public void testSuccess_createUnlock_adminUnlockingAdmin() {
RegistryLock lock =
DomainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", null, true, clock);
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true, clock);
DomainLockUtils.createRegistryUnlockRequest(DOMAIN_NAME, "TheRegistrar", true, clock);
domainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", null, true, clock);
domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true, clock);
domainLockUtils.createRegistryUnlockRequest(DOMAIN_NAME, "TheRegistrar", true, clock);
}
@Test
public void testSuccess_createLock_previousLockExpired() {
DomainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
domainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
clock.advanceBy(Duration.standardDays(1));
DomainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
domainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
}
@Test
public void testSuccess_applyLockDomain() {
RegistryLock lock =
DomainLockUtils.createRegistryLockRequest(
domainLockUtils.createRegistryLockRequest(
DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock);
domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock);
assertThat(reloadDomain().getStatusValues()).containsExactlyElementsIn(REGISTRY_LOCK_STATUSES);
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_UPDATE);
assertThat(historyEntry.getRequestedByRegistrar()).isTrue();
@@ -125,12 +129,12 @@ public final class DomainLockUtilsTest {
@Test
public void testSuccess_applyUnlockDomain() {
RegistryLock lock =
DomainLockUtils.createRegistryLockRequest(
domainLockUtils.createRegistryLockRequest(
DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock);
domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock);
RegistryLock unlock =
DomainLockUtils.createRegistryUnlockRequest(DOMAIN_NAME, "TheRegistrar", false, clock);
DomainLockUtils.verifyAndApplyUnlock(unlock.getVerificationCode(), false, clock);
domainLockUtils.createRegistryUnlockRequest(DOMAIN_NAME, "TheRegistrar", false, clock);
domainLockUtils.verifyAndApplyUnlock(unlock.getVerificationCode(), false, clock);
assertThat(reloadDomain().getStatusValues()).containsNoneIn(REGISTRY_LOCK_STATUSES);
ImmutableList<HistoryEntry> historyEntries =
@@ -149,8 +153,8 @@ public final class DomainLockUtilsTest {
@Test
public void testSuccess_applyAdminLock_onlyHistoryEntry() {
RegistryLock lock =
DomainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", null, true, clock);
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true, clock);
domainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", null, true, clock);
domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true, clock);
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_UPDATE);
assertThat(historyEntry.getRequestedByRegistrar()).isFalse();
@@ -161,16 +165,16 @@ public final class DomainLockUtilsTest {
@Test
public void testFailure_createUnlock_alreadyPendingUnlock() {
RegistryLock lock =
DomainLockUtils.createRegistryLockRequest(
domainLockUtils.createRegistryLockRequest(
DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock);
DomainLockUtils.createRegistryUnlockRequest(DOMAIN_NAME, "TheRegistrar", false, clock);
domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock);
domainLockUtils.createRegistryUnlockRequest(DOMAIN_NAME, "TheRegistrar", false, clock);
assertThat(
assertThrows(
IllegalArgumentException.class,
() ->
DomainLockUtils.createRegistryUnlockRequest(
domainLockUtils.createRegistryUnlockRequest(
DOMAIN_NAME, "TheRegistrar", false, clock)))
.hasMessageThat()
.isEqualTo("A pending unlock action already exists for example.tld");
@@ -179,13 +183,13 @@ public final class DomainLockUtilsTest {
@Test
public void testFailure_createUnlock_nonAdminUnlockingAdmin() {
RegistryLock lock =
DomainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", null, true, clock);
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true, clock);
domainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", null, true, clock);
domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true, clock);
assertThat(
assertThrows(
IllegalArgumentException.class,
() ->
DomainLockUtils.createRegistryUnlockRequest(
domainLockUtils.createRegistryUnlockRequest(
DOMAIN_NAME, "TheRegistrar", false, clock)))
.hasMessageThat()
.isEqualTo("Non-admin user cannot unlock admin-locked domain example.tld");
@@ -197,7 +201,7 @@ public final class DomainLockUtilsTest {
assertThrows(
IllegalArgumentException.class,
() ->
DomainLockUtils.createRegistryLockRequest(
domainLockUtils.createRegistryLockRequest(
"asdf.tld", "TheRegistrar", POC_ID, false, clock)))
.hasMessageThat()
.isEqualTo("Unknown domain asdf.tld");
@@ -205,12 +209,12 @@ public final class DomainLockUtilsTest {
@Test
public void testFailure_createLock_alreadyPendingLock() {
DomainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
domainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
assertThat(
assertThrows(
IllegalArgumentException.class,
() ->
DomainLockUtils.createRegistryLockRequest(
domainLockUtils.createRegistryLockRequest(
DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock)))
.hasMessageThat()
.isEqualTo("A pending or completed lock action already exists for example.tld");
@@ -223,7 +227,7 @@ public final class DomainLockUtilsTest {
assertThrows(
IllegalArgumentException.class,
() ->
DomainLockUtils.createRegistryLockRequest(
domainLockUtils.createRegistryLockRequest(
DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock)))
.hasMessageThat()
.isEqualTo("Domain example.tld is already locked");
@@ -235,7 +239,7 @@ public final class DomainLockUtilsTest {
assertThrows(
IllegalArgumentException.class,
() ->
DomainLockUtils.createRegistryUnlockRequest(
domainLockUtils.createRegistryUnlockRequest(
DOMAIN_NAME, "TheRegistrar", false, clock)))
.hasMessageThat()
.isEqualTo("Domain example.tld is already unlocked");
@@ -244,14 +248,14 @@ public final class DomainLockUtilsTest {
@Test
public void testFailure_applyLock_alreadyApplied() {
RegistryLock lock =
DomainLockUtils.createRegistryLockRequest(
domainLockUtils.createRegistryLockRequest(
DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock);
domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock);
domain = reloadDomain();
assertThat(
assertThrows(
IllegalArgumentException.class,
() -> DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock)))
() -> domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock)))
.hasMessageThat()
.isEqualTo("Domain example.tld is already locked");
assertNoDomainChanges();
@@ -260,13 +264,13 @@ public final class DomainLockUtilsTest {
@Test
public void testFailure_applyLock_expired() {
RegistryLock lock =
DomainLockUtils.createRegistryLockRequest(
domainLockUtils.createRegistryLockRequest(
DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
clock.advanceBy(Duration.standardDays(1));
assertThat(
assertThrows(
IllegalArgumentException.class,
() -> DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true, clock)))
() -> domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true, clock)))
.hasMessageThat()
.isEqualTo("The pending lock has expired; please try again");
assertNoDomainChanges();
@@ -275,11 +279,11 @@ public final class DomainLockUtilsTest {
@Test
public void testFailure_applyLock_nonAdmin_applyAdminLock() {
RegistryLock lock =
DomainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", null, true, clock);
domainLockUtils.createRegistryLockRequest(DOMAIN_NAME, "TheRegistrar", null, true, clock);
assertThat(
assertThrows(
IllegalArgumentException.class,
() -> DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock)))
() -> domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock)))
.hasMessageThat()
.isEqualTo("Non-admin user cannot complete admin lock");
assertNoDomainChanges();
@@ -288,18 +292,18 @@ public final class DomainLockUtilsTest {
@Test
public void testFailure_applyUnlock_alreadyUnlocked() {
RegistryLock lock =
DomainLockUtils.createRegistryLockRequest(
domainLockUtils.createRegistryLockRequest(
DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock);
domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), false, clock);
RegistryLock unlock =
DomainLockUtils.createRegistryUnlockRequest(DOMAIN_NAME, "TheRegistrar", false, clock);
DomainLockUtils.verifyAndApplyUnlock(unlock.getVerificationCode(), false, clock);
domainLockUtils.createRegistryUnlockRequest(DOMAIN_NAME, "TheRegistrar", false, clock);
domainLockUtils.verifyAndApplyUnlock(unlock.getVerificationCode(), false, clock);
assertThat(
assertThrows(
IllegalArgumentException.class,
() ->
DomainLockUtils.verifyAndApplyUnlock(
domainLockUtils.verifyAndApplyUnlock(
unlock.getVerificationCode(), false, clock)))
.hasMessageThat()
.isEqualTo("Domain example.tld is already unlocked");
@@ -309,7 +313,7 @@ public final class DomainLockUtilsTest {
@Test
public void testFailure_applyLock_alreadyLocked() {
RegistryLock lock =
DomainLockUtils.createRegistryLockRequest(
domainLockUtils.createRegistryLockRequest(
DOMAIN_NAME, "TheRegistrar", POC_ID, false, clock);
String verificationCode = lock.getVerificationCode();
// reload to pick up modification times, etc
@@ -318,7 +322,7 @@ public final class DomainLockUtilsTest {
assertThat(
assertThrows(
IllegalArgumentException.class,
() -> DomainLockUtils.verifyAndApplyLock(verificationCode, false, clock)))
() -> domainLockUtils.verifyAndApplyLock(verificationCode, false, clock)))
.hasMessageThat()
.isEqualTo("Domain example.tld is already locked");
@@ -0,0 +1,39 @@
// 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.truth.Truth.assertThat;
import java.io.File;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class DumpGoldenSchemaCommandTest extends CommandTestCase<DumpGoldenSchemaCommand> {
@Rule public TemporaryFolder tmp = new TemporaryFolder();
public DumpGoldenSchemaCommandTest() {}
@Test
public void testSchemaGeneration() throws Exception {
runCommand(
"--output=" + tmp.getRoot() + File.separatorChar + "golden.sql", "--start_postgresql");
assertThat(new File(tmp.getRoot(), "golden.sql").length()).isGreaterThan(1);
}
}
@@ -30,7 +30,9 @@ import google.registry.model.registrar.Registrar.Type;
import google.registry.model.registry.RegistryLockDao;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageRule;
import google.registry.testing.DeterministicStringGenerator;
import google.registry.testing.FakeClock;
import google.registry.util.StringGenerator.Alphabets;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@@ -51,6 +53,8 @@ public class LockDomainCommandTest extends CommandTestCase<LockDomainCommand> {
createTld("tld");
command.registryAdminClientId = "adminreg";
command.clock = new FakeClock();
command.domainLockUtils =
new DomainLockUtils(new DeterministicStringGenerator(Alphabets.BASE_58));
}
@Test
@@ -33,7 +33,9 @@ import google.registry.model.registry.RegistryLockDao;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageRule;
import google.registry.schema.domain.RegistryLock;
import google.registry.testing.DeterministicStringGenerator;
import google.registry.testing.FakeClock;
import google.registry.util.StringGenerator.Alphabets;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@@ -54,14 +56,16 @@ public class UnlockDomainCommandTest extends CommandTestCase<UnlockDomainCommand
createTld("tld");
command.registryAdminClientId = "adminreg";
command.clock = new FakeClock();
command.domainLockUtils =
new DomainLockUtils(new DeterministicStringGenerator(Alphabets.BASE_58));
}
private DomainBase persistLockedDomain(String domainName, String registrarId) {
DomainBase domain = persistResource(newDomainBase(domainName));
RegistryLock lock =
DomainLockUtils.createRegistryLockRequest(
command.domainLockUtils.createRegistryLockRequest(
domainName, registrarId, null, true, command.clock);
DomainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true, command.clock);
command.domainLockUtils.verifyAndApplyLock(lock.getVerificationCode(), true, command.clock);
return reloadResource(domain);
}
@@ -32,16 +32,36 @@ import com.google.common.collect.ImmutableSet;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.Registrar.State;
import google.registry.model.registrar.Registrar.Type;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageRule;
import google.registry.schema.registrar.RegistrarDao;
import google.registry.testing.AppEngineRule;
import google.registry.testing.FakeClock;
import google.registry.util.CidrAddressBlock;
import java.util.Optional;
import org.joda.money.CurrencyUnit;
import org.joda.time.DateTime;
import org.junit.Rule;
import org.junit.Test;
/** Unit tests for {@link UpdateRegistrarCommand}. */
public class UpdateRegistrarCommandTest extends CommandTestCase<UpdateRegistrarCommand> {
private final FakeClock fakeClock = new FakeClock();
@Rule
public final JpaIntegrationWithCoverageRule jpaRule =
new JpaTestRules.Builder().withClock(fakeClock).buildIntegrationWithCoverageRule();
@Test
public void testSuccess_alsoUpdateInCloudSql() throws Exception {
assertThat(loadRegistrar("NewRegistrar").verifyPassword("some_password")).isFalse();
RegistrarDao.saveNew(loadRegistrar("NewRegistrar"));
runCommand("--password=some_password", "--force", "NewRegistrar");
assertThat(loadRegistrar("NewRegistrar").verifyPassword("some_password")).isTrue();
assertThat(RegistrarDao.load("NewRegistrar").get().verifyPassword("some_password")).isTrue();
}
@Test
public void testSuccess_password() throws Exception {
assertThat(loadRegistrar("NewRegistrar").verifyPassword("some_password")).isFalse();
@@ -116,20 +116,18 @@ public class UpdateReservedListCommandTest extends
public void testSaveToCloudSql_succeeds() throws Exception {
populateInitialReservedListInDatastore(true);
populateInitialReservedListInCloudSql(true);
runCommandForced(
"--name=xn--q9jyb4c_common-reserved", "--input=" + reservedTermsPath, "--also_cloud_sql");
runCommandForced("--name=xn--q9jyb4c_common-reserved", "--input=" + reservedTermsPath);
verifyXnq9jyb4cInDatastore();
verifyXnq9jyb4cInCloudSql();
}
@Test
public void testSaveToCloudSql_succeedsEvenPreviousListNotExist() throws Exception {
// Note that, during the dual-write phase, we just always save the revered list to
// Cloud SQL (if --also_cloud_sql is set) without checking if there is a list with
// same name. This is to backfill the existing list in Datastore when we update it.
// Note that, during the dual-write phase, we always save the reserved list to Cloud SQL without
// checking if there is a list with same name. This is to backfill the existing list in Cloud
// Datastore when we update it.
populateInitialReservedListInDatastore(true);
runCommandForced(
"--name=xn--q9jyb4c_common-reserved", "--input=" + reservedTermsPath, "--also_cloud_sql");
runCommandForced("--name=xn--q9jyb4c_common-reserved", "--input=" + reservedTermsPath);
verifyXnq9jyb4cInDatastore();
assertThat(ReservedListDao.checkExists("xn--q9jyb4c_common-reserved")).isTrue();
}
@@ -43,7 +43,6 @@ import google.registry.testing.FakeClock;
import google.registry.testing.FakeResponse;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Rule;
@@ -97,7 +96,7 @@ public final class RegistryLockGetActionTest {
.setRepoId("repoId")
.setDomainName("example.test")
.setRegistrarId("TheRegistrar")
.setVerificationCode(UUID.randomUUID().toString())
.setVerificationCode("123456789ABCDEFGHJKLMNPQRSTUVWXY")
.setRegistrarPocId("johndoe@theregistrar.com")
.setLockCompletionTimestamp(fakeClock.nowUtc())
.build();
@@ -107,7 +106,7 @@ public final class RegistryLockGetActionTest {
.setRepoId("repoId")
.setDomainName("adminexample.test")
.setRegistrarId("TheRegistrar")
.setVerificationCode(UUID.randomUUID().toString())
.setVerificationCode("122222222ABCDEFGHJKLMNPQRSTUVWXY")
.isSuperuser(true)
.setLockCompletionTimestamp(fakeClock.nowUtc())
.build();
@@ -116,7 +115,7 @@ public final class RegistryLockGetActionTest {
.setRepoId("repoId")
.setDomainName("incomplete.test")
.setRegistrarId("TheRegistrar")
.setVerificationCode(UUID.randomUUID().toString())
.setVerificationCode("111111111ABCDEFGHJKLMNPQRSTUVWXY")
.setRegistrarPocId("johndoe@theregistrar.com")
.build();
@@ -126,7 +125,7 @@ public final class RegistryLockGetActionTest {
.setDomainName("unlocked.test")
.setRegistrarId("TheRegistrar")
.setRegistrarPocId("johndoe@theregistrar.com")
.setVerificationCode(UUID.randomUUID().toString())
.setVerificationCode("123456789ABCDEFGHJKLMNPQRSTUUUUU")
.setLockCompletionTimestamp(fakeClock.nowUtc())
.setUnlockRequestTimestamp(fakeClock.nowUtc())
.setUnlockCompletionTimestamp(fakeClock.nowUtc())
@@ -0,0 +1,407 @@
// 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.ui.server.registrar;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.EppResourceUtils.loadByForeignKey;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.loadRegistrar;
import static google.registry.testing.DatastoreHelper.newDomainBase;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import com.google.appengine.api.users.User;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSetMultimap;
import google.registry.model.domain.DomainBase;
import google.registry.model.registry.RegistryLockDao;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationTestRule;
import google.registry.request.JsonActionRunner;
import google.registry.request.JsonResponse;
import google.registry.request.ResponseImpl;
import google.registry.request.auth.AuthLevel;
import google.registry.request.auth.AuthResult;
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
import google.registry.request.auth.AuthenticatedRegistrarAccessor.Role;
import google.registry.request.auth.UserAuthInfo;
import google.registry.schema.domain.RegistryLock;
import google.registry.testing.AppEngineRule;
import google.registry.testing.DeterministicStringGenerator;
import google.registry.testing.FakeClock;
import google.registry.tools.DomainLockUtils;
import google.registry.util.EmailMessage;
import google.registry.util.SendEmailService;
import google.registry.util.StringGenerator.Alphabets;
import java.util.Map;
import java.util.UUID;
import javax.mail.internet.InternetAddress;
import javax.servlet.http.HttpServletResponse;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@RunWith(JUnit4.class)
public final class RegistryLockPostActionTest {
private static final String EMAIL_MESSAGE_TEMPLATE =
"Please click the link below to perform the lock \\/ unlock action on domain example.tld. "
+ "Note: this code will expire in one hour.\n\n"
+ "https:\\/\\/localhost\\/registry-lock-verify\\?lockVerificationCode="
+ "[0-9a-zA-Z_\\-]+&isLock=(true|false)";
@Rule public final AppEngineRule appEngineRule = AppEngineRule.builder().withDatastore().build();
@Rule
public final JpaIntegrationTestRule jpaRule =
new JpaTestRules.Builder().buildIntegrationTestRule();
@Rule public final MockitoRule mocks = MockitoJUnit.rule();
private final User userWithoutPermission =
new User("johndoe@theregistrar.com", "gmail.com", "31337");
// Marla Singer has registry lock auth permissions
private final User userWithLockPermission =
new User("Marla.Singer@crr.com", "gmail.com", "31337");
private final FakeClock clock = new FakeClock();
private InternetAddress outgoingAddress;
private DomainBase domain;
private RegistryLockPostAction action;
@Mock SendEmailService emailService;
@Mock HttpServletResponse mockResponse;
@Before
public void setup() throws Exception {
createTld("tld");
domain = persistResource(newDomainBase("example.tld"));
outgoingAddress = new InternetAddress("domain-registry@example.com");
action =
createAction(
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithLockPermission, false)));
}
@Test
public void testSuccess_lock() throws Exception {
Map<String, ?> response = action.handleJsonRequest(lockRequest());
assertSuccess(response, "lock", "Marla.Singer@crr.com");
}
@Test
public void testSuccess_unlock() throws Exception {
RegistryLockDao.save(
createLock().asBuilder().setLockCompletionTimestamp(clock.nowUtc()).build());
persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
Map<String, ?> response = action.handleJsonRequest(unlockRequest());
assertSuccess(response, "unlock", "Marla.Singer@crr.com");
}
@Test
public void testSuccess_unlock_adminUnlockingAdmin() throws Exception {
RegistryLockDao.save(
createLock()
.asBuilder()
.isSuperuser(true)
.setLockCompletionTimestamp(clock.nowUtc())
.build());
persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
action =
createAction(
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithoutPermission, true)));
Map<String, ?> response = action.handleJsonRequest(unlockRequest());
// we should still email the admin user's email address
assertSuccess(response, "unlock", "johndoe@theregistrar.com");
}
@Test
public void testFailure_unlock_noLock() {
persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
Map<String, ?> response = action.handleJsonRequest(unlockRequest());
assertFailureWithMessage(response, "No lock object for domain example.tld");
}
@Test
public void testFailure_unlock_alreadyUnlocked() {
persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
RegistryLockDao.save(
createLock()
.asBuilder()
.setLockCompletionTimestamp(clock.nowUtc())
.setUnlockRequestTimestamp(clock.nowUtc())
.build());
Map<String, ?> response = action.handleJsonRequest(unlockRequest());
assertFailureWithMessage(response, "A pending unlock action already exists for example.tld");
}
@Test
public void testFailure_unlock_nonAdminUnlockingAdmin() {
RegistryLockDao.save(
createLock()
.asBuilder()
.isSuperuser(true)
.setLockCompletionTimestamp(clock.nowUtc())
.build());
persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
Map<String, ?> response = action.handleJsonRequest(unlockRequest());
assertFailureWithMessage(
response, "Non-admin user cannot unlock admin-locked domain example.tld");
}
@Test
public void testSuccess_adminUser() throws Exception {
// Admin user should be able to lock/unlock regardless -- and we use the admin user's email
action =
createAction(
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithoutPermission, true)));
Map<String, ?> response = action.handleJsonRequest(lockRequest());
assertSuccess(response, "lock", "johndoe@theregistrar.com");
}
@Test
public void testSuccess_adminUser_doesNotRequirePassword() throws Exception {
action =
createAction(
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithoutPermission, true)));
Map<String, ?> response =
action.handleJsonRequest(
ImmutableMap.of(
"clientId", "TheRegistrar",
"fullyQualifiedDomainName", "example.tld",
"isLock", true));
assertSuccess(response, "lock", "johndoe@theregistrar.com");
}
@Test
public void testFailure_noInput() {
Map<String, ?> response = action.handleJsonRequest(null);
assertFailureWithMessage(response, "Null JSON");
}
@Test
public void testFailure_noClientId() {
Map<String, ?> response = action.handleJsonRequest(ImmutableMap.of());
assertFailureWithMessage(response, "Missing key for client: clientId");
}
@Test
public void testFailure_emptyClientId() {
Map<String, ?> response = action.handleJsonRequest(ImmutableMap.of("clientId", ""));
assertFailureWithMessage(response, "Missing key for client: clientId");
}
@Test
public void testFailure_noDomainName() {
Map<String, ?> response =
action.handleJsonRequest(
ImmutableMap.of("clientId", "TheRegistrar", "password", "hi", "isLock", true));
assertFailureWithMessage(response, "Missing key for fullyQualifiedDomainName");
}
@Test
public void testFailure_noLockParam() {
Map<String, ?> response =
action.handleJsonRequest(
ImmutableMap.of(
"clientId", "TheRegistrar",
"fullyQualifiedDomainName", "example.tld",
"password", "hi"));
assertFailureWithMessage(response, "Missing key for isLock");
}
@Test
public void testFailure_notAllowedOnRegistrar() {
persistResource(
loadRegistrar("TheRegistrar").asBuilder().setRegistryLockAllowed(false).build());
Map<String, ?> response = action.handleJsonRequest(lockRequest());
assertFailureWithMessage(response, "Registry lock not allowed for this registrar");
}
@Test
public void testFailure_noPassword() {
Map<String, ?> response =
action.handleJsonRequest(
ImmutableMap.of(
"clientId", "TheRegistrar",
"fullyQualifiedDomainName", "example.tld",
"isLock", true));
assertFailureWithMessage(response, "Missing key for password");
}
@Test
public void testFailure_notEnabledForRegistrarContact() {
action =
createAction(
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithoutPermission, false)));
Map<String, ?> response =
action.handleJsonRequest(
ImmutableMap.of(
"clientId", "TheRegistrar",
"fullyQualifiedDomainName", "example.tld",
"isLock", true,
"password", "hi"));
assertFailureWithMessage(response, "Incorrect registry lock password for contact");
}
@Test
public void testFailure_badPassword() {
Map<String, ?> response =
action.handleJsonRequest(
ImmutableMap.of(
"clientId", "TheRegistrar",
"fullyQualifiedDomainName", "example.tld",
"isLock", true,
"password", "badPassword"));
assertFailureWithMessage(response, "Incorrect registry lock password for contact");
}
@Test
public void testFailure_invalidDomain() {
Map<String, ?> response =
action.handleJsonRequest(
ImmutableMap.of(
"clientId", "TheRegistrar",
"fullyQualifiedDomainName", "bad.tld",
"isLock", true,
"password", "hi"));
assertFailureWithMessage(response, "Unknown domain bad.tld");
}
@Test
public void testSuccess_previousLockUnlocked() throws Exception {
RegistryLockDao.save(
createLock()
.asBuilder()
.setLockCompletionTimestamp(clock.nowUtc().minusMinutes(1))
.setUnlockRequestTimestamp(clock.nowUtc().minusMinutes(1))
.setUnlockCompletionTimestamp(clock.nowUtc().minusMinutes(1))
.build());
Map<String, ?> response = action.handleJsonRequest(lockRequest());
assertSuccess(response, "lock", "Marla.Singer@crr.com");
}
@Test
public void testSuccess_previousLockExpired() throws Exception {
RegistryLock previousLock = RegistryLockDao.save(createLock());
previousLock = RegistryLockDao.getByVerificationCode(previousLock.getVerificationCode()).get();
clock.setTo(previousLock.getLockRequestTimestamp().plusHours(2));
Map<String, ?> response = action.handleJsonRequest(lockRequest());
assertSuccess(response, "lock", "Marla.Singer@crr.com");
}
@Test
public void testFailure_alreadyPendingLock() {
RegistryLockDao.save(createLock());
Map<String, ?> response = action.handleJsonRequest(lockRequest());
assertFailureWithMessage(
response, "A pending or completed lock action already exists for example.tld");
}
@Test
public void testFailure_alreadyLocked() {
persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
Map<String, ?> response = action.handleJsonRequest(lockRequest());
assertFailureWithMessage(response, "Domain example.tld is already locked");
}
@Test
public void testFailure_alreadyUnlocked() {
Map<String, ?> response = action.handleJsonRequest(unlockRequest());
assertFailureWithMessage(response, "Domain example.tld is already unlocked");
}
private ImmutableMap<String, Object> lockRequest() {
return fullRequest(true);
}
private ImmutableMap<String, Object> unlockRequest() {
return fullRequest(false);
}
private ImmutableMap<String, Object> fullRequest(boolean lock) {
return ImmutableMap.of(
"isLock", lock,
"clientId", "TheRegistrar",
"fullyQualifiedDomainName", "example.tld",
"password", "hi");
}
private RegistryLock createLock() {
DomainBase domain = loadByForeignKey(DomainBase.class, "example.tld", clock.nowUtc()).get();
return new RegistryLock.Builder()
.setDomainName("example.tld")
.isSuperuser(false)
.setVerificationCode(UUID.randomUUID().toString())
.setRegistrarId("TheRegistrar")
.setRepoId(domain.getRepoId())
.setRegistrarPocId("Marla.Singer@crr.com")
.build();
}
private void assertSuccess(Map<String, ?> response, String lockAction, String recipient)
throws Exception {
assertThat(response)
.containsExactly(
"status", "SUCCESS",
"results", ImmutableList.of(),
"message", String.format("Successful %s", lockAction));
verifyEmail(recipient);
}
private void assertFailureWithMessage(Map<String, ?> response, String message) {
assertThat(response)
.containsExactly("status", "ERROR", "results", ImmutableList.of(), "message", message);
verifyNoMoreInteractions(emailService);
}
private void verifyEmail(String recipient) throws Exception {
ArgumentCaptor<EmailMessage> emailCaptor = ArgumentCaptor.forClass(EmailMessage.class);
verify(emailService).sendEmail(emailCaptor.capture());
EmailMessage sentMessage = emailCaptor.getValue();
assertThat(sentMessage.subject()).matches("Registry (un)?lock verification");
assertThat(sentMessage.body()).matches(EMAIL_MESSAGE_TEMPLATE);
assertThat(sentMessage.from()).isEqualTo(new InternetAddress("domain-registry@example.com"));
assertThat(sentMessage.recipients()).containsExactly(new InternetAddress(recipient));
}
private RegistryLockPostAction createAction(AuthResult authResult) {
AuthenticatedRegistrarAccessor registrarAccessor =
AuthenticatedRegistrarAccessor.createForTesting(
ImmutableSetMultimap.of("TheRegistrar", Role.OWNER, "NewRegistrar", Role.OWNER));
JsonActionRunner jsonActionRunner =
new JsonActionRunner(ImmutableMap.of(), new JsonResponse(new ResponseImpl(mockResponse)));
DomainLockUtils domainLockUtils =
new DomainLockUtils(new DeterministicStringGenerator(Alphabets.BASE_58));
return new RegistryLockPostAction(
jsonActionRunner,
authResult,
registrarAccessor,
emailService,
clock,
domainLockUtils,
outgoingAddress);
}
}
@@ -0,0 +1,351 @@
// 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.ui.server.registrar;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.testing.DatastoreHelper.createTlds;
import static google.registry.testing.DatastoreHelper.getOnlyHistoryEntryOfType;
import static google.registry.testing.DatastoreHelper.newDomainBase;
import static google.registry.testing.DatastoreHelper.persistActiveHost;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES;
import static javax.servlet.http.HttpServletResponse.SC_MOVED_TEMPORARILY;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.common.collect.ImmutableMap;
import google.registry.model.billing.BillingEvent;
import google.registry.model.billing.BillingEvent.Reason;
import google.registry.model.domain.DomainBase;
import google.registry.model.host.HostResource;
import google.registry.model.registry.Registry;
import google.registry.model.registry.RegistryLockDao;
import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.transaction.JpaTestRules;
import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageRule;
import google.registry.request.auth.AuthLevel;
import google.registry.request.auth.AuthResult;
import google.registry.request.auth.UserAuthInfo;
import google.registry.schema.domain.RegistryLock;
import google.registry.security.XsrfTokenManager;
import google.registry.testing.AppEngineRule;
import google.registry.testing.DatastoreHelper;
import google.registry.testing.DeterministicStringGenerator;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeResponse;
import google.registry.testing.InjectRule;
import google.registry.testing.UserInfo;
import google.registry.tools.DomainLockUtils;
import google.registry.util.StringGenerator;
import google.registry.util.StringGenerator.Alphabets;
import javax.servlet.http.HttpServletRequest;
import org.joda.time.Duration;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public final class RegistryLockVerifyActionTest {
private final FakeClock fakeClock = new FakeClock();
@Rule
public final AppEngineRule appEngineRule =
AppEngineRule.builder()
.withDatastore()
.withUserService(UserInfo.create("marla.singer@example.com", "12345"))
.build();
@Rule
public final JpaIntegrationWithCoverageRule jpaRule =
new JpaTestRules.Builder().withClock(fakeClock).buildIntegrationWithCoverageRule();
@Rule public final InjectRule inject = new InjectRule();
private final HttpServletRequest request = mock(HttpServletRequest.class);
private final UserService userService = UserServiceFactory.getUserService();
private final User user = new User("marla.singer@example.com", "gmail.com", "12345");
private final String lockId = "123456789ABCDEFGHJKLMNPQRSTUVWXY";
private final StringGenerator stringGenerator =
new DeterministicStringGenerator(Alphabets.BASE_58);
private FakeResponse response;
private DomainBase domain;
private AuthResult authResult;
private RegistryLockVerifyAction action;
@Before
public void setup() {
createTlds("tld", "net");
HostResource host = persistActiveHost("ns1.example.net");
domain = persistResource(newDomainBase("example.tld", host));
when(request.getRequestURI()).thenReturn("https://registry.example/registry-lock-verification");
action = createAction(lockId, true);
}
@Test
public void testSuccess_lockDomain() {
RegistryLockDao.save(createLock());
action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(reloadDomain().getStatusValues()).containsExactlyElementsIn(REGISTRY_LOCK_STATUSES);
assertThat(response.getPayload()).contains("Success: lock has been applied to example.tld");
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_UPDATE);
assertThat(historyEntry.getRequestedByRegistrar()).isTrue();
assertThat(historyEntry.getBySuperuser()).isFalse();
assertThat(historyEntry.getReason())
.isEqualTo("Lock or unlock of a domain through a RegistryLock operation");
assertBillingEvent(historyEntry);
}
@Test
public void testSuccess_unlockDomain() {
action = createAction(lockId, false);
domain = persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
RegistryLockDao.save(
createLock().asBuilder().setUnlockRequestTimestamp(fakeClock.nowUtc()).build());
action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getPayload()).contains("Success: unlock has been applied to example.tld");
assertThat(reloadDomain().getStatusValues()).containsNoneIn(REGISTRY_LOCK_STATUSES);
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_UPDATE);
assertThat(historyEntry.getRequestedByRegistrar()).isTrue();
assertThat(historyEntry.getBySuperuser()).isFalse();
assertThat(historyEntry.getReason())
.isEqualTo("Lock or unlock of a domain through a RegistryLock operation");
assertBillingEvent(historyEntry);
}
@Test
public void testSuccess_adminLock_createsOnlyHistoryEntry() {
action.authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, true));
RegistryLockDao.save(createLock().asBuilder().isSuperuser(true).build());
action.run();
HistoryEntry historyEntry = getOnlyHistoryEntryOfType(domain, HistoryEntry.Type.DOMAIN_UPDATE);
assertThat(historyEntry.getRequestedByRegistrar()).isFalse();
assertThat(historyEntry.getBySuperuser()).isTrue();
DatastoreHelper.assertNoBillingEvents();
}
@Test
public void testFailure_badVerificationCode() {
RegistryLockDao.save(
createLock().asBuilder().setVerificationCode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").build());
action.run();
assertThat(response.getPayload()).contains("Failed: Invalid verification code");
assertNoDomainChanges();
}
@Test
public void testFailure_alreadyVerified() {
RegistryLockDao.save(
createLock().asBuilder().setLockCompletionTimestamp(fakeClock.nowUtc()).build());
action.run();
assertThat(response.getPayload()).contains("Failed: Domain example.tld is already locked");
assertNoDomainChanges();
}
@Test
public void testFailure_expired() {
RegistryLockDao.save(createLock());
fakeClock.advanceBy(Duration.standardHours(2));
action.run();
assertThat(response.getPayload())
.contains("Failed: The pending lock has expired; please try again");
assertNoDomainChanges();
}
@Test
public void testFailure_nonAdmin_verifyingAdminLock() {
RegistryLockDao.save(createLock().asBuilder().isSuperuser(true).build());
action.run();
assertThat(response.getPayload()).contains("Failed: Non-admin user cannot complete admin lock");
assertNoDomainChanges();
}
@Test
public void testFailure_alreadyUnlocked() {
action = createAction(lockId, false);
RegistryLockDao.save(
createLock()
.asBuilder()
.setLockCompletionTimestamp(fakeClock.nowUtc())
.setUnlockRequestTimestamp(fakeClock.nowUtc())
.setUnlockCompletionTimestamp(fakeClock.nowUtc())
.build());
action.run();
assertThat(response.getPayload()).contains("Failed: Domain example.tld is already unlocked");
assertNoDomainChanges();
}
@Test
public void testFailure_alreadyLocked() {
RegistryLockDao.save(createLock());
domain = persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
action.run();
assertThat(response.getPayload()).contains("Failed: Domain example.tld is already locked");
assertNoDomainChanges();
}
@Test
public void testFailure_notLoggedIn() {
action.authResult = AuthResult.NOT_AUTHENTICATED;
action.run();
assertThat(response.getStatus()).isEqualTo(SC_MOVED_TEMPORARILY);
assertThat(response.getHeaders()).containsKey("Location");
assertNoDomainChanges();
}
@Test
public void testFailure_doesNotChangeLockObject() {
// A failure when performing Datastore actions means that no actions should be taken in the
// Cloud SQL RegistryLock object
RegistryLock lock = createLock();
RegistryLockDao.save(lock);
// reload the lock to pick up creation time
lock = RegistryLockDao.getByVerificationCode(lock.getVerificationCode()).get();
fakeClock.advanceOneMilli();
domain = persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
action.run();
// we would have failed during the Datastore segment of the action
assertThat(response.getPayload()).contains("Failed: Domain example.tld is already locked");
// verify that the changes to the SQL object were rolled back
RegistryLock afterAction =
RegistryLockDao.getByVerificationCode(lock.getVerificationCode()).get();
assertThat(afterAction).isEqualTo(lock);
}
@Test
public void testFailure_isLockTrue_shouldBeFalse() {
domain = persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
RegistryLockDao.save(
createLock()
.asBuilder()
.setLockCompletionTimestamp(fakeClock.nowUtc())
.setUnlockRequestTimestamp(fakeClock.nowUtc())
.build());
action.run();
assertThat(response.getPayload()).contains("Failed: Domain example.tld is already locked");
}
@Test
public void testFailure_isLockFalse_shouldBeTrue() {
action = createAction(lockId, false);
RegistryLockDao.save(createLock());
action.run();
assertThat(response.getPayload()).contains("Failed: Domain example.tld is already unlocked");
}
@Test
public void testFailure_lock_unlock_lockAgain() {
RegistryLock lock = RegistryLockDao.save(createLock());
action.run();
assertThat(response.getPayload()).contains("Success: lock has been applied to example.tld");
String unlockVerificationCode = "some-unlock-code";
RegistryLockDao.save(
lock.asBuilder()
.setVerificationCode(unlockVerificationCode)
.setUnlockRequestTimestamp(fakeClock.nowUtc())
.build());
action = createAction(unlockVerificationCode, false);
action.run();
assertThat(response.getPayload()).contains("Success: unlock has been applied to example.tld");
action = createAction(lockId, true);
action.run();
assertThat(response.getPayload()).contains("Failed: Invalid verification code");
}
@Test
public void testFailure_lock_lockAgain() {
RegistryLockDao.save(createLock());
action.run();
assertThat(response.getPayload()).contains("Success: lock has been applied to example.tld");
action = createAction(lockId, true);
action.run();
assertThat(response.getPayload()).contains("Failed: Domain example.tld is already locked");
}
@Test
public void testFailure_unlock_unlockAgain() {
action = createAction(lockId, false);
domain = persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
RegistryLockDao.save(
createLock().asBuilder().setUnlockRequestTimestamp(fakeClock.nowUtc()).build());
action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getPayload()).contains("Success: unlock has been applied to example.tld");
action = createAction(lockId, false);
action.run();
assertThat(response.getPayload()).contains("Failed: Domain example.tld is already unlocked");
}
private RegistryLock createLock() {
return new RegistryLock.Builder()
.setDomainName("example.tld")
.setRegistrarId("TheRegistrar")
.setRepoId("repoId")
.setRegistrarPocId("marla.singer@example.com")
.isSuperuser(false)
.setVerificationCode(lockId)
.build();
}
private DomainBase reloadDomain() {
return ofy().load().entity(domain).now();
}
private void assertNoDomainChanges() {
assertThat(reloadDomain()).isEqualTo(domain);
}
private void assertBillingEvent(HistoryEntry historyEntry) {
DatastoreHelper.assertBillingEvents(
new BillingEvent.OneTime.Builder()
.setReason(Reason.SERVER_STATUS)
.setTargetId(domain.getForeignKey())
.setClientId(domain.getCurrentSponsorClientId())
.setCost(Registry.get(domain.getTld()).getServerStatusChangeCost())
.setEventTime(fakeClock.nowUtc())
.setBillingTime(fakeClock.nowUtc())
.setParent(historyEntry)
.build());
}
private RegistryLockVerifyAction createAction(String lockVerificationCode, Boolean isLock) {
response = new FakeResponse();
RegistryLockVerifyAction action =
new RegistryLockVerifyAction(
fakeClock, new DomainLockUtils(stringGenerator), lockVerificationCode, isLock);
authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, false));
action.req = request;
action.response = response;
action.authResult = authResult;
action.userService = userService;
action.logoFilename = "logo.png";
action.productName = "Nomulus";
action.analyticsConfig = ImmutableMap.of("googleAnalyticsId", "sampleId");
action.xsrfTokenManager = new XsrfTokenManager(new FakeClock(), action.userService);
return action;
}
}
@@ -18,7 +18,9 @@ import static google.registry.server.Fixture.BASIC;
import static google.registry.server.Route.route;
import static google.registry.testing.AppEngineRule.makeRegistrar2;
import static google.registry.testing.AppEngineRule.makeRegistrarContact2;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.loadRegistrar;
import static google.registry.testing.DatastoreHelper.newDomainBase;
import static google.registry.testing.DatastoreHelper.persistResource;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
@@ -26,7 +28,9 @@ import com.google.common.collect.ImmutableMap;
import com.googlecode.objectify.ObjectifyFilter;
import google.registry.model.ofy.OfyFilter;
import google.registry.model.registrar.Registrar.State;
import google.registry.model.registry.RegistryLockDao;
import google.registry.module.frontend.FrontendServlet;
import google.registry.schema.domain.RegistryLock;
import google.registry.server.RegistryTestServer;
import google.registry.testing.CertificateSamples;
import org.junit.Rule;
@@ -46,7 +50,8 @@ public class RegistrarConsoleScreenshotTest extends WebDriverTestCase {
.setRoutes(
route("/registrar", FrontendServlet.class),
route("/registrar-ote-status", FrontendServlet.class),
route("/registrar-settings", FrontendServlet.class))
route("/registrar-settings", FrontendServlet.class),
route("/registry-lock-verify", FrontendServlet.class))
.setFilters(ObjectifyFilter.class, OfyFilter.class)
.setFixtures(BASIC)
.setEmail("Marla.Singer@google.com")
@@ -370,4 +375,36 @@ public class RegistrarConsoleScreenshotTest extends WebDriverTestCase {
Thread.sleep(500);
driver.diffPage("page");
}
@Test
public void registryLockVerify_success() throws Throwable {
String lockVerificationCode = "f1be78a2-2d61-458c-80f0-9dd8f2f8625f";
server.runInAppEngineEnvironment(
() -> {
createTld("tld");
persistResource(newDomainBase("example.tld"));
RegistryLockDao.save(
new RegistryLock.Builder()
.setRegistrarPocId("johndoe@theregistrar.com")
.setRepoId("repoId")
.setRegistrarId("TheRegistrar")
.setVerificationCode("f1be78a2-2d61-458c-80f0-9dd8f2f8625f")
.isSuperuser(false)
.setDomainName("example.tld")
.build());
return null;
});
driver.get(
server.getUrl(
"/registry-lock-verify?isLock=true&lockVerificationCode=" + lockVerificationCode));
driver.waitForElement(By.id("reg-content"));
driver.diffPage("page");
}
@Test
public void registryLockVerify_unknownLock() throws Throwable {
driver.get(server.getUrl("/registry-lock-verify?isLock=true&lockVerificationCode=asdfasdf"));
driver.waitForElement(By.id("reg-content"));
driver.diffPage("page");
}
}

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