1
0
mirror of https://github.com/google/nomulus synced 2026-06-09 16:33:02 +00:00

Compare commits

..

51 Commits

Author SHA1 Message Date
Ben McIlwain 9479e1e8b9 Rename Spec11 reply-to email and also use it as sending address (#201)
* Rename Spec11 reply-to email and also use it as sending address
2019-07-26 15:30:46 -04:00
Shicong Huang 110bd9c057 Fix deploying to alpha from workstation (#198)
appengineDeployAll requires appengine.deploy.version to be set
otherwiese the deployment would fail.
2019-07-26 14:37:49 -04:00
Lai Jiang 2ae5aba7ff Rename dependency_license.gradle file (#196)
The file name confused the licensee app
(https://github.com/licensee/licensee) that GitHub uses to detect
license files. It thinks this file is also a license file and was not
able to determine is type.
2019-07-26 14:19:08 -04:00
Lai Jiang b878e5acc1 Remove port to protocol map (#200)
There's no need for it. We can get the port from the protocol.
2019-07-25 22:05:12 -04:00
gbrodman 4d0409c924 Add HTML lang tags where possible (#199) 2019-07-25 21:09:32 -04:00
gbrodman 0292887cb9 Store only interesting failures, not login failures (#188)
Login failures will happen any time that we aren't coming from a
whitelisted IP for that particular TLD. Since whitelists are out of date
(and we don't whitelist IPs for every TLD anyway) those failures aren't
interesting. Store and fully-log the interesting failures if one
happened.
2019-07-24 10:55:55 -04:00
Lai Jiang 464f6ba90a Add a missing space (#197) 2019-07-24 10:50:27 -04:00
Lai Jiang 0ab0a8c2e6 Use positive booleans to improve readability (#190)
This allows us to use lgtm.com to scan for vulnerabilities.
2019-07-23 22:30:46 -04:00
Lai Jiang 2cc4e5fa94 Add a badge for LGTM analysis results (#194) 2019-07-23 21:42:19 -04:00
gbrodman 47a890253e Revert "Use parallel Gradle builds by default (#189)" (#195)
This reverts commit 5dfd96d26d.
2019-07-23 18:12:04 -04:00
Lai Jiang b452b0628d Add customized .lgtm.yml (#191)
* Add customized .lgtm.yml
2019-07-23 17:09:05 -04:00
gbrodman 5dfd96d26d Use parallel Gradle builds by default (#189) 2019-07-23 15:27:30 -04:00
Shicong Huang e2a673d914 Bring back the old GoogleCredential for Drive API (#187)
Using the new GoogleCredentials to access Drive API caused 403 forbidden
exception. So, this PR brought back the old GoogleCredential to
temporarily resolve the production issue while we are figuring out the
long term fix.

TESTED=Deployed to alpha and verified exportPremiumTerms succeeded, see
https://paste.googleplex.com/6153215760400384.
2019-07-23 11:31:35 -04:00
gbrodman bf29d159f9 Fix a few deprecations (#186) 2019-07-22 14:12:55 -04:00
Lai Jiang e17cb52bf7 Fail gracefully when copying detailed reports (#181)
* Fail gracefully when copying detailed reports

When the detailed reports are copied from GCS to registrars' Drive
folders, do not fail the entire copy operation when a single registrar
fails. Instead, send an alert email about the failure, and continue to copy the
rest of the reports.

Also, instead of creating duplicates, overwrite the existing files on
Drive.

BUG=127690361
2019-07-22 14:09:49 -04:00
Lai Jiang 7352f9b4a6 Remove unused local variable (#185) 2019-07-22 10:04:16 -04:00
Lai Jiang 5da48184f9 Merge beam and GAE configs deployment to one GCB job (#182)
* Merge beam and GAE configs deployment to one GCB job

Deployment of GAE configs requires that the credential used by gcloud to
have GAE admin role of the project to be managed. We do not want to
grant the GCB service account that role, because it would all *any* GCB
job to deploy anything to GAE. Instead we use a dedicated credential
originally created to deploy beam pipelines. This credential is
encrypted by KMS and stored on GCS. Since the beam pipeline deployment
GCB job already does the decryption, it make sense to add the config
deployment step there as well. The beam deployment steps are tweaked to
use the nomulus tool docker image instead of the jar file.

Also moved the content of deploy_configs_to_env.sh to the GCB yaml file
itself because the shell script is not uploaded to GC Bat the same time
as the yaml file when the job is triggered by Spinnaker.

Lastly, due to b/137891685, using GCB to deploy cron jobs does not work
as we cannot use service account credential to deploy to projects under
google.com.
2019-07-19 16:54:56 -04:00
gbrodman 5bd2ccd210 Add a Cloud Build task to update YAML configs (#177)
* Add a Cloud Build task to update YAML configs

* CR responses

* Move config deployment to a script

* Pin builder version

* Create different beam and deploy-config files per environment

* Update comments and make a for loop
2019-07-18 12:15:15 -04:00
Lai Jiang 8fd5ab2bec Build proxy image in Gradle (#179) 2019-07-17 20:38:03 -04:00
Lai Jiang 30f6113b05 Upgrade to Gradle 5.5.1 (#178)
Also make the default wrapper type "all" instead of "binary". This is
helpful for IDEs to understand the gradle script.
2019-07-17 17:37:44 -04:00
Michael Muller bd48041961 Build docker image of nomulus tool (#142)
* Build docker image of nomulus tool

In the course of "gradle build", build a docker image of nomulus tool so that
users can run this to allow us to bundle the java version with the image.
2019-07-16 20:18:44 -04:00
gbrodman 39ceda628c Don't extend expiration times for deleted domains (#160)
* Don't extend expiration times for deleted domains

* Flip order and add a comment

* oops forgot a period

* Use END_OF_TIME

* Add tests for expiration times of domains with pending transfers

* Add test for transfer during autorenew and clean up other tests

* Clarify tests

* Add domain expiration check in EppLifecycleDomainTest

* Add a comment and format test files
2019-07-16 18:34:21 -04:00
Aman Sanger 0e9b75e5e9 Created Prober subproject and setup basic ActionHandler and its unit tests (#133)
* Initial Commit.

* Deleted unfinished features. Added ActionHandler and its Unit Tests.

* Included prober subproject in settings.gradle

* Added Protocol Class and its Basic Unit Tests

* Added Changes Suggested by jianglai

* Fixed Gitignore to take out AutoValue generated code

* Removed AutoValue java files

* Added gitignore within prober

* Removed all generated java

* Final Changes in .gitignore

* Added Ssl and WebWhois Action Handlers and their unit tests in addition to the ProbingAction class

* Fixed build.gradle changes requested

* Removed Files irrelevant to current pull request

* Minor fixes to ActionHandler, as responded in comments, removed package-info, and updated settings.gradle

* Fully Updated ActionHandler (missing updated JavaDoc)

* Added changed Protocol and both Inbound and Outbound Markers

* Removed AutoVaue ignore clause from .gitignore

* removed unneccessary dependencies in build.gradle

* Fixed Javadoc and comments for ActionHandler

* Fixed comments and JavaDoc on other files

* EOL added

* Removed Unnecessary Files

* fixed .gradle files styles

* Merge remote-tracking branch 'upstream/master'

* Removed outbound message from ActionHandler's fields and renamed Marker Interfaces

* Fixed javadoc for Marker Interfaced

* Modified Comments on ActionHandler

* Removed LocalAddress from Protocol

* Fixed Travis Build Issues
2019-07-16 10:35:14 -04:00
Lai Jiang c1207464d8 Fix a typo (#174) 2019-07-15 17:49:22 -04:00
Weimin Yu 62eab98921 Unused deps check (#171)
* Save for later

* Check for unused dependencies

Add a task to check for unused dependencies in
dependencies.gradle. This file should only contain
dependencies explicitly added to a configuration
in root project or some subprojects.

* Check for unused dependencies

Add a task to check for unused dependencies in
dependencies.gradle. This file should only contain
dependencies explicitly added to a configuration
in root project or some subprojects.

* Check for unused dependencies

Add a task to check for unused dependencies in
dependencies.gradle. This file should only contain
dependencies explicitly added to a configuration
in root project or some subprojects.

* Check for unused dependencies

Add a task to check for unused dependencies in
dependencies.gradle. This file should only contain
dependencies explicitly added to a configuration
in root project or some subprojects.

* Check for unused dependencies

Add a task to check for unused dependencies in
dependencies.gradle. This file should only contain
dependencies explicitly added to a configuration
in root project or some subprojects.

* Check for unused dependencies

Add a task to check for unused dependencies in
dependencies.gradle. This file should only contain
dependencies explicitly added to a configuration
in root project or some subprojects.

* Check for unused dependencies

Add a task to check for unused dependencies in
dependencies.gradle. This file should only contain
dependencies explicitly added to a configuration
in root project or some subprojects.

* Check for unused dependencies

Add a task to check for unused dependencies in
dependencies.gradle. This file should only contain
dependencies explicitly added to a configuration
in root project or some subprojects.

* Check for unused dependencies

Add a task to check for unused dependencies in
dependencies.gradle. This file should only contain
dependencies explicitly added to a configuration
in root project or some subprojects.
2019-07-15 16:19:34 -04:00
Shicong Huang 633dd887f4 Fix permission issue in Beam pipeline deployment (#170) 2019-07-15 16:13:42 -04:00
Lai Jiang 650f1fdd52 Bump lodash from 4.17.11 to 4.17.14 (#173) 2019-07-15 11:26:12 -04:00
gbrodman b0f6a498fd Bump the version of the AppEngine Gradle plugin (#169)
* Bump the version of the AppEngine Gradle plugin

* deploy should depend on deployAll
2019-07-15 11:25:23 -04:00
gbrodman 77590dcd8e Add a metric for EPP processing time regardless of ID/TLD (#163)
* Add a metric for EPP processing time regardless of ID/TLD

* Change name to request_time

* Record EPP processing time by traffic type

* grammar

* request type

* semicolon
2019-07-11 14:28:37 -04:00
Weimin Yu 29e4d8de05 Check dependencies' open-source licenses (#165)
* Check dependencies' open-source licenses

Use jk1/Gradle-License-Report to verify that all
dependencies have open-source licenses.

Note that the following dependencies are not checked:

Dependencies of official Gradle plugins
Dependencies declared in buildscript block
Dependencies of jk1/Gradle-License-Report itself
2019-07-11 12:08:08 -04:00
Weimin Yu 242a560f20 Remove the maybeRuntime configuration (#164)
* Remove the maybeRuntime configuration

It contains dependencies present in the bazel
build but not needed for compile. We now know
they are not needed in runtime either.
2019-07-10 16:56:53 -04:00
gbrodman 3903abd9de Move and/or delete a bunch of random resources (#152) 2019-07-09 17:38:24 -04:00
Shicong Huang 8371cb838c Add a 30s timeout for all webdriver tests (#161)
Sometimes, the webdriver tests get stuck forever for no reason. It could
be some issue in the test container but it is hard to root cause it. So,
adding a 30s timeout can either trigger the retry earlier or let the
test just fail.
2019-07-09 14:42:32 -04:00
Shicong Huang 8dd6797614 Read golden images from src directly (#159)
This PR prevents Gradle from copying the golden images
to build/resources/test, so the screenshot test would
read golden images from src/test/resources directly and
display the path in test log if the test fails. Because
the path pointing to the actual file in src/ folder,
the engineer can easily find it.
2019-07-09 10:49:20 -04:00
Lai Jiang 730f108e13 Upgrade to Gradle 5.5 (#158) 2019-07-08 12:56:43 -04:00
gbrodman e5bafddd2f Move JS and CSS files to a Javascript source dir (#156) 2019-07-05 12:01:16 -04:00
Lai Jiang 82f51accbd Set beam deployment environment in GCB trigger (#157) 2019-07-03 16:28:28 -04:00
Shicong Huang 6536631857 Remove injected credentials from invoice pipeline (#155)
We got non-serialization object error when deploying the invoicing
pipeline. It turns out that Beam requires every field in the pipeline
object is serilizable. However, it is non-trivial to make
GoogleCredentialsBundle serilizable because almost all of its
dependency are not serilizable and not contraled by us. Also,
it is non-necessary to inject the credential as the spec11
pipeline also writes output to GCS without having injected
credential. So, removing the injected variable can solve the
problem.

TESTED=First reproduced the problem locally by deploying the invoicing pipeline with the previous code; applied this change and successfully deploy the pipeline without having any issue.
2019-07-03 15:12:48 -04:00
gbrodman 1be92968bf Attempt login to MosAPI via all available TLDs (#141)
* Attempt login to MosAPI via all available TLDs

There's no reason why we should need a TLD as input here because it
doesn't actually matter which one we use (they all have the same
password).

* Refactor the TLD loop and change cron jobs

* Re-throw the last exception if one exists

* Fix tests and exception

* Remove alpha cron job
2019-07-03 14:25:39 -04:00
Ben McIlwain 0564b207f2 Prevent accidentally using full Drive URL as folder ID (#144)
* Prevent accidentally using full Drive URL as folder ID
2019-07-03 14:22:15 -04:00
gbrodman 8afc4f4d3d Enforce that source files end in a newline (#153) 2019-07-03 12:16:11 -04:00
guyben13 888bc158fe Add explanation on how to use the MoSAPI endpoint (#137)
* Add explanation on how to use the MoSAPI endpoint

* Add a $ before each command so that it's clearer
2019-07-02 18:20:53 -04:00
Lai Jiang 25ee92b5d8 Re-add hello.xml file (#147)
* Re-add hello.xml file

Apparently it was lost during the great refactoring.

* Remove hello.xml from test resources
2019-07-02 17:21:40 -04:00
gbrodman cf507dad6d Move test resource files into src/test/resources (#143)
* Move test resource files into src/test/resources

* fix a test

* Remove references to javatests/ in Java files

* fix import order

* fix semantic merge conflict
2019-07-02 16:54:49 -04:00
gbrodman 32d5940be3 Remove old Bazel files (#151) 2019-07-02 16:24:01 -04:00
Shicong Huang 82fa3d7349 Change button color to blue style (#150) 2019-07-02 15:29:17 -04:00
Ben McIlwain 07239710ef Throw a more useful error message on attempted domain restore reports (#145)
* Throw a more useful error message on attempted domain restore reports

Per DomainRestoreRequestFlow's Javadoc, we automatically approve and instantly
enact all domain restore requests, thus we don't use or support restore
reports. This improves the registrar-visible error message to help make this
more clear.
2019-07-02 14:11:37 -04:00
gbrodman 0e8724a48f Require the license in Gradle files (#149) 2019-07-02 11:47:35 -04:00
gbrodman 8332664af9 Move golden test files to src/test/resources (#146) 2019-07-02 11:34:41 -04:00
gbrodman 39abee6279 Update checkstyle.xml with many presubmit checks (#130)
* Update checkstyle.xml with many presubmit checks

* Remove a comment
2019-07-02 10:35:32 -04:00
Shicong Huang 6daf72a54e Replace deprecated GoogleCredential with new auth lib (#129)
Replace deprecated GoogleCredential with new lib

This PR also introduced a CredentialsBundle class to carry
HttpTransport and JsonFactory object which are needed by
most of the GCP library to instantiate client.
2019-07-02 10:29:51 -04:00
1217 changed files with 2881 additions and 2152 deletions
+3
View File
@@ -4,3 +4,6 @@ python/
.*/
repos/**
**/.idea/
*.jar
!third_party/**/*.jar
!/gradle/wrapper/**/*.jar
+4
View File
@@ -0,0 +1,4 @@
extraction:
java:
prepare:
packages: "npm"
-1
View File
@@ -1,4 +1,3 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
+3 -3
View File
@@ -1,8 +1,8 @@
# Nomulus
| Internal Build | FOSS Build | License | Code Search |
|----------------|------------|---------|-------------|
|[![Build Status for Google Registry internal build](https://storage.googleapis.com/domain-registry-kokoro/build.svg)](https://storage.googleapis.com/domain-registry-kokoro/index.html)|[![Build Status for the open source build](https://travis-ci.org/google/nomulus.svg?branch=master)](https://travis-ci.org/google/nomulus)|[![License for this repo](https://img.shields.io/github/license/google/nomulus.svg)](https://github.com/jianglai/nomulus/blob/master/LICENSE)|[![Link to Source Graph](https://github.com/sourcegraph/sourcegraph/blob/master/ui/assets/img/sourcegraph-logo.svg)](https://sourcegraph.com/github.com/google/nomulus)|
| Internal Build | FOSS Build | LGTM | License | Code Search |
|----------------|------------|------|---------|-------------|
|[![Build Status for Google Registry internal build](https://storage.googleapis.com/domain-registry-kokoro/build.svg)](https://storage.googleapis.com/domain-registry-kokoro/index.html)|[![Build Status for the open source build](https://travis-ci.org/google/nomulus.svg?branch=master)](https://travis-ci.org/google/nomulus)|[![Total alerts](https://img.shields.io/lgtm/alerts/g/google/nomulus.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/google/nomulus/alerts/)|[![License for this repo](https://img.shields.io/github/license/google/nomulus.svg)](https://github.com/google/nomulus/blob/master/LICENSE)|[![Link to Source Graph](https://github.com/sourcegraph/sourcegraph/blob/master/ui/assets/img/sourcegraph-logo.svg)](https://sourcegraph.com/github.com/google/nomulus)|
![Nomulus logo](./nomulus-logo.png)
+22 -2
View File
@@ -1,3 +1,17 @@
// 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.
def environments = ['production', 'sandbox', 'alpha', 'crash']
def projects = ['production': 'domain-registry',
@@ -61,7 +75,13 @@ if (project.path == ":services:default") {
appengine {
deploy {
project = gcpProject
// appengineDeployAll task requires the version to be set. So,
// this config lets gcloud select a version name when deploying
// to alpha from our workstation.
if (environment != 'production' && environment != 'sandbox') {
version = 'GCLOUD_CONFIG'
}
projectId = gcpProject
}
}
@@ -69,6 +89,6 @@ dependencies {
compile project(':core')
}
rootProject.deploy.dependsOn appengineDeploy
rootProject.deploy.dependsOn appengineDeployAll
rootProject.stage.dependsOn appengineStage
appengineDeploy.dependsOn rootProject.verifyDeployment
+84 -6
View File
@@ -1,5 +1,19 @@
// 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.
buildscript {
if (project.disableDependencyLocking.toBoolean() == false) {
if (rootProject.enableDependencyLocking.toBoolean()) {
// Lock buildscript dependencies.
configurations.classpath {
resolutionStrategy.activateDependencyLocking()
@@ -7,7 +21,7 @@ buildscript {
}
dependencies {
classpath 'com.google.cloud.tools:appengine-gradle-plugin:1.3.3'
classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.0.1'
classpath "net.ltgt.gradle:gradle-errorprone-plugin:0.6.1"
classpath 'org.sonatype.aether:aether-api:1.13.1'
classpath 'org.sonatype.aether:aether-impl:1.13.1'
@@ -32,6 +46,10 @@ plugins {
id 'com.diffplug.gradle.spotless' version '3.18.0'
}
wrapper {
distributionType = Wrapper.DistributionType.ALL
}
apply plugin: google.registry.gradle.plugin.ReportUploaderPlugin
reportUploader {
@@ -57,6 +75,19 @@ reportUploader {
apply from: 'dependencies.gradle'
apply from: 'dependency_lic.gradle'
// Custom task to run checkLicense in buildSrc, which is not triggered
// by root project tasks. A shell task is used because buildSrc tasks
// cannot be referenced in the same way as tasks from a regular included
// build.
task checkBuildSrcLicense(type:Exec) {
workingDir "${rootDir}/buildSrc"
commandLine '../gradlew', 'checkLicense'
}
tasks.checkLicense.dependsOn(tasks.checkBuildSrcLicense)
tasks.build.dependsOn(tasks.checkLicense)
// Provide defaults for all of the project properties.
// showAllOutput: boolean. If true, dump all test output during the build.
@@ -138,16 +169,17 @@ subprojects {
}
}
if (rootProject.disableDependencyLocking.toBoolean() == false) {
if (rootProject.enableDependencyLocking.toBoolean()) {
buildscript {
// Lock buildscript dependencies.
configurations.classpath {
resolutionStrategy.activateDependencyLocking()
}
}
// Lock application dependencies.
dependencyLocking {
lockAllConfigurations()
// Lock application dependencies except for the gradle-license-report
// plugin. See dependency_lic.gradle for more information.
configurations.findAll { it.name != 'dependencyLicenseReport' }.each {
it.resolutionStrategy.activateDependencyLocking()
}
}
@@ -223,3 +255,49 @@ subprojects {
}
}
}
task checkDependenciesDotGradle {
def buildSrcDepsFile = File.createTempFile('buildSrc', 'deps')
buildSrcDepsFile.deleteOnExit()
dependsOn createGetBuildSrcDirectDepsTask(buildSrcDepsFile)
doLast {
Set<String> depsInUse = []
allprojects {
configurations.all {
it.dependencies.findAll { it.group != null }.each {
// Note: .toString() is required since GString should
// not be mixed with Java Strings.
depsInUse.add("${it.group}:${it.name}".toString())
}
}
}
if (buildSrcDepsFile.exists()) {
depsInUse.addAll(buildSrcDepsFile.readLines())
}
def unusedDeps =
rootProject.dependencyMap.keySet()
.findAll { !depsInUse.contains(it) }
.toSorted()
if (unusedDeps.isEmpty()) {
return
}
logger.error(
"Unused dependencies in dependencies.gradle:\n${unusedDeps.toListString()}")
throw new IllegalStateException(
"The dependencies.gradle file should only contain direct dependencies.")
}
}
tasks.build.dependsOn(tasks.checkDependenciesDotGradle)
def createGetBuildSrcDirectDepsTask(outputFileName) {
return tasks
.create(
"getBuildSrcDeps_${java.util.UUID.randomUUID()}".toString(),
Exec) {
workingDir "${rootDir}/buildSrc"
commandLine '../gradlew', 'exportDenpendencies',
"-PdependencyExportFile=${outputFileName}"
}
}
+35 -2
View File
@@ -1,5 +1,19 @@
// 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.
buildscript {
if (rootProject.disableDependencyLocking.toBoolean() == false) {
if (project.enableDependencyLocking.toBoolean()) {
// Lock buildscript dependencies.
configurations.classpath {
resolutionStrategy.activateDependencyLocking()
@@ -18,7 +32,7 @@ plugins {
id 'com.diffplug.gradle.spotless' version '3.18.0'
}
if (rootProject.disableDependencyLocking.toBoolean() == false) {
if (rootProject.enableDependencyLocking.toBoolean()) {
// Lock application dependencies.
dependencyLocking {
lockAllConfigurations()
@@ -38,6 +52,7 @@ repositories {
}
apply from: '../dependencies.gradle'
apply from: '../dependency_lic.gradle'
apply from: '../java_common.gradle'
sourceSets {
@@ -74,3 +89,21 @@ gradle.projectsEvaluated {
options.compilerArgs << "-Xlint:unchecked"
}
}
task exportDenpendencies {
def outputFileProperty = 'dependencyExportFile'
def output = project.hasProperty(outputFileProperty)
? new PrintStream(
new File(project.getProperty(outputFileProperty)))
: System.out
doLast {
project.configurations.all {
it.dependencies.findAll {
it.group != null
}.each {
output.println("${it.group}:${it.name}")
}
}
}
}
+1 -1
View File
@@ -1 +1 @@
disableDependencyLocking=false
enableDependencyLocking=false
+47
View File
@@ -25,6 +25,53 @@ by Joshua Bloch in his book Effective Java -->
<property name="message" value='TODO is preferred to FIXME. e.g. "TODO(johndoe): Refactor when v2 is released."' />
</module>
<!-- Checks that String.toUpper/LowerCase() is never used without locale. -->
<module name="RegexpSingleline">
<property name="format" value="\.to(Upper|Lower)Case\(\)"/>
<property name="message" value="String.toUpper/LowerCase() can have unexpected results depending on locale. Either set the locale explicitly - e.g., toUpperCase(Locale.ENGLISH) - or use Ascii.toUpper/LowerCase(...) instead." />
</module>
<!-- Checks that DateTime.now(...) is always passed a DateTimeZone parameter. -->
<module name="RegexpSingleline">
<property name="format" value="DateTime\.now\(\)"/>
<property name="message" value="DateTime.now() must be called with a DateTimeZone parameter, e.g. DateTime.now(UTC)" />
</module>
<!-- Checks that Javadoc does not include a malformed @see tag. -->
<module name="RegexpSingleline">
<property name="format" value='@see\s\"http(|s)://'/>
<property name="message" value='Your Javadocs appear to use invalid &lt;a&gt; tag syntax in @see tags. Please use the correct syntax: @see &lt;a href="http(s)://your_url"&gt;url_description&lt;/a&gt;'/>
</module>
<!-- Checks that our Ofy wrapper is used instead of the "real" ofy(). -->
<module name="RegexpSingleline">
<property name="format" value="com\.googlecode\.objectify\.ObjectifyService\.ofy"/>
<property name="message" value="Use google.registry.model.ofy.ObjectifyService.ofy(). Do not use com.googlecode.objectify.v4.ObjectifyService.ofy()."/>
</module>
<!-- Checks that java.util.Optional is used instead of Guava's Optional. -->
<module name="RegexpSingleline">
<property name="format" value="com\.google\.common\.base\.Optional"/>
<property name="message" value="Use java.util.Optional instead of Guava's Optional."/>
</module>
<!-- Checks that our backport JUnit exception assertion methods are used instead of the ones slated for release in JUnit 4.13. -->
<module name="RegexpSingleline">
<property name="format" value="org\.junit\.Assert\.(assert|expect)Throws"/>
<property name="message" value="Use the exception assertion methods in google.registry.testing.JUnitBackports instead of those in JUnit."/>
</module>
<!-- Checks that the deprecated ExpectedException is not used. -->
<module name="RegexpSingleline">
<property name="format" value="org\.junit\.rules\.ExpectedException"/>
<property name="message" value="Use assertThrows and expectThrows from JUnitBackports instead of the deprecated methods on ExpectedException."/>
</module>
<!-- Checks that the deprecated MockitoJUnitRunner is not used. -->
<module name="RegexpSingleline">
<property name="format" value="MockitoJUnitRunner"/>
<property name="message" value="MockitoJUnitRunner is deprecated. Use @RunWith(JUnit4.class) and MockitoRule instead."/>
</module>
<!-- All Java AST specific tests live under TreeWalker module. -->
<module name="TreeWalker">
+2
View File
@@ -9,4 +9,6 @@
<suppress files="[/\\].*[/\\]generated.*[/\\]" checks="."/>
<!-- Ignore Javadoc checks in test files -->
<suppress files="[/\\].*[/\\]src/test/java/.*[/\\]" checks="JavadocType"/>
<!-- ofy() regex check doesn't apply to these files -->
<suppress files="AugmentedDeleter.java|AugmentedSaver.java|Ofy.java" checks="RegexpSingleline"/>
</suppressions>
@@ -0,0 +1,172 @@
{
"allowedLicenses": [
{
"moduleLicense": "Apache Software License, Version 1.1"
},
{
"moduleLicense": "Apache 2"
},
{
"moduleLicense": "Apache 2.0"
},
{
"moduleLicense": "Apache-2.0"
},
{
"moduleLicense": "Apache License 2.0"
},
{
"moduleLicense": "Apache License v2.0"
},
{
"moduleLicense": "Apache License, Version 2.0"
},
{
"moduleLicense": "Apache Software License - Version 2.0"
},
{
"moduleLicense": "The Apache License, Version 2.0"
},
{
"moduleLicense": "The Apache Software License, Version 2.0"
},
{
"moduleLicense": "The Apache Software License, version 2.0"
},
{
"moduleLicense": "Bouncy Castle Licence"
},
{
"moduleLicense": "BSD"
},
{
"moduleLicense": "BSD 2-Clause License"
},
{
"moduleLicense": "BSD 2-Clause license"
},
{
"moduleLicense": "BSD 3-clause"
},
{
"moduleLicense": "BSD 3-Clause"
},
{
"moduleLicense": "BSD 3-Clause License"
},
{
"moduleLicense": "The 3-Clause BSD License"
},
{
"moduleLicense": "BSD License"
},
{
"moduleLicense": "BSD New License"
},
{
"moduleLicense": "BSD New license"
},
{
"moduleLicense": "BSD style"
},
{
"moduleLicense": "New BSD License"
},
{
"moduleLicense": "Revised BSD"
},
{
"moduleLicense": "The BSD License"
},
{
"moduleLicense": "CC0 1.0 Universal License"
},
{
"moduleLicense": "CDDL 1.1"
},
{
"moduleLicense": "CDDL + GPLv2 with classpath exception"
},
{
"moduleLicense": "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0"
},
{
"moduleLicense": "https://glassfish.java.net/public/CDDL+GPL_1_1.html, https://glassfish.java.net/public/CDDL+GPL_1_1.html"
},
{
"moduleLicense": "CDDL+GPL License"
},
{
"moduleLicense": "\\n Dual license consisting of the CDDL v1.1 and GPL v2\\n "
},
{
"moduleLicense": "Eclipse Public License - Version 1.0"
},
{
"moduleLicense": "Eclipse Public License - v 1.0"
},
{
"moduleLicense": "Eclipse Public License 1.0"
},
{
"moduleLicense": "Google App Engine Terms of Service"
},
{
"moduleLicense": "GNU General Public License, version 2, with the Classpath Exception"
},
{
"moduleLicense": "GNU GENERAL PUBLIC LICENSE, Version 2 + Classpath Exception"
},
{
"moduleLicense": "GPL2 w/ CPE"
},
{
"moduleLicense": "GNU Lesser Public License"
},
{
"moduleLicense": "The Go license"
},
{
"moduleLicense": "GWT Terms"
},
{
"moduleLicense": "ICU License"
},
{
"moduleLicense": "The JSON License"
},
{
"moduleLicense": "LGPL-2.1+"
},
{
"moduleLicense": "LGPL, version 2.1"
},
{
"moduleLicense": "MIT"
},
{
"moduleLicense": "MIT License"
},
{
"moduleLicense": "MIT license"
},
{
"moduleLicense": "The MIT License"
},
{
"moduleLicense": "The MIT license"
},
{
"moduleLicense": "Mozilla Public License Version 2.0"
},
{
"moduleLicense": "Public Domain"
},
{
"moduleLicense": "PUBLIC DOMAIN"
},
{
"moduleLicense": "The W3C Software License"
}
]
}
@@ -0,0 +1,32 @@
{
"bundles" : [
{
"bundleName" : "Apache 2.0",
"licenseName" : "Apache 2.0",
"licenseUrl" : "http://www.apache.org/licenses/LICENSE-2.0"
},
{
"bundleName" : "MIT License",
"licenseName" : "MIT License",
"licenseUrl" : "http://www.opensource.org/licenses/mit-license.php"
}
],
"transformationRules" : [
{
"bundleName" : "Apache 2.0",
"licenseUrlPattern" : "http://www.apache.org/licenses/LICENSE-2.0"
},
{
"bundleName" : "Apache 2.0",
"licenseUrlPattern" : "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"bundleName" : "MIT License",
"licenseUrlPattern" : "http://www.opensource.org/licenses/mit-license.php"
},
{
"bundleName" : "MIT License",
"licenseUrlPattern" : "http://www.opensource.org/licenses/MIT"
}
]
}
+6 -2
View File
@@ -76,12 +76,17 @@ PRESUBMITS = {
# License check
PresubmitCheck(
r".*Copyright 20\d{2} The Nomulus Authors\. All Rights Reserved\.",
("java", "js", "soy", "sql", "py", "sh"), {
("java", "js", "soy", "sql", "py", "sh", "gradle"), {
".git", "/build/", "/generated/", "node_modules/",
"JUnitBackports.java"
}, REQUIRED):
"File did not include the license header.",
# Files must end in a newline
PresubmitCheck(r".*\n$", ("java", "js", "soy", "sql", "py", "sh", "gradle"),
{"node_modules/"}, REQUIRED):
"Source files must end in a newline.",
# System.(out|err).println should only appear in tools/
PresubmitCheck(
r".*\bSystem\.(out|err)\.print", "java", {
@@ -158,7 +163,6 @@ PRESUBMITS = {
def get_files():
result = []
for root, dirnames, filenames in os.walk("."):
for filename in filenames:
yield os.path.join(root, filename)
+3
View File
@@ -0,0 +1,3 @@
FROM gcr.io/distroless/java:debug
ADD build/libs/nomulus.jar /nomulus.jar
ENTRYPOINT ["/usr/bin/java", "-jar", "/nomulus.jar"]
+29 -69
View File
@@ -1,3 +1,17 @@
// 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.
plugins {
id 'java-library'
}
@@ -11,6 +25,7 @@ def screenshotsForGoldensDir = "${project.buildDir}/screenshots_for_goldens"
def newGoldensDir = "${project.buildDir}/new_golden_images"
def goldensDir =
"${javaTestDir}/google/registry/webdriver/goldens/chrome-linux"
def jsDir = "${project.projectDir}/src/main/javascript"
// Tests that conflict with (mostly unidentified) members of the main test
// suite. It is unclear if they are offenders (i.e., those that pollute global
@@ -82,14 +97,14 @@ sourceSets {
}
}
processTestResources {
exclude '**/webdriver/*'
}
configurations {
css
jaxb
soy
// Label for all dependencies inherited from Bazel build but not used in
// either compile or testRuntime. However, they may be needed at runtime.
// TODO(weiminyu): identify runtime dependencies and remove the rest.
maybeRuntime
closureCompiler
// Exclude non-canonical servlet-api jars. Our AppEngine deployment uses
@@ -125,53 +140,38 @@ dependencies {
"${rootDir}/third_party/objectify/v4_1/objectify-4.1.3.jar")
testImplementation project(':third_party')
testRuntime files(sourceSets.test.resources.srcDirs)
compile deps['com.beust:jcommander']
compile deps['com.google.api-client:google-api-client']
maybeRuntime deps['com.google.api-client:google-api-client-appengine']
maybeRuntime deps['com.google.api-client:google-api-client-jackson2']
compile deps['com.google.monitoring-client:metrics']
compile deps['com.google.monitoring-client:stackdriver']
compile deps['com.google.api-client:google-api-client-java6']
maybeRuntime deps['com.google.api-client:google-api-client-servlet']
compile deps['com.google.apis:google-api-services-admin-directory']
compile deps['com.google.apis:google-api-services-appengine']
compile deps['com.google.apis:google-api-services-bigquery']
maybeRuntime deps['com.google.apis:google-api-services-clouddebugger']
compile deps['com.google.apis:google-api-services-cloudkms']
maybeRuntime deps['com.google.apis:google-api-services-cloudresourcemanager']
compile deps['com.google.apis:google-api-services-dataflow']
compile deps['com.google.apis:google-api-services-dns']
compile deps['com.google.apis:google-api-services-drive']
compile deps['com.google.apis:google-api-services-groupssettings']
compile deps['com.google.apis:google-api-services-monitoring']
compile deps['com.google.apis:google-api-services-sheets']
maybeRuntime deps['com.google.apis:google-api-services-storage']
testCompileOnly deps['com.google.appengine:appengine-api-1.0-sdk']
maybeRuntime deps['com.google.appengine:appengine-api-labs']
maybeRuntime deps['com.google.appengine:appengine-api-stubs']
testCompile deps['com.google.appengine:appengine-api-stubs']
compile deps['com.google.appengine.tools:appengine-gcs-client']
compile deps['com.google.appengine.tools:appengine-mapreduce']
compile deps['com.google.appengine.tools:appengine-pipeline']
compile deps['com.google.appengine:appengine-remote-api']
maybeRuntime deps['com.google.appengine:appengine-tools-sdk']
compile deps['com.google.auth:google-auth-library-credentials']
compile deps['com.google.auth:google-auth-library-oauth2-http']
maybeRuntime deps['com.google.auto:auto-common']
maybeRuntime deps['com.google.auto.factory:auto-factory']
compile deps['com.google.code.gson:gson']
compile deps['com.google.auto.value:auto-value-annotations']
maybeRuntime deps['com.google.cloud.bigdataoss:gcsio']
maybeRuntime deps['com.google.cloud.bigdataoss:util']
compile deps['com.google.code.findbugs:jsr305']
compile deps['com.google.dagger:dagger']
maybeRuntime deps['com.google.dagger:dagger-producers']
compile deps['com.google.errorprone:error_prone_annotations']
maybeRuntime deps['com.google.errorprone:javac-shaded']
compile deps['com.google.flogger:flogger']
runtime deps['com.google.flogger:flogger-system-backend']
maybeRuntime deps['com.google.gdata:core']
maybeRuntime deps['com.google.googlejavaformat:google-java-format']
compile deps['com.google.guava:guava']
gradleLint.ignore('unused-dependency') {
compile deps['com.google.gwt:gwt-user']
@@ -180,89 +180,48 @@ dependencies {
compile deps['com.google.http-client:google-http-client-appengine']
compile deps['com.google.http-client:google-http-client-jackson2']
compile deps['com.google.oauth-client:google-oauth-client']
maybeRuntime deps['com.google.oauth-client:google-oauth-client-appengine']
compile deps['com.google.oauth-client:google-oauth-client-java6']
compile deps['com.google.oauth-client:google-oauth-client-jetty']
maybeRuntime deps['com.google.oauth-client:google-oauth-client-servlet']
maybeRuntime deps['com.google.protobuf:protobuf-java']
compile deps['com.google.re2j:re2j']
compile deps['com.google.template:soy']
maybeRuntime deps['com.googlecode.charts4j:charts4j']
compile deps['com.googlecode.json-simple:json-simple']
compile deps['com.jcraft:jsch']
maybeRuntime deps['com.jcraft:jzlib']
maybeRuntime deps['com.squareup:javapoet']
maybeRuntime deps['com.squareup:javawriter']
maybeRuntime deps['com.sun.activation:javax.activation']
maybeRuntime deps['com.thoughtworks.paranamer:paranamer']
testCompile deps['com.thoughtworks.qdox:qdox']
maybeRuntime deps['commons-codec:commons-codec']
maybeRuntime deps['commons-logging:commons-logging']
compile deps['dnsjava:dnsjava']
maybeRuntime deps['io.netty:netty-buffer']
maybeRuntime deps['io.netty:netty-codec']
maybeRuntime deps['io.netty:netty-codec-http']
maybeRuntime deps['io.netty:netty-common']
maybeRuntime deps['io.netty:netty-handler']
maybeRuntime deps['io.netty:netty-resolver']
maybeRuntime deps['io.netty:netty-tcnative']
maybeRuntime deps['io.netty:netty-tcnative-boringssl-static']
maybeRuntime deps['io.netty:netty-transport']
maybeRuntime deps['it.unimi.dsi:fastutil']
maybeRuntime deps['javax.annotation:jsr250-api']
runtime deps['org.glassfish.jaxb:jaxb-runtime']
testCompile deps['javax.annotation:jsr250-api']
compile deps['javax.inject:javax.inject']
compile deps['javax.mail:mail']
compile deps['javax.servlet:servlet-api']
compile deps['javax.xml.bind:jaxb-api']
maybeRuntime deps['javax.xml.soap:javax.xml.soap-api']
compile deps['jline:jline']
compile deps['joda-time:joda-time']
compile deps['org.apache.avro:avro']
maybeRuntime deps['org.apache.beam:beam-runners-direct-java']
testCompile deps['org.apache.beam:beam-runners-direct-java']
compile deps['org.apache.beam:beam-runners-google-cloud-dataflow-java']
maybeRuntime deps['org.apache.beam:beam-sdks-common-runner-api']
compile deps['org.apache.beam:beam-sdks-java-core']
compile deps['org.apache.beam:beam-sdks-java-extensions-google-cloud-platform-core']
compile deps['org.apache.beam:beam-sdks-java-io-google-cloud-platform']
maybeRuntime deps['org.apache.commons:commons-compress']
testCompile deps['org.apache.commons:commons-text']
maybeRuntime deps['org.apache.ftpserver:ftplet-api']
testCompile deps['org.apache.ftpserver:ftplet-api']
maybeRuntime deps['org.apache.ftpserver:ftpserver-core']
testCompile deps['org.apache.ftpserver:ftpserver-core']
compile deps['org.apache.httpcomponents:httpclient']
compile deps['org.apache.httpcomponents:httpcore']
maybeRuntime deps['org.apache.mina:mina-core']
maybeRuntime deps['org.apache.sshd:sshd-core']
testCompile deps['org.apache.sshd:sshd-core']
maybeRuntime deps['org.apache.sshd:sshd-scp']
testCompile deps['org.apache.sshd:sshd-scp']
maybeRuntime deps['org.apache.sshd:sshd-sftp']
testCompile deps['org.apache.sshd:sshd-sftp']
maybeRuntime deps['org.apache.tomcat:tomcat-annotations-api']
testCompile deps['org.apache.tomcat:tomcat-annotations-api']
compile deps['org.bouncycastle:bcpg-jdk15on']
testCompile deps['org.bouncycastle:bcpkix-jdk15on']
compile deps['org.bouncycastle:bcprov-jdk15on']
maybeRuntime deps['org.codehaus.jackson:jackson-core-asl']
maybeRuntime deps['org.codehaus.jackson:jackson-mapper-asl']
compile deps['org.joda:joda-money']
compile deps['org.json:json']
maybeRuntime deps['org.khronos:opengl-api']
maybeRuntime deps['org.mortbay.jetty:jetty']
testCompile deps['org.mortbay.jetty:jetty']
maybeRuntime deps['org.mortbay.jetty:jetty-util']
testCompile deps['org.seleniumhq.selenium:selenium-api']
testCompile deps['org.seleniumhq.selenium:selenium-chrome-driver']
testCompile deps['org.seleniumhq.selenium:selenium-java']
testCompile deps['org.seleniumhq.selenium:selenium-remote-driver']
maybeRuntime deps['org.slf4j:slf4j-api']
testCompile deps['org.testcontainers:selenium']
maybeRuntime deps['org.tukaani:xz']
maybeRuntime deps['org.xerial.snappy:snappy-java']
compile deps['xerces:xmlParserAPIs']
compile deps['xpp3:xpp3']
@@ -466,7 +425,7 @@ task soyToJS {
}
task stylesheetsToJavascript {
def cssSourceDir = "${javaDir}/google/registry/ui/css"
def cssSourceDir = "${jsDir}/google/registry/ui/css"
def outputDir = "${resourcesDir}/google/registry/ui/css"
inputs.dir cssSourceDir
outputs.dir outputDir
@@ -519,8 +478,8 @@ task compileProdJS(type: JavaExec) {
def outputDir = "${resourcesDir}/google/registry/ui"
def nodeModulesDir = "${rootDir}/node_modules"
def cssSourceDir = "${resourcesDir}/google/registry/ui/css"
def jsSourceDir = "${javaDir}/google/registry/ui/js"
def externsDir = "${javaDir}/google/registry/ui/externs"
def jsSourceDir = "${jsDir}/google/registry/ui/js"
def externsDir = "${jsDir}/google/registry/ui/externs"
def soySourceDir = "${generatedDir}/google/registry/ui/soy"
[nodeModulesDir, cssSourceDir, jsSourceDir, externsDir, soySourceDir].each {
@@ -686,10 +645,11 @@ test {
}.dependsOn(fragileTest, outcastTest)
createUberJar('nomulus', 'nomulus', 'google.registry.tools.RegistryTool')
createUberJar('gtechTool', 'gtech_tool', 'google.registry.tools.GtechTool')
project.nomulus.dependsOn project(':third_party').jar
project.gtechTool.dependsOn project(':third_party').jar
project.build.dependsOn nomulus
project.build.dependsOn gtechTool
task buildToolImage(dependsOn: nomulus, type: Exec) {
commandLine 'docker', 'build', '-t', 'nomulus-tool', '.'
}
project.build.dependsOn buildToolImage
project.build.dependsOn ':stage'
@@ -17,10 +17,11 @@ package google.registry.beam.invoicing;
import com.google.auth.oauth2.GoogleCredentials;
import google.registry.beam.invoicing.BillingEvent.InvoiceGroupingKey;
import google.registry.beam.invoicing.BillingEvent.InvoiceGroupingKey.InvoiceGroupingKeyCoder;
import google.registry.config.CredentialModule.LocalCredential;
import google.registry.config.RegistryConfig.Config;
import google.registry.reporting.billing.BillingModule;
import google.registry.reporting.billing.GenerateInvoicesAction;
import google.registry.tools.AuthModule.LocalOAuth2Credentials;
import google.registry.util.GoogleCredentialsBundle;
import java.io.Serializable;
import javax.inject.Inject;
import org.apache.beam.runners.dataflow.DataflowRunner;
@@ -57,35 +58,31 @@ import org.apache.beam.sdk.values.TypeDescriptors;
*/
public class InvoicingPipeline implements Serializable {
@Inject
@Config("projectId")
String projectId;
private final String projectId;
private final String beamBucketUrl;
private final String invoiceTemplateUrl;
private final String beamStagingUrl;
private final String billingBucketUrl;
private final String invoiceFilePrefix;
private final GoogleCredentials googleCredentials;
@Inject
@Config("apacheBeamBucketUrl")
String beamBucketUrl;
@Inject
@Config("invoiceTemplateUrl")
String invoiceTemplateUrl;
@Inject
@Config("beamStagingUrl")
String beamStagingUrl;
@Inject
@Config("billingBucketUrl")
String billingBucketUrl;
@Inject
@Config("invoiceFilePrefix")
String invoiceFilePrefix;
@Inject @LocalOAuth2Credentials
GoogleCredentials credentials;
@Inject
InvoicingPipeline() {}
public InvoicingPipeline(
@Config("projectId") String projectId,
@Config("apacheBeamBucketUrl") String beamBucketUrl,
@Config("invoiceTemplateUrl") String invoiceTemplateUrl,
@Config("beamStagingUrl") String beamStagingUrl,
@Config("billingBucketUrl") String billingBucketUrl,
@Config("invoiceFilePrefix") String invoiceFilePrefix,
@LocalCredential GoogleCredentialsBundle googleCredentialsBundle) {
this.projectId = projectId;
this.beamBucketUrl = beamBucketUrl;
this.invoiceTemplateUrl = invoiceTemplateUrl;
this.beamStagingUrl = beamStagingUrl;
this.billingBucketUrl = billingBucketUrl;
this.invoiceFilePrefix = invoiceFilePrefix;
this.googleCredentials = googleCredentialsBundle.getGoogleCredentials();
}
/** Custom options for running the invoicing pipeline. */
interface InvoicingPipelineOptions extends DataflowPipelineOptions {
@@ -105,12 +102,15 @@ public class InvoicingPipeline implements Serializable {
public void deploy() {
// We can't store options as a member variable due to serialization concerns.
InvoicingPipelineOptions options = PipelineOptionsFactory.as(InvoicingPipelineOptions.class);
options.setGcpCredential(credentials);
options.setProject(projectId);
options.setRunner(DataflowRunner.class);
// This causes p.run() to stage the pipeline as a template on GCS, as opposed to running it.
options.setTemplateLocation(invoiceTemplateUrl);
options.setStagingLocation(beamStagingUrl);
// This credential is used when Dataflow deploys the template to GCS in target GCP project.
// So, make sure the credential has write permission to GCS in that project.
options.setGcpCredential(googleCredentials);
Pipeline p = Pipeline.create(options);
PCollection<BillingEvent> billingEvents =
@@ -17,9 +17,12 @@ package google.registry.beam.spec11;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.beam.BeamUtils.getQueryFromFile;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.auto.value.AutoValue;
import google.registry.beam.spec11.SafeBrowsingTransforms.EvaluateSafeBrowsingFn;
import google.registry.config.CredentialModule.LocalCredential;
import google.registry.config.RegistryConfig.Config;
import google.registry.util.GoogleCredentialsBundle;
import google.registry.util.Retrier;
import google.registry.util.SqlTemplate;
import java.io.Serializable;
@@ -77,26 +80,29 @@ public class Spec11Pipeline implements Serializable {
/** The JSON object field we put the threat match array for Spec11 reports. */
public static final String THREAT_MATCHES_FIELD = "threatMatches";
@Inject
@Config("projectId")
String projectId;
private final String projectId;
private final String beamStagingUrl;
private final String spec11TemplateUrl;
private final String reportingBucketUrl;
private final GoogleCredentials googleCredentials;
private final Retrier retrier;
@Inject
@Config("beamStagingUrl")
String beamStagingUrl;
@Inject
@Config("spec11TemplateUrl")
String spec11TemplateUrl;
@Inject
@Config("reportingBucketUrl")
String reportingBucketUrl;
@Inject Retrier retrier;
@Inject
Spec11Pipeline() {}
public Spec11Pipeline(
@Config("projectId") String projectId,
@Config("beamStagingUrl") String beamStagingUrl,
@Config("spec11TemplateUrl") String spec11TemplateUrl,
@Config("reportingBucketUrl") String reportingBucketUrl,
@LocalCredential GoogleCredentialsBundle googleCredentialsBundle,
Retrier retrier
) {
this.projectId = projectId;
this.beamStagingUrl = beamStagingUrl;
this.spec11TemplateUrl = spec11TemplateUrl;
this.reportingBucketUrl = reportingBucketUrl;
this.googleCredentials = googleCredentialsBundle.getGoogleCredentials();
this.retrier = retrier;
}
/** Custom options for running the spec11 pipeline. */
interface Spec11PipelineOptions extends DataflowPipelineOptions {
@@ -134,6 +140,9 @@ public class Spec11Pipeline implements Serializable {
// This causes p.run() to stage the pipeline as a template on GCS, as opposed to running it.
options.setTemplateLocation(spec11TemplateUrl);
options.setStagingLocation(beamStagingUrl);
// This credential is used when Dataflow deploys the template to GCS in target GCP project.
// So, make sure the credential has write permission to GCS in that project.
options.setGcpCredential(googleCredentials);
Pipeline p = Pipeline.create(options);
PCollection<Subdomain> domains =
@@ -14,7 +14,6 @@
package google.registry.bigquery;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.services.bigquery.Bigquery;
import com.google.api.services.bigquery.model.TableFieldSchema;
import com.google.common.collect.ImmutableList;
@@ -23,24 +22,29 @@ import dagger.Provides;
import dagger.multibindings.Multibinds;
import google.registry.config.CredentialModule.DefaultCredential;
import google.registry.config.RegistryConfig.Config;
import google.registry.util.GoogleCredentialsBundle;
import java.util.Map;
/** Dagger module for Google {@link Bigquery} connection objects. */
@Module
public abstract class BigqueryModule {
/** Provides a map of BigQuery table names to field names. */
@Multibinds
abstract Map<String, ImmutableList<TableFieldSchema>> bigquerySchemas();
// No subclasses.
private BigqueryModule() {}
@Provides
static Bigquery provideBigquery(
@DefaultCredential GoogleCredential credential, @Config("projectId") String projectId) {
return new Bigquery.Builder(credential.getTransport(), credential.getJsonFactory(), credential)
@DefaultCredential GoogleCredentialsBundle credentialsBundle,
@Config("projectId") String projectId) {
return new Bigquery.Builder(
credentialsBundle.getHttpTransport(),
credentialsBundle.getJsonFactory(),
credentialsBundle.getHttpRequestInitializer())
.setApplicationName(projectId)
.build();
}
// No subclasses.
private BigqueryModule() {}
/** Provides a map of BigQuery table names to field names. */
@Multibinds
abstract Map<String, ImmutableList<TableFieldSchema>> bigquerySchemas();
}
@@ -1,60 +0,0 @@
# Copyright 2017 The Nomulus Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Common routines for Nomulus build rules."""
ZIPPER = "@bazel_tools//tools/zip:zipper"
def long_path(ctx, file_):
"""Constructs canonical runfile path relative to TEST_SRCDIR.
Args:
ctx: A Skylark rule context.
file_: A File object that should appear in the runfiles for the test.
Returns:
A string path relative to TEST_SRCDIR suitable for use in tests and
testing infrastructure.
"""
if file_.short_path.startswith("../"):
return file_.short_path[3:]
if file_.owner and file_.owner.workspace_root:
return file_.owner.workspace_root + "/" + file_.short_path
return ctx.workspace_name + "/" + file_.short_path
def collect_runfiles(targets):
"""Aggregates runfiles from targets.
Args:
targets: A list of Bazel targets.
Returns:
A list of Bazel files.
"""
data = depset()
for target in targets:
if hasattr(target, "runfiles"):
data += target.runfiles.files
continue
if hasattr(target, "data_runfiles"):
data += target.data_runfiles.files
if hasattr(target, "default_runfiles"):
data += target.default_runfiles.files
return data
def _get_runfiles(target, attribute):
runfiles = getattr(target, attribute, None)
if runfiles:
return runfiles.files
return []
@@ -1,53 +0,0 @@
# Copyright 2017 The Nomulus Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Build macro for creating App Engine EAR archives for Nomulus."""
load("//java/google/registry/builddefs:defs.bzl", "ZIPPER")
def registry_ear_file(name, out, configs, wars, **kwargs):
"""Creates an EAR archive by combining WAR archives."""
cmd = [
"set -e",
"repo=$$(pwd)",
"zipper=$$repo/$(location %s)" % ZIPPER,
"tmp=$$(mktemp -d $${TMPDIR:-/tmp}/registry_ear_file.XXXXXXXXXX)",
"cd $${tmp}",
]
for target, dest in configs.items():
cmd += [
"mkdir -p $${tmp}/$$(dirname %s)" % dest,
"ln -s $${repo}/$(location %s) $${tmp}/%s" % (target, dest),
]
for target, dest in wars.items():
cmd += [
"mkdir " + dest,
"cd " + dest,
"$${zipper} x $${repo}/$(location %s)" % target,
"cd ..",
]
cmd += [
"$${zipper} cC $${repo}/$@ $$(find . | sed 1d | cut -c 3- | LC_ALL=C sort)",
"cd $${repo}",
"rm -rf $${tmp}",
]
native.genrule(
name = name,
srcs = configs.keys() + wars.keys(),
outs = [out],
cmd = "\n".join(cmd),
tools = [ZIPPER],
message = "Generating EAR archive",
**kwargs
)
@@ -1,256 +0,0 @@
# Copyright 2017 The Nomulus Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Zip file creator that allows arbitrary path renaming.
This rule takes two main inputs: a bunch of filesets and a dictionary of
hard-coded source to dest mappings. It then applies those mappings to the input
file paths, to create a zip file with the same name as the rule.
The following preconditions must be met:
- Sources and destinations can't begin or end with slash.
- Every file must be matched by a mapping.
- Every mapping must match something.
The source can either be an exact match or a prefix.
- If a match is exact, the destination replaces the entire path. If the
destination path is empty, then the path remains the same.
- If the match is a prefix, then the destination replaces the source prefix in
the path. If the destination is empty, then the source prefix is removed.
- If source is an empty string, it matches everything. In this case,
destination becomes the path prefix.
Prefixes are matched with component granularity, not characters. Mappings with
more components take precedence. Mappings with equal components are sorted
asciibetically.
Mappings apply to the "long path" of a file, i.e. relative to TEST_SRCDIR,
e.g. workspace_name/pkg/file. Long paths do not take into consideration
bazel-foo/ output directories.
The deps attribute allows zip_file() rules to depend on other zip_file() rules.
In such cases, the contents of directly dependent zip files are unzipped and
then re-zipped. Mappings specified by the current rule do not apply to the
files extracted from dependent zips. However those files can be overridden.
The simplest example of this rule, which simply zips up short paths, is as
follows:
# //my/package/BUILD
zip_file(
name = "doodle",
srcs = ["hello.txt"],
mappings = {"": ""},
)
The rule above would create a zip file name //my/package/doodle.zip which would
contain a single file named "my/package/hello.txt".
If we wanted to strip the package path, we could do the following:
# //my/package/BUILD
zip_file(
name = "doodle",
srcs = ["hello.txt"],
mappings = {"my/package": ""},
)
In this case, doodle.zip would contain a single file: "hello.txt".
If we wanted to rename hello.txt, we could do the following:
# //my/package/BUILD
zip_file(
name = "doodle",
srcs = ["hello.txt"],
mappings = {"my/package/hello.txt": "my/package/world.txt"},
)
A zip file can be assembled across many rules. For example:
# //webapp/html/BUILD
zip_file(
name = "assets",
srcs = glob(["*.html"]),
mappings = {"webapp/html": ""},
)
# //webapp/js/BUILD
zip_file(
name = "assets",
srcs = glob(["*.js"]),
mappings = {"webapp/js": "assets/js"},
)
# //webapp/BUILD
zip_file(
name = "war",
deps = [
"//webapp/html:assets",
"//webapp/js:assets",
],
mappings = {"webapp/html": ""},
)
You can exclude files with the "exclude" attribute:
# //webapp/BUILD
zip_file(
name = "war_without_tears",
deps = ["war"],
exclude = ["assets/js/tears.js"],
)
Note that "exclude" excludes based on the mapped path relative to the root of
the zipfile. If the file doesn't exist, you'll get an error.
"""
load(
"//java/google/registry/builddefs:defs.bzl",
"ZIPPER",
"collect_runfiles",
"long_path",
)
def _zip_file(ctx):
"""Implementation of zip_file() rule."""
for s, d in ctx.attr.mappings.items():
if (s.startswith("/") or s.endswith("/") or
d.startswith("/") or d.endswith("/")):
fail("mappings should not begin or end with slash")
srcs = depset()
srcs += ctx.files.srcs
srcs += ctx.files.data
srcs += collect_runfiles(ctx.attr.data)
mapped = _map_sources(ctx, srcs, ctx.attr.mappings)
cmd = [
"#!/bin/sh",
"set -e",
'repo="$(pwd)"',
'zipper="${repo}/%s"' % ctx.file._zipper.path,
'archive="${repo}/%s"' % ctx.outputs.out.path,
'tmp="$(mktemp -d "${TMPDIR:-/tmp}/zip_file.XXXXXXXXXX")"',
'cd "${tmp}"',
]
cmd += [
'"${zipper}" x "${repo}/%s"' % dep.zip_file.path
for dep in ctx.attr.deps
]
cmd += ["rm %s" % filename for filename in ctx.attr.exclude]
cmd += [
'mkdir -p "${tmp}/%s"' % zip_path
for zip_path in depset(
[
zip_path[:zip_path.rindex("/")]
for _, zip_path in mapped
if "/" in zip_path
],
).to_list()
]
cmd += [
'ln -sf "${repo}/%s" "${tmp}/%s"' % (path, zip_path)
for path, zip_path in mapped
]
cmd += [
("find . | sed 1d | cut -c 3- | LC_ALL=C sort" +
' | xargs "${zipper}" cC "${archive}"'),
'cd "${repo}"',
'rm -rf "${tmp}"',
]
if hasattr(ctx, "bin_dir"):
script = ctx.new_file(ctx.bin_dir, "%s.sh" % ctx.label.name)
else:
# TODO(kchodorow): remove this once Bazel 4.0+ is required.
script = ctx.new_file(ctx.configuration.bin_dir, "%s.sh" % ctx.label.name)
ctx.actions.write(output = script, content = "\n".join(cmd), is_executable = True)
inputs = [ctx.file._zipper]
inputs += [dep.zip_file for dep in ctx.attr.deps]
inputs += srcs.to_list()
ctx.actions.run(
inputs = inputs,
outputs = [ctx.outputs.out],
executable = script,
mnemonic = "zip",
progress_message = "Creating zip with %d inputs %s" % (
len(inputs),
ctx.label,
),
)
return struct(files = depset([ctx.outputs.out]), zip_file = ctx.outputs.out)
def _map_sources(ctx, srcs, mappings):
"""Calculates paths in zip file for srcs."""
# order mappings with more path components first
mappings = sorted([
(-len(source.split("/")), source, dest)
for source, dest in mappings.items()
])
# get rid of the integer part of tuple used for sorting
mappings = [(source, dest) for _, source, dest in mappings]
mappings_indexes = range(len(mappings))
used = {i: False for i in mappings_indexes}
mapped = []
for file_ in srcs:
run_path = long_path(ctx, file_)
zip_path = None
for i in mappings_indexes:
source = mappings[i][0]
dest = mappings[i][1]
if not source:
if dest:
zip_path = dest + "/" + run_path
else:
zip_path = run_path
elif source == run_path:
if dest:
zip_path = dest
else:
zip_path = run_path
elif run_path.startswith(source + "/"):
if dest:
zip_path = dest + run_path[len(source):]
else:
zip_path = run_path[len(source) + 1:]
else:
continue
used[i] = True
break
if not zip_path:
fail("no mapping matched: " + run_path)
mapped += [(file_.path, zip_path)]
for i in mappings_indexes:
if not used[i]:
fail('superfluous mapping: "%s" -> "%s"' % mappings[i])
return mapped
zip_file = rule(
implementation = _zip_file,
output_to_genfiles = True,
attrs = {
"out": attr.output(mandatory = True),
"srcs": attr.label_list(allow_files = True),
"data": attr.label_list(allow_files = True),
"deps": attr.label_list(providers = ["zip_file"]),
"exclude": attr.string_list(),
"mappings": attr.string_dict(),
"_zipper": attr.label(default = Label(ZIPPER), allow_single_file = True),
},
)
@@ -17,31 +17,28 @@ package google.registry.config;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.googleapis.util.Utils;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.common.collect.ImmutableList;
import dagger.Module;
import dagger.Provides;
import google.registry.config.RegistryConfig.Config;
import google.registry.keyring.api.KeyModule.Key;
import google.registry.util.GoogleCredentialsBundle;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.security.GeneralSecurityException;
import javax.inject.Qualifier;
import javax.inject.Singleton;
/**
* Dagger module that provides all {@link GoogleCredential GoogleCredentials} used in the
* application.
*/
/** Dagger module that provides all {@link GoogleCredentials} used in the application. */
@Module
public abstract class CredentialModule {
/**
* Provides the default {@link GoogleCredential} from the Google Cloud runtime.
* Provides the default {@link GoogleCredentialsBundle} from the Google Cloud runtime.
*
* <p>The credential returned depends on the runtime environment:
*
@@ -58,7 +55,30 @@ public abstract class CredentialModule {
@DefaultCredential
@Provides
@Singleton
public static GoogleCredential provideDefaultCredential(
public static GoogleCredentialsBundle provideDefaultCredential(
@Config("defaultCredentialOauthScopes") ImmutableList<String> requiredScopes) {
GoogleCredentials credential;
try {
credential = GoogleCredentials.getApplicationDefault();
} catch (IOException e) {
throw new RuntimeException(e);
}
if (credential.createScopedRequired()) {
credential = credential.createScoped(requiredScopes);
}
return GoogleCredentialsBundle.create(credential);
}
/**
* Provides the default {@link GoogleCredential} from the Google Cloud runtime for G Suite
* Drive API.
* TODO(b/138195359): Deprecate this credential once we figure out how to use
* {@link GoogleCredentials} for G Suite Drive API.
*/
@GSuiteDriveCredential
@Provides
@Singleton
public static GoogleCredential provideGSuiteDriveCredential(
@Config("defaultCredentialOauthScopes") ImmutableList<String> requiredScopes) {
GoogleCredential credential;
try {
@@ -66,35 +86,6 @@ public abstract class CredentialModule {
} catch (IOException e) {
throw new RuntimeException(e);
}
if (credential.createScopedRequired()) {
return credential.createScoped(requiredScopes);
}
return credential;
}
/**
* Provides a {@link GoogleCredential} from the service account's JSON key file.
*
* <p>On App Engine, a thread created using Java's built-in API needs this credential when it
* calls App Engine API. The Google Sheets API also needs this credential.
*/
@JsonCredential
@Provides
@Singleton
public static GoogleCredential provideJsonCredential(
@Config("defaultCredentialOauthScopes") ImmutableList<String> requiredScopes,
@Key("jsonCredential") String jsonCredential) {
GoogleCredential credential;
try {
credential =
GoogleCredential.fromStream(
new ByteArrayInputStream(jsonCredential.getBytes(UTF_8)),
// We cannot use UrlFetchTransport as that uses App Engine API.
GoogleNetHttpTransport.newTrustedTransport(),
Utils.getDefaultJsonFactory());
} catch (IOException | GeneralSecurityException e) {
throw new RuntimeException(e);
}
if (credential.createScopedRequired()) {
credential = credential.createScoped(requiredScopes);
}
@@ -102,7 +93,32 @@ public abstract class CredentialModule {
}
/**
* Provides a {@link GoogleCredential} with delegated admin access for a G Suite domain.
* Provides a {@link GoogleCredentialsBundle} from the service account's JSON key file.
*
* <p>On App Engine, a thread created using Java's built-in API needs this credential when it
* calls App Engine API. The Google Sheets API also needs this credential.
*/
@JsonCredential
@Provides
@Singleton
public static GoogleCredentialsBundle provideJsonCredential(
@Config("defaultCredentialOauthScopes") ImmutableList<String> requiredScopes,
@Key("jsonCredential") String jsonCredential) {
GoogleCredentials credential;
try {
credential =
GoogleCredentials.fromStream(new ByteArrayInputStream(jsonCredential.getBytes(UTF_8)));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
if (credential.createScopedRequired()) {
credential = credential.createScoped(requiredScopes);
}
return GoogleCredentialsBundle.create(credential);
}
/**
* Provides a {@link GoogleCredentialsBundle} with delegated admin access for a G Suite domain.
*
* <p>The G Suite domain must grant delegated admin access to the registry service account with
* all scopes in {@code requiredScopes}, including ones not related to G Suite.
@@ -110,18 +126,14 @@ public abstract class CredentialModule {
@DelegatedCredential
@Provides
@Singleton
public static GoogleCredential provideDelegatedCredential(
public static GoogleCredentialsBundle provideDelegatedCredential(
@Config("delegatedCredentialOauthScopes") ImmutableList<String> requiredScopes,
@JsonCredential GoogleCredential googleCredential,
@JsonCredential GoogleCredentialsBundle credentialsBundle,
@Config("gSuiteAdminAccountEmailAddress") String gSuiteAdminAccountEmailAddress) {
return new GoogleCredential.Builder()
.setTransport(Utils.getDefaultTransport())
.setJsonFactory(Utils.getDefaultJsonFactory())
.setServiceAccountId(googleCredential.getServiceAccountId())
.setServiceAccountPrivateKey(googleCredential.getServiceAccountPrivateKey())
.setServiceAccountScopes(requiredScopes)
.setServiceAccountUser(gSuiteAdminAccountEmailAddress)
.build();
return GoogleCredentialsBundle.create(credentialsBundle
.getGoogleCredentials()
.createDelegated(gSuiteAdminAccountEmailAddress)
.createScoped(requiredScopes));
}
/** Dagger qualifier for the Application Default Credential. */
@@ -130,6 +142,13 @@ public abstract class CredentialModule {
@Retention(RetentionPolicy.RUNTIME)
public @interface DefaultCredential {}
/** Dagger qualifier for the credential for G Suite Drive API. */
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface GSuiteDriveCredential {}
/**
* Dagger qualifier for a credential from a service account's JSON key, to be used in non-request
* threads.
@@ -894,9 +894,9 @@ public final class RegistryConfig {
* @see google.registry.reporting.spec11.Spec11EmailUtils
*/
@Provides
@Config("spec11ReplyToEmailAddress")
public static InternetAddress provideSpec11ReplyToEmailAddress(RegistryConfigSettings config) {
return parseEmailAddress(config.misc.spec11ReplyToEmailAddress);
@Config("spec11OutgoingEmailAddress")
public static InternetAddress provideSpec11OutgoingEmailAddress(RegistryConfigSettings config) {
return parseEmailAddress(config.misc.spec11OutgoingEmailAddress);
}
/**
@@ -173,7 +173,7 @@ public class RegistryConfigSettings {
public static class Misc {
public String sheetExportId;
public String alertRecipientEmailAddress;
public String spec11ReplyToEmailAddress;
public String spec11OutgoingEmailAddress;
public int asyncDeleteDelaySeconds;
public int transientFailureRetries;
}
@@ -357,9 +357,9 @@ misc:
# Address we send alert summary emails to.
alertRecipientEmailAddress: email@example.com
# Address to which the Spec 11 emails to registrars should be replied. This needs
# to be a deliverable email address in case the registrars want to contact us.
spec11ReplyToEmailAddress: reply-to@example.com
# Address from which Spec 11 emails to registrars are sent. This needs
# to be a deliverable email address to handle replies from registrars as well.
spec11OutgoingEmailAddress: abuse@example.com
# How long to delay processing of asynchronous deletions. This should always
# be longer than eppResourceCachingSeconds, to prevent deleted contacts or
@@ -14,7 +14,6 @@
package google.registry.dns.writer.clouddns;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.services.dns.Dns;
import com.google.common.util.concurrent.RateLimiter;
import dagger.Binds;
@@ -26,6 +25,7 @@ import dagger.multibindings.StringKey;
import google.registry.config.CredentialModule.DefaultCredential;
import google.registry.config.RegistryConfig.Config;
import google.registry.dns.writer.DnsWriter;
import google.registry.util.GoogleCredentialsBundle;
import java.util.Optional;
import javax.inject.Named;
@@ -35,12 +35,15 @@ public abstract class CloudDnsWriterModule {
@Provides
static Dns provideDns(
@DefaultCredential GoogleCredential credential,
@DefaultCredential GoogleCredentialsBundle credentialsBundle,
@Config("projectId") String projectId,
@Config("cloudDnsRootUrl") Optional<String> rootUrl,
@Config("cloudDnsServicePath") Optional<String> servicePath) {
Dns.Builder builder =
new Dns.Builder(credential.getTransport(), credential.getJsonFactory(), credential)
new Dns.Builder(
credentialsBundle.getHttpTransport(),
credentialsBundle.getJsonFactory(),
credentialsBundle.getHttpRequestInitializer())
.setApplicationName(projectId);
rootUrl.ifPresent(builder::setRootUrl);
@@ -112,8 +112,6 @@
<target>backend</target>
</cron>
<!--
TODO(b/134576418) enable this cron job once we're sure the Action works
<cron>
<url><![CDATA[/_dr/task/updateRegistrarRdapBaseUrls]]></url>
<description>
@@ -122,7 +120,6 @@
<schedule>every day 02:34</schedule>
<target>backend</target>
</cron>
-->
<cron>
<url><![CDATA[/_dr/task/deleteOldCommitLogs]]></url>
@@ -20,7 +20,7 @@ import dagger.Component;
import dagger.Module;
import dagger.Provides;
import google.registry.config.CredentialModule;
import google.registry.config.CredentialModule.DefaultCredential;
import google.registry.config.CredentialModule.GSuiteDriveCredential;
import google.registry.config.RegistryConfig.Config;
import google.registry.config.RegistryConfig.ConfigModule;
import google.registry.storage.drive.DriveConnection;
@@ -32,8 +32,13 @@ public final class DriveModule {
@Provides
static Drive provideDrive(
@DefaultCredential GoogleCredential credential, @Config("projectId") String projectId) {
return new Drive.Builder(credential.getTransport(), credential.getJsonFactory(), credential)
@GSuiteDriveCredential GoogleCredential googleCredential,
@Config("projectId") String projectId) {
return new Drive.Builder(
googleCredential.getTransport(),
googleCredential.getJsonFactory(),
googleCredential)
.setApplicationName(projectId)
.build();
}
@@ -14,11 +14,11 @@
package google.registry.export.datastore;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import dagger.Module;
import dagger.Provides;
import google.registry.config.CredentialModule;
import google.registry.config.RegistryConfig;
import google.registry.util.GoogleCredentialsBundle;
import javax.inject.Singleton;
/** Dagger module that configures provision of {@link DatastoreAdmin}. */
@@ -28,10 +28,12 @@ public abstract class DatastoreAdminModule {
@Singleton
@Provides
static DatastoreAdmin provideDatastoreAdmin(
@CredentialModule.DefaultCredential GoogleCredential credential,
@CredentialModule.DefaultCredential GoogleCredentialsBundle credentialsBundle,
@RegistryConfig.Config("projectId") String projectId) {
return new DatastoreAdmin.Builder(
credential.getTransport(), credential.getJsonFactory(), credential)
credentialsBundle.getHttpTransport(),
credentialsBundle.getJsonFactory(),
credentialsBundle.getHttpRequestInitializer())
.setApplicationName(projectId)
.setProjectId(projectId)
.build();
@@ -14,12 +14,12 @@
package google.registry.export.sheet;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.services.sheets.v4.Sheets;
import dagger.Module;
import dagger.Provides;
import google.registry.config.CredentialModule.JsonCredential;
import google.registry.config.RegistryConfig.Config;
import google.registry.util.GoogleCredentialsBundle;
/** Dagger module for {@link Sheets}. */
@Module
@@ -27,8 +27,12 @@ public final class SheetsServiceModule {
@Provides
static Sheets provideSheets(
@JsonCredential GoogleCredential credential, @Config("projectId") String projectId) {
return new Sheets.Builder(credential.getTransport(), credential.getJsonFactory(), credential)
@JsonCredential GoogleCredentialsBundle credentialsBundle,
@Config("projectId") String projectId) {
return new Sheets.Builder(
credentialsBundle.getHttpTransport(),
credentialsBundle.getJsonFactory(),
credentialsBundle.getHttpRequestInitializer())
.setApplicationName(projectId)
.build();
}
@@ -209,6 +209,7 @@ public abstract class EppException extends Exception {
/** Specified command is not implemented. */
@EppResultCode(Code.UNIMPLEMENTED_COMMAND)
public static class UnimplementedCommandException extends EppException {
public UnimplementedCommandException(InnerCommand command) {
super(String.format(
"No flow found for %s with extension %s",
@@ -217,6 +218,10 @@ public abstract class EppException extends Exception {
? ((ResourceCommandWrapper) command).getResourceCommand().getClass().getSimpleName()
: null));
}
public UnimplementedCommandException(String message) {
super(message);
}
}
/** Abstract exception class. Do not throw this directly or catch in tests. */
@@ -39,6 +39,13 @@ public class EppMetrics {
LabelDescriptor.create("tld", "The TLD acted on by the command (if applicable)."),
LabelDescriptor.create("status", "The return status of the command."));
private static final ImmutableSet<LabelDescriptor> LABEL_DESCRIPTORS =
ImmutableSet.of(
LabelDescriptor.create("command", "The name of the command."),
LabelDescriptor.create("traffic_type",
"The traffic type of the command; one of CANARY, PROBER, or REAL."),
LabelDescriptor.create("status", "The return status of the command."));
private static final IncrementableMetric eppRequestsByRegistrar =
MetricRegistryImpl.getDefault()
.newIncrementableMetric(
@@ -73,6 +80,19 @@ public class EppMetrics {
LABEL_DESCRIPTORS_BY_TLD,
DEFAULT_FITTER);
private static final EventMetric requestTime =
MetricRegistryImpl.getDefault()
.newEventMetric(
"/epp/request_time",
"EPP Request Time",
"milliseconds",
LABEL_DESCRIPTORS,
DEFAULT_FITTER);
private enum TrafficType {
CANARY, PROBER, REAL
}
@Inject
public EppMetrics() {}
@@ -97,15 +117,21 @@ public class EppMetrics {
metric.getStatus().isPresent() ? String.valueOf(metric.getStatus().get().code) : "";
long processingTime =
metric.getEndTimestamp().getMillis() - metric.getStartTimestamp().getMillis();
processingTimeByRegistrar.record(
processingTime,
metric.getCommandName().orElse(""),
metric.getClientId().orElse(""),
eppStatusCode);
processingTimeByTld.record(
processingTime,
metric.getCommandName().orElse(""),
metric.getTld().orElse(""),
eppStatusCode);
String commandName = metric.getCommandName().orElse("");
processingTimeByRegistrar
.record(processingTime, commandName, metric.getClientId().orElse(""), eppStatusCode);
String tld = metric.getTld().orElse("");
processingTimeByTld.record(processingTime, commandName, tld, eppStatusCode);
requestTime.record(processingTime, commandName, getTrafficType(tld).toString(), eppStatusCode);
}
private static TrafficType getTrafficType(String tld) {
if (tld.endsWith("canary.test")) {
return TrafficType.CANARY;
} else if (tld.endsWith(".test")) {
return TrafficType.PROBER;
} else {
return TrafficType.REAL;
}
}
}
@@ -82,6 +82,9 @@ public class FlowPicker {
/** Marker class for unimplemented flows. */
private abstract static class UnimplementedFlow implements Flow {}
/** Marker class for unimplemented restore flows. */
private abstract static class UnimplementedRestoreFlow implements Flow {}
/** A function type that takes an {@link EppInput} and returns a {@link Flow} class. */
private abstract static class FlowProvider {
/** Get the flow associated with this {@link EppInput} or return null to signal no match. */
@@ -160,7 +163,7 @@ public class FlowPicker {
// Restore command with an op of "report" is not currently supported.
return (rgpUpdateExtension.get().getRestoreCommand().getRestoreOp() == RestoreOp.REQUEST)
? DomainRestoreRequestFlow.class
: UnimplementedFlow.class;
: UnimplementedRestoreFlow.class;
}};
/**
@@ -265,8 +268,11 @@ public class FlowPicker {
Class<? extends Flow> flowClass = flowProvider.get(eppInput);
if (flowClass == UnimplementedFlow.class) {
break; // We found it, but it's marked as not implemented.
}
if (flowClass != null) {
} else if (flowClass == UnimplementedRestoreFlow.class) {
throw new UnimplementedCommandException(
"Domain restores are approved and enacted instantly, "
+ "therefore domain restore reports are not supported");
} else if (flowClass != null) {
return flowClass; // We found it!
}
}
@@ -14,12 +14,12 @@
package google.registry.groups;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.services.admin.directory.Directory;
import dagger.Module;
import dagger.Provides;
import google.registry.config.CredentialModule.DelegatedCredential;
import google.registry.config.RegistryConfig.Config;
import google.registry.util.GoogleCredentialsBundle;
/** Dagger module for the Google {@link Directory} service. */
@Module
@@ -27,8 +27,12 @@ public final class DirectoryModule {
@Provides
static Directory provideDirectory(
@DelegatedCredential GoogleCredential credential, @Config("projectId") String projectId) {
return new Directory.Builder(credential.getTransport(), credential.getJsonFactory(), credential)
@DelegatedCredential GoogleCredentialsBundle credentialsBundle,
@Config("projectId") String projectId) {
return new Directory.Builder(
credentialsBundle.getHttpTransport(),
credentialsBundle.getJsonFactory(),
credentialsBundle.getHttpRequestInitializer())
.setApplicationName(projectId)
.build();
}
@@ -14,12 +14,12 @@
package google.registry.groups;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.services.groupssettings.Groupssettings;
import dagger.Module;
import dagger.Provides;
import google.registry.config.CredentialModule.DelegatedCredential;
import google.registry.config.RegistryConfig.Config;
import google.registry.util.GoogleCredentialsBundle;
/** Dagger module for the Google {@link Groupssettings} service. */
@Module
@@ -27,9 +27,12 @@ public final class GroupssettingsModule {
@Provides
static Groupssettings provideDirectory(
@DelegatedCredential GoogleCredential credential, @Config("projectId") String projectId) {
@DelegatedCredential GoogleCredentialsBundle credentialsBundle,
@Config("projectId") String projectId) {
return new Groupssettings.Builder(
credential.getTransport(), credential.getJsonFactory(), credential)
credentialsBundle.getHttpTransport(),
credentialsBundle.getJsonFactory(),
credentialsBundle.getHttpRequestInitializer())
.setApplicationName(projectId)
.build();
}
@@ -14,7 +14,6 @@
package google.registry.keyring.kms;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.services.cloudkms.v1.CloudKMS;
import dagger.Binds;
import dagger.Module;
@@ -24,6 +23,7 @@ import dagger.multibindings.StringKey;
import google.registry.config.CredentialModule.DefaultCredential;
import google.registry.config.RegistryConfig.Config;
import google.registry.keyring.api.Keyring;
import google.registry.util.GoogleCredentialsBundle;
/** Dagger module for Cloud KMS. */
@Module
@@ -31,20 +31,23 @@ public abstract class KmsModule {
public static final String NAME = "KMS";
@Binds
@IntoMap
@StringKey(NAME)
abstract Keyring provideKeyring(KmsKeyring keyring);
@Provides
static CloudKMS provideKms(
@DefaultCredential GoogleCredential credential,
@DefaultCredential GoogleCredentialsBundle credentialsBundle,
@Config("cloudKmsProjectId") String projectId) {
return new CloudKMS.Builder(credential.getTransport(), credential.getJsonFactory(), credential)
return new CloudKMS.Builder(
credentialsBundle.getHttpTransport(),
credentialsBundle.getJsonFactory(),
credentialsBundle.getHttpRequestInitializer())
.setApplicationName(projectId)
.build();
}
@Binds
@IntoMap
@StringKey(NAME)
abstract Keyring provideKeyring(KmsKeyring keyring);
@Binds
abstract KmsConnection provideKmsConnection(KmsConnectionImpl kmsConnectionImpl);
}
@@ -28,6 +28,7 @@ import static google.registry.util.CollectionUtils.forceEmptyToNull;
import static google.registry.util.CollectionUtils.nullToEmpty;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import static google.registry.util.CollectionUtils.union;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import static google.registry.util.DateTimeUtils.earliestOf;
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
import static google.registry.util.DateTimeUtils.leapSafeAddYears;
@@ -104,7 +105,7 @@ public class DomainBase extends EppResource
* from (creationTime, deletionTime) there can only be one domain in Datastore with this name.
* However, there can be many domains with the same name and non-overlapping lifetimes.
*
* @invariant fullyQualifiedDomainName == fullyQualifiedDomainName.toLowerCase()
* @invariant fullyQualifiedDomainName == fullyQualifiedDomainName.toLowerCase(Locale.ENGLISH)
*/
@Index
String fullyQualifiedDomainName;
@@ -376,10 +377,10 @@ public class DomainBase extends EppResource
Optional<DateTime> newLastEppUpdateTime = Optional.empty();
// There is no transfer. Do any necessary autorenews.
// There is no transfer. Do any necessary autorenews for active domains.
Builder builder = asBuilder();
if (isBeforeOrAt(registrationExpirationTime, now)) {
if (isBeforeOrAt(registrationExpirationTime, now) && END_OF_TIME.equals(getDeletionTime())) {
// Autorenew by the number of years between the old expiration time and now.
DateTime lastAutorenewTime = leapSafeAddYears(
registrationExpirationTime,
@@ -15,9 +15,9 @@
package google.registry.model.domain.fee;
import com.google.common.base.Ascii;
import com.google.common.base.CharMatcher;
import google.registry.model.ImmutableObject;
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName;
import java.util.Locale;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlValue;
@@ -47,7 +47,7 @@ public class FeeExtensionCommandDescriptor extends ImmutableObject {
public CommandName getCommand() {
// Require the xml string to be lowercase.
if (command != null && CharMatcher.javaLowerCase().matchesAllOf(command)) {
if (command != null && command.toLowerCase(Locale.ENGLISH).equals(command)) {
try {
return CommandName.valueOf(Ascii.toUpperCase(command));
} catch (IllegalArgumentException e) {
@@ -15,9 +15,9 @@
package google.registry.model.domain.fee12;
import com.google.common.base.Ascii;
import com.google.common.base.CharMatcher;
import google.registry.model.domain.Period;
import google.registry.model.domain.fee.FeeCheckCommandExtensionItem;
import java.util.Locale;
import java.util.Optional;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
@@ -83,7 +83,7 @@ public class FeeCheckCommandExtensionItemV12 extends FeeCheckCommandExtensionIte
@Override
public CommandName getCommandName() {
// Require the xml string to be lowercase.
if (commandName != null && CharMatcher.javaLowerCase().matchesAllOf(commandName)) {
if (commandName != null && commandName.toLowerCase(Locale.ENGLISH).equals(commandName)) {
try {
return CommandName.valueOf(Ascii.toUpperCase(commandName));
} catch (IllegalArgumentException e) {
@@ -868,7 +868,9 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
return this;
}
public Builder setDriveFolderId(String driveFolderId) {
public Builder setDriveFolderId(@Nullable String driveFolderId) {
checkArgument(driveFolderId == null || !driveFolderId.contains("/"),
"Drive folder ID must not be a full URL");
getInstance().driveFolderId = driveFolderId;
return this;
}
@@ -14,7 +14,6 @@
package google.registry.monitoring.whitebox;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.services.monitoring.v3.Monitoring;
import com.google.api.services.monitoring.v3.model.MonitoredResource;
import com.google.appengine.api.modules.ModulesService;
@@ -27,6 +26,7 @@ import dagger.Module;
import dagger.Provides;
import google.registry.config.CredentialModule.JsonCredential;
import google.registry.config.RegistryConfig.Config;
import google.registry.util.GoogleCredentialsBundle;
import org.joda.time.Duration;
/** Dagger module for Google Stackdriver service connection objects. */
@@ -39,9 +39,12 @@ public final class StackdriverModule {
@Provides
static Monitoring provideMonitoring(
@JsonCredential GoogleCredential credential, @Config("projectId") String projectId) {
@JsonCredential GoogleCredentialsBundle credentialsBundle,
@Config("projectId") String projectId) {
return new Monitoring.Builder(
credential.getTransport(), credential.getJsonFactory(), credential)
credentialsBundle.getHttpTransport(),
credentialsBundle.getJsonFactory(),
credentialsBundle.getHttpRequestInitializer())
.setApplicationName(projectId)
.build();
}
@@ -14,6 +14,7 @@
package google.registry.rdap;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -23,6 +24,7 @@ import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpTransport;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.flogger.FluentLogger;
@@ -34,8 +36,9 @@ import com.google.gson.JsonObject;
import com.googlecode.objectify.Key;
import google.registry.keyring.api.KeyModule;
import google.registry.model.registrar.Registrar;
import google.registry.model.registry.Registries;
import google.registry.model.registry.Registry.TldType;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import java.io.IOException;
import java.io.InputStream;
@@ -56,7 +59,18 @@ import javax.inject.Inject;
* <p>It is a "login/query/logout" system where you login using the ICANN Reporting credentials, get
* a cookie you then send to get the list and finally logout.
*
* <p>The username is [TLD]_ry. It could be any "real" TLD.
* <p>For clarity, this is how one would contact this endpoint "manually", from a whitelisted IP
* server:
*
* <p>$ curl [base]/login -I --user [tld]_ry:[password]
*
* <p>get the id=xxx value from the reply
*
* <p>$ curl [base]/registrarRdapBaseUrl/list -b 'id=xxx'
*
* <p>$ curl [base]/logout -b 'id=xxx'
*
* <p>where [base] is https://mosapi.icann.org/mosapi/v1/[tld]
*/
@Action(
service = Action.Service.BACKEND,
@@ -75,41 +89,29 @@ public final class UpdateRegistrarRdapBaseUrlsAction implements Runnable {
@Inject HttpTransport httpTransport;
@Inject @KeyModule.Key("icannReportingPassword") String password;
/**
* The TLD for which we make the request.
*
* <p>The actual value doesn't matter, as long as it's a TLD that has access to the ICANN
* Reporter. It's just used to login.
*/
@Inject @Parameter("tld") String tld;
@Inject
UpdateRegistrarRdapBaseUrlsAction() {}
private String loginAndGetId(HttpRequestFactory requestFactory) {
try {
logger.atInfo().log("Logging in to MoSAPI");
HttpRequest request =
requestFactory.buildGetRequest(new GenericUrl(String.format(LOGIN_URL, tld)));
request.getHeaders().setBasicAuthentication(String.format("%s_ry", tld), password);
HttpResponse response = request.execute();
private String loginAndGetId(HttpRequestFactory requestFactory, String tld) throws IOException {
logger.atInfo().log("Logging in to MoSAPI");
HttpRequest request =
requestFactory.buildGetRequest(new GenericUrl(String.format(LOGIN_URL, tld)));
request.getHeaders().setBasicAuthentication(String.format("%s_ry", tld), password);
HttpResponse response = request.execute();
Optional<HttpCookie> idCookie =
HttpCookie.parse(response.getHeaders().getFirstHeaderStringValue("Set-Cookie")).stream()
.filter(cookie -> cookie.getName().equals(COOKIE_ID))
.findAny();
checkState(
idCookie.isPresent(),
"Didn't get the ID cookie from the login response. Code: %s, headers: %s",
response.getStatusCode(),
response.getHeaders());
return idCookie.get().getValue();
} catch (IOException e) {
throw new UncheckedIOException("Error logging in to MoSAPI server: " + e.getMessage(), e);
}
Optional<HttpCookie> idCookie =
HttpCookie.parse(response.getHeaders().getFirstHeaderStringValue("Set-Cookie")).stream()
.filter(cookie -> cookie.getName().equals(COOKIE_ID))
.findAny();
checkState(
idCookie.isPresent(),
"Didn't get the ID cookie from the login response. Code: %s, headers: %s",
response.getStatusCode(),
response.getHeaders());
return idCookie.get().getValue();
}
private void logout(HttpRequestFactory requestFactory, String id) {
private void logout(HttpRequestFactory requestFactory, String id, String tld) {
try {
HttpRequest request =
requestFactory.buildGetRequest(new GenericUrl(String.format(LOGOUT_URL, tld)));
@@ -122,9 +124,8 @@ public final class UpdateRegistrarRdapBaseUrlsAction implements Runnable {
}
}
private ImmutableSetMultimap<String, String> getRdapBaseUrlsPerIanaId() {
HttpRequestFactory requestFactory = httpTransport.createRequestFactory();
String id = loginAndGetId(requestFactory);
private ImmutableSetMultimap<String, String> getRdapBaseUrlsPerIanaIdWithTld(
String tld, String id, HttpRequestFactory requestFactory) {
String content;
try {
HttpRequest request =
@@ -139,7 +140,7 @@ public final class UpdateRegistrarRdapBaseUrlsAction implements Runnable {
throw new UncheckedIOException(
"Error reading RDAP list from MoSAPI server: " + e.getMessage(), e);
} finally {
logout(requestFactory, id);
logout(requestFactory, id, tld);
}
logger.atInfo().log("list reply: '%s'", content);
@@ -160,6 +161,36 @@ public final class UpdateRegistrarRdapBaseUrlsAction implements Runnable {
return builder.build();
}
private ImmutableSetMultimap<String, String> getRdapBaseUrlsPerIanaId() {
// All TLDs have the same data, so just keep trying until one works
// (the expectation is that all / any should work)
ImmutableList<String> tlds = ImmutableList.sortedCopyOf(Registries.getTldsOfType(TldType.REAL));
checkArgument(!tlds.isEmpty(), "There must exist at least one REAL TLD.");
Throwable finalThrowable = null;
for (String tld : tlds) {
HttpRequestFactory requestFactory = httpTransport.createRequestFactory();
String id;
try {
id = loginAndGetId(requestFactory, tld);
} catch (Throwable e) {
// Login failures are bad but not unexpected for certain TLDs. We shouldn't store those
// but rather should only store useful Throwables.
logger.atWarning().log("Error logging in to MoSAPI server: " + e.getMessage(), e);
continue;
}
try {
return getRdapBaseUrlsPerIanaIdWithTld(tld, id, requestFactory);
} catch (Throwable throwable) {
logger.atWarning().log(
String.format(
"Error retrieving RDAP urls with TLD %s: %s", tld, throwable.getMessage()));
finalThrowable = throwable;
}
}
throw new RuntimeException(
String.format("Error contacting MosAPI server. Tried TLDs %s", tlds), finalThrowable);
}
@Override
public void run() {
ImmutableSetMultimap<String, String> ianaToBaseUrls = getRdapBaseUrlsPerIanaId();
@@ -17,7 +17,6 @@ package google.registry.reporting;
import static google.registry.request.RequestParameters.extractOptionalParameter;
import static google.registry.request.RequestParameters.extractRequiredParameter;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.services.dataflow.Dataflow;
import dagger.Module;
import dagger.Provides;
@@ -26,6 +25,7 @@ import google.registry.config.RegistryConfig.Config;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.Parameter;
import google.registry.util.Clock;
import google.registry.util.GoogleCredentialsBundle;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import org.joda.time.DateTimeZone;
@@ -118,8 +118,12 @@ public class ReportingModule {
/** Constructs a {@link Dataflow} API client with default settings. */
@Provides
static Dataflow provideDataflow(
@DefaultCredential GoogleCredential credential, @Config("projectId") String projectId) {
return new Dataflow.Builder(credential.getTransport(), credential.getJsonFactory(), credential)
@DefaultCredential GoogleCredentialsBundle credentialsBundle,
@Config("projectId") String projectId) {
return new Dataflow.Builder(
credentialsBundle.getHttpTransport(),
credentialsBundle.getJsonFactory(),
credentialsBundle.getHttpRequestInitializer())
.setApplicationName(String.format("%s billing", projectId))
.build();
}
@@ -22,6 +22,7 @@ import static javax.servlet.http.HttpServletResponse.SC_OK;
import com.google.appengine.tools.cloudstorage.GcsFilename;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.flogger.FluentLogger;
import com.google.common.io.ByteStreams;
@@ -38,6 +39,7 @@ import google.registry.util.Retrier;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.inject.Inject;
/** Copy all registrar detail reports in a given bucket's subdirectory from GCS to Drive. */
@@ -95,6 +97,8 @@ public final class CopyDetailReportsAction implements Runnable {
response.setPayload(String.format("Failure, encountered %s", e.getMessage()));
return;
}
ImmutableMap.Builder<String, Throwable> copyErrorsBuilder =
new ImmutableMap.Builder<String, Throwable>();
for (String detailReportName : detailReportObjectNames) {
// The standard report format is "invoice_details_yyyy-MM_registrarId_tld.csv
// TODO(larryruili): Determine a safer way of enforcing this.
@@ -117,7 +121,7 @@ public final class CopyDetailReportsAction implements Runnable {
try (InputStream input =
gcsUtils.openInputStream(
new GcsFilename(billingBucket, invoiceDirectoryPrefix + detailReportName))) {
driveConnection.createFile(
driveConnection.createOrUpdateFile(
detailReportName,
MediaType.CSV_UTF_8,
driveFolderId,
@@ -129,15 +133,31 @@ public final class CopyDetailReportsAction implements Runnable {
},
IOException.class);
} catch (Throwable e) {
emailUtils.sendAlertEmail(
String alertMessage =
String.format(
"Warning: CopyDetailReportsAction failed.\nEncountered: %s on file: %s",
getRootCause(e).getMessage(), detailReportName));
throw e;
"Warning: CopyDetailReportsAction failed for registrar %s.\n"
+ "Encountered: %s on file: %s",
registrarId, getRootCause(e).getMessage(), detailReportName);
copyErrorsBuilder.put(registrarId, e);
logger.atSevere().withCause(e).log(alertMessage);
}
}
response.setStatus(SC_OK);
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
response.setPayload("Copied detail reports.");
StringBuilder payload = new StringBuilder().append("Copied detail reports.\n");
ImmutableMap<String, Throwable> copyErrors = copyErrorsBuilder.build();
if (!copyErrors.isEmpty()) {
payload.append("The following errors were encountered:\n");
payload.append(
copyErrors.entrySet().stream()
.map(
entrySet ->
String.format(
"Registrar: %s\nError: %s\n",
entrySet.getKey(), entrySet.getValue().getMessage()))
.collect(Collectors.joining()));
}
response.setPayload(payload.toString());
emailUtils.sendAlertEmail(payload.toString());
}
}
@@ -59,22 +59,19 @@ public class Spec11EmailUtils {
private final SendEmailService emailService;
private final InternetAddress outgoingEmailAddress;
private final InternetAddress alertRecipientAddress;
private final InternetAddress spec11ReplyToAddress;
private final ImmutableList<String> spec11WebResources;
private final String registryName;
@Inject
Spec11EmailUtils(
SendEmailService emailService,
@Config("gSuiteOutgoingEmailAddress") InternetAddress outgoingEmailAddress,
@Config("alertRecipientEmailAddress") InternetAddress alertRecipientAddress,
@Config("spec11ReplyToEmailAddress") InternetAddress spec11ReplyToAddress,
@Config("spec11OutgoingEmailAddress") InternetAddress spec11OutgoingEmailAddress,
@Config("spec11WebResources") ImmutableList<String> spec11WebResources,
@Config("registryName") String registryName) {
this.emailService = emailService;
this.outgoingEmailAddress = outgoingEmailAddress;
this.outgoingEmailAddress = spec11OutgoingEmailAddress;
this.alertRecipientAddress = alertRecipientAddress;
this.spec11ReplyToAddress = spec11ReplyToAddress;
this.spec11WebResources = spec11WebResources;
this.registryName = registryName;
}
@@ -149,7 +146,7 @@ public class Spec11EmailUtils {
.setContentType(MediaType.HTML_UTF_8)
.setFrom(outgoingEmailAddress)
.addRecipient(getEmailAddressForRegistrar(registrarThreatMatches.clientId()))
.setBcc(spec11ReplyToAddress)
.setBcc(outgoingEmailAddress)
.build());
}
@@ -172,7 +169,7 @@ public class Spec11EmailUtils {
ImmutableMap.of(
"date", date.toString(),
"registry", registryName,
"replyToEmail", spec11ReplyToAddress.getAddress(),
"replyToEmail", outgoingEmailAddress.getAddress(),
"threats", threatMatchMap,
"resources", spec11WebResources);
renderer.setData(data);
@@ -54,6 +54,10 @@ public class DriveConnection {
/**
* Creates a file with the given parent.
*
* <p>If a file with the same path already exists, a duplicate is created. If overwriting the
* existing file is the desired behavior, use {@link #createOrUpdateFile(String, MediaType,
* String, byte[])} instead.
*
* @returns the file id.
*/
public String createFile(String title, MediaType mimeType, String parentFolderId, byte[] bytes)
@@ -14,13 +14,12 @@
package google.registry.tools;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.util.Utils;
import com.google.api.services.appengine.v1.Appengine;
import dagger.Module;
import dagger.Provides;
import google.registry.config.CredentialModule.LocalCredential;
import google.registry.config.RegistryConfig.Config;
import google.registry.util.GoogleCredentialsBundle;
import javax.inject.Singleton;
/** Module providing the instance of {@link Appengine} to access App Engine Admin Api. */
@@ -30,9 +29,12 @@ public abstract class AppEngineAdminApiModule {
@Provides
@Singleton
public static Appengine provideAppengine(
@LocalCredential GoogleCredential credential, @Config("projectId") String projectId) {
@LocalCredential GoogleCredentialsBundle credentialsBundle,
@Config("projectId") String projectId) {
return new Appengine.Builder(
Utils.getDefaultTransport(), Utils.getDefaultJsonFactory(), credential)
credentialsBundle.getHttpTransport(),
credentialsBundle.getJsonFactory(),
credentialsBundle.getHttpRequestInitializer())
.setApplicationName(projectId)
.build();
}
@@ -20,7 +20,6 @@ import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets.Details;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.util.store.AbstractDataStoreFactory;
@@ -39,10 +38,10 @@ import google.registry.config.CredentialModule.DefaultCredential;
import google.registry.config.CredentialModule.LocalCredential;
import google.registry.config.CredentialModule.LocalCredentialJson;
import google.registry.config.RegistryConfig.Config;
import google.registry.util.GoogleCredentialsBundle;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -60,14 +59,6 @@ public class AuthModule {
private static final File DATA_STORE_DIR =
new File(System.getProperty("user.home"), ".config/nomulus/credentials");
@Module
abstract static class LocalCredentialModule {
@Binds
@DefaultCredential
abstract GoogleCredential provideLocalCredentialAsDefaultCredential(
@LocalCredential GoogleCredential credential);
}
@Provides
@StoredCredential
static Credential provideCredential(
@@ -86,38 +77,21 @@ public class AuthModule {
@Provides
@LocalCredential
public static GoogleCredential provideLocalCredential(
public static GoogleCredentialsBundle provideLocalCredential(
@LocalCredentialJson String credentialJson,
@Config("localCredentialOauthScopes") ImmutableList<String> scopes) {
try {
GoogleCredential credential =
GoogleCredential.fromStream(new ByteArrayInputStream(credentialJson.getBytes(UTF_8)));
GoogleCredentials credential =
GoogleCredentials.fromStream(new ByteArrayInputStream(credentialJson.getBytes(UTF_8)));
if (credential.createScopedRequired()) {
credential = credential.createScoped(scopes);
}
return credential;
return GoogleCredentialsBundle.create(credential);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Provides
@LocalOAuth2Credentials
public static GoogleCredentials provideLocalOAuth2Credentials(
@LocalCredentialJson String credentialJson,
@Config("localCredentialOauthScopes") ImmutableList<String> scopes) {
try {
GoogleCredentials credentials =
GoogleCredentials.fromStream(new ByteArrayInputStream(credentialJson.getBytes(UTF_8)));
if (credentials.createScopedRequired()) {
credentials = credentials.createScoped(scopes);
}
return credentials;
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
@Provides
public static GoogleAuthorizationCodeFlow provideAuthorizationCodeFlow(
JsonFactory jsonFactory,
@@ -126,7 +100,7 @@ public class AuthModule {
AbstractDataStoreFactory dataStoreFactory) {
try {
return new GoogleAuthorizationCodeFlow.Builder(
new NetHttpTransport(), jsonFactory, clientSecrets, requiredOauthScopes)
new NetHttpTransport(), jsonFactory, clientSecrets, requiredOauthScopes)
.setDataStoreFactory(dataStoreFactory)
.build();
} catch (IOException ex) {
@@ -198,16 +172,11 @@ public class AuthModule {
}
}
/** Raised when we need a user login. */
static class LoginRequiredException extends RuntimeException {
LoginRequiredException() {}
}
/**
* Dagger qualifier for the {@link Credential} constructed from the data stored on disk.
*
* <p>This {@link Credential} should not be used in another module, hence the private qualifier.
* It's only use is to build a {@link GoogleCredential}, which is used in injection sites
* It's only use is to build a {@link GoogleCredentials}, which is used in injection sites
* elsewhere.
*/
@Qualifier
@@ -227,9 +196,16 @@ public class AuthModule {
@Retention(RetentionPolicy.RUNTIME)
@interface OAuthClientId {}
/** Dagger qualifier for the local OAuth2 Credentials. */
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface LocalOAuth2Credentials {}
@Module
abstract static class LocalCredentialModule {
@Binds
@DefaultCredential
abstract GoogleCredentialsBundle provideLocalCredentialAsDefaultCredential(
@LocalCredential GoogleCredentialsBundle credential);
}
/** Raised when we need a user login. */
static class LoginRequiredException extends RuntimeException {
LoginRequiredException() {}
}
}
@@ -52,7 +52,7 @@ final class CreateAnchorTenantCommand extends MutatingEppToolCommand {
@Parameter(
names = {"--contact"},
description = "Contact ID for the request. This will be used for registrant, admin contact,"
description = "Contact ID for the request. This will be used for registrant, admin contact, "
+ "and tech contact.",
required = true)
private String contact;
@@ -237,7 +237,7 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
@Nullable
@Parameter(
names = "--drive_folder_id",
description = "Id of this registrar's folder in Drive",
description = "Id (not full URL) of this registrar's folder in Drive",
converter = OptionalStringParameter.class,
validateWith = OptionalStringParameter.class)
Optional<String> driveFolderId;
@@ -19,7 +19,6 @@ import static google.registry.util.CollectionUtils.findDuplicates;
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
import com.beust.jcommander.Parameter;
import com.google.common.base.CharMatcher;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -255,7 +254,7 @@ abstract class CreateOrUpdateTldCommand extends MutatingCommand {
tld,
canonicalizeDomainName(tld));
checkArgument(
!CharMatcher.javaDigit().matches(tld.charAt(0)),
!Character.isDigit(tld.charAt(0)),
"TLDs cannot begin with a number");
Registry oldRegistry = getOldRegistry(tld);
// TODO(b/26901539): Add a flag to set the pricing engine once we have more than one option.
@@ -14,13 +14,13 @@
package google.registry.tools;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.javanet.NetHttpTransport;
import dagger.Module;
import dagger.Provides;
import google.registry.config.CredentialModule.DefaultCredential;
import google.registry.config.RegistryConfig;
import google.registry.util.GoogleCredentialsBundle;
/**
* Module for providing the HttpRequestFactory.
@@ -30,12 +30,12 @@ import google.registry.config.RegistryConfig;
*/
@Module
class RequestFactoryModule {
static final int REQUEST_TIMEOUT_MS = 10 * 60 * 1000;
@Provides
static HttpRequestFactory provideHttpRequestFactory(
@DefaultCredential GoogleCredential credential) {
@DefaultCredential GoogleCredentialsBundle credentialsBundle) {
if (RegistryConfig.areServersLocal()) {
return new NetHttpTransport()
.createRequestFactory(
@@ -47,11 +47,12 @@ class RequestFactoryModule {
return new NetHttpTransport()
.createRequestFactory(
request -> {
credential.initialize(request);
credentialsBundle.getHttpRequestInitializer().initialize(request);
// GAE request times out after 10 min, so here we set the timeout to 10 min. This is
// needed to support some nomulus commands like updating premium lists that take
// a lot of time to complete.
// See https://developers.google.com/api-client-library/java/google-api-java-client/errors
// See
// https://developers.google.com/api-client-library/java/google-api-java-client/errors
request.setConnectTimeout(REQUEST_TIMEOUT_MS);
request.setReadTimeout(REQUEST_TIMEOUT_MS);
});
@@ -1,8 +0,0 @@
# -*-protobuf-*-
requirement: {
type: BANNED_PROPERTY_WRITE
error_message: 'Assignment to Element.prototype.innerHTML is not allowed. '
'Use goog.dom.safe.setInnerHtml instead. '
value: 'Element.prototype.innerHTML'
}
@@ -1,6 +1,6 @@
<!doctype html>
<meta http-equiv="refresh" content="0;URL=/registrar">
<title>Nomulus</title>
<body>
<body lang="en-US">
If this page doesn't change automatically, please go
to <a href="/registrar">https://www.registry.google/registrar</a>
@@ -1,410 +0,0 @@
# Copyright 2017 The Nomulus Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# .'``'. ...
# :o o `....'` ;
# `. O :'
# `': `.
# `:. `.
# : `. `.
# `..'`... `.
# `... `.
# DO NOT EDIT ``... `.
# THIS FILE `````.
#
# When you make changes to the XML schemas (*.xsd) or the JAXB bindings file
# (bindings.xjb), you must regenerate this file with the following commands:
#
# bazel run java/google/registry/xjc:list_generated_files | tee /tmp/lol
# mv /tmp/lol java/google/registry/xjc/generated_files.bzl
#
pkginfo_generated_files = [
"contact/package-info.java",
"domain/package-info.java",
"dsig/package-info.java",
"epp/package-info.java",
"eppcom/package-info.java",
"fee06/package-info.java",
"fee11/package-info.java",
"fee12/package-info.java",
"host/package-info.java",
"iirdea/package-info.java",
"launch/package-info.java",
"mark/package-info.java",
"rde/package-info.java",
"rdecontact/package-info.java",
"rdedomain/package-info.java",
"rdeeppparams/package-info.java",
"rdeheader/package-info.java",
"rdehost/package-info.java",
"rdeidn/package-info.java",
"rdenndn/package-info.java",
"rdenotification/package-info.java",
"rdepolicy/package-info.java",
"rderegistrar/package-info.java",
"rdereport/package-info.java",
"rgp/package-info.java",
"secdns/package-info.java",
"smd/package-info.java",
]
xjc_generated_files = [
"contact/ObjectFactory.java",
"contact/XjcContactAddRemType.java",
"contact/XjcContactAddrType.java",
"contact/XjcContactAuthIDType.java",
"contact/XjcContactAuthInfoType.java",
"contact/XjcContactCheck.java",
"contact/XjcContactCheckIDType.java",
"contact/XjcContactCheckType.java",
"contact/XjcContactChgPostalInfoType.java",
"contact/XjcContactChgType.java",
"contact/XjcContactChkData.java",
"contact/XjcContactCreData.java",
"contact/XjcContactCreate.java",
"contact/XjcContactDelete.java",
"contact/XjcContactDiscloseType.java",
"contact/XjcContactE164Type.java",
"contact/XjcContactInfData.java",
"contact/XjcContactInfo.java",
"contact/XjcContactIntLocType.java",
"contact/XjcContactPaCLIDType.java",
"contact/XjcContactPanData.java",
"contact/XjcContactPostalInfoEnumType.java",
"contact/XjcContactPostalInfoType.java",
"contact/XjcContactStatusType.java",
"contact/XjcContactStatusValueType.java",
"contact/XjcContactTransfer.java",
"contact/XjcContactTrnData.java",
"contact/XjcContactUpdate.java",
"domain/ObjectFactory.java",
"domain/XjcDomainAddRemType.java",
"domain/XjcDomainAuthInfoChgType.java",
"domain/XjcDomainAuthInfoType.java",
"domain/XjcDomainCheck.java",
"domain/XjcDomainCheckNameType.java",
"domain/XjcDomainCheckType.java",
"domain/XjcDomainChgType.java",
"domain/XjcDomainChkData.java",
"domain/XjcDomainContactAttrType.java",
"domain/XjcDomainContactType.java",
"domain/XjcDomainCreData.java",
"domain/XjcDomainCreate.java",
"domain/XjcDomainDelete.java",
"domain/XjcDomainHostAttrType.java",
"domain/XjcDomainHostsType.java",
"domain/XjcDomainInfData.java",
"domain/XjcDomainInfo.java",
"domain/XjcDomainInfoNameType.java",
"domain/XjcDomainNsType.java",
"domain/XjcDomainPUnitType.java",
"domain/XjcDomainPaNameType.java",
"domain/XjcDomainPanData.java",
"domain/XjcDomainPeriodType.java",
"domain/XjcDomainRenData.java",
"domain/XjcDomainRenew.java",
"domain/XjcDomainStatusType.java",
"domain/XjcDomainStatusValueType.java",
"domain/XjcDomainTransfer.java",
"domain/XjcDomainTrnData.java",
"domain/XjcDomainUpdate.java",
"dsig/ObjectFactory.java",
"dsig/XjcDsigCanonicalizationMethod.java",
"dsig/XjcDsigDSAKeyValue.java",
"dsig/XjcDsigDigestMethod.java",
"dsig/XjcDsigDigestValue.java",
"dsig/XjcDsigKeyInfo.java",
"dsig/XjcDsigKeyName.java",
"dsig/XjcDsigKeyValue.java",
"dsig/XjcDsigManifest.java",
"dsig/XjcDsigMgmtData.java",
"dsig/XjcDsigObject.java",
"dsig/XjcDsigPGPData.java",
"dsig/XjcDsigRSAKeyValue.java",
"dsig/XjcDsigReference.java",
"dsig/XjcDsigRetrievalMethod.java",
"dsig/XjcDsigSPKIData.java",
"dsig/XjcDsigSignature.java",
"dsig/XjcDsigSignatureMethod.java",
"dsig/XjcDsigSignatureProperties.java",
"dsig/XjcDsigSignatureProperty.java",
"dsig/XjcDsigSignatureValue.java",
"dsig/XjcDsigSignedInfo.java",
"dsig/XjcDsigTransform.java",
"dsig/XjcDsigTransforms.java",
"dsig/XjcDsigX509Data.java",
"dsig/XjcDsigX509IssuerSerialType.java",
"epp/ObjectFactory.java",
"epp/XjcEpp.java",
"epp/XjcEppCommandType.java",
"epp/XjcEppCredsOptionsType.java",
"epp/XjcEppDcpAccessType.java",
"epp/XjcEppDcpExpiryType.java",
"epp/XjcEppDcpOursType.java",
"epp/XjcEppDcpPurposeType.java",
"epp/XjcEppDcpRecipientType.java",
"epp/XjcEppDcpRetentionType.java",
"epp/XjcEppDcpStatementType.java",
"epp/XjcEppDcpType.java",
"epp/XjcEppElement.java",
"epp/XjcEppErrValueType.java",
"epp/XjcEppExtAnyType.java",
"epp/XjcEppExtErrValueType.java",
"epp/XjcEppExtURIType.java",
"epp/XjcEppGreetingType.java",
"epp/XjcEppLoginSvcType.java",
"epp/XjcEppLoginType.java",
"epp/XjcEppMixedMsgType.java",
"epp/XjcEppMsgQType.java",
"epp/XjcEppMsgType.java",
"epp/XjcEppPollOpType.java",
"epp/XjcEppPollType.java",
"epp/XjcEppReadWriteType.java",
"epp/XjcEppResponse.java",
"epp/XjcEppResultType.java",
"epp/XjcEppSvcMenuType.java",
"epp/XjcEppTrIDType.java",
"epp/XjcEppTransferOpType.java",
"epp/XjcEppTransferType.java",
"eppcom/ObjectFactory.java",
"eppcom/XjcEppcomExtAuthInfoType.java",
"eppcom/XjcEppcomPwAuthInfoType.java",
"eppcom/XjcEppcomReasonType.java",
"eppcom/XjcEppcomTrStatusType.java",
"fee06/ObjectFactory.java",
"fee06/XjcFee06Check.java",
"fee06/XjcFee06ChkData.java",
"fee06/XjcFee06CommandType.java",
"fee06/XjcFee06CreData.java",
"fee06/XjcFee06Create.java",
"fee06/XjcFee06CreditType.java",
"fee06/XjcFee06DelData.java",
"fee06/XjcFee06DomainCDType.java",
"fee06/XjcFee06DomainCheckType.java",
"fee06/XjcFee06FeeType.java",
"fee06/XjcFee06InfData.java",
"fee06/XjcFee06Info.java",
"fee06/XjcFee06RenData.java",
"fee06/XjcFee06Renew.java",
"fee06/XjcFee06Transfer.java",
"fee06/XjcFee06TransformCommandType.java",
"fee06/XjcFee06TransformResultType.java",
"fee06/XjcFee06TrnData.java",
"fee06/XjcFee06UpdData.java",
"fee06/XjcFee06Update.java",
"fee11/ObjectFactory.java",
"fee11/XjcFee11Check.java",
"fee11/XjcFee11ChkData.java",
"fee11/XjcFee11CommandType.java",
"fee11/XjcFee11CreData.java",
"fee11/XjcFee11Create.java",
"fee11/XjcFee11CreditType.java",
"fee11/XjcFee11DelData.java",
"fee11/XjcFee11FeeType.java",
"fee11/XjcFee11ObjectCDType.java",
"fee11/XjcFee11RenData.java",
"fee11/XjcFee11Renew.java",
"fee11/XjcFee11Transfer.java",
"fee11/XjcFee11TransformCommandType.java",
"fee11/XjcFee11TransformResultType.java",
"fee11/XjcFee11TrnData.java",
"fee11/XjcFee11UpdData.java",
"fee11/XjcFee11Update.java",
"fee12/ObjectFactory.java",
"fee12/XjcFee12Check.java",
"fee12/XjcFee12ChkData.java",
"fee12/XjcFee12CommandCDType.java",
"fee12/XjcFee12CommandCheckType.java",
"fee12/XjcFee12CreData.java",
"fee12/XjcFee12Create.java",
"fee12/XjcFee12CreditType.java",
"fee12/XjcFee12DelData.java",
"fee12/XjcFee12FeeType.java",
"fee12/XjcFee12ObjectCDType.java",
"fee12/XjcFee12RenData.java",
"fee12/XjcFee12Renew.java",
"fee12/XjcFee12Transfer.java",
"fee12/XjcFee12TransformCommandType.java",
"fee12/XjcFee12TransformResultType.java",
"fee12/XjcFee12TrnData.java",
"fee12/XjcFee12UpdData.java",
"fee12/XjcFee12Update.java",
"host/ObjectFactory.java",
"host/XjcHostAddRemType.java",
"host/XjcHostAddrType.java",
"host/XjcHostCheck.java",
"host/XjcHostCheckNameType.java",
"host/XjcHostCheckType.java",
"host/XjcHostChgType.java",
"host/XjcHostChkData.java",
"host/XjcHostCreData.java",
"host/XjcHostCreate.java",
"host/XjcHostDelete.java",
"host/XjcHostInfData.java",
"host/XjcHostInfo.java",
"host/XjcHostIpType.java",
"host/XjcHostPaNameType.java",
"host/XjcHostPanData.java",
"host/XjcHostSNameType.java",
"host/XjcHostStatusType.java",
"host/XjcHostStatusValueType.java",
"host/XjcHostUpdate.java",
"iirdea/ObjectFactory.java",
"iirdea/XjcIirdeaCode.java",
"iirdea/XjcIirdeaResponse.java",
"iirdea/XjcIirdeaResponseElement.java",
"iirdea/XjcIirdeaResult.java",
"launch/ObjectFactory.java",
"launch/XjcLaunchCdNameType.java",
"launch/XjcLaunchCdType.java",
"launch/XjcLaunchCheck.java",
"launch/XjcLaunchCheckFormType.java",
"launch/XjcLaunchChkData.java",
"launch/XjcLaunchClaimKeyType.java",
"launch/XjcLaunchCodeMarkType.java",
"launch/XjcLaunchCodeType.java",
"launch/XjcLaunchCreData.java",
"launch/XjcLaunchCreate.java",
"launch/XjcLaunchCreateNoticeType.java",
"launch/XjcLaunchDelete.java",
"launch/XjcLaunchIdContainerType.java",
"launch/XjcLaunchInfData.java",
"launch/XjcLaunchInfo.java",
"launch/XjcLaunchNoticeIDType.java",
"launch/XjcLaunchObjectType.java",
"launch/XjcLaunchPhaseType.java",
"launch/XjcLaunchPhaseTypeValue.java",
"launch/XjcLaunchStatusType.java",
"launch/XjcLaunchStatusValueType.java",
"launch/XjcLaunchUpdate.java",
"mark/ObjectFactory.java",
"mark/XjcMarkAbstractMark.java",
"mark/XjcMarkAbstractMarkType.java",
"mark/XjcMarkAddrType.java",
"mark/XjcMarkContactType.java",
"mark/XjcMarkContactTypeType.java",
"mark/XjcMarkCourtType.java",
"mark/XjcMarkE164Type.java",
"mark/XjcMarkEntitlementType.java",
"mark/XjcMarkHolderType.java",
"mark/XjcMarkMark.java",
"mark/XjcMarkMarkType.java",
"mark/XjcMarkProtectionType.java",
"mark/XjcMarkTrademarkType.java",
"mark/XjcMarkTreatyOrStatuteType.java",
"rde/ObjectFactory.java",
"rde/XjcRdeContent.java",
"rde/XjcRdeContentType.java",
"rde/XjcRdeContentsType.java",
"rde/XjcRdeDelete.java",
"rde/XjcRdeDeleteType.java",
"rde/XjcRdeDeletesType.java",
"rde/XjcRdeDeposit.java",
"rde/XjcRdeDepositTypeType.java",
"rde/XjcRdeMenuType.java",
"rde/XjcRdeRrType.java",
"rdecontact/ObjectFactory.java",
"rdecontact/XjcRdeContact.java",
"rdecontact/XjcRdeContactAbstract.java",
"rdecontact/XjcRdeContactDelete.java",
"rdecontact/XjcRdeContactDeleteType.java",
"rdecontact/XjcRdeContactElement.java",
"rdecontact/XjcRdeContactTransferDataType.java",
"rdedomain/ObjectFactory.java",
"rdedomain/XjcRdeDomain.java",
"rdedomain/XjcRdeDomainAbstract.java",
"rdedomain/XjcRdeDomainDelete.java",
"rdedomain/XjcRdeDomainDeleteType.java",
"rdedomain/XjcRdeDomainElement.java",
"rdedomain/XjcRdeDomainTransferDataType.java",
"rdeeppparams/ObjectFactory.java",
"rdeeppparams/XjcRdeEppParams.java",
"rdeeppparams/XjcRdeEppParamsAbstract.java",
"rdeeppparams/XjcRdeEppParamsElement.java",
"rdeheader/ObjectFactory.java",
"rdeheader/XjcRdeHeader.java",
"rdeheader/XjcRdeHeaderCount.java",
"rdeheader/XjcRdeHeaderElement.java",
"rdehost/ObjectFactory.java",
"rdehost/XjcRdeHost.java",
"rdehost/XjcRdeHostAbstractHost.java",
"rdehost/XjcRdeHostDelete.java",
"rdehost/XjcRdeHostDeleteType.java",
"rdehost/XjcRdeHostElement.java",
"rdeidn/ObjectFactory.java",
"rdeidn/XjcRdeIdn.java",
"rdeidn/XjcRdeIdnDelete.java",
"rdeidn/XjcRdeIdnDeleteType.java",
"rdeidn/XjcRdeIdnElement.java",
"rdenndn/ObjectFactory.java",
"rdenndn/XjcRdeNndn.java",
"rdenndn/XjcRdeNndnAbstract.java",
"rdenndn/XjcRdeNndnDelete.java",
"rdenndn/XjcRdeNndnDeleteType.java",
"rdenndn/XjcRdeNndnElement.java",
"rdenndn/XjcRdeNndnNameState.java",
"rdenndn/XjcRdeNndnNameStateValue.java",
"rdenotification/ObjectFactory.java",
"rdenotification/XjcRdeNotification.java",
"rdenotification/XjcRdeNotificationElement.java",
"rdenotification/XjcRdeNotificationName.java",
"rdenotification/XjcRdeNotificationStatusType.java",
"rdepolicy/ObjectFactory.java",
"rdepolicy/XjcRdePolicy.java",
"rdepolicy/XjcRdePolicyElement.java",
"rderegistrar/ObjectFactory.java",
"rderegistrar/XjcRdeRegistrar.java",
"rderegistrar/XjcRdeRegistrarAbstract.java",
"rderegistrar/XjcRdeRegistrarAddrType.java",
"rderegistrar/XjcRdeRegistrarDelete.java",
"rderegistrar/XjcRdeRegistrarDeleteType.java",
"rderegistrar/XjcRdeRegistrarElement.java",
"rderegistrar/XjcRdeRegistrarPostalInfoEnumType.java",
"rderegistrar/XjcRdeRegistrarPostalInfoType.java",
"rderegistrar/XjcRdeRegistrarStatusType.java",
"rderegistrar/XjcRdeRegistrarWhoisInfoType.java",
"rdereport/ObjectFactory.java",
"rdereport/XjcRdeReport.java",
"rdereport/XjcRdeReportReport.java",
"rgp/ObjectFactory.java",
"rgp/XjcRgpInfData.java",
"rgp/XjcRgpMixedType.java",
"rgp/XjcRgpOpType.java",
"rgp/XjcRgpReportTextType.java",
"rgp/XjcRgpReportType.java",
"rgp/XjcRgpRespDataType.java",
"rgp/XjcRgpRestoreType.java",
"rgp/XjcRgpStatusType.java",
"rgp/XjcRgpStatusValueType.java",
"rgp/XjcRgpUpData.java",
"rgp/XjcRgpUpdate.java",
"secdns/ObjectFactory.java",
"secdns/XjcSecdnsChgType.java",
"secdns/XjcSecdnsCreate.java",
"secdns/XjcSecdnsDsDataType.java",
"secdns/XjcSecdnsDsOrKeyType.java",
"secdns/XjcSecdnsInfData.java",
"secdns/XjcSecdnsKeyDataType.java",
"secdns/XjcSecdnsRemType.java",
"secdns/XjcSecdnsUpdate.java",
"smd/ObjectFactory.java",
"smd/XjcSmdAbstractSignedMark.java",
"smd/XjcSmdAbstractSignedMarkElement.java",
"smd/XjcSmdEncodedSignedMark.java",
"smd/XjcSmdIssuerInfo.java",
"smd/XjcSmdSignedMark.java",
"smd/XjcSmdSignedMarkElement.java",
]
@@ -4402,3 +4402,92 @@ Component: Footers
border:1px solid #a7a7a7;
border-bottom:0;
}
/*------------------------------------------------------------------
Blue style override
------------------------------------------------------------------*/
.componentName, #stickersheet h2.componentName a { color: #dd4b3a; }
.kd-content-sidebar .kd-sidebarlistitem.selected > a { border-left-color:#dd4b3a; }
.kd-appbar .kd-appname, .kd-appbar .kd-appname a { color:#dd4b3a; }
.kd-button-submit.disabled, .kd-button-submit.disabled:hover, .kd-button-submit.disabled:active {
border-color:#3079ee;
background-color: #4d90ff;
}
.kd-button-action.disabled, .kd-button-action.disabled:hover, .kd-button-action.disabled:active {
border-color: #b0281b;
background-color: #d14837;
}
.kd-button-submit:focus, .kd-button-submit.focus{
border-color:#4d90ff
}
.kd-button-action:focus, .kd-button-action.focus{
border-color:#d14837;
}
.kd-button-submit {
border-color: #3079ee;
background-color: #4d90ff;
background-image: -webkit-gradient(linear,left top,left bottom,from(#4d90ff),to(#4787ed));
/* @alternate */ background-image: -webkit-linear-gradient(top,#4d90ff,#4787ed);
/* @alternate */ background-image: -moz-linear-gradient(top,#4d90ff,#4787ed);
/* @alternate */ background-image: -ms-linear-gradient(top,#4d90ff,#4787ed);
/* @alternate */ background-image: -o-linear-gradient(top,#4d90ff,#4787ed);
/* @alternate */ background-image: linear-gradient(top,#4d90ff,#4787ed);
/* filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#4d90ff',EndColorStr='#4787ed'); */
}
.kd-button-submit:hover {
border-color: #2f5bb8;
background-color: #357ae9;
background-image: -webkit-gradient(linear,left top,left bottom,from(#4d90ff),to(#357ae9));
/* @alternate */ background-image: -webkit-linear-gradient(top,#4d90ff,#357ae9);
/* @alternate */ background-image: -moz-linear-gradient(top,#4d90ff,#357ae9);
/* @alternate */ background-image: -ms-linear-gradient(top,#4d90ff,#357ae9);
/* @alternate */ background-image: -o-linear-gradient(top,#4d90ff,#357ae9);
/* @alternate */ background-image: linear-gradient(top,#4d90ff,#357ae9);
/* filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#4d90ff',EndColorStr='#357ae9'); */
}
.kd-button-action {
margin-bottom: 16px;
border: 1px solid transparent;
color: #fff;
text-transform: uppercase;
letter-spacing: 1;
background-color: #d14837;
background-image: -webkit-gradient(linear,left top,left bottom,from(#dd4b3a),to(#d14837));
/* @alternate */ background-image: -webkit-linear-gradient(top,#dd4b3a,#d14837);
/* @alternate */ background-image: -moz-linear-gradient(top,#dd4b3a,#d14837);
/* @alternate */ background-image: -ms-linear-gradient(top,#dd4b3a,#d14837);
/* @alternate */ background-image: -o-linear-gradient(top,#dd4b3a,#d14837);
/* @alternate */ background-image: linear-gradient(top,#dd4b3a,#d14837);
/* filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#dd4b3a',EndColorStr='#d14837'); */
}
.kd-button-action:hover {
border-color: #b0281b;
border-bottom-color: #af3020;
background-color: #c53728;
background-image: -webkit-gradient(linear,left top,left bottom,from(#dd4b3a),to(#c53728));
/* @alternate */ background-image: -webkit-linear-gradient(top,#dd4b3a,#c53728);
/* @alternate */ background-image: -moz-linear-gradient(top,#dd4b3a,#c53728);
/* @alternate */ background-image: -ms-linear-gradient(top,#dd4b3a,#c53728);
/* @alternate */ background-image: -o-linear-gradient(top,#dd4b3a,#c53728);
/* @alternate */ background-image: linear-gradient(top,#dd4b3a,#c53728);
/* filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#dd4b3a',EndColorStr='#c53728'); */
}
.kd-button-action:active,
.kd-button-action:focus:active,
.kd-button-action.focus:active {
border-color: #992a1c;
background-color: #b0281b;
background-image: -webkit-gradient(linear,left top,left bottom,from(#dd4b3a),to(#b0281b));
/* @alternate */ background-image: -webkit-linear-gradient(top,#dd4b3a,#b0281b);
/* @alternate */ background-image: -moz-linear-gradient(top,#dd4b3a,#b0281b);
/* @alternate */ background-image: -ms-linear-gradient(top,#dd4b3a,#b0281b);
/* @alternate */ background-image: -o-linear-gradient(top,#dd4b3a,#b0281b);
/* @alternate */ background-image: linear-gradient(top,#dd4b3a,#b0281b);
}
.kd-accordion .expanded .row > a { color:#d14837; }
@@ -69,7 +69,7 @@
{@param logoutUrl: string}
{@param logoFilename: string}
{@param productName: string}
<div id="kd-googlebar" role="banner">
<div id="kd-googlebar" role="banner" lang="en-US">
<a class="{css('logo')}" href="/registrar">
<img src="/assets/images/{$logoFilename}" alt="{$productName}">
</a>
@@ -43,7 +43,7 @@
{param analyticsConfig: $analyticsConfig /}
{/call}
{call registry.soy.console.googlebar data="all" /}
<div id="reg-app">
<div id="reg-app" lang="en-US">
<div id="reg-appbar" class="{css('kd-appbar')}">
<div class="{css('kd-description')}">
Accessing <span class="{css('kd-value')}">{$clientId}</span> as{sp}
@@ -240,12 +240,12 @@
{param required: true /}
{/call}
{call registry.soy.forms.inputFieldRowWithValue}
{param label: 'DRIVE ID' /}
{param label: 'Drive ID' /}
{param name: 'driveId' /}
{param value: $driveId /}
{param placeholder: 'required' /}
{param description kind="text"}
Id of this registrar's folder in Drive.
ID (not full URL) of this registrar's folder in Google Drive.
{/param}
{param required: true /}
{/call}
@@ -16,8 +16,10 @@ package google.registry.beam.invoicing;
import static com.google.common.truth.Truth.assertThat;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import google.registry.util.GoogleCredentialsBundle;
import google.registry.util.ResourceUtils;
import java.io.File;
import java.io.IOException;
@@ -58,15 +60,17 @@ public class InvoicingPipelineTest {
@Before
public void initializePipeline() throws IOException {
invoicingPipeline = new InvoicingPipeline();
invoicingPipeline.projectId = "test-project";
File beamTempFolder = tempFolder.newFolder();
invoicingPipeline.beamBucketUrl = beamTempFolder.getAbsolutePath();
invoicingPipeline.invoiceFilePrefix = "REG-INV";
invoicingPipeline.beamStagingUrl = beamTempFolder.getAbsolutePath() + "/staging";
invoicingPipeline.invoiceTemplateUrl =
beamTempFolder.getAbsolutePath() + "/templates/invoicing";
invoicingPipeline.billingBucketUrl = tempFolder.getRoot().getAbsolutePath();
String beamTempFolderPath = beamTempFolder.getAbsolutePath();
invoicingPipeline = new InvoicingPipeline(
"test-project",
beamTempFolderPath,
beamTempFolderPath + "/templates/invoicing",
beamTempFolderPath + "/staging",
tempFolder.getRoot().getAbsolutePath(),
"REG-INV",
GoogleCredentialsBundle.create(GoogleCredentials.create(null))
);
}
private ImmutableList<BillingEvent> getInputEvents() {
@@ -21,11 +21,13 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.common.collect.ImmutableList;
import com.google.common.io.CharStreams;
import google.registry.beam.spec11.SafeBrowsingTransforms.EvaluateSafeBrowsingFn;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeSleeper;
import google.registry.util.GoogleCredentialsBundle;
import google.registry.util.ResourceUtils;
import google.registry.util.Retrier;
import java.io.ByteArrayInputStream;
@@ -50,6 +52,7 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicStatusLine;
import org.joda.time.DateTime;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -78,16 +81,21 @@ public class Spec11PipelineTest {
@Rule public final transient TestPipeline p = TestPipeline.fromOptions(pipelineOptions);
@Rule public final TemporaryFolder tempFolder = new TemporaryFolder();
private final Retrier retrier = new Retrier(
new FakeSleeper(new FakeClock(DateTime.parse("2019-07-15TZ"))), 1);
private Spec11Pipeline spec11Pipeline;
@Before
public void initializePipeline() throws IOException {
spec11Pipeline = new Spec11Pipeline();
spec11Pipeline.projectId = "test-project";
spec11Pipeline.reportingBucketUrl = tempFolder.getRoot().getAbsolutePath();
File beamTempFolder = tempFolder.newFolder();
spec11Pipeline.beamStagingUrl = beamTempFolder.getAbsolutePath() + "/staging";
spec11Pipeline.spec11TemplateUrl = beamTempFolder.getAbsolutePath() + "/templates/invoicing";
spec11Pipeline = new Spec11Pipeline(
"test-project",
beamTempFolder.getAbsolutePath() + "/staging",
beamTempFolder.getAbsolutePath() + "/templates/invoicing",
tempFolder.getRoot().getAbsolutePath(),
GoogleCredentialsBundle.create(GoogleCredentials.create(null)),
retrier
);
}
private static final ImmutableList<String> BAD_DOMAINS =
@@ -1 +0,0 @@
hello
@@ -1 +0,0 @@
OMG IM AN OVERRIDE
@@ -1 +0,0 @@
world
@@ -1,66 +0,0 @@
# Copyright 2017 The Nomulus Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Build rule for unit testing the zip_file() rule."""
load("//java/google/registry/builddefs:defs.bzl", "ZIPPER")
def _impl(ctx):
"""Implementation of zip_contents_test() rule."""
cmd = [
"set -e",
'repo="$(pwd)"',
'zipper="${repo}/%s"' % ctx.file._zipper.short_path,
'archive="${repo}/%s"' % ctx.file.src.short_path,
('listing="$("${zipper}" v "${archive}"' +
' | grep -v ^d | awk \'{print $3}\' | LC_ALL=C sort)"'),
'if [[ "${listing}" != "%s" ]]; then' % (
"\n".join(ctx.attr.contents.keys())
),
' echo "archive had different file listing:"',
' "${zipper}" v "${archive}" | grep -v ^d',
" exit 1",
"fi",
'tmp="$(mktemp -d "${TMPDIR:-/tmp}/zip_contents_test.XXXXXXXXXX")"',
'cd "${tmp}"',
'"${zipper}" x "${archive}"',
]
for path, data in ctx.attr.contents.items():
cmd += [
'if [[ "$(cat "%s")" != "%s" ]]; then' % (path, data),
' echo "%s had different contents:"' % path,
' cat "%s"' % path,
" exit 1",
"fi",
]
cmd += [
'cd "${repo}"',
'rm -rf "${tmp}"',
]
ctx.actions.write(
output = ctx.outputs.executable,
content = "\n".join(cmd),
is_executable = True,
)
return struct(runfiles = ctx.runfiles([ctx.file.src, ctx.file._zipper]))
zip_contents_test = rule(
implementation = _impl,
test = True,
attrs = {
"src": attr.label(allow_single_file = True),
"contents": attr.string_dict(),
"_zipper": attr.label(default = Label(ZIPPER), allow_single_file = True),
},
)
@@ -96,7 +96,7 @@ public class FlowContext {
}
/**
* Helper to locate test files for this flow by looking in javatests/ for all files with the
* Helper to locate test files for this flow by looking in src/test/java/ for all files with the
* exact same relative filename as the flow file, but with a "*Test{,Case}.java" suffix.
*/
private static Set<String> getTestFilenames(String flowName) throws IOException {
@@ -35,10 +35,10 @@ import org.junit.runners.JUnit4;
* condition. For example, there should always be a matching pair of lines such as the following:
*
* <pre>
* java/.../flows/session/LoginFlow.java:
* src/main/java/.../flows/session/LoginFlow.java:
* @error {&#64;link AlreadyLoggedInException}
*
* javatests/.../flows/session/LoginFlowTest.java:
* src/test/java/.../flows/session/LoginFlowTest.java:
* import .....flows.session.LoginFlow.AlreadyLoggedInException;
* </pre>
*
@@ -17,17 +17,17 @@ package google.registry.export.datastore;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.auth.oauth2.AccessToken;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.common.collect.ImmutableList;
import google.registry.testing.TestDataHelper;
import google.registry.util.GoogleCredentialsBundle;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Optional;
import org.junit.Before;
import org.junit.Rule;
@@ -48,27 +48,44 @@ public class DatastoreAdminTest {
@Rule public final MockitoRule mocks = MockitoJUnit.rule();
private HttpTransport httpTransport;
private GoogleCredential googleCredential;
private DatastoreAdmin datastoreAdmin;
private static HttpRequest simulateSendRequest(HttpRequest httpRequest) {
try {
httpRequest.setUrl(new GenericUrl("https://localhost:65537")).execute();
} catch (Exception expected) {
}
return httpRequest;
}
private static Optional<String> getAccessToken(HttpRequest httpRequest) {
return httpRequest.getHeaders().getAuthorizationAsList().stream()
.filter(header -> header.startsWith(AUTH_HEADER_PREFIX))
.map(header -> header.substring(AUTH_HEADER_PREFIX.length()))
.findAny();
}
private static Optional<String> getRequestContent(HttpRequest httpRequest) throws IOException {
if (httpRequest.getContent() == null) {
return Optional.empty();
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
httpRequest.getContent().writeTo(outputStream);
outputStream.close();
return Optional.of(outputStream.toString(StandardCharsets.UTF_8.name()));
}
@Before
public void setup() {
httpTransport = new NetHttpTransport();
googleCredential =
new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(JacksonFactory.getDefaultInstance())
.setClock(() -> 0)
.build();
googleCredential.setAccessToken(ACCESS_TOKEN);
googleCredential.setExpiresInSeconds(1_000L);
Date oneHourLater = new Date(System.currentTimeMillis() + 3_600_000);
GoogleCredentials googleCredentials = GoogleCredentials
.create(new AccessToken(ACCESS_TOKEN, oneHourLater));
GoogleCredentialsBundle credentialsBundle = GoogleCredentialsBundle.create(googleCredentials);
datastoreAdmin =
new DatastoreAdmin.Builder(
googleCredential.getTransport(),
googleCredential.getJsonFactory(),
googleCredential)
credentialsBundle.getHttpTransport(),
credentialsBundle.getJsonFactory(),
credentialsBundle.getHttpRequestInitializer())
.setApplicationName("MyApplication")
.setProjectId("MyCloudProject")
.build();
@@ -151,29 +168,4 @@ public class DatastoreAdminTest {
simulateSendRequest(httpRequest);
assertThat(getAccessToken(httpRequest)).hasValue(ACCESS_TOKEN);
}
private static HttpRequest simulateSendRequest(HttpRequest httpRequest) {
try {
httpRequest.setUrl(new GenericUrl("https://localhost:65537")).execute();
} catch (Exception expected) {
}
return httpRequest;
}
private static Optional<String> getAccessToken(HttpRequest httpRequest) {
return httpRequest.getHeaders().getAuthorizationAsList().stream()
.filter(header -> header.startsWith(AUTH_HEADER_PREFIX))
.map(header -> header.substring(AUTH_HEADER_PREFIX.length()))
.findAny();
}
private static Optional<String> getRequestContent(HttpRequest httpRequest) throws IOException {
if (httpRequest.getContent() == null) {
return Optional.empty();
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
httpRequest.getContent().writeTo(outputStream);
outputStream.close();
return Optional.of(outputStream.toString(StandardCharsets.UTF_8.name()));
}
}

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