Compare commits
32 Commits
nomulus-20
...
nomulus-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
332f491ac7 | ||
|
|
4bd7c18fe9 | ||
|
|
fdb0664841 | ||
|
|
a9ba770bfa | ||
|
|
4d96e5a6b1 | ||
|
|
1171c5cfcb | ||
|
|
91e241374d | ||
|
|
634202c0e9 | ||
|
|
020ed33003 | ||
|
|
0f61066b1d | ||
|
|
03711481cd | ||
|
|
c32fb2fc71 | ||
|
|
6e77c89cd6 | ||
|
|
5e41e84b8d | ||
|
|
bfd569ee44 | ||
|
|
b13a33347f | ||
|
|
d17a6edf12 | ||
|
|
7255ebff29 | ||
|
|
cacc90097a | ||
|
|
0ef8984767 | ||
|
|
7a4abd93dc | ||
|
|
142c910e3b | ||
|
|
c68d54a5ed | ||
|
|
d17188b820 | ||
|
|
cbe59b6950 | ||
|
|
2b3c6525ff | ||
|
|
72dd8658cf | ||
|
|
c0490f7777 | ||
|
|
a22a38527b | ||
|
|
08203033a2 | ||
|
|
d0482a8f2c | ||
|
|
e6a2db8075 |
10
.github/workflows/codeql.yml
vendored
@@ -6,8 +6,6 @@ on:
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ 'master' ]
|
||||
schedule:
|
||||
- cron: '24 4 * * *'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
@@ -49,13 +47,13 @@ jobs:
|
||||
|
||||
# Build with Gradle
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
with:
|
||||
build-scan-publish: true
|
||||
build-scan-terms-of-service-url: "https://gradle.com/terms-of-service"
|
||||
build-scan-terms-of-service-agree: "yes"
|
||||
build-scan-terms-of-use-url: "https://gradle.com/terms-of-service"
|
||||
build-scan-terms-of-use-agree: "yes"
|
||||
- name: Execute Gradle build
|
||||
run: ./gradlew build -x test -x jIFC
|
||||
run: ./gradlew --no-daemon --no-build-cache --no-configuration-cache --rerun-tasks clean build -x test -x jIFC
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
|
||||
@@ -34,6 +34,8 @@ Guy Bensky <guyben@google.com>
|
||||
Weimin Yu <weiminyu@google.com>
|
||||
Shicong Huang <shicong@google.com>
|
||||
Gustav Brodman <gbrodman@google.com>
|
||||
Aman Sanger <sangera@google.com>
|
||||
Sarah Botwinick <sarahbot@google.com>
|
||||
Legina Chen <legina@google.com>
|
||||
Rachel Guan <rachelguan@google.com>
|
||||
Juan Celhay <jicelhay@google.com>
|
||||
|
||||
@@ -47,20 +47,9 @@ war {
|
||||
|
||||
if (project.path == ":services:default") {
|
||||
war {
|
||||
from("${coreResourcesDir}/google/registry/ui") {
|
||||
include "registrar_bin.js"
|
||||
if (environment != "production") {
|
||||
include "registrar_bin.js.map"
|
||||
}
|
||||
into("assets/js")
|
||||
}
|
||||
from("${coreResourcesDir}/google/registry/ui/css") {
|
||||
include "registrar*"
|
||||
into("assets/css")
|
||||
}
|
||||
from("${coreResourcesDir}/google/registry/ui/assets/images") {
|
||||
include "**/*"
|
||||
into("assets/images")
|
||||
from("${coreResourcesDir}/google/registry/ui/html") {
|
||||
include "*.html"
|
||||
into("registrar")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,7 +93,6 @@ appengineDeployAll.finalizedBy ':deployCloudSchedulerAndQueue'
|
||||
rootProject.deploy.dependsOn appengineDeployAll
|
||||
|
||||
rootProject.stage.dependsOn appengineStage
|
||||
tasks['war'].dependsOn ':core:compileProdJS'
|
||||
tasks['war'].dependsOn ':core:processResources'
|
||||
tasks['war'].dependsOn ':core:jar'
|
||||
|
||||
|
||||
@@ -55,11 +55,11 @@ org.jacoco:org.jacoco.core:0.8.12=jacocoAnt
|
||||
org.jacoco:org.jacoco.report:0.8.12=jacocoAnt
|
||||
org.javassist:javassist:3.28.0-GA=checkstyle
|
||||
org.jspecify:jspecify:0.3.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||
org.junit.jupiter:junit-jupiter-api:5.11.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-engine:5.11.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-commons:1.11.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-engine:1.11.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit:junit-bom:5.11.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-api:5.11.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-engine:5.11.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-commons:1.11.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-engine:1.11.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit:junit-bom:5.11.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.ow2.asm:asm-commons:9.7=jacocoAnt
|
||||
org.ow2.asm:asm-tree:9.7=jacocoAnt
|
||||
|
||||
199
console-webapp/package-lock.json
generated
@@ -5075,9 +5075,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.16.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.16.4.tgz",
|
||||
"integrity": "sha512-GkhjAaQ8oUTOKE4g4gsZ0u8K/IHU1+2WQSgS1TwTcYvL+sjbaQjNHFXbOJ6kgqGHIO1DfUhI/Sphi9GkRT9K+Q==",
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz",
|
||||
"integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -5088,9 +5088,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.16.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.16.4.tgz",
|
||||
"integrity": "sha512-Bvm6D+NPbGMQOcxvS1zUl8H7DWlywSXsphAeOnVeiZLQ+0J6Is8T7SrjGTH29KtYkiY9vld8ZnpV3G2EPbom+w==",
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz",
|
||||
"integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -5101,9 +5101,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.16.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.16.4.tgz",
|
||||
"integrity": "sha512-i5d64MlnYBO9EkCOGe5vPR/EeDwjnKOGGdd7zKFhU5y8haKhQZTN2DgVtpODDMxUr4t2K90wTUJg7ilgND6bXw==",
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz",
|
||||
"integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -5114,9 +5114,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.16.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.16.4.tgz",
|
||||
"integrity": "sha512-WZupV1+CdUYehaZqjaFTClJI72fjJEgTXdf4NbW69I9XyvdmztUExBtcI2yIIU6hJtYvtwS6pkTkHJz+k08mAQ==",
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz",
|
||||
"integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -5127,9 +5127,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.16.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.16.4.tgz",
|
||||
"integrity": "sha512-ADm/xt86JUnmAfA9mBqFcRp//RVRt1ohGOYF6yL+IFCYqOBNwy5lbEK05xTsEoJq+/tJzg8ICUtS82WinJRuIw==",
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz",
|
||||
"integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -5140,9 +5140,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.16.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.16.4.tgz",
|
||||
"integrity": "sha512-tJfJaXPiFAG+Jn3cutp7mCs1ePltuAgRqdDZrzb1aeE3TktWWJ+g7xK9SNlaSUFw6IU4QgOxAY4rA+wZUT5Wfg==",
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz",
|
||||
"integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -5153,9 +5153,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.16.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.16.4.tgz",
|
||||
"integrity": "sha512-7dy1BzQkgYlUTapDTvK997cgi0Orh5Iu7JlZVBy1MBURk7/HSbHkzRnXZa19ozy+wwD8/SlpJnOOckuNZtJR9w==",
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz",
|
||||
"integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -5166,9 +5166,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.16.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.16.4.tgz",
|
||||
"integrity": "sha512-zsFwdUw5XLD1gQe0aoU2HVceI6NEW7q7m05wA46eUAyrkeNYExObfRFQcvA6zw8lfRc5BHtan3tBpo+kqEOxmg==",
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz",
|
||||
"integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -5179,9 +5179,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||
"version": "4.16.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.16.4.tgz",
|
||||
"integrity": "sha512-p8C3NnxXooRdNrdv6dBmRTddEapfESEUflpICDNKXpHvTjRRq1J82CbU5G3XfebIZyI3B0s074JHMWD36qOW6w==",
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz",
|
||||
"integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -5192,9 +5192,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.16.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.16.4.tgz",
|
||||
"integrity": "sha512-Lh/8ckoar4s4Id2foY7jNgitTOUQczwMWNYi+Mjt0eQ9LKhr6sK477REqQkmy8YHY3Ca3A2JJVdXnfb3Rrwkng==",
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz",
|
||||
"integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -5205,9 +5205,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.16.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.16.4.tgz",
|
||||
"integrity": "sha512-1xwwn9ZCQYuqGmulGsTZoKrrn0z2fAur2ujE60QgyDpHmBbXbxLaQiEvzJWDrscRq43c8DnuHx3QorhMTZgisQ==",
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz",
|
||||
"integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -5218,9 +5218,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.16.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.16.4.tgz",
|
||||
"integrity": "sha512-LuOGGKAJ7dfRtxVnO1i3qWc6N9sh0Em/8aZ3CezixSTM+E9Oq3OvTsvC4sm6wWjzpsIlOCnZjdluINKESflJLA==",
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz",
|
||||
"integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -5231,9 +5231,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.16.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.16.4.tgz",
|
||||
"integrity": "sha512-ch86i7KkJKkLybDP2AtySFTRi5fM3KXp0PnHocHuJMdZwu7BuyIKi35BE9guMlmTpwwBTB3ljHj9IQXnTCD0vA==",
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz",
|
||||
"integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -5244,9 +5244,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.16.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.16.4.tgz",
|
||||
"integrity": "sha512-Ma4PwyLfOWZWayfEsNQzTDBVW8PZ6TUUN1uFTBQbF2Chv/+sjenE86lpiEwj2FiviSmSZ4Ap4MaAfl1ciF4aSA==",
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz",
|
||||
"integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -5257,9 +5257,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.16.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.16.4.tgz",
|
||||
"integrity": "sha512-9m/ZDrQsdo/c06uOlP3W9G2ENRVzgzbSXmXHT4hwVaDQhYcRpi9bgBT0FTG9OhESxwK0WjQxYOSfv40cU+T69w==",
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz",
|
||||
"integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -5270,9 +5270,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.16.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.16.4.tgz",
|
||||
"integrity": "sha512-YunpoOAyGLDseanENHmbFvQSfVL5BxW3k7hhy0eN4rb3gS/ct75dVD0EXOWIqFT/nE8XYW6LP6vz6ctKRi0k9A==",
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz",
|
||||
"integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -7378,9 +7378,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
|
||||
"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
@@ -8140,9 +8140,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io": {
|
||||
"version": "6.5.4",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz",
|
||||
"integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==",
|
||||
"version": "6.6.2",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz",
|
||||
"integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/cookie": "^0.4.1",
|
||||
@@ -8150,25 +8150,46 @@
|
||||
"@types/node": ">=10.0.0",
|
||||
"accepts": "~1.3.4",
|
||||
"base64id": "2.0.0",
|
||||
"cookie": "~0.4.1",
|
||||
"cookie": "~0.7.2",
|
||||
"cors": "~2.8.5",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
"ws": "~8.11.0"
|
||||
"ws": "~8.17.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-parser": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz",
|
||||
"integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==",
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
|
||||
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io/node_modules/ws": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "5.16.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz",
|
||||
@@ -8819,9 +8840,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz",
|
||||
"integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==",
|
||||
"version": "4.21.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
|
||||
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
@@ -8829,7 +8850,7 @@
|
||||
"body-parser": "1.20.3",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.6.0",
|
||||
"cookie": "0.7.1",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
@@ -8861,9 +8882,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/cookie": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
||||
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
|
||||
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
@@ -13678,9 +13699,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.16.4",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.16.4.tgz",
|
||||
"integrity": "sha512-kuaTJSUbz+Wsb2ATGvEknkI12XV40vIiHmLuFlejoo7HtDok/O5eDDD0UpCVY5bBX5U5RYo8wWP83H7ZsqVEnA==",
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz",
|
||||
"integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.5"
|
||||
@@ -13693,22 +13714,22 @@
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.16.4",
|
||||
"@rollup/rollup-android-arm64": "4.16.4",
|
||||
"@rollup/rollup-darwin-arm64": "4.16.4",
|
||||
"@rollup/rollup-darwin-x64": "4.16.4",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.16.4",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.16.4",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.16.4",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.16.4",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.16.4",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.16.4",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.16.4",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.16.4",
|
||||
"@rollup/rollup-linux-x64-musl": "4.16.4",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.16.4",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.16.4",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.16.4",
|
||||
"@rollup/rollup-android-arm-eabi": "4.22.4",
|
||||
"@rollup/rollup-android-arm64": "4.22.4",
|
||||
"@rollup/rollup-darwin-arm64": "4.22.4",
|
||||
"@rollup/rollup-darwin-x64": "4.22.4",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.22.4",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.22.4",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.22.4",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.22.4",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.22.4",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.22.4",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.22.4",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.22.4",
|
||||
"@rollup/rollup-linux-x64-musl": "4.22.4",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.22.4",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.22.4",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.22.4",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
@@ -14224,16 +14245,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io": {
|
||||
"version": "4.7.5",
|
||||
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz",
|
||||
"integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==",
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.0.tgz",
|
||||
"integrity": "sha512-8U6BEgGjQOfGz3HHTYaC/L1GaxDCJ/KM0XTkJly0EhZ5U/du9uNEZy4ZgYzEzIqlx2CMm25CrCqr1ck899eLNA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.4",
|
||||
"base64id": "~2.0.0",
|
||||
"cors": "~2.8.5",
|
||||
"debug": "~4.3.2",
|
||||
"engine.io": "~6.5.2",
|
||||
"engine.io": "~6.6.0",
|
||||
"socket.io-adapter": "~2.5.2",
|
||||
"socket.io-parser": "~4.2.4"
|
||||
},
|
||||
|
||||
@@ -18,15 +18,12 @@ import { BillingInfoComponent } from './billingInfo/billingInfo.component';
|
||||
import { DomainListComponent } from './domains/domainList.component';
|
||||
import { HomeComponent } from './home/home.component';
|
||||
import { RegistryLockVerifyComponent } from './lock/registryLockVerify.component';
|
||||
import { NewOteComponent } from './ote/newOte.component';
|
||||
import { OteStatusComponent } from './ote/oteStatus.component';
|
||||
import { RegistrarDetailsComponent } from './registrar/registrarDetails.component';
|
||||
import { RegistrarComponent } from './registrar/registrarsTable.component';
|
||||
import { ResourcesComponent } from './resources/resources.component';
|
||||
import ContactComponent from './settings/contact/contact.component';
|
||||
import SecurityComponent from './settings/security/security.component';
|
||||
import { SettingsComponent } from './settings/settings.component';
|
||||
import UsersComponent from './settings/users/users.component';
|
||||
import WhoisComponent from './settings/whois/whois.component';
|
||||
import { SupportComponent } from './support/support.component';
|
||||
|
||||
@@ -37,6 +34,7 @@ export interface RouteWithIcon extends Route {
|
||||
export const PATHS = {
|
||||
NewOteComponent: 'new-ote',
|
||||
OteStatusComponent: 'ote-status/:registrarId',
|
||||
UsersComponent: 'users',
|
||||
};
|
||||
export const routes: RouteWithIcon[] = [
|
||||
{ path: '', redirectTo: '/home', pathMatch: 'full' },
|
||||
@@ -94,10 +92,6 @@ export const routes: RouteWithIcon[] = [
|
||||
component: SecurityComponent,
|
||||
title: 'Security',
|
||||
},
|
||||
{
|
||||
path: UsersComponent.PATH,
|
||||
component: UsersComponent,
|
||||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
@@ -128,6 +122,13 @@ export const routes: RouteWithIcon[] = [
|
||||
title: 'Resources',
|
||||
iconName: 'description',
|
||||
},
|
||||
{
|
||||
path: PATHS.UsersComponent,
|
||||
title: 'Users',
|
||||
iconName: 'manage_accounts',
|
||||
loadComponent: () =>
|
||||
import('./users/users.component').then((mod) => mod.UsersComponent),
|
||||
},
|
||||
{
|
||||
path: SupportComponent.PATH,
|
||||
component: SupportComponent,
|
||||
|
||||
@@ -32,8 +32,6 @@ import { HeaderComponent } from './header/header.component';
|
||||
import { HomeComponent } from './home/home.component';
|
||||
import { RegistryLockVerifyComponent } from './lock/registryLockVerify.component';
|
||||
import { NavigationComponent } from './navigation/navigation.component';
|
||||
import { NewOteComponent } from './ote/newOte.component';
|
||||
import { OteStatusComponent } from './ote/oteStatus.component';
|
||||
import NewRegistrarComponent from './registrar/newRegistrar.component';
|
||||
import { RegistrarDetailsComponent } from './registrar/registrarDetails.component';
|
||||
import { RegistrarSelectorComponent } from './registrar/registrarSelector.component';
|
||||
@@ -58,6 +56,14 @@ import { SnackBarModule } from './snackbar.module';
|
||||
import { SupportComponent } from './support/support.component';
|
||||
import { TldsComponent } from './tlds/tlds.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [SelectedRegistrarWrapper],
|
||||
imports: [MaterialModule],
|
||||
exports: [SelectedRegistrarWrapper],
|
||||
providers: [],
|
||||
})
|
||||
export class SelectedRegistrarModule {}
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
@@ -80,7 +86,6 @@ import { TldsComponent } from './tlds/tlds.component';
|
||||
ResourcesComponent,
|
||||
SecurityComponent,
|
||||
SecurityEditComponent,
|
||||
SelectedRegistrarWrapper,
|
||||
SettingsComponent,
|
||||
SettingsContactComponent,
|
||||
SupportComponent,
|
||||
@@ -96,8 +101,8 @@ import { TldsComponent } from './tlds/tlds.component';
|
||||
FormsModule,
|
||||
MaterialModule,
|
||||
SnackBarModule,
|
||||
SelectedRegistrarModule,
|
||||
],
|
||||
exports: [SelectedRegistrarWrapper],
|
||||
providers: [
|
||||
BackendService,
|
||||
BreakPointObserverService,
|
||||
|
||||
@@ -95,9 +95,9 @@
|
||||
|
||||
<ng-container matColumnDef="statuses">
|
||||
<mat-header-cell *matHeaderCellDef>Statuses</mat-header-cell>
|
||||
<mat-cell *matCellDef="let element">{{
|
||||
element.statuses
|
||||
}}</mat-cell>
|
||||
<mat-cell *matCellDef="let element">
|
||||
<span>{{ element.statuses?.join(", ") }}</span>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="registryLock">
|
||||
|
||||
@@ -36,6 +36,11 @@
|
||||
.mat-column-registryLock {
|
||||
max-width: 150px;
|
||||
}
|
||||
.mat-column-statuses span {
|
||||
padding: 10px 0;
|
||||
overflow: hidden;
|
||||
word-break: break-word;
|
||||
}
|
||||
}
|
||||
|
||||
&__domains-spinner {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
|
||||
import { Component, ViewChild, effect } from '@angular/core';
|
||||
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
@@ -91,7 +91,10 @@ export class DomainListComponent {
|
||||
loadLocks() {
|
||||
this.registryLockService.retrieveLocks().subscribe({
|
||||
error: (err: HttpErrorResponse) => {
|
||||
this._snackBar.open(err.message);
|
||||
if (err.status !== HttpStatusCode.Forbidden) {
|
||||
// Some users may not have registry lock permissions and that's OK
|
||||
this._snackBar.open(err.message);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -17,8 +17,9 @@ import { Component } from '@angular/core';
|
||||
import { MatTreeNestedDataSource } from '@angular/material/tree';
|
||||
import { NavigationEnd, Router } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { RouteWithIcon, routes } from '../app-routing.module';
|
||||
import { RouteWithIcon, routes, PATHS } from '../app-routing.module';
|
||||
import { RESTRICTED_ELEMENTS } from '../shared/directives/userLevelVisiblity.directive';
|
||||
import { RegistrarComponent } from '../registrar/registrarsTable.component';
|
||||
|
||||
interface NavMenuNode extends RouteWithIcon {
|
||||
parentRoute?: RouteWithIcon;
|
||||
@@ -59,9 +60,12 @@ export class NavigationComponent {
|
||||
}
|
||||
|
||||
getElementId(node: RouteWithIcon) {
|
||||
return node.path === 'registrars'
|
||||
? RESTRICTED_ELEMENTS.REGISTRAR_ELEMENT
|
||||
: null;
|
||||
if (node.path === RegistrarComponent.PATH) {
|
||||
return RESTRICTED_ELEMENTS.REGISTRAR_ELEMENT;
|
||||
} else if (node.path === PATHS.UsersComponent) {
|
||||
return RESTRICTED_ELEMENTS.USERS;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
syncExpandedNavigationWithRoute(url: string) {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { switchMap } from 'rxjs';
|
||||
import { switchMap, timeout } from 'rxjs';
|
||||
import {
|
||||
IpAllowListItem,
|
||||
RegistrarService,
|
||||
@@ -69,6 +69,7 @@ export class SecurityService {
|
||||
uiToApiConverter(newSecuritySettings)
|
||||
)
|
||||
.pipe(
|
||||
timeout(2000),
|
||||
switchMap(() => {
|
||||
return this.registrarService.loadRegistrars();
|
||||
})
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<mat-form-field appearance="outline">
|
||||
<input
|
||||
matInput
|
||||
[disabled]="isUpdating"
|
||||
type="text"
|
||||
[(ngModel)]="ip.value"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
@@ -20,12 +21,19 @@
|
||||
mat-icon-button
|
||||
aria-label="Remove"
|
||||
(click)="removeIpEntry(ip)"
|
||||
[disabled]="isUpdating"
|
||||
>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
<button mat-button color="primary" (click)="createIpEntry()" type="button">
|
||||
<button
|
||||
mat-button
|
||||
[disabled]="isUpdating"
|
||||
color="primary"
|
||||
(click)="createIpEntry()"
|
||||
type="button"
|
||||
>
|
||||
+ Add IP
|
||||
</button>
|
||||
|
||||
@@ -35,6 +43,7 @@
|
||||
<textarea
|
||||
class="console-app__clientCertificateValue"
|
||||
matInput
|
||||
[disabled]="isUpdating"
|
||||
[(ngModel)]="dataSource.clientCertificate"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
></textarea>
|
||||
@@ -44,6 +53,7 @@
|
||||
<mat-form-field appearance="outline">
|
||||
<textarea
|
||||
matInput
|
||||
[disabled]="isUpdating"
|
||||
[(ngModel)]="dataSource.failoverClientCertificate"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
></textarea>
|
||||
@@ -51,6 +61,7 @@
|
||||
<button
|
||||
mat-flat-button
|
||||
color="primary"
|
||||
[disabled]="isUpdating"
|
||||
aria-label="Save security settings"
|
||||
type="submit"
|
||||
class="settings-security__edit-save"
|
||||
|
||||
@@ -29,6 +29,7 @@ import { SecurityService, apiToUiConverter } from './security.service';
|
||||
})
|
||||
export default class SecurityEditComponent {
|
||||
dataSource: SecuritySettings = {};
|
||||
isUpdating = false;
|
||||
|
||||
constructor(
|
||||
public securityService: SecurityService,
|
||||
@@ -43,12 +44,15 @@ export default class SecurityEditComponent {
|
||||
}
|
||||
|
||||
save() {
|
||||
this.isUpdating = true;
|
||||
this.securityService.saveChanges(this.dataSource).subscribe({
|
||||
complete: () => {
|
||||
this.isUpdating = false;
|
||||
this.goBack();
|
||||
},
|
||||
error: (err: HttpErrorResponse) => {
|
||||
this._snackBar.open(err.error);
|
||||
this._snackBar.open(err.error || err.message);
|
||||
this.isUpdating = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<p>users works!</p>
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright 2024 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.
|
||||
@@ -1,36 +0,0 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import UsersComponent from './users.component';
|
||||
|
||||
describe('UsersComponent', () => {
|
||||
let component: UsersComponent;
|
||||
let fixture: ComponentFixture<UsersComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [UsersComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(UsersComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -18,10 +18,17 @@ import { UserDataService } from '../services/userData.service';
|
||||
export enum RESTRICTED_ELEMENTS {
|
||||
REGISTRAR_ELEMENT,
|
||||
OTE,
|
||||
USERS,
|
||||
}
|
||||
|
||||
export const DISABLED_ELEMENTS_PER_ROLE = {
|
||||
NONE: [RESTRICTED_ELEMENTS.REGISTRAR_ELEMENT, RESTRICTED_ELEMENTS.OTE],
|
||||
NONE: [
|
||||
RESTRICTED_ELEMENTS.REGISTRAR_ELEMENT,
|
||||
RESTRICTED_ELEMENTS.OTE,
|
||||
RESTRICTED_ELEMENTS.USERS,
|
||||
],
|
||||
SUPPORT_LEAD: [RESTRICTED_ELEMENTS.USERS],
|
||||
SUPPORT_AGENT: [RESTRICTED_ELEMENTS.USERS],
|
||||
};
|
||||
|
||||
@Directive({
|
||||
|
||||
@@ -21,6 +21,7 @@ import { DomainLocksResult } from 'src/app/domains/registryLock.service';
|
||||
import { RegistryLockVerificationResponse } from 'src/app/lock/registryLockVerify.service';
|
||||
import { OteCreateResponse } from 'src/app/ote/newOte.component';
|
||||
import { OteStatusResponse } from 'src/app/ote/oteStatus.component';
|
||||
import { User } from 'src/app/users/users.service';
|
||||
import {
|
||||
Registrar,
|
||||
SecuritySettingsBackendModel,
|
||||
@@ -159,6 +160,18 @@ export class BackendService {
|
||||
);
|
||||
}
|
||||
|
||||
getUsers(registrarId: string): Observable<User[]> {
|
||||
return this.http
|
||||
.get<User[]>(`/console-api/users?registrarId=${registrarId}`)
|
||||
.pipe(catchError((err) => this.errorCatcher<User[]>(err)));
|
||||
}
|
||||
|
||||
createUser(registrarId: string): Observable<User> {
|
||||
return this.http
|
||||
.post<User>(`/console-api/users?registrarId=${registrarId}`, {})
|
||||
.pipe(catchError((err) => this.errorCatcher<User>(err)));
|
||||
}
|
||||
|
||||
getUserData(): Observable<UserData> {
|
||||
return this.http
|
||||
.get<UserData>('/console-api/userdata')
|
||||
|
||||
43
console-webapp/src/app/users/users.component.html
Normal file
@@ -0,0 +1,43 @@
|
||||
<app-selected-registrar-wrapper>
|
||||
@if (!isLoading) {
|
||||
<div class="console-app__users">
|
||||
<div class="console-app__users-header">
|
||||
<h1 class="mat-headline-4">Users</h1>
|
||||
<div class="spacer"></div>
|
||||
<button
|
||||
mat-flat-button
|
||||
(click)="createNewUser()"
|
||||
aria-label="Create new user"
|
||||
color="primary"
|
||||
>
|
||||
Create a Viewer User
|
||||
</button>
|
||||
</div>
|
||||
<mat-table
|
||||
[dataSource]="dataSource"
|
||||
class="mat-elevation-z0"
|
||||
class="console-app__users-table"
|
||||
matSort
|
||||
>
|
||||
<ng-container
|
||||
*ngFor="let column of columns"
|
||||
[matColumnDef]="column.columnDef"
|
||||
>
|
||||
<mat-header-cell *matHeaderCellDef>
|
||||
{{ column.header }}
|
||||
</mat-header-cell>
|
||||
<mat-cell
|
||||
*matCellDef="let row"
|
||||
[innerHTML]="column.cell(row)"
|
||||
></mat-cell>
|
||||
</ng-container>
|
||||
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
||||
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
|
||||
</mat-table>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="console-app__users-spinner">
|
||||
<mat-spinner />
|
||||
</div>
|
||||
}
|
||||
</app-selected-registrar-wrapper>
|
||||
@@ -12,13 +12,27 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
.console-app {
|
||||
&__users-spinner {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-users',
|
||||
templateUrl: './users.component.html',
|
||||
styleUrls: ['./users.component.scss'],
|
||||
})
|
||||
export default class UsersComponent {
|
||||
public static PATH = 'users';
|
||||
$min-width: 756px;
|
||||
$max-width: 1024px;
|
||||
|
||||
&__users-table {
|
||||
min-width: $min-width !important;
|
||||
max-width: $max-width;
|
||||
}
|
||||
|
||||
&__users-new {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
&__users-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
122
console-webapp/src/app/users/users.component.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { Component, effect, ViewChild } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { MatSort } from '@angular/material/sort';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { SelectedRegistrarModule } from '../app.module';
|
||||
import { MaterialModule } from '../material.module';
|
||||
import { RegistrarService } from '../registrar/registrar.service';
|
||||
import { SnackBarModule } from '../snackbar.module';
|
||||
import { User, UsersService } from './users.service';
|
||||
|
||||
const roleToDescription = (role: String) => {
|
||||
if (!role) return 'N/A';
|
||||
else if (role.toLowerCase().startsWith('account_manager')) {
|
||||
return 'Viewer';
|
||||
}
|
||||
return 'Editor';
|
||||
};
|
||||
|
||||
export const columns = [
|
||||
{
|
||||
columnDef: 'emailAddress',
|
||||
header: 'User email',
|
||||
cell: (record: User) => `${record.emailAddress || ''}`,
|
||||
},
|
||||
{
|
||||
columnDef: 'role',
|
||||
header: 'User role',
|
||||
cell: (record: User) => `${roleToDescription(record.role)}`,
|
||||
},
|
||||
];
|
||||
|
||||
@Component({
|
||||
selector: 'app-users',
|
||||
templateUrl: './users.component.html',
|
||||
styleUrls: ['./users.component.scss'],
|
||||
standalone: true,
|
||||
imports: [
|
||||
MaterialModule,
|
||||
SnackBarModule,
|
||||
CommonModule,
|
||||
SelectedRegistrarModule,
|
||||
],
|
||||
providers: [UsersService],
|
||||
})
|
||||
export class UsersComponent {
|
||||
dataSource: MatTableDataSource<User>;
|
||||
columns = columns;
|
||||
displayedColumns = this.columns.map((c) => c.columnDef);
|
||||
isLoading = false;
|
||||
|
||||
@ViewChild(MatSort) sort!: MatSort;
|
||||
|
||||
constructor(
|
||||
protected registrarService: RegistrarService,
|
||||
protected usersService: UsersService,
|
||||
private _snackBar: MatSnackBar
|
||||
) {
|
||||
this.dataSource = new MatTableDataSource<User>(usersService.users());
|
||||
|
||||
effect(() => {
|
||||
if (registrarService.registrarId()) {
|
||||
this.loadUsers();
|
||||
}
|
||||
});
|
||||
effect(() => {
|
||||
this.dataSource.data = usersService.users();
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.dataSource.sort = this.sort;
|
||||
}
|
||||
|
||||
loadUsers() {
|
||||
this.isLoading = true;
|
||||
this.usersService.fetchUsers().subscribe({
|
||||
error: (err: HttpErrorResponse) => {
|
||||
this._snackBar.open(err.error || err.message);
|
||||
},
|
||||
complete: () => {
|
||||
this.isLoading = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
createNewUser() {
|
||||
this.isLoading = true;
|
||||
this.usersService.createNewUser().subscribe({
|
||||
next: (newUser) => {
|
||||
this._snackBar.open(
|
||||
`New user with email ${newUser.emailAddress} has been created.`,
|
||||
'',
|
||||
{
|
||||
duration: 2000,
|
||||
}
|
||||
);
|
||||
},
|
||||
error: (err: HttpErrorResponse) => {
|
||||
this._snackBar.open(err.error || err.message);
|
||||
},
|
||||
complete: () => {
|
||||
this.isLoading = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
58
console-webapp/src/app/users/users.service.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
import { Injectable, signal } from '@angular/core';
|
||||
import { tap } from 'rxjs';
|
||||
import { RegistrarService } from '../registrar/registrar.service';
|
||||
import { BackendService } from '../shared/services/backend.service';
|
||||
|
||||
export interface CreateAutoTimestamp {
|
||||
creationTime: string;
|
||||
}
|
||||
|
||||
export interface User {
|
||||
emailAddress: String;
|
||||
role: String;
|
||||
password?: String;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
users = signal<User[]>([]);
|
||||
|
||||
constructor(
|
||||
private backendService: BackendService,
|
||||
private registrarService: RegistrarService
|
||||
) {}
|
||||
|
||||
fetchUsers() {
|
||||
return this.backendService
|
||||
.getUsers(this.registrarService.registrarId())
|
||||
.pipe(
|
||||
tap((users: User[]) => {
|
||||
this.users.set(users);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
createNewUser() {
|
||||
return this.backendService
|
||||
.createUser(this.registrarService.registrarId())
|
||||
.pipe(
|
||||
tap((newUser: User) => {
|
||||
this.users.set([...this.users(), newUser]);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -98,10 +98,8 @@ processTestResources {
|
||||
}
|
||||
|
||||
configurations {
|
||||
css
|
||||
jaxb
|
||||
soy
|
||||
closureCompiler
|
||||
devtool
|
||||
|
||||
nonprodImplementation.extendsFrom implementation
|
||||
@@ -120,8 +118,6 @@ configurations {
|
||||
all {
|
||||
// servlet-api:3.1 pulled in but not used by soy compiler
|
||||
exclude group: 'javax.servlet', module: 'javax.servlet-api'
|
||||
// Jetty's servlet-api:2.5 implementation, pulled in by other Jetty jars
|
||||
exclude group: 'org.mortbay.jetty', module: 'servlet-api'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +175,6 @@ dependencies {
|
||||
implementation deps['com.google.oauth-client:google-oauth-client-java6']
|
||||
implementation deps['com.google.oauth-client:google-oauth-client-jetty']
|
||||
implementation deps['com.google.oauth-client:google-oauth-client-servlet']
|
||||
implementation deps['com.google.protobuf:protobuf-java']
|
||||
implementation deps['com.google.re2j:re2j']
|
||||
implementation deps['com.google.template:soy']
|
||||
implementation deps['com.googlecode.json-simple:json-simple']
|
||||
@@ -297,22 +292,16 @@ dependencies {
|
||||
jaxb deps['org.glassfish.jaxb:jaxb-xjc']
|
||||
|
||||
// Dependency needed for soy to java compilation.
|
||||
soy deps['com.google.inject:guice']
|
||||
soy deps['com.google.protobuf:protobuf-java']
|
||||
// Read dependencies.gradle on why the protobuf version is pinned directly.
|
||||
soy 'com.google.protobuf:protobuf-java:[3.25.5, 4.0.0)!!'
|
||||
soy deps['com.google.template:soy']
|
||||
|
||||
// Dependencies needed for compiling stylesheets to javascript
|
||||
css deps['com.google.closure-stylesheets:closure-stylesheets']
|
||||
css deps['args4j:args4j']
|
||||
|
||||
// Tool dependencies. used for doc generation.
|
||||
implementation files("${System.properties['java.home']}/../lib/tools.jar")
|
||||
|
||||
// Flyway classes needed to generate the golden file.
|
||||
implementation deps['org.flywaydb:flyway-core']
|
||||
implementation deps['org.flywaydb:flyway-database-postgresql']
|
||||
|
||||
closureCompiler deps['com.google.javascript:closure-compiler']
|
||||
}
|
||||
|
||||
task jaxbToJava {
|
||||
@@ -383,9 +372,8 @@ task soyToJava {
|
||||
// Relative paths of soy directories.
|
||||
def spec11SoyDir = "google/registry/reporting/spec11/soy"
|
||||
def toolsSoyDir = "google/registry/tools/soy"
|
||||
def registrarSoyDir = "google/registry/ui/soy/registrar"
|
||||
|
||||
def soyRelativeDirs = [spec11SoyDir, toolsSoyDir, registrarSoyDir]
|
||||
def soyRelativeDirs = [spec11SoyDir, toolsSoyDir]
|
||||
soyRelativeDirs.each {
|
||||
inputs.dir "${resourcesSourceDir}/${it}"
|
||||
outputs.dir "${generatedDir}/${it}"
|
||||
@@ -398,9 +386,7 @@ task soyToJava {
|
||||
args "--javaPackage", "${javaPackage}",
|
||||
"--outputDirectory", "${outputDirectory}",
|
||||
"--javaClassNameSource", "filename",
|
||||
"--allowExternalCalls", "true",
|
||||
"--srcs", "${soyFiles.join(',')}",
|
||||
"--compileTimeGlobalsFile", "${resourcesSourceDir}/google/registry/ui/globals.txt"
|
||||
"--srcs", "${soyFiles.join(',')}"
|
||||
}
|
||||
|
||||
// Replace the "@link" tags after the "@deprecated" tags in the generated
|
||||
@@ -427,12 +413,6 @@ task soyToJava {
|
||||
dir: "${resourcesSourceDir}/${toolsSoyDir}",
|
||||
include: ['**/*.soy']))
|
||||
|
||||
soyToJava('google.registry.ui.soy.registrar',
|
||||
"${generatedDir}/${registrarSoyDir}",
|
||||
fileTree(
|
||||
dir: "${resourcesSourceDir}/${registrarSoyDir}",
|
||||
include: ['**/*.soy']))
|
||||
|
||||
soyToJava('google.registry.reporting.spec11.soy',
|
||||
"${generatedDir}/${spec11SoyDir}",
|
||||
fileTree(
|
||||
@@ -441,134 +421,8 @@ task soyToJava {
|
||||
}
|
||||
}
|
||||
|
||||
task soyToJS(type: JavaExec) {
|
||||
def rootSoyDirectory = "${resourcesSourceDir}/google/registry/ui/soy/registrar"
|
||||
def outputSoyDirectory = "${generatedDir}/google/registry/ui/soy/registrar"
|
||||
inputs.dir rootSoyDirectory
|
||||
outputs.dir outputSoyDirectory
|
||||
|
||||
def inputSoyFiles = files {
|
||||
file("${rootSoyDirectory}").listFiles()
|
||||
}.filter {
|
||||
it.name.endsWith(".soy")
|
||||
}
|
||||
|
||||
classpath configurations.soy
|
||||
main = "com.google.template.soy.SoyToJsSrcCompiler"
|
||||
args "--outputPathFormat", "${outputSoyDirectory}/{INPUT_FILE_NAME}.js",
|
||||
"--allowExternalCalls", "false",
|
||||
"--srcs", "${inputSoyFiles.join(',')}",
|
||||
"--compileTimeGlobalsFile", "${resourcesSourceDir}/google/registry/ui/globals.txt"
|
||||
}
|
||||
|
||||
task stylesheetsToJavascript {
|
||||
def cssSourceDir = "${jsDir}/google/registry/ui/css"
|
||||
def outputDir = "${resourcesDir}/google/registry/ui/css"
|
||||
inputs.dir cssSourceDir
|
||||
outputs.dir outputDir
|
||||
|
||||
ext.cssCompile = { outputName, debug, cssFiles ->
|
||||
javaexec {
|
||||
main = "com.google.common.css.compiler.commandline.ClosureCommandLineCompiler"
|
||||
classpath configurations.css
|
||||
|
||||
def argsBuffer = [
|
||||
"--output-file", "${outputName}.css",
|
||||
"--output-source-map", "${outputName}.css.map",
|
||||
"--input-orientation", "LTR",
|
||||
"--output-orientation", "NOCHANGE",
|
||||
"--output-renaming-map", "${outputName}.css.js",
|
||||
"--output-renaming-map-format", "CLOSURE_COMPILED_SPLIT_HYPHENS"
|
||||
]
|
||||
if (debug) {
|
||||
argsBuffer.addAll(["--rename", "DEBUG", "--pretty-print"])
|
||||
} else {
|
||||
argsBuffer.addAll(["--rename", "CLOSURE"])
|
||||
}
|
||||
|
||||
argsBuffer.addAll(cssFiles)
|
||||
args argsBuffer
|
||||
}
|
||||
}
|
||||
|
||||
doLast {
|
||||
file("${outputDir}").mkdirs()
|
||||
def ignoredFiles = ["demo_css.css", "registrar_imports_raw.css"]
|
||||
def sourceFiles = []
|
||||
// include all CSS files that we find except for the ones explicitly ignored
|
||||
fileTree(cssSourceDir).each {
|
||||
if (it.name.endsWith(".css") && !ignoredFiles.contains(it.name)) {
|
||||
sourceFiles << (cssSourceDir + "/" + it.name)
|
||||
}
|
||||
}
|
||||
|
||||
// The css files have to be passed to the compiler in alphabetic order to
|
||||
// avoid some flaky style issues
|
||||
sourceFiles.sort()
|
||||
|
||||
cssCompile("${outputDir}/registrar_bin", false, sourceFiles)
|
||||
cssCompile("${outputDir}/registrar_dbg", true, sourceFiles)
|
||||
}
|
||||
}
|
||||
|
||||
task compileProdJS(type: JavaExec) {
|
||||
def outputDir = "${resourcesDir}/google/registry/ui"
|
||||
def nodeModulesDir = "${rootDir}/node_modules"
|
||||
def cssSourceDir = "${resourcesDir}/google/registry/ui/css"
|
||||
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 {
|
||||
inputs.dir "${it}"
|
||||
}
|
||||
outputs.dir outputDir
|
||||
|
||||
classpath configurations.closureCompiler
|
||||
main = 'com.google.javascript.jscomp.CommandLineRunner'
|
||||
|
||||
def closureArgs = []
|
||||
closureArgs << "--js_output_file=${outputDir}/registrar_bin.js"
|
||||
// sourcemap-related configuration
|
||||
closureArgs << "--create_source_map=${outputDir}/registrar_bin.js.map"
|
||||
closureArgs << "--source_map_include_content=true"
|
||||
closureArgs << "--source_map_location_mapping=${rootDir}/|"
|
||||
closureArgs << "--output_wrapper=\"%output% //# sourceMappingURL=registrar_bin.js.map\""
|
||||
|
||||
// compilation options
|
||||
closureArgs << "--compilation_level=ADVANCED"
|
||||
closureArgs << "--entry_point=goog:registry.registrar.main"
|
||||
closureArgs << "--generate_exports"
|
||||
|
||||
// manually include all the required js files
|
||||
closureArgs << "--js=${nodeModulesDir}/google-closure-library/**/*.js"
|
||||
closureArgs << "--js=${jsDir}/*.js"
|
||||
closureArgs << "--js=${cssSourceDir}/registrar_bin.css.js"
|
||||
closureArgs << "--js=${jsSourceDir}/**.js"
|
||||
closureArgs << "--js=${externsDir}/json.js"
|
||||
closureArgs << "--js=${soySourceDir}/**.js"
|
||||
args closureArgs
|
||||
}
|
||||
compileProdJS.dependsOn soyToJava
|
||||
|
||||
compileJava.dependsOn jaxbToJava
|
||||
compileJava.dependsOn soyToJava
|
||||
// The Closure JS compiler does not support Windows. It is fine to disable it if
|
||||
// all we want to do is to complile the Java code on Windows.
|
||||
if (!System.properties['os.name'].toLowerCase().contains('windows')) {
|
||||
compileJava.dependsOn compileProdJS
|
||||
assemble.dependsOn compileProdJS
|
||||
}
|
||||
|
||||
// stylesheetsToJavascript must happen after processResources, which wipes the
|
||||
// resources folder before copying data into it.
|
||||
stylesheetsToJavascript.dependsOn processResources
|
||||
classes.dependsOn stylesheetsToJavascript
|
||||
compileProdJS.dependsOn stylesheetsToJavascript
|
||||
compileProdJS.dependsOn rootProject.npmInstall
|
||||
compileProdJS.dependsOn processResources
|
||||
compileProdJS.dependsOn processTestResources
|
||||
compileProdJS.dependsOn soyToJS
|
||||
|
||||
// Make testing artifacts available to be depended up on by other projects.
|
||||
// TODO: factor out google.registry.testing to be a separate project.
|
||||
@@ -829,16 +683,7 @@ artifacts {
|
||||
devtool devtool
|
||||
}
|
||||
|
||||
task copyJsFilesForTestServer(dependsOn: assemble, type: Copy) {
|
||||
// Unfortunately the test server relies on having some compiled JS/CSS
|
||||
// in place, so copy it over here
|
||||
from "${resourcesDir}/google/registry/ui/"
|
||||
include '**/*.js'
|
||||
include '**/*.css'
|
||||
into "${project.projectDir}/src/main/resources/google/registry/ui/"
|
||||
}
|
||||
|
||||
task runTestServer(dependsOn: copyJsFilesForTestServer, type: JavaExec) {
|
||||
task runTestServer(type: JavaExec) {
|
||||
main = 'google.registry.server.RegistryTestServerMain'
|
||||
classpath = sourceSets.test.runtimeClasspath
|
||||
}
|
||||
@@ -910,6 +755,9 @@ task fragileTest(type: FilteringTest) {
|
||||
// Common exclude pattern. See README in parent directory for explanation.
|
||||
tests = fragileTestPatterns
|
||||
|
||||
// Screenshot tests depend on the console being built
|
||||
dependsOn(rootProject.project('console-webapp').tasks.named('buildConsoleWebapp'))
|
||||
|
||||
if (rootProject.findProperty("skipDockerIncompatibleTests") == "true") {
|
||||
exclude dockerIncompatibleTestPatterns
|
||||
}
|
||||
|
||||
@@ -2,19 +2,16 @@
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
aopalliance:aopalliance:1.0=annotationProcessor,compileClasspath,deploy_jar,errorprone,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
args4j:args4j:2.0.23=soy
|
||||
args4j:args4j:2.0.26=css
|
||||
args4j:args4j:2.33=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
cglib:cglib-nodep:2.2=css
|
||||
args4j:args4j:2.33=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
|
||||
com.charleskorn.kaml:kaml:0.20.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.core:jackson-annotations:2.18.0-rc1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.core:jackson-core:2.18.0-rc1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.core:jackson-databind:2.18.0-rc1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-toml:2.18.0-rc1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.18.0-rc1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.datatype:jackson-datatype-joda:2.18.0-rc1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.0-rc1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson:jackson-bom:2.18.0-rc1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.core:jackson-annotations:2.18.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.core:jackson-core:2.18.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.core:jackson-databind:2.18.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-toml:2.18.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.18.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.datatype:jackson-datatype-joda:2.18.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson:jackson-bom:2.18.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml:classmate:1.5.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.github.ben-manes.caffeine:caffeine:3.0.5=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.github.ben-manes.caffeine:caffeine:3.1.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -38,7 +35,7 @@ com.google.api-client:google-api-client-servlet:2.2.0=testRuntimeClasspath
|
||||
com.google.api-client:google-api-client-servlet:2.7.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath
|
||||
com.google.api-client:google-api-client:2.7.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:gapic-google-cloud-storage-v2:2.32.1-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
com.google.api.grpc:gapic-google-cloud-storage-v2:2.43.0-beta=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:gapic-google-cloud-storage-v2:2.43.2-beta=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1:3.5.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.177.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta2:0.177.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -49,8 +46,8 @@ com.google.api.grpc:grpc-google-cloud-spanner-admin-database-v1:6.66.0=compileCl
|
||||
com.google.api.grpc:grpc-google-cloud-spanner-admin-instance-v1:6.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-spanner-v1:6.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-storage-v2:2.32.1-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-storage-v2:2.43.0-beta=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-common-protos:2.44.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-storage-v2:2.43.2-beta=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-common-protos:2.39.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1:3.5.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta1:0.177.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta2:0.177.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -62,29 +59,29 @@ com.google.api.grpc:proto-google-cloud-monitoring-v3:3.44.0=compileClasspath,dep
|
||||
com.google.api.grpc:proto-google-cloud-pubsub-v1:1.111.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-pubsublite-v1:1.13.5=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-secretmanager-v1:2.29.0=testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-secretmanager-v1:2.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-secretmanager-v1:2.52.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:2.29.0=testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta2:2.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta2:2.52.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:6.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:6.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-spanner-executor-v1:6.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-spanner-v1:6.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-storage-v2:2.32.1-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-storage-v2:2.43.0-beta=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-storage-v2:2.43.2-beta=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2:2.29.0=testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2:2.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2:2.52.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.119.0=testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.139.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.142.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.119.0=testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.139.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-common-protos:2.44.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-iam-v1:1.39.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api:api-common:2.36.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api:gax-grpc:2.53.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api:gax-httpjson:2.53.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api:gax:2.53.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-admin-directory:directory_v1-rev20240917-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-bigquery:v2-rev20240905-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.142.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-common-protos:2.46.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-iam-v1:1.41.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api:api-common:2.38.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api:gax-grpc:2.55.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api:gax-httpjson:2.55.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api:gax:2.55.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-admin-directory:directory_v1-rev20240924-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-bigquery:v2-rev20240919-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-cloudresourcemanager:v1-rev20240310-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-dataflow:v1b3-rev20240817-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-dns:v1-rev20240719-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -94,14 +91,14 @@ com.google.apis:google-api-services-groupssettings:v1-rev20220614-2.0.0=compileC
|
||||
com.google.apis:google-api-services-healthcare:v1-rev20240130-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-iam:v2-rev20240530-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-iamcredentials:v1-rev20211203-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-monitoring:v3-rev20240829-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-monitoring:v3-rev20240929-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-pubsub:v1-rev20220904-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-sheets:v4-rev20240826-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-sqladmin:v1beta4-rev20240724-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-sheets:v4-rev20241001-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-sqladmin:v1beta4-rev20240925-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-storage:v1-rev20240319-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
com.google.apis:google-api-services-storage:v1-rev20240819-2.0.0=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.auth:google-auth-library-credentials:1.25.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.auth:google-auth-library-oauth2-http:1.25.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-storage:v1-rev20240924-2.0.0=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.auth:google-auth-library-credentials:1.28.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.auth:google-auth-library-oauth2-http:1.28.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.auto.service:auto-service-annotations:1.0.1=errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.auto.service:auto-service-annotations:1.1.1=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.auto.service:auto-service:1.1.1=annotationProcessor
|
||||
@@ -110,75 +107,76 @@ com.google.auto.value:auto-value-annotations:1.9=annotationProcessor,errorprone,
|
||||
com.google.auto.value:auto-value:1.10.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.auto.value:auto-value:1.11.0=annotationProcessor,testAnnotationProcessor
|
||||
com.google.auto:auto-common:1.2.1=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.closure-stylesheets:closure-stylesheets:1.5.0=css
|
||||
com.google.cloud.bigdataoss:gcsio:2.2.16=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.bigdataoss:util:2.2.16=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.bigtable:bigtable-client-core-config:1.28.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.datastore:datastore-v1-proto-client:2.19.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.opentelemetry:detector-resources-support:0.28.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath
|
||||
com.google.cloud.opentelemetry:detector-resources-support:0.31.0=testRuntimeClasspath
|
||||
com.google.cloud.opentelemetry:detector-resources-support:0.32.0=testRuntimeClasspath
|
||||
com.google.cloud.opentelemetry:exporter-metrics:0.31.0=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.opentelemetry:shared-resourcemapping:0.31.0=testRuntimeClasspath
|
||||
com.google.cloud.sql:jdbc-socket-factory-core:1.20.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.sql:postgres-socket-factory:1.20.1=deploy_jar,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud.opentelemetry:shared-resourcemapping:0.32.0=testRuntimeClasspath
|
||||
com.google.cloud.sql:jdbc-socket-factory-core:1.21.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.sql:postgres-socket-factory:1.21.0=deploy_jar,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-bigquerystorage:3.5.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-bigtable:2.39.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-core-grpc:2.38.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
com.google.cloud:google-cloud-core-grpc:2.43.0=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-core-grpc:2.45.0=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-core-http:2.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
com.google.cloud:google-cloud-core-http:2.43.0=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-core-http:2.45.0=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-core:2.38.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
com.google.cloud:google-cloud-core:2.43.0=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-core:2.45.0=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-firestore:3.21.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-monitoring:3.44.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-nio:0.127.23=testCompileClasspath
|
||||
com.google.cloud:google-cloud-nio:0.127.25=testCompileClasspath
|
||||
com.google.cloud:google-cloud-nio:0.127.6=testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-pubsub:1.129.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-pubsublite:1.13.5=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-secretmanager:2.29.0=testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-secretmanager:2.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath
|
||||
com.google.cloud:google-cloud-secretmanager:2.52.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath
|
||||
com.google.cloud:google-cloud-spanner:6.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-storage:2.32.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
com.google.cloud:google-cloud-storage:2.43.0=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-storage:2.43.2=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-tasks:2.29.0=testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-tasks:2.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath
|
||||
com.google.cloud:google-cloud-tasks:2.52.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath
|
||||
com.google.cloud:grpc-gcp:1.5.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:libraries-bom:26.26.0=testRuntimeClasspath
|
||||
com.google.cloud:proto-google-cloud-firestore-bundle-v1:3.21.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.code.findbugs:jsr305:3.0.1=css
|
||||
com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,checkstyle,compileClasspath,deploy_jar,errorprone,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.code.gson:gson:2.10.1=soy
|
||||
com.google.code.gson:gson:2.11.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.code.gson:gson:2.7=css,soy
|
||||
com.google.common.html.types:types:1.0.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.common.html.types:types:1.0.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.dagger:dagger-compiler:2.51.1=annotationProcessor,testAnnotationProcessor
|
||||
com.google.dagger:dagger-spi:2.51.1=annotationProcessor,testAnnotationProcessor
|
||||
com.google.dagger:dagger:2.51.1=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.devtools.ksp:symbol-processing-api:1.9.20-1.0.14=annotationProcessor,testAnnotationProcessor
|
||||
com.google.errorprone:error_prone_annotation:2.23.0=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.errorprone:error_prone_annotations:2.20.0=soy
|
||||
com.google.errorprone:error_prone_annotations:2.23.0=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.errorprone:error_prone_annotations:2.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.errorprone:error_prone_annotations:2.7.1=checkstyle,soy
|
||||
com.google.errorprone:error_prone_annotations:2.33.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.errorprone:error_prone_annotations:2.7.1=checkstyle
|
||||
com.google.errorprone:error_prone_check_api:2.23.0=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.errorprone:error_prone_core:2.23.0=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.errorprone:error_prone_type_annotations:2.23.0=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.errorprone:javac-shaded:9-dev-r4023-3=annotationProcessor,testAnnotationProcessor
|
||||
com.google.errorprone:javac:9+181-r4173-1=errorproneJavac
|
||||
com.google.escapevelocity:escapevelocity:0.9.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.escapevelocity:escapevelocity:1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.flatbuffers:flatbuffers-java:23.5.26=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.flogger:flogger-system-backend:0.7.4=soy
|
||||
com.google.flogger:flogger-system-backend:0.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.flogger:flogger:0.7.4=soy
|
||||
com.google.flogger:flogger:0.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.flogger:google-extensions:0.7.4=soy
|
||||
com.google.flogger:google-extensions:0.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.googlejavaformat:google-java-format:1.5=annotationProcessor,testAnnotationProcessor
|
||||
com.google.guava:failureaccess:1.0.1=checkstyle,errorprone,nonprodAnnotationProcessor,soy
|
||||
com.google.guava:failureaccess:1.0.2=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.guava:guava-parent:32.1.1-jre=errorprone,nonprodAnnotationProcessor
|
||||
com.google.guava:guava-testlib:33.3.0-jre=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.guava:guava:20.0=css
|
||||
com.google.guava:guava:31.0.1-jre=checkstyle,soy
|
||||
com.google.guava:guava:32.1.1-jre=errorprone,nonprodAnnotationProcessor
|
||||
com.google.guava:guava-parent:32.1.1-jre=errorprone,nonprodAnnotationProcessor,soy
|
||||
com.google.guava:guava-testlib:33.3.1-jre=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.guava:guava:31.0.1-jre=checkstyle
|
||||
com.google.guava:guava:32.1.1-jre=errorprone,nonprodAnnotationProcessor,soy
|
||||
com.google.guava:guava:33.0.0-jre=annotationProcessor,testAnnotationProcessor
|
||||
com.google.guava:guava:33.3.0-jre=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.guava:guava:33.3.1-jre=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.gwt:gwt-user:2.10.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client-apache-v2:1.45.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client-appengine:1.43.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
@@ -188,15 +186,10 @@ com.google.http-client:google-http-client-jackson2:1.43.3=compileClasspath,deplo
|
||||
com.google.http-client:google-http-client-jackson2:1.45.0=testCompileClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client-protobuf:1.44.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client:1.45.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.inject.extensions:guice-multibindings:4.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.inject:guice:4.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.inject:guice:5.1.0=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.inject:guice:7.0.0=soy
|
||||
com.google.j2objc:j2objc-annotations:1.3=checkstyle,soy
|
||||
com.google.inject:guice:7.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.j2objc:j2objc-annotations:1.3=checkstyle
|
||||
com.google.j2objc:j2objc-annotations:3.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.javascript:closure-compiler-externs:v20160713=css
|
||||
com.google.javascript:closure-compiler-unshaded:v20160713=css
|
||||
com.google.javascript:closure-compiler:v20210505=closureCompiler
|
||||
com.google.jsinterop:jsinterop-annotations:1.0.1=soy
|
||||
com.google.jsinterop:jsinterop-annotations:2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.monitoring-client:contrib:1.0.7=testCompileClasspath,testRuntimeClasspath
|
||||
@@ -208,16 +201,14 @@ com.google.oauth-client:google-oauth-client-jetty:1.36.0=compileClasspath,deploy
|
||||
com.google.oauth-client:google-oauth-client-servlet:1.34.1=testRuntimeClasspath
|
||||
com.google.oauth-client:google-oauth-client-servlet:1.36.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath
|
||||
com.google.oauth-client:google-oauth-client:1.36.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.protobuf:protobuf-java-util:3.25.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.protobuf:protobuf-java:2.5.0=css
|
||||
com.google.protobuf:protobuf-java-util:3.25.5=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.protobuf:protobuf-java:3.19.6=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.protobuf:protobuf-java:3.25.5=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.re2j:re2j:1.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.template:soy:2021-02-01=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.template:soy:2024-02-26=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.truth:truth:1.4.4=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.googlecode.json-simple:json-simple:1.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.ibm.icu:icu4j:57.1=soy
|
||||
com.ibm.icu:icu4j:73.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.ibm.icu:icu4j:73.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
|
||||
com.jcraft:jsch:0.1.55=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.lmax:disruptor:3.4.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.puppycrawl.tools:checkstyle:9.3=checkstyle
|
||||
@@ -247,14 +238,15 @@ com.sun.istack:istack-commons-tools:4.1.2=jaxb
|
||||
com.sun.xml.bind.external:relaxng-datatype:4.0.5=jaxb
|
||||
com.sun.xml.bind.external:rngom:4.0.5=jaxb
|
||||
com.sun.xml.dtd-parser:dtd-parser:1.5.1=jaxb
|
||||
com.zaxxer:HikariCP:5.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.zaxxer:HikariCP:6.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
commons-beanutils:commons-beanutils:1.9.4=checkstyle
|
||||
commons-codec:commons-codec:1.17.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
commons-collections:commons-collections:3.2.2=checkstyle
|
||||
commons-dbutils:commons-dbutils:1.8.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
commons-io:commons-io:2.16.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
commons-io:commons-io:2.17.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
commons-logging:commons-logging:1.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
dnsjava:dnsjava:3.6.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
dev.failsafe:failsafe:3.3.2=testCompileClasspath,testRuntimeClasspath
|
||||
dnsjava:dnsjava:3.6.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
guru.nidi.com.eclipsesource.j2v8:j2v8_linux_x86_64:4.6.0=testRuntimeClasspath
|
||||
guru.nidi.com.eclipsesource.j2v8:j2v8_macosx_x86_64:4.6.0=testRuntimeClasspath
|
||||
guru.nidi.com.eclipsesource.j2v8:j2v8_win32_x86:4.6.0=testRuntimeClasspath
|
||||
@@ -267,29 +259,29 @@ io.apicurio:apicurio-registry-protobuf-schema-utilities:3.0.0.M2=compileClasspat
|
||||
io.github.classgraph:classgraph:4.8.162=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.github.eisop:dataflow-errorprone:3.34.0-eisop1=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,deploy_jar,errorprone,nonprodAnnotationProcessor,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-alts:1.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-api:1.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-auth:1.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-alts:1.67.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-api:1.67.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-auth:1.67.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-census:1.62.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-context:1.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-core:1.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-googleapis:1.66.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-grpclb:1.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-inprocess:1.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-netty-shaded:1.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-context:1.67.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-core:1.67.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-googleapis:1.67.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-grpclb:1.67.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-inprocess:1.67.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-netty-shaded:1.67.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-netty:1.62.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-opentelemetry:1.66.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-protobuf-lite:1.66.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-protobuf:1.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-opentelemetry:1.67.1=testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-protobuf-lite:1.67.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-protobuf:1.67.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-rls:1.62.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.grpc:grpc-rls:1.66.0=testRuntimeClasspath
|
||||
io.grpc:grpc-rls:1.67.1=testRuntimeClasspath
|
||||
io.grpc:grpc-services:1.62.2=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
io.grpc:grpc-services:1.66.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-stub:1.66.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-services:1.67.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-stub:1.67.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-util:1.62.2=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
io.grpc:grpc-util:1.66.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-util:1.67.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-xds:1.62.2=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
io.grpc:grpc-xds:1.66.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-xds:1.67.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.netty:netty-buffer:4.1.100.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-codec-http2:4.1.100.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-codec-http:4.1.100.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -318,23 +310,25 @@ io.opencensus:opencensus-proto:0.2.0=compileClasspath,deploy_jar,nonprodCompileC
|
||||
io.opentelemetry.contrib:opentelemetry-gcp-resources:1.37.0-alpha=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry.semconv:opentelemetry-semconv:1.25.0-alpha=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-api-incubator:1.37.0-alpha=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-api-incubator:1.42.0-alpha=testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-api-incubator:1.42.1-alpha=testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-api:1.37.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-api:1.42.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-api:1.42.1=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-bom:1.33.0=testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-context:1.37.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-context:1.42.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-context:1.42.1=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-exporter-logging:1.42.1=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-common:1.37.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-common:1.42.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.42.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-common:1.42.1=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.42.1=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.42.1=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-logs:1.37.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-logs:1.42.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-logs:1.42.1=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-metrics:1.37.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-metrics:1.42.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-metrics:1.42.1=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-trace:1.37.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-trace:1.42.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-trace:1.42.1=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk:1.37.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk:1.42.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk:1.42.1=testCompileClasspath,testRuntimeClasspath
|
||||
io.outfoxx:swiftpoet:1.3.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.perfmark:perfmark-api:0.27.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.smallrye:jandex:3.1.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
@@ -348,21 +342,19 @@ jakarta.servlet:jakarta.servlet-api:6.0.0=testCompileClasspath,testRuntimeClassp
|
||||
jakarta.servlet:jakarta.servlet-api:6.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
jakarta.transaction:jakarta.transaction-api:2.0.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
jakarta.xml.bind:jakarta.xml.bind-api:4.0.2=compileClasspath,deploy_jar,jaxb,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
javacc:javacc:4.1=css
|
||||
javax.annotation:javax.annotation-api:1.3.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
javax.annotation:jsr250-api:1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
|
||||
javax.inject:javax.inject:1=annotationProcessor,compileClasspath,deploy_jar,errorprone,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
javax.inject:javax.inject:1=annotationProcessor,compileClasspath,deploy_jar,errorprone,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
javax.jdo:jdo2-api:2.3-20090302111651=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
javax.servlet:servlet-api:2.5=testRuntimeClasspath
|
||||
javax.validation:validation-api:1.0.0.GA=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
joda-time:joda-time:2.12.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
junit:junit:4.13.2=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
net.arnx:nashorn-promise:0.1.1=testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy-agent:1.15.0=testCompileClasspath,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy-agent:1.15.3=testCompileClasspath,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.12=compileClasspath,nonprodCompileClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.15=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.15.0=testCompileClasspath,testRuntimeClasspath
|
||||
net.java.dev.javacc:javacc:4.1=css
|
||||
net.bytebuddy:byte-buddy:1.15.3=testCompileClasspath,testRuntimeClasspath
|
||||
net.java.dev.jna:jna:5.13.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
net.ltgt.gradle.incap:incap:0.2=annotationProcessor,testAnnotationProcessor
|
||||
net.sf.saxon:Saxon-HE:10.6=checkstyle
|
||||
@@ -375,7 +367,7 @@ org.antlr:antlr4:4.13.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonp
|
||||
org.apache.arrow:arrow-format:15.0.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.arrow:arrow-memory-core:15.0.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.arrow:arrow-vector:15.0.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.avro:avro:1.11.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.avro:avro:1.12.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-model-fn-execution:2.59.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-model-job-management:2.59.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-model-pipeline:2.59.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -397,8 +389,8 @@ org.apache.beam:beam-sdks-java-transform-service-launcher:2.59.0=compileClasspat
|
||||
org.apache.beam:beam-vendor-grpc-1_60_1:0.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-vendor-guava-32_1_2-jre:0.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.commons:commons-compress:1.26.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.commons:commons-csv:1.11.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.commons:commons-exec:1.3=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.commons:commons-csv:1.12.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.commons:commons-exec:1.4.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.commons:commons-lang3:3.14.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.commons:commons-text:1.12.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.ftpserver:ftplet-api:1.2.0=testCompileClasspath,testRuntimeClasspath
|
||||
@@ -406,42 +398,41 @@ org.apache.ftpserver:ftpserver-core:1.2.0=testCompileClasspath,testRuntimeClassp
|
||||
org.apache.httpcomponents:httpclient:4.5.14=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.httpcomponents:httpcore:4.4.16=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.mina:mina-core:2.1.6=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.sshd:sshd-common:2.13.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.sshd:sshd-core:2.13.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.sshd:sshd-scp:2.13.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.sshd:sshd-sftp:2.13.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.tomcat:tomcat-annotations-api:11.0.0-M26=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.sshd:sshd-common:2.14.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.sshd:sshd-core:2.14.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.sshd:sshd-scp:2.14.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.sshd:sshd-sftp:2.14.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.tomcat:tomcat-annotations-api:11.0.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
|
||||
org.bouncycastle:bcpg-jdk18on:1.78.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.bouncycastle:bcpkix-jdk18on:1.78.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.bouncycastle:bcprov-jdk18on:1.78.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.bouncycastle:bcutil-jdk18on:1.78.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.checkerframework:checker-compat-qual:2.5.3=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
org.checkerframework:checker-compat-qual:2.5.3=compileClasspath,nonprodCompileClasspath,soy,testCompileClasspath
|
||||
org.checkerframework:checker-compat-qual:2.5.5=annotationProcessor,testAnnotationProcessor
|
||||
org.checkerframework:checker-compat-qual:2.5.6=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.checkerframework:checker-qual:3.12.0=checkstyle,soy
|
||||
org.checkerframework:checker-qual:3.33.0=errorprone,nonprodAnnotationProcessor
|
||||
org.checkerframework:checker-qual:3.12.0=checkstyle
|
||||
org.checkerframework:checker-qual:3.33.0=errorprone,nonprodAnnotationProcessor,soy
|
||||
org.checkerframework:checker-qual:3.41.0=annotationProcessor,testAnnotationProcessor
|
||||
org.checkerframework:checker-qual:3.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.24=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.conscrypt:conscrypt-openjdk-uber:2.5.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.easymock:easymock:3.0=css
|
||||
org.eclipse.angus:angus-activation:2.0.2=deploy_jar,jaxb,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.eclipse.angus:jakarta.mail:2.0.3=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.eclipse.collections:eclipse-collections-api:11.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.collections:eclipse-collections:11.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty.ee10:jetty-ee10-servlet:12.0.13=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty.ee10:jetty-ee10-webapp:12.0.13=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-ee:12.0.13=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-http:12.0.13=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-io:12.0.13=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-security:12.0.13=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-server:12.0.13=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-session:12.0.13=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-util:12.0.13=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-xml:12.0.13=testCompileClasspath,testRuntimeClasspath
|
||||
org.flywaydb:flyway-core:10.18.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.flywaydb:flyway-database-postgresql:10.18.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty.ee10:jetty-ee10-servlet:12.0.14=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty.ee10:jetty-ee10-webapp:12.0.14=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-ee:12.0.14=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-http:12.0.14=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-io:12.0.14=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-security:12.0.14=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-server:12.0.14=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-session:12.0.14=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-util:12.0.14=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-xml:12.0.14=testCompileClasspath,testRuntimeClasspath
|
||||
org.flywaydb:flyway-core:10.19.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.flywaydb:flyway-database-postgresql:10.19.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.glassfish.jaxb:codemodel:4.0.5=jaxb
|
||||
org.glassfish.jaxb:jaxb-core:4.0.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.glassfish.jaxb:jaxb-core:4.0.5=jaxb
|
||||
@@ -452,7 +443,6 @@ org.glassfish.jaxb:txw2:4.0.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspat
|
||||
org.glassfish.jaxb:txw2:4.0.5=jaxb
|
||||
org.glassfish.jaxb:xsom:4.0.5=jaxb
|
||||
org.gwtproject:gwt-user:2.10.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.hamcrest:hamcrest-core:1.1=css
|
||||
org.hamcrest:hamcrest-core:1.3=nonprodCompileClasspath,nonprodRuntimeClasspath
|
||||
org.hamcrest:hamcrest-core:3.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.hamcrest:hamcrest-library:3.0=testCompileClasspath,testRuntimeClasspath
|
||||
@@ -487,69 +477,71 @@ org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.1=deploy_jar,nonprodRuntime
|
||||
org.jetbrains:annotations:13.0=annotationProcessor,testAnnotationProcessor
|
||||
org.jetbrains:annotations:17.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jline:jline:3.27.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.joda:joda-money:1.0.5=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.json:json:20160212=soy
|
||||
org.joda:joda-money:2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.json:json:20230618=soy
|
||||
org.json:json:20240303=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jsoup:jsoup:1.18.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jspecify:jspecify:0.3.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.junit-pioneer:junit-pioneer:2.2.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-api:5.11.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-engine:5.11.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-migrationsupport:5.11.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-params:5.11.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-commons:1.11.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-engine:1.11.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-launcher:1.11.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-runner:1.11.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-suite-api:1.11.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-suite-commons:1.11.0=testRuntimeClasspath
|
||||
org.junit:junit-bom:5.11.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.mockito:mockito-core:1.10.19=css
|
||||
org.mockito:mockito-core:5.13.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.mockito:mockito-junit-jupiter:5.13.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.objenesis:objenesis:2.1=css
|
||||
org.jspecify:jspecify:0.3.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
|
||||
org.jspecify:jspecify:1.0.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit-pioneer:junit-pioneer:2.3.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-api:5.11.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-engine:5.11.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-migrationsupport:5.11.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-params:5.11.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-commons:1.11.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-engine:1.11.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-launcher:1.11.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-runner:1.11.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-suite-api:1.11.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-suite-commons:1.11.2=testRuntimeClasspath
|
||||
org.junit:junit-bom:5.11.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.mockito:mockito-core:5.14.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.mockito:mockito-junit-jupiter:5.14.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.objenesis:objenesis:3.3=testRuntimeClasspath
|
||||
org.ogce:xpp3:1.1.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.ow2.asm:asm-analysis:7.0=soy
|
||||
org.ow2.asm:asm-analysis:9.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.ow2.asm:asm-commons:7.0=soy
|
||||
org.ow2.asm:asm-commons:9.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.ow2.asm:asm-analysis:9.5=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
|
||||
org.ow2.asm:asm-commons:9.5=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
|
||||
org.ow2.asm:asm-commons:9.7=jacocoAnt
|
||||
org.ow2.asm:asm-tree:7.0=soy
|
||||
org.ow2.asm:asm-tree:9.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.ow2.asm:asm-tree:9.5=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
|
||||
org.ow2.asm:asm-tree:9.7=jacocoAnt
|
||||
org.ow2.asm:asm-util:7.0=soy
|
||||
org.ow2.asm:asm-util:9.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.ow2.asm:asm:7.0=soy
|
||||
org.ow2.asm:asm:9.2=compileClasspath,nonprodCompileClasspath
|
||||
org.ow2.asm:asm-util:9.5=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
|
||||
org.ow2.asm:asm:9.5=compileClasspath,nonprodCompileClasspath,soy
|
||||
org.ow2.asm:asm:9.7=deploy_jar,jacocoAnt,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.pcollections:pcollections:3.1.4=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
org.postgresql:postgresql:42.7.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.reflections:reflections:0.10.2=checkstyle
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-api:3.141.59=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-chrome-driver:3.141.59=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-edge-driver:3.141.59=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-firefox-driver:3.141.59=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-ie-driver:3.141.59=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-java:3.141.59=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-opera-driver:3.141.59=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-remote-driver:3.141.59=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-safari-driver:3.141.59=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-support:3.141.59=testCompileClasspath,testRuntimeClasspath
|
||||
org.slf4j:jcl-over-slf4j:1.7.32=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-api:4.25.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-chrome-driver:4.25.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-chromium-driver:4.25.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-devtools-v127:4.25.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-devtools-v128:4.25.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-devtools-v129:4.25.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-devtools-v85:4.25.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-edge-driver:4.25.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-firefox-driver:4.25.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-http:4.25.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-ie-driver:4.25.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-java:4.25.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-json:4.25.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-manager:4.25.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-os:4.25.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-remote-driver:4.25.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-safari-driver:4.25.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-support:4.25.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.slf4j:jcl-over-slf4j:1.7.36=testCompileClasspath,testRuntimeClasspath
|
||||
org.slf4j:jul-to-slf4j:1.7.30=testRuntimeClasspath
|
||||
org.slf4j:slf4j-api:2.0.16=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.slf4j:slf4j-jdk14:2.0.13=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.snakeyaml:snakeyaml-engine:2.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.testcontainers:database-commons:1.20.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:jdbc:1.20.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:junit-jupiter:1.20.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:postgresql:1.20.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:selenium:1.20.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:testcontainers:1.20.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.threeten:threetenbp:1.6.9=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:database-commons:1.20.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:jdbc:1.20.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:junit-jupiter:1.20.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:postgresql:1.20.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:selenium:1.20.2=testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:testcontainers:1.20.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.threeten:threetenbp:1.7.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.w3c.css:sac:1.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.webjars.npm:viz.js-graphviz-java:2.1.3=testRuntimeClasspath
|
||||
org.xerial.snappy:snappy-java:1.1.10.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
|
||||
@@ -36,6 +36,7 @@ import google.registry.config.RegistryConfig.ConfigModule;
|
||||
import google.registry.flows.custom.CustomLogicFactoryModule;
|
||||
import google.registry.flows.custom.CustomLogicModule;
|
||||
import google.registry.flows.domain.DomainPricingLogic;
|
||||
import google.registry.flows.domain.DomainPricingLogic.AllocationTokenInvalidForCurrencyException;
|
||||
import google.registry.flows.domain.DomainPricingLogic.AllocationTokenInvalidForPremiumNameException;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingBase.Flag;
|
||||
@@ -414,7 +415,8 @@ public class ExpandBillingRecurrencesPipeline implements Serializable {
|
||||
.setCancellationMatchingBillingEvent(billingRecurrence)
|
||||
.setTargetId(billingRecurrence.getTargetId())
|
||||
.build();
|
||||
} catch (AllocationTokenInvalidForPremiumNameException e) {
|
||||
} catch (AllocationTokenInvalidForCurrencyException
|
||||
| AllocationTokenInvalidForPremiumNameException e) {
|
||||
// This should not be reached since we are not using an allocation token
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -46,8 +46,6 @@ import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
@@ -138,22 +136,10 @@ public final class RegistryConfig {
|
||||
return config.gcpProject.locationId;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The filename of the logo to be displayed in the header of the registrar console.
|
||||
*
|
||||
* @see google.registry.ui.server.registrar.ConsoleUiAction
|
||||
*/
|
||||
@Provides
|
||||
@Config("logoFilename")
|
||||
public static String provideLogoFilename(RegistryConfigSettings config) {
|
||||
return config.registrarConsole.logoFilename;
|
||||
}
|
||||
|
||||
/**
|
||||
* The product name of this specific registry. Used throughout the registrar console.
|
||||
*
|
||||
* @see google.registry.ui.server.registrar.ConsoleUiAction
|
||||
* @see google.registry.ui.server.console.ConsoleUserDataAction
|
||||
*/
|
||||
@Provides
|
||||
@Config("productName")
|
||||
@@ -180,23 +166,11 @@ public final class RegistryConfig {
|
||||
return config.misc.isEmailSendingEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* The e-mail address for questions about integrating with the registry. Used in the
|
||||
* "contact-us" section of the registrar console.
|
||||
*
|
||||
* @see google.registry.ui.server.registrar.ConsoleUiAction
|
||||
*/
|
||||
@Provides
|
||||
@Config("integrationEmail")
|
||||
public static String provideIntegrationEmail(RegistryConfigSettings config) {
|
||||
return config.registrarConsole.integrationEmailAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* The e-mail address for general support. Used in the "contact-us" section of the registrar
|
||||
* console.
|
||||
*
|
||||
* @see google.registry.ui.server.registrar.ConsoleUiAction
|
||||
* @see google.registry.ui.server.console.ConsoleUserDataAction
|
||||
*/
|
||||
@Provides
|
||||
@Config("supportEmail")
|
||||
@@ -204,18 +178,6 @@ public final class RegistryConfig {
|
||||
return config.registrarConsole.supportEmailAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* The "From" e-mail address for announcements. Used in the "contact-us" section of the
|
||||
* registrar console.
|
||||
*
|
||||
* @see google.registry.ui.server.registrar.ConsoleUiAction
|
||||
*/
|
||||
@Provides
|
||||
@Config("announcementsEmail")
|
||||
public static String provideAnnouncementsEmail(RegistryConfigSettings config) {
|
||||
return config.registrarConsole.announcementsEmailAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* The DUM file name, used as a file name base for DUM csv file
|
||||
*
|
||||
@@ -230,7 +192,7 @@ public final class RegistryConfig {
|
||||
/**
|
||||
* The contact phone number. Used in the "contact-us" section of the registrar console.
|
||||
*
|
||||
* @see google.registry.ui.server.registrar.ConsoleUiAction
|
||||
* @see google.registry.ui.server.console.ConsoleUserDataAction
|
||||
*/
|
||||
@Provides
|
||||
@Config("supportPhoneNumber")
|
||||
@@ -242,7 +204,7 @@ public final class RegistryConfig {
|
||||
* The URL for technical support docs. Used in the "contact-us" section of the registrar
|
||||
* console.
|
||||
*
|
||||
* @see google.registry.ui.server.registrar.ConsoleUiAction
|
||||
* @see google.registry.ui.server.console.ConsoleUserDataAction
|
||||
*/
|
||||
@Provides
|
||||
@Config("technicalDocsUrl")
|
||||
@@ -250,22 +212,6 @@ public final class RegistryConfig {
|
||||
return config.registrarConsole.technicalDocsUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration for analytics services installed in the web console.
|
||||
*
|
||||
* @see google.registry.ui.server.registrar.ConsoleUiAction
|
||||
* @see google.registry.ui.soy.registrar.AnalyticsSoyInfo
|
||||
*/
|
||||
@Provides
|
||||
@Config("analyticsConfig")
|
||||
public static Map<String, Object> provideAnalyticsConfig(RegistryConfigSettings config) {
|
||||
// Can't be an ImmutableMap because it may contain null values.
|
||||
HashMap<String, Object> analyticsConfig = new HashMap<>();
|
||||
analyticsConfig.put(
|
||||
"googleAnalyticsId", config.registrarConsole.analyticsConfig.googleAnalyticsId);
|
||||
return Collections.unmodifiableMap(analyticsConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Google Cloud Storage bucket for storing zone files.
|
||||
*
|
||||
@@ -520,7 +466,7 @@ public final class RegistryConfig {
|
||||
* Returns the email address(es) that notifications of registrar and/or registrar contact
|
||||
* updates should be sent to, or the empty list if updates should not be sent.
|
||||
*
|
||||
* @see google.registry.ui.server.registrar.RegistrarSettingsAction
|
||||
* @see google.registry.ui.server.SendEmailUtils
|
||||
*/
|
||||
@Provides
|
||||
@Config("registrarChangesNotificationEmailAddresses")
|
||||
@@ -914,17 +860,6 @@ public final class RegistryConfig {
|
||||
return URI.create(config.rde.uploadUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the registrar console is enabled.
|
||||
*
|
||||
* @see google.registry.ui.server.registrar.ConsoleUiAction
|
||||
*/
|
||||
@Provides
|
||||
@Config("registrarConsoleEnabled")
|
||||
public static boolean provideRegistrarConsoleEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximum amount of time for syncing a spreadsheet, before killing.
|
||||
*
|
||||
|
||||
@@ -189,18 +189,9 @@ public class RegistryConfigSettings {
|
||||
/** Configuration for the web-based registrar console. */
|
||||
public static class RegistrarConsole {
|
||||
public String dumFileName;
|
||||
public String logoFilename;
|
||||
public String supportPhoneNumber;
|
||||
public String supportEmailAddress;
|
||||
public String announcementsEmailAddress;
|
||||
public String integrationEmailAddress;
|
||||
public String technicalDocsUrl;
|
||||
public AnalyticsConfig analyticsConfig;
|
||||
}
|
||||
|
||||
/** Configuration for analytics services installed in the registrar console */
|
||||
public static class AnalyticsConfig {
|
||||
public String googleAnalyticsId;
|
||||
}
|
||||
|
||||
/** Configuration for monitoring. */
|
||||
|
||||
@@ -372,6 +372,11 @@ credentialOAuth:
|
||||
delegatedCredentialOauthScopes:
|
||||
# View and manage groups on your domain in Directory API.
|
||||
- https://www.googleapis.com/auth/admin.directory.group
|
||||
# View and manage users in Google Workspace
|
||||
- https://www.googleapis.com/auth/admin.directory.user
|
||||
# Security scope which seems to be required to create users via API,
|
||||
# based on https://github.com/googleapis/google-api-nodejs-client/issues/1884
|
||||
- https://www.googleapis.com/auth/admin.directory.user.security
|
||||
# View and manage group settings in Group Settings API.
|
||||
- https://www.googleapis.com/auth/apps.groups.settings
|
||||
# Send email through Gmail.
|
||||
@@ -420,30 +425,15 @@ registrarConsole:
|
||||
# DUM download file name, excluding the extension
|
||||
dumFileName: dum_file_name
|
||||
|
||||
# Filename of the logo to use in the header of the console. This filename is
|
||||
# relative to ui/assets/images/
|
||||
logoFilename: logo.png
|
||||
|
||||
# Contact phone number for support with the registry.
|
||||
supportPhoneNumber: +1 (888) 555 0123
|
||||
|
||||
# Contact email address for support with the registry.
|
||||
supportEmailAddress: support@example.com
|
||||
|
||||
# From: email address used to send announcements from the registry.
|
||||
announcementsEmailAddress: announcements@example.com
|
||||
|
||||
# Contact email address for questions about integrating with the registry.
|
||||
integrationEmailAddress: integration@example.com
|
||||
|
||||
# URL linking to directory of technical support docs on the registry.
|
||||
technicalDocsUrl: http://example.com/your_support_docs/
|
||||
|
||||
# Configuration for all analytics services installed in the web console
|
||||
analyticsConfig:
|
||||
# Google Analytics account where data on console use is sent, optional
|
||||
googleAnalyticsId: null
|
||||
|
||||
monitoring:
|
||||
# Max queries per second for the Google Cloud Monitoring V3 (aka Stackdriver)
|
||||
# API. The limit can be adjusted by contacting Cloud Support.
|
||||
|
||||
@@ -24,9 +24,7 @@
|
||||
|
||||
<static-files>
|
||||
<include path="/*.html" expiration="1m"/>
|
||||
<include path="/assets/js/**" expiration="1m"/>
|
||||
<include path="/assets/css/**" expiration="1m"/>
|
||||
<include path="/assets/images/**" expiration="1m"/>
|
||||
<include path="/assets/sources/**" expiration="1m"/>
|
||||
<include path="/registrar/*.html" expiration="1m"/>
|
||||
|
||||
</static-files>
|
||||
</appengine-web-app>
|
||||
|
||||
@@ -18,58 +18,6 @@
|
||||
<url-pattern>/_dr/epp</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Registrar Console endpoint, which accepts EPP XHRs from GAE GAIA-authenticated sessions. -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>frontend-servlet</servlet-name>
|
||||
<url-pattern>/registrar-xhr</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Registrar Console. -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>frontend-servlet</servlet-name>
|
||||
<url-pattern>/registrar</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Registrar creation console. -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>frontend-servlet</servlet-name>
|
||||
<url-pattern>/registrar-create</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- OT&E creation console. -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>frontend-servlet</servlet-name>
|
||||
<url-pattern>/registrar-ote-setup</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- OT&E status console. -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>frontend-servlet</servlet-name>
|
||||
<url-pattern>/registrar-ote-status</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Registrar Self-serve Settings. -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>frontend-servlet</servlet-name>
|
||||
<url-pattern>/registrar-settings</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Registry lock get/post/verify. -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>frontend-servlet</servlet-name>
|
||||
<url-pattern>/registry-lock-get</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>frontend-servlet</servlet-name>
|
||||
<url-pattern>/registry-lock-post</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>frontend-servlet</servlet-name>
|
||||
<url-pattern>/registry-lock-verify</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Registrar console endpoints -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>frontend-servlet</servlet-name>
|
||||
@@ -84,47 +32,18 @@
|
||||
Admin-only internal section. Requests for paths covered by the URL patterns below will be
|
||||
checked for a logged-in user account that's allowed to access the AppEngine admin console
|
||||
(NOTE: this includes Editor/Viewer permissions in addition to Owner and the new IAM
|
||||
App Engine Admin role. See https://cloud.google.com/appengine/docs/java/access-control
|
||||
App Engine Admin role. See https://cloud.google.com/appengine/docs/java/access-control
|
||||
specifically the "Access handlers that have a login:admin restriction" line.)
|
||||
|
||||
TODO(b/28219927): lift some of these restrictions so that we can allow OAuth authentication
|
||||
for endpoints that need to be accessed by open-source automated processes.
|
||||
</description>
|
||||
|
||||
<!-- Internal AppEngine endpoints. The '_ah' is short for app hosting. -->
|
||||
<url-pattern>/_ah/*</url-pattern>
|
||||
|
||||
<!-- Verbatim JavaScript sources (only visible to admins for debugging). -->
|
||||
<url-pattern>/assets/sources/*</url-pattern>
|
||||
|
||||
<!-- TODO(b/26776367): Move these files to /assets/sources. -->
|
||||
<url-pattern>/assets/js/registrar_bin.js.map</url-pattern>
|
||||
<url-pattern>/assets/js/registrar_dbg.js</url-pattern>
|
||||
<url-pattern>/assets/css/registrar_dbg.css</url-pattern>
|
||||
|
||||
</web-resource-collection>
|
||||
<auth-constraint>
|
||||
<role-name>admin</role-name>
|
||||
</auth-constraint>
|
||||
|
||||
<!-- Repeated here since catch-all rule below is not inherited. -->
|
||||
<user-data-constraint>
|
||||
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
|
||||
</user-data-constraint>
|
||||
</security-constraint>
|
||||
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>Registrar console</web-resource-name>
|
||||
<description>
|
||||
Registrar console requires user login. This is in addition to the
|
||||
code-level "requireLogin" configuration on individual @Actions.
|
||||
</description>
|
||||
<url-pattern>/registrar*</url-pattern>
|
||||
</web-resource-collection>
|
||||
<auth-constraint>
|
||||
<role-name>*</role-name>
|
||||
</auth-constraint>
|
||||
<!-- Repeated here since catch-all rule below is not inherited. -->
|
||||
<user-data-constraint>
|
||||
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
|
||||
|
||||
@@ -24,9 +24,6 @@
|
||||
|
||||
<static-files>
|
||||
<include path="/*.html" expiration="1m"/>
|
||||
<include path="/assets/js/**" expiration="1m"/>
|
||||
<include path="/assets/css/**" expiration="1m"/>
|
||||
<include path="/assets/images/**" expiration="1m"/>
|
||||
<include path="/assets/sources/**" expiration="1m"/>
|
||||
<include path="/registrar/*.html" expiration="1m"/>
|
||||
</static-files>
|
||||
</appengine-web-app>
|
||||
|
||||
@@ -28,16 +28,7 @@
|
||||
<include path="/*.html">
|
||||
<http-header name="Cache-Control" value="max-age=0,must-revalidate" />
|
||||
</include>
|
||||
<include path="/assets/js/**">
|
||||
<http-header name="Cache-Control" value="max-age=0,must-revalidate" />
|
||||
</include>
|
||||
<include path="/assets/css/**">
|
||||
<http-header name="Cache-Control" value="max-age=0,must-revalidate" />
|
||||
</include>
|
||||
<include path="/assets/images/**">
|
||||
<http-header name="Cache-Control" value="max-age=0,must-revalidate" />
|
||||
</include>
|
||||
<include path="/assets/sources/**">
|
||||
<include path="/registrar/*.html">
|
||||
<http-header name="Cache-Control" value="max-age=0,must-revalidate" />
|
||||
</include>
|
||||
</static-files>
|
||||
|
||||
@@ -23,10 +23,7 @@
|
||||
|
||||
<static-files>
|
||||
<include path="/*.html" expiration="1d"/>
|
||||
<include path="/assets/js/**" expiration="1d"/>
|
||||
<include path="/assets/css/**" expiration="1d"/>
|
||||
<include path="/assets/images/**" expiration="1d"/>
|
||||
<include path="/assets/sources/**" expiration="1d"/>
|
||||
<include path="/registrar/*.html" expiration="1d"/>
|
||||
</static-files>
|
||||
|
||||
<!-- Prevent uncaught servlet errors from leaking a stack trace. -->
|
||||
|
||||
@@ -27,10 +27,7 @@
|
||||
|
||||
<static-files>
|
||||
<include path="/*.html" expiration="1h"/>
|
||||
<include path="/assets/js/**" expiration="1h"/>
|
||||
<include path="/assets/css/**" expiration="1h"/>
|
||||
<include path="/assets/images/**" expiration="1h"/>
|
||||
<include path="/assets/sources/**" expiration="1h"/>
|
||||
<include path="/registrar/*.html" expiration="1h"/>
|
||||
</static-files>
|
||||
|
||||
<!-- Prevent uncaught servlet errors from leaking a stack trace. -->
|
||||
|
||||
@@ -23,10 +23,7 @@
|
||||
|
||||
<static-files>
|
||||
<include path="/*.html" expiration="1d"/>
|
||||
<include path="/assets/js/**" expiration="1d"/>
|
||||
<include path="/assets/css/**" expiration="1d"/>
|
||||
<include path="/assets/images/**" expiration="1d"/>
|
||||
<include path="/assets/sources/**" expiration="1d"/>
|
||||
<include path="/registrar/*.html" expiration="1d"/>
|
||||
</static-files>
|
||||
|
||||
<!-- Prevent uncaught servlet errors from leaking a stack trace. -->
|
||||
|
||||
@@ -95,7 +95,7 @@ public final class DomainPricingLogic {
|
||||
false, tld.getCreateBillingCost(dateTime), domainPrices.getRenewCost());
|
||||
}
|
||||
Money domainCreateCost =
|
||||
getDomainCreateCostWithDiscount(domainPrices, years, allocationToken);
|
||||
getDomainCreateCostWithDiscount(domainPrices, years, allocationToken, tld);
|
||||
// Apply a sunrise discount if configured and applicable
|
||||
if (isSunriseCreate) {
|
||||
domainCreateCost =
|
||||
@@ -134,7 +134,8 @@ public final class DomainPricingLogic {
|
||||
int years,
|
||||
@Nullable BillingRecurrence billingRecurrence,
|
||||
Optional<AllocationToken> allocationToken)
|
||||
throws AllocationTokenInvalidForPremiumNameException {
|
||||
throws AllocationTokenInvalidForCurrencyException,
|
||||
AllocationTokenInvalidForPremiumNameException {
|
||||
checkArgument(years > 0, "Number of years must be positive");
|
||||
Money renewCost;
|
||||
DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime);
|
||||
@@ -172,7 +173,8 @@ public final class DomainPricingLogic {
|
||||
years,
|
||||
allocationToken,
|
||||
tld.getStandardRenewCost(dateTime),
|
||||
Optional.empty());
|
||||
Optional.empty(),
|
||||
tld);
|
||||
isRenewCostPremiumPrice = false;
|
||||
}
|
||||
default ->
|
||||
@@ -262,14 +264,15 @@ public final class DomainPricingLogic {
|
||||
|
||||
/** Returns the domain create cost with allocation-token-related discounts applied. */
|
||||
private Money getDomainCreateCostWithDiscount(
|
||||
DomainPrices domainPrices, int years, Optional<AllocationToken> allocationToken)
|
||||
DomainPrices domainPrices, int years, Optional<AllocationToken> allocationToken, Tld tld)
|
||||
throws EppException {
|
||||
return getDomainCostWithDiscount(
|
||||
domainPrices.isPremium(),
|
||||
years,
|
||||
allocationToken,
|
||||
domainPrices.getCreateCost(),
|
||||
Optional.of(domainPrices.getRenewCost()));
|
||||
Optional.of(domainPrices.getRenewCost()),
|
||||
tld);
|
||||
}
|
||||
|
||||
/** Returns the domain renew cost with allocation-token-related discounts applied. */
|
||||
@@ -279,7 +282,8 @@ public final class DomainPricingLogic {
|
||||
DateTime dateTime,
|
||||
int years,
|
||||
Optional<AllocationToken> allocationToken)
|
||||
throws AllocationTokenInvalidForPremiumNameException {
|
||||
throws AllocationTokenInvalidForCurrencyException,
|
||||
AllocationTokenInvalidForPremiumNameException {
|
||||
// Short-circuit if the user sent an anchor-tenant or otherwise NONPREMIUM-renewal token
|
||||
if (allocationToken.isPresent()) {
|
||||
AllocationToken token = allocationToken.get();
|
||||
@@ -293,7 +297,8 @@ public final class DomainPricingLogic {
|
||||
years,
|
||||
allocationToken,
|
||||
domainPrices.getRenewCost(),
|
||||
Optional.empty());
|
||||
Optional.empty(),
|
||||
tld);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -310,8 +315,10 @@ public final class DomainPricingLogic {
|
||||
int years,
|
||||
Optional<AllocationToken> allocationToken,
|
||||
Money firstYearCost,
|
||||
Optional<Money> subsequentYearCost)
|
||||
throws AllocationTokenInvalidForPremiumNameException {
|
||||
Optional<Money> subsequentYearCost,
|
||||
Tld tld)
|
||||
throws AllocationTokenInvalidForCurrencyException,
|
||||
AllocationTokenInvalidForPremiumNameException {
|
||||
checkArgument(years > 0, "Registration years to get cost for must be positive.");
|
||||
validateTokenForPossiblePremiumName(allocationToken, isPremium);
|
||||
Money totalDomainFlowCost =
|
||||
@@ -320,13 +327,31 @@ public final class DomainPricingLogic {
|
||||
// Apply the allocation token discount, if applicable.
|
||||
if (allocationToken.isPresent()
|
||||
&& allocationToken.get().getTokenBehavior().equals(TokenBehavior.DEFAULT)) {
|
||||
int discountedYears = Math.min(years, allocationToken.get().getDiscountYears());
|
||||
if (discountedYears > 0) {
|
||||
var discount =
|
||||
firstYearCost
|
||||
.plus(subsequentYearCost.orElse(firstYearCost).multipliedBy(discountedYears - 1))
|
||||
.multipliedBy(allocationToken.get().getDiscountFraction(), RoundingMode.HALF_EVEN);
|
||||
totalDomainFlowCost = totalDomainFlowCost.minus(discount);
|
||||
if (allocationToken.get().getDiscountPrice().isPresent()) {
|
||||
if (!tld.getCurrency()
|
||||
.equals(allocationToken.get().getDiscountPrice().get().getCurrencyUnit())) {
|
||||
throw new AllocationTokenInvalidForCurrencyException();
|
||||
}
|
||||
|
||||
int nonDiscountedYears = Math.max(0, years - allocationToken.get().getDiscountYears());
|
||||
totalDomainFlowCost =
|
||||
allocationToken
|
||||
.get()
|
||||
.getDiscountPrice()
|
||||
.get()
|
||||
.multipliedBy(allocationToken.get().getDiscountYears())
|
||||
.plus(subsequentYearCost.orElse(firstYearCost).multipliedBy(nonDiscountedYears));
|
||||
} else {
|
||||
// Assumes token has discount fraction set.
|
||||
int discountedYears = Math.min(years, allocationToken.get().getDiscountYears());
|
||||
if (discountedYears > 0) {
|
||||
var discount =
|
||||
firstYearCost
|
||||
.plus(subsequentYearCost.orElse(firstYearCost).multipliedBy(discountedYears - 1))
|
||||
.multipliedBy(
|
||||
allocationToken.get().getDiscountFraction(), RoundingMode.HALF_EVEN);
|
||||
totalDomainFlowCost = totalDomainFlowCost.minus(discount);
|
||||
}
|
||||
}
|
||||
}
|
||||
return totalDomainFlowCost;
|
||||
@@ -339,4 +364,10 @@ public final class DomainPricingLogic {
|
||||
super("Token not valid for premium name");
|
||||
}
|
||||
}
|
||||
|
||||
public static class AllocationTokenInvalidForCurrencyException extends CommandUseErrorException {
|
||||
public AllocationTokenInvalidForCurrencyException() {
|
||||
super("Token and domain currencies do not match.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ public class AllocationTokenFlowUtils {
|
||||
Optional<AllocationToken> token, boolean isPremium)
|
||||
throws AllocationTokenInvalidForPremiumNameException {
|
||||
if (token.isPresent()
|
||||
&& token.get().getDiscountFraction() != 0.0
|
||||
&& (token.get().getDiscountFraction() != 0.0 || token.get().getDiscountPrice().isPresent())
|
||||
&& isPremium
|
||||
&& !token.get().shouldDiscountPremiums()) {
|
||||
throw new AllocationTokenInvalidForPremiumNameException();
|
||||
@@ -288,6 +288,7 @@ public class AllocationTokenFlowUtils {
|
||||
super("Alloc token not in promo period");
|
||||
}
|
||||
}
|
||||
|
||||
/** The allocation token is not valid for this TLD. */
|
||||
public static class AllocationTokenNotValidForTldException
|
||||
extends AssociationProhibitsOperationException {
|
||||
|
||||
@@ -66,7 +66,6 @@ public class FeatureFlag extends ImmutableObject implements Buildable {
|
||||
TEST_FEATURE,
|
||||
MINIMUM_DATASET_CONTACTS_OPTIONAL,
|
||||
MINIMUM_DATASET_CONTACTS_PROHIBITED,
|
||||
NEW_CONSOLE
|
||||
}
|
||||
|
||||
/** The name of the flag/feature. */
|
||||
|
||||
@@ -22,6 +22,7 @@ import static google.registry.util.PasswordUtils.SALT_SUPPLIER;
|
||||
import static google.registry.util.PasswordUtils.hashPassword;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.UpdateAutoTimestampEntity;
|
||||
import google.registry.util.PasswordUtils;
|
||||
@@ -50,12 +51,13 @@ public class UserBase extends UpdateAutoTimestampEntity implements Buildable {
|
||||
private static final long serialVersionUID = 6936728603828566721L;
|
||||
|
||||
/** Email address of the user in question. */
|
||||
@Transient String emailAddress;
|
||||
@Transient @Expose String emailAddress;
|
||||
|
||||
/** Optional external email address to use for registry lock confirmation emails. */
|
||||
@Column String registryLockEmailAddress;
|
||||
|
||||
/** Roles (which grant permissions) associated with this user. */
|
||||
@Expose
|
||||
@Column(nullable = false)
|
||||
UserRoles userRoles;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.persistence.converter.RegistrarToRoleMapUserType;
|
||||
@@ -53,6 +54,7 @@ public class UserRoles extends ImmutableObject implements Buildable {
|
||||
private GlobalRole globalRole = GlobalRole.NONE;
|
||||
|
||||
/** Any per-registrar roles that this user may have. */
|
||||
@Expose
|
||||
@Type(RegistrarToRoleMapUserType.class)
|
||||
private Map<String, RegistrarRole> registrarRoles = ImmutableMap.of();
|
||||
|
||||
|
||||
@@ -123,8 +123,8 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
* Bypasses the premium list to use the standard creation price. Does not affect the renewal
|
||||
* price.
|
||||
*
|
||||
* <p>This cannot be specified along with a discount fraction, and any renewals (automatic or
|
||||
* otherwise) will use the premium price for the domain if one exists.
|
||||
* <p>This cannot be specified along with a discount fraction/price, and any renewals (automatic
|
||||
* or otherwise) will use the premium price for the domain if one exists.
|
||||
*
|
||||
* <p>Tokens with this behavior must be tied to a single particular domain.
|
||||
*/
|
||||
@@ -248,6 +248,22 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
@AttributeOverride(name = "currency", column = @Column(name = "renewalPriceCurrency"))
|
||||
Money renewalPrice;
|
||||
|
||||
/**
|
||||
* A discount that allows the setting of promotional prices. This field is different from {@code
|
||||
* discountFraction} because the price set here is treated as the domain price, versus {@code
|
||||
* discountFraction} that applies a fraction discount to the domain base price.
|
||||
*
|
||||
* <p>Prefer this method of discount when attempting to set a promotional price across TLDs with
|
||||
* different base prices.
|
||||
*/
|
||||
@Nullable
|
||||
@AttributeOverride(
|
||||
name = "amount",
|
||||
// Override Hibernate default (numeric(38,2)) to match real schema definition (numeric(19,2)).
|
||||
column = @Column(name = "discountPriceAmount", precision = 19, scale = 2))
|
||||
@AttributeOverride(name = "currency", column = @Column(name = "discountPriceCurrency"))
|
||||
Money discountPrice;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(nullable = false)
|
||||
RegistrationBehavior registrationBehavior = RegistrationBehavior.DEFAULT;
|
||||
@@ -299,6 +315,10 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
return discountFraction;
|
||||
}
|
||||
|
||||
public Optional<Money> getDiscountPrice() {
|
||||
return Optional.ofNullable(discountPrice);
|
||||
}
|
||||
|
||||
public boolean shouldDiscountPremiums() {
|
||||
return discountPremiums;
|
||||
}
|
||||
@@ -406,8 +426,10 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
getInstance().discountFraction > 0 || !getInstance().discountPremiums,
|
||||
"Discount premiums can only be specified along with a discount fraction");
|
||||
checkArgument(
|
||||
getInstance().discountFraction > 0 || getInstance().discountYears == 1,
|
||||
"Discount years can only be specified along with a discount fraction");
|
||||
getInstance().discountFraction > 0
|
||||
|| getInstance().discountPrice != null
|
||||
|| getInstance().discountYears == 1,
|
||||
"Discount years can only be specified along with a discount fraction/price");
|
||||
if (getInstance().getTokenType().equals(REGISTER_BSA)) {
|
||||
checkArgumentNotNull(
|
||||
getInstance().domainName, "REGISTER_BSA tokens must be tied to a domain");
|
||||
@@ -418,7 +440,7 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
}
|
||||
if (getInstance().registrationBehavior.equals(RegistrationBehavior.NONPREMIUM_CREATE)) {
|
||||
checkArgument(
|
||||
getInstance().discountFraction == 0.0,
|
||||
getInstance().discountFraction == 0.0 && getInstance().discountPrice == null,
|
||||
"NONPREMIUM_CREATE tokens cannot apply a discount");
|
||||
checkArgumentNotNull(
|
||||
getInstance().domainName, "NONPREMIUM_CREATE tokens must be tied to a domain");
|
||||
@@ -454,6 +476,12 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
"BULK_PRICING tokens must have exactly one allowed client registrar");
|
||||
}
|
||||
|
||||
if (getInstance().discountFraction != 0.0) {
|
||||
checkArgument(
|
||||
getInstance().discountPrice == null,
|
||||
"discountFraction and discountPrice can't be set together");
|
||||
}
|
||||
|
||||
if (getInstance().domainName != null) {
|
||||
try {
|
||||
DomainFlowUtils.validateDomainName(getInstance().domainName);
|
||||
@@ -559,5 +587,10 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
getInstance().registrationBehavior = registrationBehavior;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDiscountPrice(@Nullable Money discountPrice) {
|
||||
getInstance().discountPrice = discountPrice;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +119,7 @@ import google.registry.ui.server.console.ConsoleRegistryLockAction;
|
||||
import google.registry.ui.server.console.ConsoleRegistryLockVerifyAction;
|
||||
import google.registry.ui.server.console.ConsoleUpdateRegistrarAction;
|
||||
import google.registry.ui.server.console.ConsoleUserDataAction;
|
||||
import google.registry.ui.server.console.ConsoleUsersAction;
|
||||
import google.registry.ui.server.console.RegistrarsAction;
|
||||
import google.registry.ui.server.console.settings.ContactAction;
|
||||
import google.registry.ui.server.console.settings.SecurityAction;
|
||||
@@ -189,6 +190,8 @@ interface RequestComponent {
|
||||
|
||||
ConsoleUserDataAction consoleUserDataAction();
|
||||
|
||||
ConsoleUsersAction consoleUsersAction();
|
||||
|
||||
ConsoleDumDownloadAction consoleDumDownloadAction();
|
||||
|
||||
ContactAction contactAction();
|
||||
|
||||
@@ -35,18 +35,11 @@ import google.registry.ui.server.console.ConsoleRegistryLockAction;
|
||||
import google.registry.ui.server.console.ConsoleRegistryLockVerifyAction;
|
||||
import google.registry.ui.server.console.ConsoleUpdateRegistrarAction;
|
||||
import google.registry.ui.server.console.ConsoleUserDataAction;
|
||||
import google.registry.ui.server.console.ConsoleUsersAction;
|
||||
import google.registry.ui.server.console.RegistrarsAction;
|
||||
import google.registry.ui.server.console.settings.ContactAction;
|
||||
import google.registry.ui.server.console.settings.SecurityAction;
|
||||
import google.registry.ui.server.console.settings.WhoisRegistrarFieldsAction;
|
||||
import google.registry.ui.server.registrar.ConsoleOteSetupAction;
|
||||
import google.registry.ui.server.registrar.ConsoleRegistrarCreatorAction;
|
||||
import google.registry.ui.server.registrar.ConsoleUiAction;
|
||||
import google.registry.ui.server.registrar.OteStatusAction;
|
||||
import google.registry.ui.server.registrar.RegistrarSettingsAction;
|
||||
import google.registry.ui.server.registrar.RegistryLockGetAction;
|
||||
import google.registry.ui.server.registrar.RegistryLockPostAction;
|
||||
import google.registry.ui.server.registrar.RegistryLockVerifyAction;
|
||||
|
||||
/** Dagger component with per-request lifetime for "default" App Engine module. */
|
||||
@RequestScope
|
||||
@@ -68,36 +61,28 @@ public interface FrontendRequestComponent {
|
||||
|
||||
ConsoleOteAction consoleOteAction();
|
||||
|
||||
ConsoleOteSetupAction consoleOteSetupAction();
|
||||
ConsoleRegistrarCreatorAction consoleRegistrarCreatorAction();
|
||||
|
||||
ConsoleRegistryLockAction consoleRegistryLockAction();
|
||||
|
||||
ConsoleRegistryLockVerifyAction consoleRegistryLockVerifyAction();
|
||||
|
||||
ConsoleUiAction consoleUiAction();
|
||||
|
||||
ConsoleUpdateRegistrarAction consoleUpdateRegistrarAction();
|
||||
|
||||
ConsoleUserDataAction consoleUserDataAction();
|
||||
|
||||
ConsoleUsersAction consoleUsersAction();
|
||||
|
||||
ConsoleDumDownloadAction consoleDumDownloadAction();
|
||||
|
||||
ContactAction contactAction();
|
||||
|
||||
EppTlsAction eppTlsAction();
|
||||
|
||||
FlowComponent.Builder flowComponentBuilder();
|
||||
OteStatusAction oteStatusAction();
|
||||
|
||||
RegistrarsAction registrarsAction();
|
||||
|
||||
RegistrarSettingsAction registrarSettingsAction();
|
||||
|
||||
RegistryLockGetAction registryLockGetAction();
|
||||
|
||||
RegistryLockPostAction registryLockPostAction();
|
||||
RegistryLockVerifyAction registryLockVerifyAction();
|
||||
SecurityAction securityAction();
|
||||
|
||||
WhoisRegistrarFieldsAction whoisRegistrarFieldsAction();
|
||||
|
||||
@Subcomponent.Builder
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
// Copyright 2024 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.persistence.transaction;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static google.registry.persistence.PersistenceModule.TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.collect.UnmodifiableIterator;
|
||||
import jakarta.persistence.TypedQuery;
|
||||
import jakarta.persistence.metamodel.EntityType;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/** Helper for querying large data sets in batches. */
|
||||
public final class BatchedQueries {
|
||||
|
||||
private BatchedQueries() {}
|
||||
|
||||
private static final int DEFAULT_BATCH_SIZE = 500;
|
||||
|
||||
public static <T> Stream<ImmutableList<T>> loadAllOf(Class<T> entityType) {
|
||||
return loadAllOf(entityType, DEFAULT_BATCH_SIZE);
|
||||
}
|
||||
|
||||
public static <T> Stream<ImmutableList<T>> loadAllOf(Class<T> entityType, int batchSize) {
|
||||
return loadAllOf(tm(), entityType, batchSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all entities of type {@code T} in batches.
|
||||
*
|
||||
* <p>This method must not be nested in any transaction; same for the traversal of the returned
|
||||
* {@link Stream}. Each batch is loaded in a separate transaction at the {@code
|
||||
* TRANSACTION_REPEATABLE_READ} isolation level, and loads the snapshot of the batch at the
|
||||
* batch's start time. New insertions or updates since then are not reflected in the result.
|
||||
*/
|
||||
public static <T> Stream<ImmutableList<T>> loadAllOf(
|
||||
JpaTransactionManager jpaTm, Class<T> entityType, int batchSize) {
|
||||
checkState(!jpaTm.inTransaction(), "loadAllOf cannot be nested in a transaction");
|
||||
checkArgument(batchSize > 0, "batchSize must be positive");
|
||||
EntityType<T> jpaEntityType = jpaTm.getMetaModel().entity(entityType);
|
||||
if (!jpaEntityType.hasSingleIdAttribute()) {
|
||||
// We should support multi-column primary keys on a case-by-case basis.
|
||||
throw new UnsupportedOperationException(
|
||||
"Types with multi-column primary key not supported yet");
|
||||
}
|
||||
return Streams.stream(
|
||||
new BatchedIterator<>(new SingleColIdBatchQuery<>(jpaTm, jpaEntityType), batchSize));
|
||||
}
|
||||
|
||||
public interface BatchQuery<T> {
|
||||
ImmutableList<T> readBatch(Optional<T> lastRead, int batchSize);
|
||||
}
|
||||
|
||||
private static class SingleColIdBatchQuery<T> implements BatchQuery<T> {
|
||||
|
||||
private final JpaTransactionManager jpaTm;
|
||||
private final Class<T> entityType;
|
||||
private final String initialJpqlQuery;
|
||||
private final String subsequentJpqlTemplate;
|
||||
|
||||
private SingleColIdBatchQuery(JpaTransactionManager jpaTm, EntityType<T> jpaEntityType) {
|
||||
checkArgument(
|
||||
jpaEntityType.hasSingleIdAttribute(),
|
||||
"%s must have a single ID attribute",
|
||||
jpaEntityType.getJavaType().getSimpleName());
|
||||
this.jpaTm = jpaTm;
|
||||
this.entityType = jpaEntityType.getJavaType();
|
||||
var idAttr = jpaEntityType.getId(jpaEntityType.getIdType().getJavaType());
|
||||
this.initialJpqlQuery =
|
||||
String.format("FROM %s ORDER BY %s", jpaEntityType.getName(), idAttr.getName());
|
||||
this.subsequentJpqlTemplate =
|
||||
String.format(
|
||||
"FROM %1$s WHERE %2$s > :id ORDER BY %2$s",
|
||||
jpaEntityType.getName(), idAttr.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<T> readBatch(Optional<T> lastRead, int batchSize) {
|
||||
checkState(!jpaTm.inTransaction(), "Stream cannot be accessed in a transaction");
|
||||
return jpaTm.transact(
|
||||
TRANSACTION_REPEATABLE_READ,
|
||||
() -> {
|
||||
var entityManager = jpaTm.getEntityManager();
|
||||
Optional<Object> lastReadId =
|
||||
lastRead.map(
|
||||
entityManager.getEntityManagerFactory().getPersistenceUnitUtil()
|
||||
::getIdentifier);
|
||||
TypedQuery<T> query =
|
||||
lastRead.isEmpty()
|
||||
? entityManager.createQuery(initialJpqlQuery, entityType)
|
||||
: entityManager
|
||||
.createQuery(subsequentJpqlTemplate, entityType)
|
||||
.setParameter("id", lastReadId.get());
|
||||
|
||||
var results = ImmutableList.copyOf(query.setMaxResults(batchSize).getResultList());
|
||||
results.forEach(entityManager::detach);
|
||||
return results;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static class BatchedIterator<T> extends UnmodifiableIterator<ImmutableList<T>> {
|
||||
|
||||
private final BatchQuery<T> batchQuery;
|
||||
|
||||
private final int batchSize;
|
||||
|
||||
private ImmutableList<T> cachedBatch = null;
|
||||
|
||||
private BatchedIterator(BatchQuery<T> batchQuery, int batchSize) {
|
||||
this.batchQuery = batchQuery;
|
||||
this.batchSize = batchSize;
|
||||
this.cachedBatch = readNextBatch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return !cachedBatch.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<T> next() {
|
||||
var toReturn = cachedBatch;
|
||||
cachedBatch = cachedBatch.size() < batchSize ? ImmutableList.of() : readNextBatch();
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
private ImmutableList<T> readNextBatch() {
|
||||
Optional<T> lastRead =
|
||||
cachedBatch == null
|
||||
? Optional.empty()
|
||||
: Optional.ofNullable(Iterables.getLast(cachedBatch, null));
|
||||
return batchQuery.readBatch(lastRead, batchSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.Query;
|
||||
import jakarta.persistence.TypedQuery;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.metamodel.Metamodel;
|
||||
|
||||
/** Sub-interface of {@link TransactionManager} which defines JPA related methods. */
|
||||
public interface JpaTransactionManager extends TransactionManager {
|
||||
@@ -31,6 +32,9 @@ public interface JpaTransactionManager extends TransactionManager {
|
||||
*/
|
||||
EntityManager getStandaloneEntityManager();
|
||||
|
||||
/** Returns the JPA {@link Metamodel}. */
|
||||
Metamodel getMetaModel();
|
||||
|
||||
/**
|
||||
* Returns the {@link EntityManager} for the current request.
|
||||
*
|
||||
|
||||
@@ -55,6 +55,7 @@ import jakarta.persistence.TemporalType;
|
||||
import jakarta.persistence.TypedQuery;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.metamodel.EntityType;
|
||||
import jakarta.persistence.metamodel.Metamodel;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
@@ -116,6 +117,11 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
return emf.createEntityManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Metamodel getMetaModel() {
|
||||
return this.emf.getMetamodel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityManager getEntityManager() {
|
||||
assertInTransaction();
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.beust.jcommander.IStringConverter;
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.beust.jcommander.converters.IParameterSplitter;
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -103,7 +104,10 @@ class CurlCommand implements CommandWithConnection {
|
||||
throw new IllegalArgumentException("You may not specify a body for a get method.");
|
||||
}
|
||||
|
||||
Service service = useGke ? GkeService.valueOf(serviceName) : GaeService.valueOf(serviceName);
|
||||
Service service =
|
||||
useGke
|
||||
? GkeService.valueOf(Ascii.toUpperCase(serviceName))
|
||||
: GaeService.valueOf(Ascii.toUpperCase(serviceName));
|
||||
|
||||
ServiceConnection connectionToService = connection.withService(service, canary);
|
||||
String response =
|
||||
|
||||
@@ -47,6 +47,7 @@ import google.registry.model.domain.token.AllocationToken.RegistrationBehavior;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.tools.params.MoneyParameter;
|
||||
import google.registry.tools.params.TransitionListParameter.TokenStatusTransitions;
|
||||
import google.registry.util.CollectionUtils;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
@@ -140,6 +141,17 @@ class GenerateAllocationTokensCommand implements Command {
|
||||
arity = 1)
|
||||
private Boolean discountPremiums;
|
||||
|
||||
@Parameter(
|
||||
names = {"--discount_price"},
|
||||
description =
|
||||
"A discount that allows the setting of promotional prices. This field is different from "
|
||||
+ "{@code discountFraction} because the price set here is treated as the domain "
|
||||
+ "price, versus {@code discountFraction} that applies a fraction discount to the "
|
||||
+ "domain base price. Use CURRENCY PRICE format, example: USD 777.99",
|
||||
converter = MoneyParameter.class,
|
||||
validateWith = MoneyParameter.class)
|
||||
private Money discountPrice;
|
||||
|
||||
@Parameter(
|
||||
names = {"--discount_years"},
|
||||
description = "The number of years the discount applies for. Default is 1, max value is 10.")
|
||||
@@ -233,6 +245,7 @@ class GenerateAllocationTokensCommand implements Command {
|
||||
.collect(toImmutableSet()));
|
||||
Optional.ofNullable(discountFraction).ifPresent(token::setDiscountFraction);
|
||||
Optional.ofNullable(discountPremiums).ifPresent(token::setDiscountPremiums);
|
||||
Optional.ofNullable(discountPrice).ifPresent(token::setDiscountPrice);
|
||||
Optional.ofNullable(discountYears).ifPresent(token::setDiscountYears);
|
||||
Optional.ofNullable(tokenStatusTransitions)
|
||||
.ifPresent(token::setTokenStatusTransitions);
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static com.google.common.base.Verify.verify;
|
||||
@@ -35,7 +34,6 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.common.net.MediaType;
|
||||
import com.google.re2j.Matcher;
|
||||
import com.google.re2j.Pattern;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.request.Action.GaeService;
|
||||
@@ -60,6 +58,8 @@ public class ServiceConnection {
|
||||
/** Pattern to heuristically extract title tag contents in HTML responses. */
|
||||
protected static final Pattern HTML_TITLE_TAG_PATTERN = Pattern.compile("<title>(.*?)</title>");
|
||||
|
||||
private static final String CANARY_HEADER = "canary";
|
||||
|
||||
private final Service service;
|
||||
private final boolean useCanary;
|
||||
private final HttpRequestFactory requestFactory;
|
||||
@@ -70,9 +70,6 @@ public class ServiceConnection {
|
||||
}
|
||||
|
||||
private ServiceConnection(Service service, HttpRequestFactory requestFactory, boolean useCanary) {
|
||||
// Currently, only GAE supports connecting to canary.
|
||||
// TODO (jianglai): decide how to implement canary for GKE.
|
||||
checkArgument(useCanary == false || service instanceof GaeService, "Canary is only for GAE");
|
||||
this.service = service;
|
||||
this.requestFactory = requestFactory;
|
||||
this.useCanary = useCanary;
|
||||
@@ -80,15 +77,17 @@ public class ServiceConnection {
|
||||
|
||||
/** Returns a copy of this connection that talks to a different service endpoint. */
|
||||
public ServiceConnection withService(Service service, boolean useCanary) {
|
||||
Class<? extends Service> oldServiceClazz = this.service.getClass();
|
||||
Class<? extends Service> newServiceClazz = service.getClass();
|
||||
if (oldServiceClazz != newServiceClazz) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"Cannot switch from %s to %s",
|
||||
oldServiceClazz.getSimpleName(), newServiceClazz.getSimpleName()));
|
||||
}
|
||||
return new ServiceConnection(service, requestFactory, useCanary);
|
||||
}
|
||||
|
||||
/** Returns the contents of the title tag in the given HTML, or null if not found. */
|
||||
private static String extractHtmlTitle(String html) {
|
||||
Matcher matcher = HTML_TITLE_TAG_PATTERN.matcher(html);
|
||||
return (matcher.find() ? matcher.group(1) : null);
|
||||
}
|
||||
|
||||
/** Returns the HTML from the connection error stream, if any, otherwise the empty string. */
|
||||
private static String getErrorHtmlAsString(HttpResponse response) throws IOException {
|
||||
return CharStreams.toString(new InputStreamReader(response.getContent(), UTF_8));
|
||||
@@ -107,19 +106,22 @@ public class ServiceConnection {
|
||||
HttpHeaders headers = request.getHeaders();
|
||||
headers.setCacheControl("no-cache");
|
||||
headers.put(X_REQUESTED_WITH, ImmutableList.of("RegistryTool"));
|
||||
if (useCanary) {
|
||||
headers.set(CANARY_HEADER, "true");
|
||||
}
|
||||
request.setHeaders(headers);
|
||||
request.setFollowRedirects(false);
|
||||
request.setThrowExceptionOnExecuteError(false);
|
||||
request.setUnsuccessfulResponseHandler(
|
||||
(request1, response, supportsRetry) -> {
|
||||
String errorTitle = extractHtmlTitle(getErrorHtmlAsString(response));
|
||||
String error = getErrorHtmlAsString(response);
|
||||
throw new IOException(
|
||||
String.format(
|
||||
"Error from %s: %d %s%s",
|
||||
request1.getUrl().toString(),
|
||||
response.getStatusCode(),
|
||||
response.getStatusMessage(),
|
||||
(errorTitle == null ? "" : ": " + errorTitle)));
|
||||
error));
|
||||
});
|
||||
HttpResponse response = null;
|
||||
try {
|
||||
@@ -135,8 +137,8 @@ public class ServiceConnection {
|
||||
@VisibleForTesting
|
||||
URL getServer() {
|
||||
URL url = service.getServiceUrl();
|
||||
if (useCanary) {
|
||||
verify(!isNullOrEmpty(url.getHost()), "Null host in url");
|
||||
verify(!isNullOrEmpty(url.getHost()), "Null host in url");
|
||||
if (useCanary && service instanceof GaeService) {
|
||||
url =
|
||||
makeUrl(
|
||||
String.format(
|
||||
|
||||
@@ -34,6 +34,7 @@ import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.RegistrationBehavior;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.tools.params.MoneyParameter;
|
||||
import google.registry.tools.params.StringListParameter;
|
||||
import google.registry.tools.params.TransitionListParameter.TokenStatusTransitions;
|
||||
import java.util.List;
|
||||
@@ -91,6 +92,17 @@ final class UpdateAllocationTokensCommand extends UpdateOrDeleteAllocationTokens
|
||||
arity = 1)
|
||||
private Boolean discountPremiums;
|
||||
|
||||
@Parameter(
|
||||
names = {"--discount_price"},
|
||||
description =
|
||||
"A discount that allows the setting of promotional prices. This field is different from "
|
||||
+ "{@code discountFraction} because the price set here is treated as the domain "
|
||||
+ "price, versus {@code discountFraction} that applies a fraction discount to the "
|
||||
+ "domain base price. Use CURRENCY PRICE format, example: USD 777.99",
|
||||
converter = MoneyParameter.class,
|
||||
validateWith = MoneyParameter.class)
|
||||
private Money discountPrice;
|
||||
|
||||
@Parameter(
|
||||
names = {"-y", "--discount_years"},
|
||||
description = "The number of years the discount applies for. Default is 1, max value is 10.")
|
||||
@@ -203,6 +215,7 @@ final class UpdateAllocationTokensCommand extends UpdateOrDeleteAllocationTokens
|
||||
.collect(toImmutableSet())));
|
||||
Optional.ofNullable(discountFraction).ifPresent(builder::setDiscountFraction);
|
||||
Optional.ofNullable(discountPremiums).ifPresent(builder::setDiscountPremiums);
|
||||
Optional.ofNullable(discountPrice).ifPresent(builder::setDiscountPrice);
|
||||
Optional.ofNullable(discountYears).ifPresent(builder::setDiscountYears);
|
||||
Optional.ofNullable(tokenStatusTransitions).ifPresent(builder::setTokenStatusTransitions);
|
||||
|
||||
|
||||
@@ -15,11 +15,12 @@
|
||||
package google.registry.tools.server;
|
||||
|
||||
import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.persistence.transaction.BatchedQueries.loadAllOf;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static java.util.Comparator.comparing;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.EppResourceUtils;
|
||||
import google.registry.model.host.Host;
|
||||
@@ -51,7 +52,8 @@ public final class ListHostsAction extends ListObjectsAction<Host> {
|
||||
@Override
|
||||
public ImmutableSet<Host> loadObjects() {
|
||||
final DateTime now = clock.nowUtc();
|
||||
return tm().transact(() -> tm().loadAllOf(Host.class)).stream()
|
||||
return loadAllOf(Host.class)
|
||||
.flatMap(ImmutableList::stream)
|
||||
.filter(host -> EppResourceUtils.isActive(host, now))
|
||||
.collect(toImmutableSortedSet(comparing(Host::getHostName)));
|
||||
}
|
||||
|
||||
@@ -108,6 +108,7 @@ public class RefreshDnsForAllDomainsAction implements Runnable {
|
||||
public void run() {
|
||||
assertTldsExist(tlds);
|
||||
checkArgument(batchSize > 0, "Must specify a positive number for batch size");
|
||||
logger.atInfo().log("Enqueueing DNS refresh tasks for TLDs %s.", tlds);
|
||||
Duration smear = tm().transact(TRANSACTION_REPEATABLE_READ, this::calculateSmear);
|
||||
|
||||
ImmutableList<String> domainsBatch;
|
||||
@@ -119,6 +120,7 @@ public class RefreshDnsForAllDomainsAction implements Runnable {
|
||||
TRANSACTION_REPEATABLE_READ, () -> refreshBatch(lastInPreviousBatchOpt, smear));
|
||||
lastInPreviousBatch = domainsBatch.isEmpty() ? null : getLast(domainsBatch);
|
||||
} while (domainsBatch.size() == batchSize);
|
||||
logger.atInfo().log("Finished enqueueing DNS refresh tasks.");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,7 +136,9 @@ public class RefreshDnsForAllDomainsAction implements Runnable {
|
||||
.setParameter("tlds", tlds)
|
||||
.setParameter("activeOrDeletedSince", activeOrDeletedSince)
|
||||
.getSingleResult();
|
||||
return Duration.standardSeconds(Math.max(activeDomains / refreshQps, 1));
|
||||
Duration smear = Duration.standardSeconds(Math.max(activeDomains / refreshQps, 1));
|
||||
logger.atInfo().log("Smearing %d domain DNS refresh tasks across %s.", activeDomains, smear);
|
||||
return smear;
|
||||
}
|
||||
|
||||
private ImmutableList<String> getBatch(Optional<String> lastInPreviousBatch) {
|
||||
|
||||
|
Before Width: | Height: | Size: 673 B |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 138 B |
|
Before Width: | Height: | Size: 138 B |
|
Before Width: | Height: | Size: 184 B |
|
Before Width: | Height: | Size: 1016 B |
|
Before Width: | Height: | Size: 309 B |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 535 B |
|
Before Width: | Height: | Size: 327 B |
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
|
||||
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
|
||||
]>
|
||||
<svg version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
|
||||
x="0px" y="0px" width="21px" height="21px" viewBox="0 0 21 21" overflow="visible" enable-background="new 0 0 21 21"
|
||||
xml:space="preserve">
|
||||
<defs>
|
||||
</defs>
|
||||
<path fill="#FFFFFF" d="M17.646,16.501L14,12.855c0.566-0.811,0.903-1.792,0.903-2.855c0-2.762-2.238-5-5-5c-2.761,0-5,2.238-5,5
|
||||
s2.239,5,5,5c1.064,0,2.045-0.337,2.856-0.903l3.646,3.646c0.343,0.343,0.898,0.344,1.241,0.001
|
||||
C17.99,17.399,17.99,16.845,17.646,16.501z M9.903,13.2c-1.767,0-3.199-1.433-3.199-3.2s1.433-3.2,3.199-3.2
|
||||
c1.77,0,3.199,1.433,3.199,3.2S11.673,13.2,9.903,13.2z"/>
|
||||
<rect opacity="0" fill="#4387FD" width="21" height="21"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
|
||||
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
|
||||
]>
|
||||
<svg version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
|
||||
x="0px" y="0px" width="21px" height="21px" viewBox="0 0 21 21" overflow="visible" enable-background="new 0 0 21 21"
|
||||
xml:space="preserve">
|
||||
<defs>
|
||||
</defs>
|
||||
<path fill="#FFFFFF" d="M17.646,16.501L14,12.855c0.566-0.811,0.903-1.792,0.903-2.855c0-2.762-2.238-5-5-5c-2.761,0-5,2.238-5,5
|
||||
s2.239,5,5,5c1.064,0,2.045-0.337,2.856-0.903l3.646,3.646c0.343,0.343,0.898,0.344,1.241,0.001
|
||||
C17.99,17.399,17.99,16.845,17.646,16.501z M9.903,13.2c-1.767,0-3.199-1.433-3.199-3.2s1.433-3.2,3.199-3.2
|
||||
c1.77,0,3.199,1.433,3.199,3.2S11.673,13.2,9.903,13.2z"/>
|
||||
<rect opacity="0" fill="#4387FD" width="21" height="21"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
|
||||
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
|
||||
]>
|
||||
<svg version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
|
||||
x="0px" y="0px" width="21px" height="21px" viewBox="0 0 21 21" overflow="visible" enable-background="new 0 0 21 21"
|
||||
xml:space="preserve">
|
||||
<defs>
|
||||
</defs>
|
||||
<path d="M16.895,11.64C16.96,11.269,17,10.89,17,10.5s-0.04-0.77-0.106-1.14l1.535-1.129l0.183-0.683l-1.5-2.598l-0.684-0.184
|
||||
l-1.738,0.762c-0.581-0.49-1.25-0.875-1.979-1.138L12.5,2.5L12,2H9L8.5,2.499L8.29,4.395C7.562,4.658,6.896,5.043,6.314,5.531
|
||||
L4.571,4.768L3.889,4.951l-1.5,2.598l0.183,0.684l1.535,1.129C4.04,9.731,4,10.111,4,10.5s0.04,0.769,0.106,1.139l-1.535,1.129
|
||||
l-0.183,0.684l1.5,2.598l0.682,0.184l1.744-0.764c0.58,0.488,1.248,0.873,1.975,1.137l0.21,1.896L9,19h3l0.5-0.5l0.21-1.892
|
||||
c0.729-0.263,1.398-0.648,1.979-1.138l1.738,0.762l0.684-0.184l1.499-2.598l-0.181-0.683L16.895,11.64z M14.15,10.5
|
||||
c0,2.016-1.635,3.65-3.65,3.65c-2.016,0-3.65-1.635-3.65-3.65s1.635-3.65,3.65-3.65C12.515,6.85,14.15,8.484,14.15,10.5z"/>
|
||||
<rect opacity="0" fill="#4387FD" width="21" height="21"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 556 B |
|
Before Width: | Height: | Size: 480 B |
@@ -1,14 +0,0 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<info>
|
||||
<contact:info
|
||||
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
|
||||
<contact:id>sh8013</contact:id>
|
||||
<contact:authInfo>
|
||||
<contact:pw>2fooBAR</contact:pw>
|
||||
</contact:authInfo>
|
||||
</contact:info>
|
||||
</info>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
@@ -1,14 +0,0 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<transfer op="request">
|
||||
<contact:transfer
|
||||
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
|
||||
<contact:id>sh8013</contact:id>
|
||||
<contact:authInfo>
|
||||
<contact:pw>2fooBAR</contact:pw>
|
||||
</contact:authInfo>
|
||||
</contact:transfer>
|
||||
</transfer>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
@@ -1,18 +0,0 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<domain:create
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:period unit="y">2</domain:period>
|
||||
<domain:registrant>jd1234</domain:registrant>
|
||||
<domain:contact type="admin">sh8013</domain:contact>
|
||||
<domain:contact type="tech">sh8013</domain:contact>
|
||||
<domain:authInfo>
|
||||
<domain:pw>2fooBAR</domain:pw>
|
||||
</domain:authInfo>
|
||||
</domain:create>
|
||||
</create>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
@@ -1,11 +0,0 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<delete>
|
||||
<domain:delete
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
</domain:delete>
|
||||
</delete>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
@@ -1,17 +0,0 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<domain:update
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example.tld</domain:name>
|
||||
<domain:chg/>
|
||||
</domain:update>
|
||||
</update>
|
||||
<extension>
|
||||
<rgp:update xmlns:rgp="urn:ietf:params:xml:ns:rgp-1.0">
|
||||
<rgp:restore op="request"/>
|
||||
</rgp:update>
|
||||
</extension>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
@@ -1,18 +0,0 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<login>
|
||||
<clID>NewRegistrar</clID>
|
||||
<pw>foo-BAR2</pw>
|
||||
<options>
|
||||
<version>1.0</version>
|
||||
<lang>en</lang>
|
||||
</options>
|
||||
<svcs>
|
||||
<objURI>urn:ietf:params:xml:ns:host-1.0</objURI>
|
||||
<objURI>urn:ietf:params:xml:ns:domain-1.0</objURI>
|
||||
<objURI>urn:ietf:params:xml:ns:contact-1.0</objURI>
|
||||
</svcs>
|
||||
</login>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
@@ -1,6 +0,0 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<logout/>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
@@ -1,6 +0,0 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<poll op="req"/>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
@@ -1,6 +1,6 @@
|
||||
<!doctype html>
|
||||
<meta http-equiv="refresh" content="0;URL=/registrar">
|
||||
<meta http-equiv="refresh" content="0;URL=/console">
|
||||
<title>Nomulus</title>
|
||||
<body lang="en-US">
|
||||
If this page doesn't change automatically, please go
|
||||
to <a href="/registrar">https://www.registry.google/registrar</a>
|
||||
to <a href="/console">https://www.registry.google/console</a>
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.ui.server;
|
||||
|
||||
import static com.google.common.base.Suppliers.memoize;
|
||||
import static com.google.common.io.Resources.asCharSource;
|
||||
import static com.google.common.io.Resources.getResource;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.io.Resources;
|
||||
import com.google.re2j.Matcher;
|
||||
import com.google.re2j.Pattern;
|
||||
import com.google.template.soy.SoyFileSet;
|
||||
import com.google.template.soy.SoyUtils;
|
||||
import com.google.template.soy.parseinfo.SoyFileInfo;
|
||||
import com.google.template.soy.shared.SoyCssRenamingMap;
|
||||
import com.google.template.soy.tofu.SoyTofu;
|
||||
import google.registry.ui.ConsoleDebug;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** Helper methods for rendering Soy templates from Java code. */
|
||||
public final class SoyTemplateUtils {
|
||||
|
||||
@VisibleForTesting
|
||||
public static final Supplier<SoyCssRenamingMap> CSS_RENAMING_MAP_SUPPLIER =
|
||||
SoyTemplateUtils.createCssRenamingMapSupplier(
|
||||
Resources.getResource("google/registry/ui/css/registrar_bin.css.js"),
|
||||
Resources.getResource("google/registry/ui/css/registrar_dbg.css.js"));
|
||||
|
||||
/** Returns a memoized supplier containing compiled tofu. */
|
||||
public static Supplier<SoyTofu> createTofuSupplier(final SoyFileInfo... soyInfos) {
|
||||
return memoize(
|
||||
() -> {
|
||||
ConsoleDebug debugMode = ConsoleDebug.get();
|
||||
SoyFileSet.Builder builder = SoyFileSet.builder();
|
||||
for (SoyFileInfo soyInfo : soyInfos) {
|
||||
builder.add(getResource(soyInfo.getClass(), soyInfo.getFileName()));
|
||||
}
|
||||
Map<String, Object> globals;
|
||||
try {
|
||||
globals =
|
||||
new HashMap<>(SoyUtils.parseCompileTimeGlobals(asCharSource(SOY_GLOBALS, UTF_8)));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to load soy globals", e);
|
||||
}
|
||||
globals.put("DEBUG", debugMode.ordinal());
|
||||
builder.setCompileTimeGlobals(globals);
|
||||
return builder.build().compileToTofu();
|
||||
});
|
||||
}
|
||||
|
||||
/** Returns a memoized supplier of the thing you pass to {@code setCssRenamingMap()}. */
|
||||
public static Supplier<SoyCssRenamingMap> createCssRenamingMapSupplier(
|
||||
final URL cssMap,
|
||||
final URL cssMapDebug) {
|
||||
return memoize(
|
||||
() -> {
|
||||
final ImmutableMap<String, String> renames = getCssRenames(cssMap, cssMapDebug);
|
||||
return (cssClassName) -> {
|
||||
List<String> result = new ArrayList<>();
|
||||
for (String part : CSS_CLASS_SPLITTER.split(cssClassName)) {
|
||||
result.add(renames.getOrDefault(part, part));
|
||||
}
|
||||
return CSS_CLASS_JOINER.join(result);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private static ImmutableMap<String, String> getCssRenames(URL cssMap, URL cssMapDebug) {
|
||||
try {
|
||||
return switch (ConsoleDebug.get()) {
|
||||
case RAW -> ImmutableMap.of(); // See firstNonNull() above for clarification.
|
||||
case DEBUG -> extractCssRenames(Resources.toString(cssMapDebug, UTF_8));
|
||||
default -> extractCssRenames(Resources.toString(cssMap, UTF_8));
|
||||
};
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to load css map", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract class name rewrites from a {@code .css.js} mapping file.
|
||||
*
|
||||
* <p>This is the file created when you pass {@code --css_renaming_output_file} to the Closure
|
||||
* Stylesheets compiler. In order for this to work, {@code --output_renaming_map_format} should
|
||||
* be {@code CLOSURE_COMPILED} or {@code CLOSURE_UNCOMPILED}.
|
||||
*
|
||||
* <p>Here's an example of what the {@code .css.js} file looks like:<pre>
|
||||
*
|
||||
* goog.setCssNameMapping({
|
||||
* "nonLatin": "a",
|
||||
* "secondary": "b",
|
||||
* "mobile": "c"
|
||||
* });</pre>
|
||||
*
|
||||
* <p>This is a burden that's only necessary for tofu, since the closure compiler is smart enough
|
||||
* to substitute CSS class names when soy is compiled to JavaScript.
|
||||
*/
|
||||
private static ImmutableMap<String, String> extractCssRenames(String json) {
|
||||
ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<>();
|
||||
Matcher matcher = KEY_VALUE_PATTERN.matcher(json);
|
||||
while (matcher.find()) {
|
||||
builder.put(matcher.group(1), matcher.group(2));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private static final URL SOY_GLOBALS = getResource("google/registry/ui/globals.txt");
|
||||
private static final Splitter CSS_CLASS_SPLITTER = Splitter.on('-');
|
||||
private static final Joiner CSS_CLASS_JOINER = Joiner.on('-');
|
||||
private static final Pattern KEY_VALUE_PATTERN =
|
||||
Pattern.compile("['\"]([^'\"]+)['\"]: ['\"]([^'\"]+)['\"]");
|
||||
|
||||
private SoyTemplateUtils() {}
|
||||
}
|
||||
@@ -16,10 +16,8 @@ package google.registry.ui.server.console;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.NEW_CONSOLE;
|
||||
import static google.registry.model.common.FeatureFlag.isActiveNow;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
@@ -35,19 +33,15 @@ import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.export.sheet.SyncRegistrarsSheetAction;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.model.registrar.RegistrarPocBase;
|
||||
import google.registry.request.HttpException;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
import google.registry.ui.server.registrar.ConsoleUiAction;
|
||||
import google.registry.util.DiffUtils;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
@@ -82,25 +76,6 @@ public abstract class ConsoleApiAction implements Runnable {
|
||||
}
|
||||
User user = consoleApiParams.authResult().user().get();
|
||||
|
||||
// This allows us to enable console to a selected cohort of users with release
|
||||
// We can ignore it in tests
|
||||
UserRoles userRoles = user.getUserRoles();
|
||||
boolean hasGlobalOrTestingRole =
|
||||
!GlobalRole.NONE.equals(userRoles.getGlobalRole())
|
||||
|| userRoles.hasPermission(
|
||||
registryAdminClientId, ConsolePermission.VIEW_REGISTRAR_DETAILS);
|
||||
|
||||
if (!hasGlobalOrTestingRole
|
||||
&& RegistryEnvironment.get() != RegistryEnvironment.UNITTEST
|
||||
&& tm().transact(() -> !isActiveNow(NEW_CONSOLE))) {
|
||||
try {
|
||||
consoleApiParams.response().sendRedirect(ConsoleUiAction.PATH);
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (consoleApiParams.request().getMethod().equals(GET.toString())) {
|
||||
getHandler(user);
|
||||
@@ -196,7 +171,8 @@ public abstract class ConsoleApiAction implements Runnable {
|
||||
// there's an update besides the lastUpdateTime
|
||||
cloudTasksUtils.enqueue(
|
||||
SyncRegistrarsSheetAction.QUEUE,
|
||||
cloudTasksUtils.createTask(SyncRegistrarsSheetAction.class, GET, ImmutableMultimap.of()));
|
||||
cloudTasksUtils.createTask(
|
||||
SyncRegistrarsSheetAction.class, POST, ImmutableMultimap.of()));
|
||||
}
|
||||
|
||||
String environment = Ascii.toLowerCase(String.valueOf(RegistryEnvironment.get()));
|
||||
@@ -208,10 +184,10 @@ public abstract class ConsoleApiAction implements Runnable {
|
||||
registrar.getRegistrarName(), registrar.getRegistrarId(), environment),
|
||||
String.format(
|
||||
"""
|
||||
The following changes were made in registry %s environment to the registrar %s by\
|
||||
%s:
|
||||
The following changes were made in registry %s environment to the registrar %s by\
|
||||
%s:
|
||||
|
||||
%s""",
|
||||
%s""",
|
||||
environment,
|
||||
registrar.getRegistrarId(),
|
||||
consoleApiParams.authResult().userIdForLogging(),
|
||||
|
||||
@@ -19,7 +19,6 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.ui.server.registrar.RegistryLockPostAction.VERIFICATION_EMAIL_TEMPLATE;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
|
||||
|
||||
@@ -64,6 +63,12 @@ import org.joda.time.Duration;
|
||||
public class ConsoleRegistryLockAction extends ConsoleApiAction {
|
||||
|
||||
static final String PATH = "/console-api/registry-lock";
|
||||
static final String VERIFICATION_EMAIL_TEMPLATE =
|
||||
"""
|
||||
Please click the link below to perform the lock / unlock action on domain %s. Note: this\
|
||||
code will expire in one hour.
|
||||
|
||||
%s""";
|
||||
|
||||
private final DomainLockUtils domainLockUtils;
|
||||
private final GmailClient gmailClient;
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
// Copyright 2024 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.ui.server.console;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.model.console.RegistrarRole.ACCOUNT_MANAGER;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
|
||||
import com.google.api.services.directory.Directory;
|
||||
import com.google.api.services.directory.model.UserName;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.GkeService;
|
||||
import google.registry.request.HttpException.BadRequestException;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.StringGenerator;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
@Action(
|
||||
service = Action.GaeService.DEFAULT,
|
||||
gkeService = GkeService.CONSOLE,
|
||||
path = ConsoleUsersAction.PATH,
|
||||
method = {GET, POST},
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public class ConsoleUsersAction extends ConsoleApiAction {
|
||||
static final String PATH = "/console-api/users";
|
||||
private static final int PASSWORD_LENGTH = 16;
|
||||
|
||||
private static final Splitter EMAIL_SPLITTER = Splitter.on('@').trimResults();
|
||||
|
||||
private final Gson gson;
|
||||
private final String registrarId;
|
||||
private final Directory directory;
|
||||
private final StringGenerator passwordGenerator;
|
||||
|
||||
@Inject
|
||||
public ConsoleUsersAction(
|
||||
ConsoleApiParams consoleApiParams,
|
||||
Gson gson,
|
||||
Directory directory,
|
||||
@Named("base58StringGenerator") StringGenerator passwordGenerator,
|
||||
@Parameter("registrarId") String registrarId) {
|
||||
super(consoleApiParams);
|
||||
this.gson = gson;
|
||||
this.registrarId = registrarId;
|
||||
this.directory = directory;
|
||||
this.passwordGenerator = passwordGenerator;
|
||||
}
|
||||
|
||||
private static String generateNewEmailAddress(User user, String increment) {
|
||||
List<String> emailParts = EMAIL_SPLITTER.splitToList(user.getEmailAddress());
|
||||
return String.format("%s-%s@%s", emailParts.get(0), increment, emailParts.get(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postHandler(User user) {
|
||||
// Temporary flag while testing
|
||||
if (user.getUserRoles().isAdmin()) {
|
||||
checkPermission(user, registrarId, ConsolePermission.MANAGE_USERS);
|
||||
tm().transact(() -> runInTransaction(user));
|
||||
} else {
|
||||
consoleApiParams.response().setStatus(SC_FORBIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getHandler(User user) {
|
||||
checkPermission(user, registrarId, ConsolePermission.MANAGE_USERS);
|
||||
List<ImmutableMap> users =
|
||||
getAllUsers().stream()
|
||||
.filter(u -> u.getUserRoles().getRegistrarRoles().containsKey(registrarId))
|
||||
.map(
|
||||
u ->
|
||||
ImmutableMap.of(
|
||||
"emailAddress",
|
||||
u.getEmailAddress(),
|
||||
"role",
|
||||
u.getUserRoles().getRegistrarRoles().get(registrarId)))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
consoleApiParams.response().setPayload(gson.toJson(users));
|
||||
consoleApiParams.response().setStatus(SC_OK);
|
||||
}
|
||||
|
||||
private void runInTransaction(User user) throws IOException {
|
||||
String nextAvailableIncrement =
|
||||
Stream.of("1", "2", "3")
|
||||
.filter(
|
||||
increment ->
|
||||
tm().loadByKeyIfPresent(
|
||||
VKey.create(User.class, generateNewEmailAddress(user, increment)))
|
||||
.isEmpty())
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new BadRequestException("Extra users amount is limited to 3"));
|
||||
|
||||
com.google.api.services.directory.model.User newUser =
|
||||
new com.google.api.services.directory.model.User();
|
||||
|
||||
newUser.setName(
|
||||
new UserName().setFamilyName(registrarId).setGivenName("User" + nextAvailableIncrement));
|
||||
newUser.setPassword(passwordGenerator.createString(PASSWORD_LENGTH));
|
||||
newUser.setPrimaryEmail(generateNewEmailAddress(user, nextAvailableIncrement));
|
||||
|
||||
try {
|
||||
directory.users().insert(newUser).execute();
|
||||
} catch (IOException e) {
|
||||
setFailedResponse("Failed to create the user workspace account", SC_INTERNAL_SERVER_ERROR);
|
||||
throw e;
|
||||
}
|
||||
|
||||
UserRoles userRoles =
|
||||
new UserRoles.Builder()
|
||||
.setRegistrarRoles(ImmutableMap.of(registrarId, ACCOUNT_MANAGER))
|
||||
.build();
|
||||
|
||||
User.Builder builder =
|
||||
new User.Builder().setUserRoles(userRoles).setEmailAddress(newUser.getPrimaryEmail());
|
||||
tm().put(builder.build());
|
||||
|
||||
consoleApiParams.response().setStatus(SC_OK);
|
||||
consoleApiParams
|
||||
.response()
|
||||
.setPayload(
|
||||
gson.toJson(
|
||||
ImmutableMap.of(
|
||||
"password",
|
||||
newUser.getPassword(),
|
||||
"emailAddress",
|
||||
newUser.getPrimaryEmail(),
|
||||
"role",
|
||||
ACCOUNT_MANAGER)));
|
||||
}
|
||||
|
||||
private ImmutableList<User> getAllUsers() {
|
||||
return tm().transact(
|
||||
() ->
|
||||
tm().loadAllOf(User.class).stream()
|
||||
.filter(u -> !u.getUserRoles().getRegistrarRoles().isEmpty())
|
||||
.collect(toImmutableList()));
|
||||
}
|
||||
}
|
||||
@@ -16,19 +16,24 @@ package google.registry.ui.server.console.settings;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.model.registrar.RegistrarPocBase.Type;
|
||||
import google.registry.persistence.transaction.QueryComposer.Comparator;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.GaeService;
|
||||
@@ -36,11 +41,14 @@ import google.registry.request.Action.GkeService;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.ui.forms.FormException;
|
||||
import google.registry.ui.server.RegistrarFormFields;
|
||||
import google.registry.ui.server.console.ConsoleApiAction;
|
||||
import google.registry.ui.server.console.ConsoleApiParams;
|
||||
import google.registry.ui.server.registrar.RegistrarSettingsAction;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.inject.Inject;
|
||||
|
||||
@Action(
|
||||
@@ -97,18 +105,20 @@ public class ContactAction extends ConsoleApiAction {
|
||||
String.format("Unknown registrar %s", registrarId)));
|
||||
|
||||
ImmutableSet<RegistrarPoc> oldContacts = registrar.getContacts();
|
||||
// TODO: @ptkach - refactor out contacts update functionality after RegistrarSettingsAction is
|
||||
// deprecated
|
||||
ImmutableSet<RegistrarPoc> updatedContacts =
|
||||
RegistrarSettingsAction.readContacts(
|
||||
registrar,
|
||||
oldContacts,
|
||||
Collections.singletonMap(
|
||||
"contacts",
|
||||
contacts.get().stream().map(RegistrarPoc::toJsonMap).collect(toImmutableList())));
|
||||
RegistrarFormFields.getRegistrarContactBuilders(
|
||||
oldContacts,
|
||||
Collections.singletonMap(
|
||||
"contacts",
|
||||
contacts.get().stream()
|
||||
.map(RegistrarPoc::toJsonMap)
|
||||
.collect(toImmutableList())))
|
||||
.stream()
|
||||
.map(builder -> builder.setRegistrar(registrar).build())
|
||||
.collect(toImmutableSet());
|
||||
|
||||
try {
|
||||
RegistrarSettingsAction.checkContactRequirements(oldContacts, updatedContacts);
|
||||
checkContactRequirements(oldContacts, updatedContacts);
|
||||
} catch (FormException e) {
|
||||
logger.atWarning().withCause(e).log(
|
||||
"Error processing contacts post request for registrar: %s", registrarId);
|
||||
@@ -127,4 +137,165 @@ public class ContactAction extends ConsoleApiAction {
|
||||
|
||||
consoleApiParams.response().setStatus(SC_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforces business logic checks on registrar contacts.
|
||||
*
|
||||
* @throws FormException if the checks fail.
|
||||
*/
|
||||
private static void checkContactRequirements(
|
||||
ImmutableSet<RegistrarPoc> existingContacts, ImmutableSet<RegistrarPoc> updatedContacts) {
|
||||
// Check that no two contacts use the same email address.
|
||||
Set<String> emails = new HashSet<>();
|
||||
for (RegistrarPoc contact : updatedContacts) {
|
||||
if (!emails.add(contact.getEmailAddress())) {
|
||||
throw new ContactRequirementException(
|
||||
String.format(
|
||||
"One email address (%s) cannot be used for multiple contacts",
|
||||
contact.getEmailAddress()));
|
||||
}
|
||||
}
|
||||
// Check that required contacts don't go away, once they are set.
|
||||
Multimap<Type, RegistrarPoc> oldContactsByType = HashMultimap.create();
|
||||
for (RegistrarPoc contact : existingContacts) {
|
||||
for (Type t : contact.getTypes()) {
|
||||
oldContactsByType.put(t, contact);
|
||||
}
|
||||
}
|
||||
Multimap<Type, RegistrarPoc> newContactsByType = HashMultimap.create();
|
||||
for (RegistrarPoc contact : updatedContacts) {
|
||||
for (Type t : contact.getTypes()) {
|
||||
newContactsByType.put(t, contact);
|
||||
}
|
||||
}
|
||||
for (Type t : difference(oldContactsByType.keySet(), newContactsByType.keySet())) {
|
||||
if (t.isRequired()) {
|
||||
throw new ContactRequirementException(t);
|
||||
}
|
||||
}
|
||||
ensurePhoneNumberNotRemovedForContactTypes(oldContactsByType, newContactsByType, Type.TECH);
|
||||
Optional<RegistrarPoc> domainWhoisAbuseContact =
|
||||
getDomainWhoisVisibleAbuseContact(updatedContacts);
|
||||
// If the new set has a domain WHOIS abuse contact, it must have a phone number.
|
||||
if (domainWhoisAbuseContact.isPresent()
|
||||
&& domainWhoisAbuseContact.get().getPhoneNumber() == null) {
|
||||
throw new ContactRequirementException(
|
||||
"The abuse contact visible in domain WHOIS query must have a phone number");
|
||||
}
|
||||
// If there was a domain WHOIS abuse contact in the old set, the new set must have one.
|
||||
if (getDomainWhoisVisibleAbuseContact(existingContacts).isPresent()
|
||||
&& domainWhoisAbuseContact.isEmpty()) {
|
||||
throw new ContactRequirementException(
|
||||
"An abuse contact visible in domain WHOIS query must be designated");
|
||||
}
|
||||
checkContactRegistryLockRequirements(existingContacts, updatedContacts);
|
||||
}
|
||||
|
||||
private static void checkContactRegistryLockRequirements(
|
||||
ImmutableSet<RegistrarPoc> existingContacts, ImmutableSet<RegistrarPoc> updatedContacts) {
|
||||
// Any contact(s) with new passwords must be allowed to set them
|
||||
for (RegistrarPoc updatedContact : updatedContacts) {
|
||||
if (updatedContact.isRegistryLockAllowed()
|
||||
|| updatedContact.isAllowedToSetRegistryLockPassword()) {
|
||||
RegistrarPoc existingContact =
|
||||
existingContacts.stream()
|
||||
.filter(
|
||||
contact -> contact.getEmailAddress().equals(updatedContact.getEmailAddress()))
|
||||
.findFirst()
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new FormException(
|
||||
"Cannot set registry lock password directly on new contact"));
|
||||
// Can't modify registry lock email address
|
||||
if (!Objects.equals(
|
||||
updatedContact.getRegistryLockEmailAddress(),
|
||||
existingContact.getRegistryLockEmailAddress())) {
|
||||
throw new FormException("Cannot modify registryLockEmailAddress through the UI");
|
||||
}
|
||||
if (updatedContact.isRegistryLockAllowed()) {
|
||||
// the password must have been set before or the user was allowed to set it now
|
||||
if (!existingContact.isAllowedToSetRegistryLockPassword()
|
||||
&& !existingContact.isRegistryLockAllowed()) {
|
||||
throw new FormException("Registrar contact not allowed to set registry lock password");
|
||||
}
|
||||
}
|
||||
if (updatedContact.isAllowedToSetRegistryLockPassword()) {
|
||||
if (!existingContact.isAllowedToSetRegistryLockPassword()) {
|
||||
throw new FormException(
|
||||
"Cannot modify isAllowedToSetRegistryLockPassword through the UI");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Any previously-existing contacts with registry lock enabled cannot be deleted
|
||||
existingContacts.stream()
|
||||
.filter(RegistrarPoc::isRegistryLockAllowed)
|
||||
.forEach(
|
||||
contact -> {
|
||||
Optional<RegistrarPoc> updatedContactOptional =
|
||||
updatedContacts.stream()
|
||||
.filter(
|
||||
updatedContact ->
|
||||
updatedContact.getEmailAddress().equals(contact.getEmailAddress()))
|
||||
.findFirst();
|
||||
if (updatedContactOptional.isEmpty()) {
|
||||
throw new FormException(
|
||||
String.format(
|
||||
"Cannot delete the contact %s that has registry lock enabled",
|
||||
contact.getEmailAddress()));
|
||||
}
|
||||
if (!updatedContactOptional.get().isRegistryLockAllowed()) {
|
||||
throw new FormException(
|
||||
String.format(
|
||||
"Cannot remove the ability to use registry lock on the contact %s",
|
||||
contact.getEmailAddress()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the registrar contact whose phone number and email address is visible in domain WHOIS
|
||||
* query as abuse contact (if any).
|
||||
*
|
||||
* <p>Frontend processing ensures that only one contact can be set as abuse contact in domain
|
||||
* WHOIS record.
|
||||
*
|
||||
* <p>Therefore, it is possible to return inside the loop once one such contact is found.
|
||||
*/
|
||||
private static Optional<RegistrarPoc> getDomainWhoisVisibleAbuseContact(
|
||||
Set<RegistrarPoc> contacts) {
|
||||
return contacts.stream().filter(RegistrarPoc::getVisibleInDomainWhoisAsAbuse).findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that for each given registrar type, a phone number is present after update, if there was
|
||||
* one before.
|
||||
*/
|
||||
private static void ensurePhoneNumberNotRemovedForContactTypes(
|
||||
Multimap<Type, RegistrarPoc> oldContactsByType,
|
||||
Multimap<Type, RegistrarPoc> newContactsByType,
|
||||
Type... types) {
|
||||
for (Type type : types) {
|
||||
if (oldContactsByType.get(type).stream().anyMatch(contact -> contact.getPhoneNumber() != null)
|
||||
&& newContactsByType.get(type).stream()
|
||||
.noneMatch(contact -> contact.getPhoneNumber() != null)) {
|
||||
throw new ContactRequirementException(
|
||||
String.format(
|
||||
"Please provide a phone number for at least one %s contact",
|
||||
type.getDisplayName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Thrown when a set of contacts doesn't meet certain constraints. */
|
||||
private static class ContactRequirementException extends FormException {
|
||||
ContactRequirementException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
ContactRequirementException(Type type) {
|
||||
super(String.format("Must have at least one %s contact", type.getDisplayName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,210 +0,0 @@
|
||||
// Copyright 2018 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.ui.server.registrar;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static google.registry.ui.server.SoyTemplateUtils.CSS_RENAMING_MAP_SUPPLIER;
|
||||
import static google.registry.util.RegistryEnvironment.PRODUCTION;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN;
|
||||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.template.soy.tofu.SoyTofu;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.OteAccountBuilder;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.GaeService;
|
||||
import google.registry.request.Action.Method;
|
||||
import google.registry.request.HttpException.BadRequestException;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.tools.IamClient;
|
||||
import google.registry.ui.server.SendEmailUtils;
|
||||
import google.registry.ui.server.SoyTemplateUtils;
|
||||
import google.registry.ui.soy.registrar.OteSetupConsoleSoyInfo;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import google.registry.util.StringGenerator;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
/**
|
||||
* Action that serves OT&E setup web page.
|
||||
*
|
||||
* <p>This Action does 2 things: - for GET, just returns the form that asks for the clientId and
|
||||
* email. - for POST, receives the clientId and email and generates the OTE entities.
|
||||
*
|
||||
* <p>TODO(b/120201577): once we can have 2 different Actions with the same path (different
|
||||
* Methods), separate this class to 2 Actions.
|
||||
*/
|
||||
@Action(
|
||||
service = GaeService.DEFAULT,
|
||||
path = ConsoleOteSetupAction.PATH,
|
||||
method = {Method.POST, Method.GET},
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public final class ConsoleOteSetupAction extends HtmlAction {
|
||||
|
||||
public static final String PATH = "/registrar-ote-setup";
|
||||
private static final int PASSWORD_LENGTH = 16;
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private static final Supplier<SoyTofu> TOFU_SUPPLIER =
|
||||
SoyTemplateUtils.createTofuSupplier(
|
||||
google.registry.ui.soy.registrar.AnalyticsSoyInfo.getInstance(),
|
||||
google.registry.ui.soy.registrar.ConsoleSoyInfo.getInstance(),
|
||||
google.registry.ui.soy.registrar.ConsoleUtilsSoyInfo.getInstance(),
|
||||
google.registry.ui.soy.registrar.FormsSoyInfo.getInstance(),
|
||||
google.registry.ui.soy.registrar.OteSetupConsoleSoyInfo.getInstance());
|
||||
|
||||
@Inject AuthenticatedRegistrarAccessor registrarAccessor;
|
||||
@Inject SendEmailUtils sendEmailUtils;
|
||||
|
||||
@Inject
|
||||
@Named("base58StringGenerator")
|
||||
StringGenerator passwordGenerator;
|
||||
|
||||
@Inject
|
||||
@Parameter("consoleClientId")
|
||||
Optional<String> clientId;
|
||||
|
||||
@Inject
|
||||
@Parameter("email")
|
||||
Optional<String> email;
|
||||
|
||||
@Inject
|
||||
@Parameter("password")
|
||||
Optional<String> optionalPassword;
|
||||
|
||||
@Inject CloudTasksUtils cloudTasksUtils;
|
||||
|
||||
@Inject IamClient iamClient;
|
||||
|
||||
@Inject
|
||||
@Config("gSuiteConsoleUserGroupEmailAddress")
|
||||
Optional<String> maybeGroupEmailAddress;
|
||||
|
||||
@Inject
|
||||
ConsoleOteSetupAction() {}
|
||||
|
||||
@Override
|
||||
public void runAfterLogin(Map<String, Object> data) {
|
||||
checkState(
|
||||
!RegistryEnvironment.get().equals(PRODUCTION), "Can't create OT&E in prod");
|
||||
|
||||
if (!registrarAccessor.isAdmin()) {
|
||||
response.setStatus(SC_FORBIDDEN);
|
||||
response.setPayload(
|
||||
TOFU_SUPPLIER
|
||||
.get()
|
||||
.newRenderer(OteSetupConsoleSoyInfo.WHOAREYOU)
|
||||
.setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get())
|
||||
.setData(data)
|
||||
.render());
|
||||
return;
|
||||
}
|
||||
switch (method) {
|
||||
case POST -> runPost(data);
|
||||
case GET -> runGet(data);
|
||||
default ->
|
||||
throw new BadRequestException(
|
||||
String.format("Action cannot be called with method %s", method));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return PATH;
|
||||
}
|
||||
|
||||
private void runPost(Map<String, Object> data) {
|
||||
try {
|
||||
checkState(clientId.isPresent() && email.isPresent(), "Must supply clientId and email");
|
||||
|
||||
data.put("baseClientId", clientId.get());
|
||||
data.put("contactEmail", email.get());
|
||||
|
||||
String password = optionalPassword.orElse(passwordGenerator.createString(PASSWORD_LENGTH));
|
||||
OteAccountBuilder oteAccountBuilder =
|
||||
OteAccountBuilder.forRegistrarId(clientId.get())
|
||||
.addUser(email.get())
|
||||
.setPassword(password);
|
||||
ImmutableMap<String, String> clientIdToTld = oteAccountBuilder.buildAndPersist();
|
||||
|
||||
oteAccountBuilder.grantIapPermission(maybeGroupEmailAddress, cloudTasksUtils, iamClient);
|
||||
|
||||
sendExternalUpdates(clientIdToTld);
|
||||
|
||||
data.put("clientIdToTld", clientIdToTld);
|
||||
data.put("password", password);
|
||||
|
||||
response.setPayload(
|
||||
TOFU_SUPPLIER
|
||||
.get()
|
||||
.newRenderer(OteSetupConsoleSoyInfo.RESULT_SUCCESS)
|
||||
.setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get())
|
||||
.setData(data)
|
||||
.render());
|
||||
} catch (Throwable e) {
|
||||
logger.atWarning().withCause(e).log(
|
||||
"Failed to setup OT&E. clientId: %s, email: %s", clientId.get(), email.get());
|
||||
data.put("errorMessage", e.getMessage());
|
||||
response.setPayload(
|
||||
TOFU_SUPPLIER
|
||||
.get()
|
||||
.newRenderer(OteSetupConsoleSoyInfo.FORM_PAGE)
|
||||
.setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get())
|
||||
.setData(data)
|
||||
.render());
|
||||
}
|
||||
}
|
||||
|
||||
private void runGet(Map<String, Object> data) {
|
||||
// set the values to pre-fill, if given
|
||||
data.put("baseClientId", clientId.orElse(null));
|
||||
data.put("contactEmail", email.orElse(null));
|
||||
|
||||
response.setPayload(
|
||||
TOFU_SUPPLIER
|
||||
.get()
|
||||
.newRenderer(OteSetupConsoleSoyInfo.FORM_PAGE)
|
||||
.setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get())
|
||||
.setData(data)
|
||||
.render());
|
||||
}
|
||||
|
||||
private void sendExternalUpdates(ImmutableMap<String, String> clientIdToTld) {
|
||||
if (!sendEmailUtils.hasRecipients()) {
|
||||
return;
|
||||
}
|
||||
String environment = Ascii.toLowerCase(String.valueOf(RegistryEnvironment.get()));
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(
|
||||
String.format(
|
||||
"The following entities were created in %s by %s:\n",
|
||||
environment, registrarAccessor.userIdForLogging()));
|
||||
clientIdToTld.forEach(
|
||||
(clientId, tld) ->
|
||||
builder.append(
|
||||
String.format(" Registrar %s with access to TLD %s\n", clientId, tld)));
|
||||
builder.append(String.format("Gave user %s web access to these Registrars\n", email.get()));
|
||||
sendEmailUtils.sendEmail(
|
||||
String.format("OT&E for registrar %s created in %s", clientId.get(), environment),
|
||||
builder.toString());
|
||||
}
|
||||
}
|
||||
@@ -1,331 +0,0 @@
|
||||
// Copyright 2018 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.ui.server.registrar;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.ui.server.SoyTemplateUtils.CSS_RENAMING_MAP_SUPPLIER;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN;
|
||||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.template.soy.tofu.SoyTofu;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarAddress;
|
||||
import google.registry.model.registrar.RegistrarBase.State;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.GaeService;
|
||||
import google.registry.request.Action.Method;
|
||||
import google.registry.request.HttpException.BadRequestException;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.tools.IamClient;
|
||||
import google.registry.ui.server.SendEmailUtils;
|
||||
import google.registry.ui.server.SoyTemplateUtils;
|
||||
import google.registry.ui.soy.registrar.AnalyticsSoyInfo;
|
||||
import google.registry.ui.soy.registrar.ConsoleSoyInfo;
|
||||
import google.registry.ui.soy.registrar.ConsoleUtilsSoyInfo;
|
||||
import google.registry.ui.soy.registrar.FormsSoyInfo;
|
||||
import google.registry.ui.soy.registrar.RegistrarCreateConsoleSoyInfo;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import google.registry.util.StringGenerator;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
|
||||
/**
|
||||
* Action that serves Registrar creation page.
|
||||
*
|
||||
* <p>This Action does 2 things: - for GET, just returns the form that asks for the required
|
||||
* information. - for POST, receives the information and creates the Registrar.
|
||||
*/
|
||||
@Action(
|
||||
service = GaeService.DEFAULT,
|
||||
path = ConsoleRegistrarCreatorAction.PATH,
|
||||
method = {Method.POST, Method.GET},
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public final class ConsoleRegistrarCreatorAction extends HtmlAction {
|
||||
|
||||
private static final int PASSWORD_LENGTH = 16;
|
||||
private static final int PASSCODE_LENGTH = 5;
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
public static final String PATH = "/registrar-create";
|
||||
|
||||
private static final Supplier<SoyTofu> TOFU_SUPPLIER =
|
||||
SoyTemplateUtils.createTofuSupplier(
|
||||
AnalyticsSoyInfo.getInstance(),
|
||||
ConsoleSoyInfo.getInstance(),
|
||||
ConsoleUtilsSoyInfo.getInstance(),
|
||||
FormsSoyInfo.getInstance(),
|
||||
RegistrarCreateConsoleSoyInfo.getInstance());
|
||||
|
||||
@Inject AuthenticatedRegistrarAccessor registrarAccessor;
|
||||
@Inject SendEmailUtils sendEmailUtils;
|
||||
@Inject @Named("base58StringGenerator") StringGenerator passwordGenerator;
|
||||
@Inject @Named("digitOnlyStringGenerator") StringGenerator passcodeGenerator;
|
||||
|
||||
@Inject
|
||||
@Parameter("consoleClientId")
|
||||
Optional<String> clientId;
|
||||
|
||||
@Inject
|
||||
@Parameter("consoleName")
|
||||
Optional<String> name;
|
||||
|
||||
@Inject CloudTasksUtils cloudTasksUtils;
|
||||
|
||||
@Inject IamClient iamClient;
|
||||
|
||||
@Inject
|
||||
@Config("gSuiteConsoleUserGroupEmailAddress")
|
||||
Optional<String> maybeGroupEmailAddress;
|
||||
|
||||
@Inject @Parameter("billingAccount") Optional<String> billingAccount;
|
||||
@Inject @Parameter("ianaId") Optional<Integer> ianaId;
|
||||
@Inject @Parameter("referralEmail") Optional<String> referralEmail;
|
||||
@Inject @Parameter("driveId") Optional<String> driveId;
|
||||
@Inject @Parameter("consoleUserEmail") Optional<String> consoleUserEmail;
|
||||
|
||||
// Address fields, some of which are required and others are optional.
|
||||
@Inject @Parameter("street1") Optional<String> street1;
|
||||
@Inject @Parameter("street2") Optional<String> optionalStreet2;
|
||||
@Inject @Parameter("street3") Optional<String> optionalStreet3;
|
||||
@Inject @Parameter("city") Optional<String> city;
|
||||
@Inject @Parameter("state") Optional<String> optionalState;
|
||||
@Inject @Parameter("zip") Optional<String> optionalZip;
|
||||
@Inject @Parameter("countryCode") Optional<String> countryCode;
|
||||
|
||||
@Inject @Parameter("password") Optional<String> optionalPassword;
|
||||
@Inject @Parameter("passcode") Optional<String> optionalPasscode;
|
||||
|
||||
@Inject ConsoleRegistrarCreatorAction() {}
|
||||
|
||||
@Override
|
||||
public void runAfterLogin(Map<String, Object> data) {
|
||||
if (!registrarAccessor.isAdmin()) {
|
||||
response.setStatus(SC_FORBIDDEN);
|
||||
response.setPayload(
|
||||
TOFU_SUPPLIER
|
||||
.get()
|
||||
.newRenderer(RegistrarCreateConsoleSoyInfo.WHOAREYOU)
|
||||
.setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get())
|
||||
.setData(data)
|
||||
.render());
|
||||
return;
|
||||
}
|
||||
switch (method) {
|
||||
case POST -> runPost(data);
|
||||
case GET -> runGet(data);
|
||||
default ->
|
||||
throw new BadRequestException(
|
||||
String.format("Action cannot be called with method %s", method));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return PATH;
|
||||
}
|
||||
|
||||
private static void checkPresent(Optional<?> value, String name) {
|
||||
checkState(value.isPresent(), "Missing value for %s", name);
|
||||
}
|
||||
|
||||
private static final Splitter LINE_SPLITTER =
|
||||
Splitter.onPattern("\r?\n").trimResults().omitEmptyStrings();
|
||||
|
||||
private static final Splitter ENTRY_SPLITTER =
|
||||
Splitter.on('=').trimResults().omitEmptyStrings().limit(2);
|
||||
|
||||
private static ImmutableMap<CurrencyUnit, String> parseBillingAccount(String billingAccount) {
|
||||
try {
|
||||
return LINE_SPLITTER.splitToList(billingAccount).stream()
|
||||
.map(ENTRY_SPLITTER::splitToList)
|
||||
.peek(
|
||||
list ->
|
||||
checkState(
|
||||
list.size() == 2,
|
||||
"Can't parse line %s. The format should be [currency]=[account ID]",
|
||||
list))
|
||||
.collect(
|
||||
toImmutableMap(
|
||||
list -> CurrencyUnit.of(Ascii.toUpperCase(list.getFirst())),
|
||||
list -> list.get(1)));
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Error parsing billing accounts - " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void runPost(Map<String, Object> data) {
|
||||
try {
|
||||
checkPresent(clientId, "clientId");
|
||||
checkPresent(name, "name");
|
||||
checkPresent(billingAccount, "billingAccount");
|
||||
checkPresent(ianaId, "ianaId");
|
||||
checkPresent(referralEmail, "referralEmail");
|
||||
checkPresent(driveId, "driveId");
|
||||
checkPresent(consoleUserEmail, "consoleUserEmail");
|
||||
checkPresent(street1, "street");
|
||||
checkPresent(city, "city");
|
||||
checkPresent(countryCode, "countryCode");
|
||||
|
||||
data.put("clientId", clientId.get());
|
||||
data.put("name", name.get());
|
||||
data.put("ianaId", ianaId.get());
|
||||
data.put("referralEmail", referralEmail.get());
|
||||
data.put("billingAccount", billingAccount.get());
|
||||
data.put("driveId", driveId.get());
|
||||
data.put("consoleUserEmail", consoleUserEmail.get());
|
||||
|
||||
data.put("street1", street1.get());
|
||||
optionalStreet2.ifPresent(street2 -> data.put("street2", street2));
|
||||
optionalStreet3.ifPresent(street3 -> data.put("street3", street3));
|
||||
data.put("city", city.get());
|
||||
optionalState.ifPresent(state -> data.put("state", state));
|
||||
optionalZip.ifPresent(zip -> data.put("zip", zip));
|
||||
data.put("countryCode", countryCode.get());
|
||||
|
||||
String password = optionalPassword.orElse(passwordGenerator.createString(PASSWORD_LENGTH));
|
||||
String phonePasscode =
|
||||
optionalPasscode.orElse(passcodeGenerator.createString(PASSCODE_LENGTH));
|
||||
Registrar registrar =
|
||||
new Registrar.Builder()
|
||||
.setRegistrarId(clientId.get())
|
||||
.setRegistrarName(name.get())
|
||||
.setBillingAccountMap(parseBillingAccount(billingAccount.get()))
|
||||
.setIanaIdentifier(Long.valueOf(ianaId.get()))
|
||||
.setIcannReferralEmail(referralEmail.get())
|
||||
.setEmailAddress(referralEmail.get())
|
||||
.setDriveFolderId(driveId.get())
|
||||
.setType(Registrar.Type.REAL)
|
||||
.setPassword(password)
|
||||
.setPhonePasscode(phonePasscode)
|
||||
.setState(State.PENDING)
|
||||
.setLocalizedAddress(
|
||||
new RegistrarAddress.Builder()
|
||||
.setStreet(
|
||||
Stream.of(street1, optionalStreet2, optionalStreet3)
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.collect(toImmutableList()))
|
||||
.setCity(city.get())
|
||||
.setState(optionalState.orElse(null))
|
||||
.setCountryCode(countryCode.get())
|
||||
.setZip(optionalZip.orElse(null))
|
||||
.build())
|
||||
.build();
|
||||
User user =
|
||||
new User.Builder()
|
||||
.setEmailAddress(consoleUserEmail.get())
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder()
|
||||
.setRegistrarRoles(
|
||||
ImmutableMap.of(
|
||||
registrar.getRegistrarId(), RegistrarRole.ACCOUNT_MANAGER))
|
||||
.build())
|
||||
.build();
|
||||
tm().transact(
|
||||
() -> {
|
||||
checkState(
|
||||
Registrar.loadByRegistrarId(registrar.getRegistrarId()).isEmpty(),
|
||||
"Registrar with client ID %s already exists",
|
||||
registrar.getRegistrarId());
|
||||
tm().put(registrar);
|
||||
tm().put(user);
|
||||
});
|
||||
User.grantIapPermission(
|
||||
user.getEmailAddress(), maybeGroupEmailAddress, cloudTasksUtils, null, iamClient);
|
||||
data.put("password", password);
|
||||
data.put("passcode", phonePasscode);
|
||||
|
||||
sendExternalUpdates();
|
||||
response.setPayload(
|
||||
TOFU_SUPPLIER
|
||||
.get()
|
||||
.newRenderer(RegistrarCreateConsoleSoyInfo.RESULT_SUCCESS)
|
||||
.setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get())
|
||||
.setData(data)
|
||||
.render());
|
||||
} catch (Throwable e) {
|
||||
logger.atWarning().withCause(e).log(
|
||||
"Failed to create registrar. clientId: %s, data: %s", clientId.get(), data);
|
||||
data.put("errorMessage", e.getMessage());
|
||||
response.setPayload(
|
||||
TOFU_SUPPLIER
|
||||
.get()
|
||||
.newRenderer(RegistrarCreateConsoleSoyInfo.FORM_PAGE)
|
||||
.setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get())
|
||||
.setData(data)
|
||||
.render());
|
||||
}
|
||||
}
|
||||
|
||||
private void runGet(Map<String, Object> data) {
|
||||
// set the values to pre-fill, if given
|
||||
data.put("clientId", clientId.orElse(null));
|
||||
data.put("name", name.orElse(null));
|
||||
data.put("ianaId", ianaId.orElse(null));
|
||||
data.put("referralEmail", referralEmail.orElse(null));
|
||||
data.put("driveId", driveId.orElse(null));
|
||||
data.put("consoleUserEmail", consoleUserEmail.orElse(null));
|
||||
|
||||
response.setPayload(
|
||||
TOFU_SUPPLIER
|
||||
.get()
|
||||
.newRenderer(RegistrarCreateConsoleSoyInfo.FORM_PAGE)
|
||||
.setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get())
|
||||
.setData(data)
|
||||
.render());
|
||||
}
|
||||
|
||||
private static String toEmailLine(Optional<?> value, String name) {
|
||||
return String.format(" %s: %s\n", name, value.orElse(null));
|
||||
}
|
||||
private void sendExternalUpdates() {
|
||||
if (!sendEmailUtils.hasRecipients()) {
|
||||
return;
|
||||
}
|
||||
String environment = Ascii.toLowerCase(String.valueOf(RegistryEnvironment.get()));
|
||||
String body =
|
||||
String.format(
|
||||
"The following registrar was created in %s by %s:\n",
|
||||
environment, registrarAccessor.userIdForLogging())
|
||||
+ toEmailLine(clientId, "clientId")
|
||||
+ toEmailLine(name, "name")
|
||||
+ toEmailLine(billingAccount, "billingAccount")
|
||||
+ toEmailLine(ianaId, "ianaId")
|
||||
+ toEmailLine(referralEmail, "referralEmail")
|
||||
+ toEmailLine(driveId, "driveId")
|
||||
+ String.format("Gave user %s web access to the registrar\n", consoleUserEmail.get());
|
||||
sendEmailUtils.sendEmail(
|
||||
String.format("Registrar %s created in %s", clientId.get(), environment), body);
|
||||
}
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.ui.server.registrar;
|
||||
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.auth.AuthenticatedRegistrarAccessor.Role.ADMIN;
|
||||
import static google.registry.request.auth.AuthenticatedRegistrarAccessor.Role.OWNER;
|
||||
import static google.registry.ui.server.SoyTemplateUtils.CSS_RENAMING_MAP_SUPPLIER;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_PERMANENT_REDIRECT;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.template.soy.data.SoyMapData;
|
||||
import com.google.template.soy.tofu.SoyTofu;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.GaeService;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor.RegistrarAccessDeniedException;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor.Role;
|
||||
import google.registry.ui.server.SoyTemplateUtils;
|
||||
import google.registry.ui.soy.registrar.ConsoleSoyInfo;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Action that serves Registrar Console single HTML page (SPA). */
|
||||
@Action(
|
||||
service = GaeService.DEFAULT,
|
||||
path = ConsoleUiAction.PATH,
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public final class ConsoleUiAction extends HtmlAction {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
public static final String PATH = "/registrar";
|
||||
|
||||
private static final Supplier<SoyTofu> TOFU_SUPPLIER =
|
||||
SoyTemplateUtils.createTofuSupplier(
|
||||
google.registry.ui.soy.registrar.AnalyticsSoyInfo.getInstance(),
|
||||
google.registry.ui.soy.registrar.ConsoleSoyInfo.getInstance(),
|
||||
google.registry.ui.soy.registrar.ConsoleUtilsSoyInfo.getInstance());
|
||||
|
||||
@Inject RegistrarConsoleMetrics registrarConsoleMetrics;
|
||||
@Inject AuthenticatedRegistrarAccessor registrarAccessor;
|
||||
|
||||
@Inject
|
||||
@Config("integrationEmail")
|
||||
String integrationEmail;
|
||||
|
||||
@Inject
|
||||
@Config("supportEmail")
|
||||
String supportEmail;
|
||||
|
||||
@Inject
|
||||
@Config("announcementsEmail")
|
||||
String announcementsEmail;
|
||||
|
||||
@Inject
|
||||
@Config("supportPhoneNumber")
|
||||
String supportPhoneNumber;
|
||||
|
||||
@Inject
|
||||
@Config("technicalDocsUrl")
|
||||
String technicalDocsUrl;
|
||||
|
||||
@Inject
|
||||
@Config("registrarConsoleEnabled")
|
||||
boolean enabled;
|
||||
|
||||
@Inject
|
||||
@Parameter("consoleClientId")
|
||||
Optional<String> paramClientId;
|
||||
|
||||
@Inject
|
||||
ConsoleUiAction() {}
|
||||
|
||||
@Override
|
||||
public void runAfterLogin(Map<String, Object> data) {
|
||||
SoyMapData soyMapData = new SoyMapData();
|
||||
data.forEach((key, value) -> soyMapData.put(key, value));
|
||||
|
||||
soyMapData.put("integrationEmail", integrationEmail);
|
||||
soyMapData.put("supportEmail", supportEmail);
|
||||
soyMapData.put("announcementsEmail", announcementsEmail);
|
||||
soyMapData.put("supportPhoneNumber", supportPhoneNumber);
|
||||
soyMapData.put("technicalDocsUrl", technicalDocsUrl);
|
||||
|
||||
if (!enabled) {
|
||||
response.setStatus(SC_SERVICE_UNAVAILABLE);
|
||||
response.setPayload(
|
||||
TOFU_SUPPLIER
|
||||
.get()
|
||||
.newRenderer(ConsoleSoyInfo.DISABLED)
|
||||
.setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get())
|
||||
.setData(soyMapData)
|
||||
.render());
|
||||
return;
|
||||
}
|
||||
|
||||
// Set permanent redirect to the new console for tech support
|
||||
if (isNullOrEmpty(req.getParameter("redirect"))
|
||||
&& Stream.of(GlobalRole.SUPPORT_LEAD, GlobalRole.SUPPORT_AGENT)
|
||||
.anyMatch(
|
||||
globalRole ->
|
||||
globalRole.equals(authResult.user().get().getUserRoles().getGlobalRole()))) {
|
||||
response.setStatus(SC_PERMANENT_REDIRECT);
|
||||
try {
|
||||
response.sendRedirect("/console");
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
ImmutableSetMultimap<String, Role> roleMap = registrarAccessor.getAllRegistrarIdsWithRoles();
|
||||
soyMapData.put("allClientIds", roleMap.keySet());
|
||||
soyMapData.put("environment", RegistryEnvironment.get().toString());
|
||||
boolean newConsole =
|
||||
tm().transact(
|
||||
() ->
|
||||
FeatureFlag.getUncached(FeatureFlag.FeatureName.NEW_CONSOLE)
|
||||
.map(
|
||||
flag ->
|
||||
flag.getStatus(tm().getTransactionTime())
|
||||
.equals(FeatureFlag.FeatureStatus.ACTIVE))
|
||||
.orElse(false));
|
||||
soyMapData.put("includeDeprecationWarning", newConsole);
|
||||
// We set the initial value to the value that will show if guessClientId throws.
|
||||
String clientId = "<null>";
|
||||
try {
|
||||
clientId = paramClientId.orElse(registrarAccessor.guessRegistrarId());
|
||||
soyMapData.put("clientId", clientId);
|
||||
soyMapData.put("isOwner", roleMap.containsEntry(clientId, OWNER));
|
||||
soyMapData.put("isAdmin", roleMap.containsEntry(clientId, ADMIN));
|
||||
|
||||
// We want to load the registrar even if we won't use it later (even if we remove the
|
||||
// requireFeeExtension) - to make sure the user indeed has access to the guessed registrar.
|
||||
//
|
||||
// Note that not doing so (and just passing the "clientId" as given) isn't a security issue
|
||||
// since we double-check the access to the registrar on any read / update request. We have to
|
||||
// - since the access might get revoked between the initial page load and the request! (also
|
||||
// because the requests come from the browser, and can easily be faked)
|
||||
registrarAccessor.getRegistrar(clientId);
|
||||
} catch (RegistrarAccessDeniedException e) {
|
||||
logger.atWarning().withCause(e).log(
|
||||
"User %s doesn't have access to registrar console.", authResult.userIdForLogging());
|
||||
response.setStatus(SC_FORBIDDEN);
|
||||
response.setPayload(
|
||||
TOFU_SUPPLIER
|
||||
.get()
|
||||
.newRenderer(ConsoleSoyInfo.WHOAREYOU)
|
||||
.setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get())
|
||||
.setData(soyMapData)
|
||||
.render());
|
||||
registrarConsoleMetrics.registerConsoleRequest(
|
||||
clientId, paramClientId.isPresent(), roleMap.get(clientId), "FORBIDDEN");
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
registrarConsoleMetrics.registerConsoleRequest(
|
||||
clientId, paramClientId.isPresent(), roleMap.get(clientId), "UNEXPECTED ERROR");
|
||||
throw e;
|
||||
}
|
||||
|
||||
String payload =
|
||||
TOFU_SUPPLIER
|
||||
.get()
|
||||
.newRenderer(ConsoleSoyInfo.MAIN)
|
||||
.setCssRenamingMap(CSS_RENAMING_MAP_SUPPLIER.get())
|
||||
.setData(soyMapData)
|
||||
.render();
|
||||
response.setPayload(payload);
|
||||
registrarConsoleMetrics.registerConsoleRequest(
|
||||
clientId, paramClientId.isPresent(), roleMap.get(clientId), "SUCCESS");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return PATH;
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
// Copyright 2019 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.ui.server.registrar;
|
||||
|
||||
import static com.google.common.net.HttpHeaders.X_FRAME_OPTIONS;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
|
||||
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.RequestMethod;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Handles some of the nitty-gritty of responding to requests that should return HTML, including
|
||||
* login, redirects, analytics, and some headers.
|
||||
*
|
||||
* <p>If the user is not logged in, this will redirect to the login URL.
|
||||
*/
|
||||
public abstract class HtmlAction implements Runnable {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
@Inject HttpServletRequest req;
|
||||
@Inject Response response;
|
||||
@Inject XsrfTokenManager xsrfTokenManager;
|
||||
@Inject AuthResult authResult;
|
||||
@Inject @RequestMethod Action.Method method;
|
||||
|
||||
@Inject
|
||||
@Config("logoFilename")
|
||||
String logoFilename;
|
||||
|
||||
@Inject
|
||||
@Config("productName")
|
||||
String productName;
|
||||
|
||||
@Inject
|
||||
@Config("analyticsConfig")
|
||||
Map<String, Object> analyticsConfig;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
response.setHeader(X_FRAME_OPTIONS, "SAMEORIGIN"); // Disallow iframing.
|
||||
response.setHeader("X-Ui-Compatible", "IE=edge"); // Ask IE not to be silly.
|
||||
|
||||
if (authResult.user().isEmpty()) {
|
||||
response.setStatus(SC_UNAUTHORIZED);
|
||||
return;
|
||||
}
|
||||
response.setContentType(MediaType.HTML_UTF_8);
|
||||
|
||||
User user = authResult.user().get();
|
||||
// Using HashMap to allow null values
|
||||
HashMap<String, Object> data = new HashMap<>();
|
||||
data.put("logoFilename", logoFilename);
|
||||
data.put("productName", productName);
|
||||
data.put("username", user.getEmailAddress());
|
||||
data.put("logoutUrl", "/registrar?gcp-iap-mode=CLEAR_LOGIN_COOKIE");
|
||||
data.put("analyticsConfig", analyticsConfig);
|
||||
data.put("xsrfToken", xsrfTokenManager.generateToken(user.getEmailAddress()));
|
||||
|
||||
logger.atInfo().log(
|
||||
"User %s is accessing %s with method %s.",
|
||||
authResult.userIdForLogging(), getClass().getName(), method);
|
||||
runAfterLogin(data);
|
||||
}
|
||||
|
||||
public abstract void runAfterLogin(Map<String, Object> data);
|
||||
|
||||
public abstract String getPath();
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
// Copyright 2019 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.ui.server.registrar;
|
||||
|
||||
/**
|
||||
* Marker interface for {@link google.registry.request.Action}s that serve GET requests and return
|
||||
* JSON, rather than HTML.
|
||||
*/
|
||||
public interface JsonGetAction extends Runnable {}
|
||||
@@ -1,126 +0,0 @@
|
||||
// Copyright 2019 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.ui.server.registrar;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.security.JsonResponseHelper.Status.ERROR;
|
||||
import static google.registry.security.JsonResponseHelper.Status.SUCCESS;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.OteAccountBuilder;
|
||||
import google.registry.model.OteStats;
|
||||
import google.registry.model.OteStats.StatType;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarBase.Type;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.GaeService;
|
||||
import google.registry.request.JsonActionRunner;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor.RegistrarAccessDeniedException;
|
||||
import google.registry.security.JsonResponseHelper;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Admin servlet that allows creating or updating a registrar. Deletes are not allowed so as to
|
||||
* preserve history.
|
||||
*/
|
||||
@Action(
|
||||
service = GaeService.DEFAULT,
|
||||
path = OteStatusAction.PATH,
|
||||
method = Action.Method.POST,
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public final class OteStatusAction implements Runnable, JsonActionRunner.JsonAction {
|
||||
|
||||
public static final String PATH = "/registrar-ote-status";
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private static final String CLIENT_ID_PARAM = "clientId";
|
||||
private static final String COMPLETED_PARAM = "completed";
|
||||
private static final String DETAILS_PARAM = "details";
|
||||
private static final String STAT_TYPE_DESCRIPTION_PARAM = "description";
|
||||
private static final String STAT_TYPE_REQUIREMENT_PARAM = "requirement";
|
||||
private static final String STAT_TYPE_TIMES_PERFORMED_PARAM = "timesPerformed";
|
||||
|
||||
@Inject AuthenticatedRegistrarAccessor registrarAccessor;
|
||||
@Inject JsonActionRunner jsonActionRunner;
|
||||
|
||||
@Inject
|
||||
OteStatusAction() {}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
jsonActionRunner.run(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ?> handleJsonRequest(Map<String, ?> input) {
|
||||
try {
|
||||
checkArgument(input != null, "Malformed JSON");
|
||||
|
||||
String oteClientId = (String) input.get(CLIENT_ID_PARAM);
|
||||
checkArgument(
|
||||
!Strings.isNullOrEmpty(oteClientId), "Missing key for OT&E client: %s", CLIENT_ID_PARAM);
|
||||
|
||||
String baseClientId = OteAccountBuilder.getBaseRegistrarId(oteClientId);
|
||||
Registrar oteRegistrar = registrarAccessor.getRegistrar(oteClientId);
|
||||
verifyOteAccess(baseClientId);
|
||||
checkArgument(
|
||||
Type.OTE.equals(oteRegistrar.getType()),
|
||||
"Registrar with ID %s is not an OT&E registrar",
|
||||
oteClientId);
|
||||
|
||||
OteStats oteStats = OteStats.getFromRegistrar(baseClientId);
|
||||
return JsonResponseHelper.create(
|
||||
SUCCESS, "OT&E check completed successfully", convertOteStats(baseClientId, oteStats));
|
||||
} catch (Throwable e) {
|
||||
logger.atWarning().withCause(e).log(
|
||||
"Failed to verify OT&E status for registrar with input: %s", input);
|
||||
return JsonResponseHelper.create(
|
||||
ERROR, Optional.ofNullable(e.getMessage()).orElse("Unspecified error"));
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyOteAccess(String baseClientId) throws RegistrarAccessDeniedException {
|
||||
for (String oteClientId : OteAccountBuilder.createRegistrarIdToTldMap(baseClientId).keySet()) {
|
||||
registrarAccessor.verifyAccess(oteClientId);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> convertOteStats(String baseClientId, OteStats oteStats) {
|
||||
return ImmutableMap.of(
|
||||
CLIENT_ID_PARAM, baseClientId,
|
||||
COMPLETED_PARAM, oteStats.getFailures().isEmpty(),
|
||||
DETAILS_PARAM,
|
||||
StatType.REQUIRED_STAT_TYPES.stream()
|
||||
.map(statType -> convertSingleRequirement(statType, oteStats.getCount(statType)))
|
||||
.collect(toImmutableList()));
|
||||
}
|
||||
|
||||
private Map<String, Object> convertSingleRequirement(StatType statType, int count) {
|
||||
int requirement = statType.getRequirement();
|
||||
return ImmutableMap.of(
|
||||
STAT_TYPE_DESCRIPTION_PARAM, statType.getDescription(),
|
||||
STAT_TYPE_REQUIREMENT_PARAM, requirement,
|
||||
STAT_TYPE_TIMES_PERFORMED_PARAM, count,
|
||||
COMPLETED_PARAM, count >= requirement);
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
// Copyright 2018 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.ui.server.registrar;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.monitoring.metrics.IncrementableMetric;
|
||||
import com.google.monitoring.metrics.LabelDescriptor;
|
||||
import com.google.monitoring.metrics.MetricRegistryImpl;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor.Role;
|
||||
import javax.inject.Inject;
|
||||
|
||||
final class RegistrarConsoleMetrics {
|
||||
|
||||
private static final ImmutableSet<LabelDescriptor> CONSOLE_LABEL_DESCRIPTORS =
|
||||
ImmutableSet.of(
|
||||
LabelDescriptor.create("clientId", "target registrar client ID"),
|
||||
LabelDescriptor.create("explicitClientId", "whether the client ID is set explicitly"),
|
||||
LabelDescriptor.create("role", "Role[s] of the user making the request"),
|
||||
LabelDescriptor.create("status", "whether the request is successful"));
|
||||
|
||||
static final IncrementableMetric consoleRequestMetric =
|
||||
MetricRegistryImpl.getDefault()
|
||||
.newIncrementableMetric(
|
||||
"/console/registrar/console_requests",
|
||||
"Count of /registrar requests",
|
||||
"count",
|
||||
CONSOLE_LABEL_DESCRIPTORS);
|
||||
|
||||
private static final ImmutableSet<LabelDescriptor> SETTINGS_LABEL_DESCRIPTORS =
|
||||
ImmutableSet.of(
|
||||
LabelDescriptor.create("clientId", "target registrar client ID"),
|
||||
LabelDescriptor.create("action", "action performed"),
|
||||
LabelDescriptor.create("role", "Role[s] of the user making the request"),
|
||||
LabelDescriptor.create("status", "whether the request is successful"));
|
||||
|
||||
static final IncrementableMetric settingsRequestMetric =
|
||||
MetricRegistryImpl.getDefault()
|
||||
.newIncrementableMetric(
|
||||
"/console/registrar/setting_requests",
|
||||
"Count of /registrar-settings requests",
|
||||
"count",
|
||||
SETTINGS_LABEL_DESCRIPTORS);
|
||||
|
||||
@Inject
|
||||
public RegistrarConsoleMetrics() {}
|
||||
|
||||
void registerConsoleRequest(
|
||||
String clientId, boolean explicitClientId, ImmutableSet<Role> roles, String status) {
|
||||
consoleRequestMetric.increment(
|
||||
clientId, String.valueOf(explicitClientId), String.valueOf(roles), status);
|
||||
}
|
||||
|
||||
void registerSettingsRequest(
|
||||
String clientId, String action, ImmutableSet<Role> roles, String status) {
|
||||
settingsRequestMetric.increment(clientId, action, String.valueOf(roles), status);
|
||||
}
|
||||
}
|
||||
@@ -1,641 +0,0 @@
|
||||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.ui.server.registrar;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.security.JsonResponseHelper.Status.ERROR;
|
||||
import static google.registry.security.JsonResponseHelper.Status.SUCCESS;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
import static google.registry.util.RegistryEnvironment.PRODUCTION;
|
||||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.export.sheet.SyncRegistrarsSheetAction;
|
||||
import google.registry.flows.certs.CertificateChecker;
|
||||
import google.registry.flows.certs.CertificateChecker.InsecureCertificateException;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.model.registrar.RegistrarPocBase.Type;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.GaeService;
|
||||
import google.registry.request.HttpException.BadRequestException;
|
||||
import google.registry.request.HttpException.ForbiddenException;
|
||||
import google.registry.request.JsonActionRunner;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor.RegistrarAccessDeniedException;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor.Role;
|
||||
import google.registry.security.JsonResponseHelper;
|
||||
import google.registry.ui.forms.FormException;
|
||||
import google.registry.ui.forms.FormFieldException;
|
||||
import google.registry.ui.server.RegistrarFormFields;
|
||||
import google.registry.ui.server.SendEmailUtils;
|
||||
import google.registry.util.CollectionUtils;
|
||||
import google.registry.util.DiffUtils;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Admin servlet that allows creating or updating a registrar. Deletes are not allowed so as to
|
||||
* preserve history.
|
||||
*/
|
||||
@Action(
|
||||
service = GaeService.DEFAULT,
|
||||
path = RegistrarSettingsAction.PATH,
|
||||
method = Action.Method.POST,
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonAction {
|
||||
|
||||
public static final String PATH = "/registrar-settings";
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
static final String OP_PARAM = "op";
|
||||
static final String ARGS_PARAM = "args";
|
||||
static final String ID_PARAM = "id";
|
||||
|
||||
@Inject JsonActionRunner jsonActionRunner;
|
||||
@Inject RegistrarConsoleMetrics registrarConsoleMetrics;
|
||||
@Inject SendEmailUtils sendEmailUtils;
|
||||
@Inject AuthenticatedRegistrarAccessor registrarAccessor;
|
||||
@Inject AuthResult authResult;
|
||||
@Inject CertificateChecker certificateChecker;
|
||||
@Inject CloudTasksUtils cloudTasksUtils;
|
||||
|
||||
@Inject RegistrarSettingsAction() {}
|
||||
|
||||
private static boolean hasPhone(RegistrarPoc contact) {
|
||||
return contact.getPhoneNumber() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
jsonActionRunner.run(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> handleJsonRequest(Map<String, ?> input) {
|
||||
if (input == null) {
|
||||
throw new BadRequestException("Malformed JSON");
|
||||
}
|
||||
|
||||
String registrarId = (String) input.get(ID_PARAM);
|
||||
if (Strings.isNullOrEmpty(registrarId)) {
|
||||
throw new BadRequestException(String.format("Missing key for resource ID: %s", ID_PARAM));
|
||||
}
|
||||
|
||||
// Process the operation. Though originally derived from a CRUD
|
||||
// handler, registrar-settings really only supports read and update.
|
||||
String op = Optional.ofNullable((String) input.get(OP_PARAM)).orElse("read");
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, ?> args =
|
||||
(Map<String, Object>)
|
||||
Optional.<Object>ofNullable(input.get(ARGS_PARAM)).orElse(ImmutableMap.of());
|
||||
|
||||
logger.atInfo().log(
|
||||
"Received request '%s' on registrar '%s' with args %s", op, registrarId, args);
|
||||
String status = "SUCCESS";
|
||||
try {
|
||||
return switch (op) {
|
||||
case "update" -> update(args, registrarId).toJsonResponse();
|
||||
case "read" -> read(registrarId).toJsonResponse();
|
||||
default -> throw new IllegalArgumentException("Unknown or unsupported operation: " + op);
|
||||
};
|
||||
} catch (Throwable e) {
|
||||
logger.atWarning().withCause(e).log(
|
||||
"Failed to perform operation '%s' on registrar '%s' for args %s", op, registrarId, args);
|
||||
status = "ERROR: " + e.getClass().getSimpleName();
|
||||
if (e instanceof FormFieldException formFieldException) {
|
||||
return JsonResponseHelper.createFormFieldError(
|
||||
formFieldException.getMessage(), formFieldException.getFieldName());
|
||||
}
|
||||
return JsonResponseHelper.create(
|
||||
ERROR, Optional.ofNullable(e.getMessage()).orElse("Unspecified error"));
|
||||
} finally {
|
||||
registrarConsoleMetrics.registerSettingsRequest(
|
||||
registrarId, op, registrarAccessor.getRolesForRegistrar(registrarId), status);
|
||||
}
|
||||
}
|
||||
|
||||
record RegistrarResult(String message, Registrar registrar) {
|
||||
|
||||
Map<String, Object> toJsonResponse() {
|
||||
return JsonResponseHelper.create(SUCCESS, message(), registrar().toJsonMap());
|
||||
}
|
||||
|
||||
static RegistrarResult create(String message, Registrar registrar) {
|
||||
return new RegistrarResult(message, registrar);
|
||||
}
|
||||
}
|
||||
|
||||
record EmailInfo(
|
||||
Registrar registrar,
|
||||
Registrar updatedRegistrar,
|
||||
ImmutableSet<RegistrarPoc> contacts,
|
||||
ImmutableSet<RegistrarPoc> updatedContacts) {
|
||||
|
||||
static EmailInfo create(
|
||||
Registrar registrar,
|
||||
Registrar updatedRegistrar,
|
||||
ImmutableSet<RegistrarPoc> contacts,
|
||||
ImmutableSet<RegistrarPoc> updatedContacts) {
|
||||
return new EmailInfo(registrar, updatedRegistrar, contacts, updatedContacts);
|
||||
}
|
||||
}
|
||||
|
||||
private RegistrarResult read(String registrarId) {
|
||||
return RegistrarResult.create("Success", loadRegistrarUnchecked(registrarId));
|
||||
}
|
||||
|
||||
private Registrar loadRegistrarUnchecked(String registrarId) {
|
||||
try {
|
||||
return registrarAccessor.getRegistrar(registrarId);
|
||||
} catch (RegistrarAccessDeniedException e) {
|
||||
throw new ForbiddenException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private RegistrarResult update(final Map<String, ?> args, String registrarId) {
|
||||
// Email the updates
|
||||
sendExternalUpdatesIfNecessary(tm().transact(() -> saveUpdates(args, registrarId)));
|
||||
// Reload the result outside the transaction to get the most recent version
|
||||
return RegistrarResult.create("Saved " + registrarId, loadRegistrarUnchecked(registrarId));
|
||||
}
|
||||
|
||||
/** Saves the updates and returns info needed for the update email */
|
||||
private EmailInfo saveUpdates(final Map<String, ?> args, String registrarId) {
|
||||
// We load the registrar here rather than outside the transaction - to make
|
||||
// sure we have the latest version. This one is loaded inside the transaction, so it's
|
||||
// guaranteed to not change before we update it.
|
||||
Registrar registrar = loadRegistrarUnchecked(registrarId);
|
||||
// Detach the registrar to avoid Hibernate object-updates, since we wish to email
|
||||
// out the diffs between the existing and updated registrar objects
|
||||
tm().getEntityManager().detach(registrar);
|
||||
// Verify that the registrar hasn't been changed.
|
||||
// To do that - we find the latest update time (or null if the registrar has been
|
||||
// deleted) and compare to the update time from the args. The update time in the args
|
||||
// comes from the read that gave the UI the data - if it's out of date, then the UI
|
||||
// had out of date data.
|
||||
DateTime latest = registrar.getLastUpdateTime();
|
||||
DateTime latestFromArgs = RegistrarFormFields.LAST_UPDATE_TIME.extractUntyped(args).get();
|
||||
if (!latestFromArgs.equals(latest)) {
|
||||
logger.atWarning().log(
|
||||
"Registrar changed since reading the data!"
|
||||
+ " Last updated at %s, but args data last updated at %s.",
|
||||
latest, latestFromArgs);
|
||||
throw new IllegalStateException(
|
||||
"Registrar has been changed by someone else. Please reload and retry.");
|
||||
}
|
||||
|
||||
// Keep the current contacts, so we can later check that no required contact was
|
||||
// removed, email the changes to the contacts
|
||||
ImmutableSet<RegistrarPoc> contacts = registrar.getContacts();
|
||||
|
||||
Registrar updatedRegistrar = registrar;
|
||||
// Do OWNER only updates to the registrar from the request.
|
||||
updatedRegistrar = checkAndUpdateOwnerControlledFields(updatedRegistrar, args);
|
||||
// Do ADMIN only updates to the registrar from the request.
|
||||
updatedRegistrar = checkAndUpdateAdminControlledFields(updatedRegistrar, args);
|
||||
|
||||
// read the contacts from the request.
|
||||
ImmutableSet<RegistrarPoc> updatedContacts = readContacts(registrar, contacts, args);
|
||||
|
||||
// Save the updated contacts
|
||||
if (!updatedContacts.equals(contacts)) {
|
||||
if (!registrarAccessor.hasRoleOnRegistrar(Role.OWNER, registrar.getRegistrarId())) {
|
||||
throw new ForbiddenException("Only OWNERs can update the contacts");
|
||||
}
|
||||
checkContactRequirements(contacts, updatedContacts);
|
||||
RegistrarPoc.updateContacts(updatedRegistrar, updatedContacts);
|
||||
updatedRegistrar = updatedRegistrar.asBuilder().setContactsRequireSyncing(true).build();
|
||||
}
|
||||
|
||||
// Save the updated registrar
|
||||
if (!updatedRegistrar.equals(registrar)) {
|
||||
tm().put(updatedRegistrar);
|
||||
}
|
||||
return EmailInfo.create(registrar, updatedRegistrar, contacts, updatedContacts);
|
||||
}
|
||||
|
||||
private Map<String, Object> expandRegistrarWithContacts(
|
||||
Iterable<RegistrarPoc> contacts, Registrar registrar) {
|
||||
ImmutableSet<Map<String, Object>> expandedContacts =
|
||||
Streams.stream(contacts)
|
||||
.map(RegistrarPoc::toDiffableFieldMap)
|
||||
// Note: per the javadoc, toDiffableFieldMap includes sensitive data, but we don't want
|
||||
// to display it here
|
||||
.peek(
|
||||
map -> {
|
||||
map.remove("registryLockPasswordHash");
|
||||
map.remove("registryLockPasswordSalt");
|
||||
})
|
||||
.collect(toImmutableSet());
|
||||
// Use LinkedHashMap here to preserve ordering; null values mean we can't use ImmutableMap.
|
||||
LinkedHashMap<String, Object> result = new LinkedHashMap<>(registrar.toDiffableFieldMap());
|
||||
result.put("contacts", expandedContacts);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates registrar with the OWNER-controlled args from the http request.
|
||||
*
|
||||
* <p>If any changes were made and the user isn't an OWNER - throws a {@link ForbiddenException}.
|
||||
*/
|
||||
private Registrar checkAndUpdateOwnerControlledFields(
|
||||
Registrar initialRegistrar, Map<String, ?> args) {
|
||||
|
||||
Registrar.Builder builder = initialRegistrar.asBuilder();
|
||||
|
||||
// WHOIS
|
||||
//
|
||||
// Because of how whoisServer handles "default value", it's possible that setting the existing
|
||||
// value will still change the Registrar. So we first check whether the value has changed.
|
||||
//
|
||||
// The problem is - if the Registrar has a "null" whoisServer value, the console gets the
|
||||
// "default value" instead of the actual (null) value.
|
||||
// This was done so we display the "default" value, but it also means that it always looks like
|
||||
// the user updated the whoisServer value from "null" to the default value.
|
||||
//
|
||||
// TODO(b/119913848):once a null whoisServer value is sent to the console as "null", there's no
|
||||
// need to check for equality before setting the value in the builder.
|
||||
String updatedWhoisServer =
|
||||
RegistrarFormFields.WHOIS_SERVER_FIELD.extractUntyped(args).orElse(null);
|
||||
if (!Objects.equals(initialRegistrar.getWhoisServer(), updatedWhoisServer)) {
|
||||
builder.setWhoisServer(updatedWhoisServer);
|
||||
}
|
||||
builder.setUrl(RegistrarFormFields.URL_FIELD.extractUntyped(args).orElse(null));
|
||||
|
||||
// If the email is already null / empty - we can keep it so. But if it's set - it's required to
|
||||
// remain set.
|
||||
(Strings.isNullOrEmpty(initialRegistrar.getEmailAddress())
|
||||
? RegistrarFormFields.EMAIL_ADDRESS_FIELD_OPTIONAL
|
||||
: RegistrarFormFields.EMAIL_ADDRESS_FIELD_REQUIRED)
|
||||
.extractUntyped(args)
|
||||
.ifPresent(builder::setEmailAddress);
|
||||
builder.setPhoneNumber(
|
||||
RegistrarFormFields.PHONE_NUMBER_FIELD.extractUntyped(args).orElse(null));
|
||||
builder.setFaxNumber(RegistrarFormFields.FAX_NUMBER_FIELD.extractUntyped(args).orElse(null));
|
||||
builder.setLocalizedAddress(
|
||||
RegistrarFormFields.L10N_ADDRESS_FIELD.extractUntyped(args).orElse(null));
|
||||
|
||||
// Security
|
||||
builder.setIpAddressAllowList(
|
||||
RegistrarFormFields.IP_ADDRESS_ALLOW_LIST_FIELD
|
||||
.extractUntyped(args)
|
||||
.orElse(ImmutableList.of()));
|
||||
|
||||
Optional<String> certificateString =
|
||||
RegistrarFormFields.CLIENT_CERTIFICATE_FIELD.extractUntyped(args);
|
||||
if (certificateString.isPresent()) {
|
||||
if (validateCertificate(initialRegistrar.getClientCertificate(), certificateString.get())) {
|
||||
builder.setClientCertificate(certificateString.get(), tm().getTransactionTime());
|
||||
}
|
||||
}
|
||||
|
||||
Optional<String> failoverCertificateString =
|
||||
RegistrarFormFields.FAILOVER_CLIENT_CERTIFICATE_FIELD.extractUntyped(args);
|
||||
if (failoverCertificateString.isPresent()) {
|
||||
if (validateCertificate(
|
||||
initialRegistrar.getFailoverClientCertificate(), failoverCertificateString.get())) {
|
||||
builder.setFailoverClientCertificate(
|
||||
failoverCertificateString.get(), tm().getTransactionTime());
|
||||
}
|
||||
}
|
||||
|
||||
return checkNotChangedUnlessAllowed(builder, initialRegistrar, Role.OWNER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the registrar should accept the new certificate. Returns false if the
|
||||
* certificate is already the one stored for the registrar.
|
||||
*/
|
||||
private boolean validateCertificate(
|
||||
Optional<String> existingCertificate, String certificateString) {
|
||||
if (existingCertificate.isEmpty() || !existingCertificate.get().equals(certificateString)) {
|
||||
try {
|
||||
certificateChecker.validateCertificate(certificateString);
|
||||
} catch (InsecureCertificateException e) {
|
||||
throw new IllegalArgumentException(e.getMessage());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a registrar with the ADMIN-controlled args from the http request.
|
||||
*
|
||||
* <p>If any changes were made and the user isn't an ADMIN - throws a {@link ForbiddenException}.
|
||||
*/
|
||||
private Registrar checkAndUpdateAdminControlledFields(
|
||||
Registrar initialRegistrar, Map<String, ?> args) {
|
||||
Registrar.Builder builder = initialRegistrar.asBuilder();
|
||||
|
||||
Set<String> updatedAllowedTlds =
|
||||
RegistrarFormFields.ALLOWED_TLDS_FIELD.extractUntyped(args).orElse(ImmutableSet.of());
|
||||
// Temporarily block anyone from removing an allowed TLD.
|
||||
// This is so we can start having Support users use the console in production before we finish
|
||||
// implementing configurable access control.
|
||||
// TODO(b/119549884): remove this code once configurable access control is implemented.
|
||||
if (!Sets.difference(initialRegistrar.getAllowedTlds(), updatedAllowedTlds).isEmpty()) {
|
||||
throw new ForbiddenException("Can't remove allowed TLDs using the console.");
|
||||
}
|
||||
if (!Sets.difference(updatedAllowedTlds, initialRegistrar.getAllowedTlds()).isEmpty()) {
|
||||
// If a REAL registrar isn't in compliance with regard to having an abuse contact set,
|
||||
// prevent addition of allowed TLDs until that's fixed.
|
||||
if (Registrar.Type.REAL.equals(initialRegistrar.getType())
|
||||
&& PRODUCTION.equals(RegistryEnvironment.get())) {
|
||||
checkArgumentPresent(
|
||||
initialRegistrar.getWhoisAbuseContact(),
|
||||
"Cannot add allowed TLDs if there is no WHOIS abuse contact set.");
|
||||
}
|
||||
}
|
||||
builder.setAllowedTlds(updatedAllowedTlds);
|
||||
return checkNotChangedUnlessAllowed(builder, initialRegistrar, Role.ADMIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure {@code builder.build}is different from {@code originalRegistrar} only if we have the
|
||||
* correct role.
|
||||
*
|
||||
* <p>On success, returns {@code builder.build()}.
|
||||
*/
|
||||
private Registrar checkNotChangedUnlessAllowed(
|
||||
Registrar.Builder builder, Registrar originalRegistrar, Role allowedRole) {
|
||||
Registrar updatedRegistrar = builder.build();
|
||||
if (updatedRegistrar.equals(originalRegistrar)) {
|
||||
return updatedRegistrar;
|
||||
}
|
||||
checkArgument(
|
||||
updatedRegistrar.getRegistrarId().equals(originalRegistrar.getRegistrarId()),
|
||||
"Can't change clientId (%s -> %s)",
|
||||
originalRegistrar.getRegistrarId(),
|
||||
updatedRegistrar.getRegistrarId());
|
||||
if (registrarAccessor.hasRoleOnRegistrar(allowedRole, originalRegistrar.getRegistrarId())) {
|
||||
return updatedRegistrar;
|
||||
}
|
||||
Map<?, ?> diffs =
|
||||
DiffUtils.deepDiff(
|
||||
originalRegistrar.toDiffableFieldMap(), updatedRegistrar.toDiffableFieldMap(), true);
|
||||
|
||||
// It's expected that the update timestamp will be changed, as it gets reset whenever we change
|
||||
// nested collections. If it's the only change, just return the original registrar.
|
||||
if (diffs.keySet().equals(ImmutableSet.of("lastUpdateTime"))) {
|
||||
return originalRegistrar;
|
||||
}
|
||||
|
||||
throw new ForbiddenException(
|
||||
String.format("Unauthorized: only %s can change fields %s", allowedRole, diffs.keySet()));
|
||||
}
|
||||
|
||||
/** Reads the contacts from the supplied args. */
|
||||
public static ImmutableSet<RegistrarPoc> readContacts(
|
||||
Registrar registrar, ImmutableSet<RegistrarPoc> existingContacts, Map<String, ?> args) {
|
||||
return RegistrarFormFields.getRegistrarContactBuilders(existingContacts, args).stream()
|
||||
.map(builder -> builder.setRegistrar(registrar).build())
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforces business logic checks on registrar contacts.
|
||||
*
|
||||
* @throws FormException if the checks fail.
|
||||
*/
|
||||
public static void checkContactRequirements(
|
||||
ImmutableSet<RegistrarPoc> existingContacts, ImmutableSet<RegistrarPoc> updatedContacts) {
|
||||
// Check that no two contacts use the same email address.
|
||||
Set<String> emails = new HashSet<>();
|
||||
for (RegistrarPoc contact : updatedContacts) {
|
||||
if (!emails.add(contact.getEmailAddress())) {
|
||||
throw new ContactRequirementException(
|
||||
String.format(
|
||||
"One email address (%s) cannot be used for multiple contacts",
|
||||
contact.getEmailAddress()));
|
||||
}
|
||||
}
|
||||
// Check that required contacts don't go away, once they are set.
|
||||
Multimap<Type, RegistrarPoc> oldContactsByType = HashMultimap.create();
|
||||
for (RegistrarPoc contact : existingContacts) {
|
||||
for (Type t : contact.getTypes()) {
|
||||
oldContactsByType.put(t, contact);
|
||||
}
|
||||
}
|
||||
Multimap<Type, RegistrarPoc> newContactsByType = HashMultimap.create();
|
||||
for (RegistrarPoc contact : updatedContacts) {
|
||||
for (Type t : contact.getTypes()) {
|
||||
newContactsByType.put(t, contact);
|
||||
}
|
||||
}
|
||||
for (Type t : difference(oldContactsByType.keySet(), newContactsByType.keySet())) {
|
||||
if (t.isRequired()) {
|
||||
throw new ContactRequirementException(t);
|
||||
}
|
||||
}
|
||||
ensurePhoneNumberNotRemovedForContactTypes(oldContactsByType, newContactsByType, Type.TECH);
|
||||
Optional<RegistrarPoc> domainWhoisAbuseContact =
|
||||
getDomainWhoisVisibleAbuseContact(updatedContacts);
|
||||
// If the new set has a domain WHOIS abuse contact, it must have a phone number.
|
||||
if (domainWhoisAbuseContact.isPresent()
|
||||
&& domainWhoisAbuseContact.get().getPhoneNumber() == null) {
|
||||
throw new ContactRequirementException(
|
||||
"The abuse contact visible in domain WHOIS query must have a phone number");
|
||||
}
|
||||
// If there was a domain WHOIS abuse contact in the old set, the new set must have one.
|
||||
if (getDomainWhoisVisibleAbuseContact(existingContacts).isPresent()
|
||||
&& domainWhoisAbuseContact.isEmpty()) {
|
||||
throw new ContactRequirementException(
|
||||
"An abuse contact visible in domain WHOIS query must be designated");
|
||||
}
|
||||
checkContactRegistryLockRequirements(existingContacts, updatedContacts);
|
||||
}
|
||||
|
||||
private static void checkContactRegistryLockRequirements(
|
||||
ImmutableSet<RegistrarPoc> existingContacts, ImmutableSet<RegistrarPoc> updatedContacts) {
|
||||
// Any contact(s) with new passwords must be allowed to set them
|
||||
for (RegistrarPoc updatedContact : updatedContacts) {
|
||||
if (updatedContact.isRegistryLockAllowed()
|
||||
|| updatedContact.isAllowedToSetRegistryLockPassword()) {
|
||||
RegistrarPoc existingContact =
|
||||
existingContacts.stream()
|
||||
.filter(
|
||||
contact -> contact.getEmailAddress().equals(updatedContact.getEmailAddress()))
|
||||
.findFirst()
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new FormException(
|
||||
"Cannot set registry lock password directly on new contact"));
|
||||
// Can't modify registry lock email address
|
||||
if (!Objects.equals(
|
||||
updatedContact.getRegistryLockEmailAddress(),
|
||||
existingContact.getRegistryLockEmailAddress())) {
|
||||
throw new FormException("Cannot modify registryLockEmailAddress through the UI");
|
||||
}
|
||||
if (updatedContact.isRegistryLockAllowed()) {
|
||||
// the password must have been set before or the user was allowed to set it now
|
||||
if (!existingContact.isAllowedToSetRegistryLockPassword()
|
||||
&& !existingContact.isRegistryLockAllowed()) {
|
||||
throw new FormException("Registrar contact not allowed to set registry lock password");
|
||||
}
|
||||
}
|
||||
if (updatedContact.isAllowedToSetRegistryLockPassword()) {
|
||||
if (!existingContact.isAllowedToSetRegistryLockPassword()) {
|
||||
throw new FormException(
|
||||
"Cannot modify isAllowedToSetRegistryLockPassword through the UI");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Any previously-existing contacts with registry lock enabled cannot be deleted
|
||||
existingContacts.stream()
|
||||
.filter(RegistrarPoc::isRegistryLockAllowed)
|
||||
.forEach(
|
||||
contact -> {
|
||||
Optional<RegistrarPoc> updatedContactOptional =
|
||||
updatedContacts.stream()
|
||||
.filter(
|
||||
updatedContact ->
|
||||
updatedContact.getEmailAddress().equals(contact.getEmailAddress()))
|
||||
.findFirst();
|
||||
if (updatedContactOptional.isEmpty()) {
|
||||
throw new FormException(
|
||||
String.format(
|
||||
"Cannot delete the contact %s that has registry lock enabled",
|
||||
contact.getEmailAddress()));
|
||||
}
|
||||
if (!updatedContactOptional.get().isRegistryLockAllowed()) {
|
||||
throw new FormException(
|
||||
String.format(
|
||||
"Cannot remove the ability to use registry lock on the contact %s",
|
||||
contact.getEmailAddress()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that for each given registrar type, a phone number is present after update, if there was
|
||||
* one before.
|
||||
*/
|
||||
private static void ensurePhoneNumberNotRemovedForContactTypes(
|
||||
Multimap<Type, RegistrarPoc> oldContactsByType,
|
||||
Multimap<Type, RegistrarPoc> newContactsByType,
|
||||
Type... types) {
|
||||
for (Type type : types) {
|
||||
if (oldContactsByType.get(type).stream().anyMatch(RegistrarSettingsAction::hasPhone)
|
||||
&& newContactsByType.get(type).stream().noneMatch(RegistrarSettingsAction::hasPhone)) {
|
||||
throw new ContactRequirementException(
|
||||
String.format(
|
||||
"Please provide a phone number for at least one %s contact",
|
||||
type.getDisplayName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the registrar contact whose phone number and email address is visible in domain WHOIS
|
||||
* query as abuse contact (if any).
|
||||
*
|
||||
* <p>Frontend processing ensures that only one contact can be set as abuse contact in domain
|
||||
* WHOIS record. Therefore, it is possible to return inside the loop once one such contact is
|
||||
* found.
|
||||
*/
|
||||
private static Optional<RegistrarPoc> getDomainWhoisVisibleAbuseContact(
|
||||
Set<RegistrarPoc> contacts) {
|
||||
return contacts.stream().filter(RegistrarPoc::getVisibleInDomainWhoisAsAbuse).findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if any changes were made to the registrar besides the lastUpdateTime, and if so,
|
||||
* sends an email with a diff of the changes to the configured notification email address and all
|
||||
* contact addresses and enqueues a task to re-sync the registrar sheet.
|
||||
*/
|
||||
private void sendExternalUpdatesIfNecessary(EmailInfo emailInfo) {
|
||||
ImmutableSet<RegistrarPoc> existingContacts = emailInfo.contacts();
|
||||
if (!sendEmailUtils.hasRecipients() && existingContacts.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Registrar existingRegistrar = emailInfo.registrar();
|
||||
Map<?, ?> diffs =
|
||||
DiffUtils.deepDiff(
|
||||
expandRegistrarWithContacts(existingContacts, existingRegistrar),
|
||||
expandRegistrarWithContacts(emailInfo.updatedContacts(), emailInfo.updatedRegistrar()),
|
||||
true);
|
||||
@SuppressWarnings("unchecked")
|
||||
Set<String> changedKeys = (Set<String>) diffs.keySet();
|
||||
if (CollectionUtils.difference(changedKeys, "lastUpdateTime").isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (!RegistryEnvironment.isInTestServer()) {
|
||||
// Enqueues a sync registrar sheet task if enqueuing is not triggered by console tests and
|
||||
// there's an update besides the lastUpdateTime
|
||||
cloudTasksUtils.enqueue(
|
||||
SyncRegistrarsSheetAction.QUEUE,
|
||||
cloudTasksUtils.createTask(
|
||||
SyncRegistrarsSheetAction.class, Action.Method.POST, ImmutableMultimap.of()));
|
||||
}
|
||||
String environment = Ascii.toLowerCase(String.valueOf(RegistryEnvironment.get()));
|
||||
sendEmailUtils.sendEmail(
|
||||
String.format(
|
||||
"Registrar %s (%s) updated in registry %s environment",
|
||||
existingRegistrar.getRegistrarName(), existingRegistrar.getRegistrarId(), environment),
|
||||
String.format(
|
||||
"""
|
||||
The following changes were made in registry %s environment to the registrar %s by\
|
||||
%s:
|
||||
|
||||
%s""",
|
||||
environment,
|
||||
existingRegistrar.getRegistrarId(),
|
||||
authResult.userIdForLogging(),
|
||||
DiffUtils.prettyPrintDiffedMap(diffs, null)),
|
||||
existingContacts.stream()
|
||||
.filter(c -> c.getTypes().contains(Type.ADMIN))
|
||||
.map(RegistrarPoc::getEmailAddress)
|
||||
.collect(toImmutableList()));
|
||||
}
|
||||
|
||||
/** Thrown when a set of contacts doesn't meet certain constraints. */
|
||||
private static class ContactRequirementException extends FormException {
|
||||
ContactRequirementException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
ContactRequirementException(Type type) {
|
||||
super(String.format("Must have at least one %s contact", type.getDisplayName()));
|
||||
}
|
||||
}
|
||||
}
|
||||