mirror of
https://github.com/google/nomulus
synced 2026-07-09 17:46:52 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f8ffe7020 | |||
| 16e5018489 | |||
| af303bd26f | |||
| bf3bb5d804 | |||
| dcb16e05bd | |||
| 2facedd60f | |||
| b1ec81f054 | |||
| 779da518df | |||
| 4f53ae0e89 | |||
| da04caeea2 |
Generated
+91
-91
@@ -150,7 +150,7 @@
|
||||
"ora": "5.4.1",
|
||||
"parse5-html-rewriting-stream": "7.0.0",
|
||||
"piscina": "3.2.0",
|
||||
"postcss": "8.4.21",
|
||||
"postcss": "8.4.31",
|
||||
"postcss-loader": "7.0.2",
|
||||
"resolve-url-loader": "5.0.0",
|
||||
"rxjs": "6.6.7",
|
||||
@@ -782,12 +782,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.22.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz",
|
||||
"integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==",
|
||||
"version": "7.22.13",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
|
||||
"integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/highlight": "^7.22.10",
|
||||
"@babel/highlight": "^7.22.13",
|
||||
"chalk": "^2.4.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1072,36 +1072,36 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-environment-visitor": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz",
|
||||
"integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
|
||||
"integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-function-name": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz",
|
||||
"integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
|
||||
"integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.22.5",
|
||||
"@babel/types": "^7.22.5"
|
||||
"@babel/template": "^7.22.15",
|
||||
"@babel/types": "^7.23.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-function-name/node_modules/@babel/template": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz",
|
||||
"integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==",
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
|
||||
"integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.22.5",
|
||||
"@babel/parser": "^7.22.5",
|
||||
"@babel/types": "^7.22.5"
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/parser": "^7.22.15",
|
||||
"@babel/types": "^7.22.15"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -1287,9 +1287,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-identifier": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz",
|
||||
"integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
|
||||
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -1361,12 +1361,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight": {
|
||||
"version": "7.22.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz",
|
||||
"integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
|
||||
"integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.22.5",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"chalk": "^2.4.2",
|
||||
"js-tokens": "^4.0.0"
|
||||
},
|
||||
@@ -1375,9 +1375,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.22.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz",
|
||||
"integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
|
||||
"integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
@@ -2597,19 +2597,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse": {
|
||||
"version": "7.22.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.10.tgz",
|
||||
"integrity": "sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==",
|
||||
"version": "7.23.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
|
||||
"integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.22.10",
|
||||
"@babel/generator": "^7.22.10",
|
||||
"@babel/helper-environment-visitor": "^7.22.5",
|
||||
"@babel/helper-function-name": "^7.22.5",
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/generator": "^7.23.0",
|
||||
"@babel/helper-environment-visitor": "^7.22.20",
|
||||
"@babel/helper-function-name": "^7.23.0",
|
||||
"@babel/helper-hoist-variables": "^7.22.5",
|
||||
"@babel/helper-split-export-declaration": "^7.22.6",
|
||||
"@babel/parser": "^7.22.10",
|
||||
"@babel/types": "^7.22.10",
|
||||
"@babel/parser": "^7.23.0",
|
||||
"@babel/types": "^7.23.0",
|
||||
"debug": "^4.1.0",
|
||||
"globals": "^11.1.0"
|
||||
},
|
||||
@@ -2618,12 +2618,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse/node_modules/@babel/generator": {
|
||||
"version": "7.22.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz",
|
||||
"integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
|
||||
"integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.22.10",
|
||||
"@babel/types": "^7.23.0",
|
||||
"@jridgewell/gen-mapping": "^0.3.2",
|
||||
"@jridgewell/trace-mapping": "^0.3.17",
|
||||
"jsesc": "^2.5.1"
|
||||
@@ -2659,13 +2659,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.22.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz",
|
||||
"integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
|
||||
"integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.22.5",
|
||||
"@babel/helper-validator-identifier": "^7.22.5",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -14882,12 +14882,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"@babel/code-frame": {
|
||||
"version": "7.22.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz",
|
||||
"integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==",
|
||||
"version": "7.22.13",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
|
||||
"integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/highlight": "^7.22.10",
|
||||
"@babel/highlight": "^7.22.13",
|
||||
"chalk": "^2.4.2"
|
||||
}
|
||||
},
|
||||
@@ -15097,30 +15097,30 @@
|
||||
}
|
||||
},
|
||||
"@babel/helper-environment-visitor": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz",
|
||||
"integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
|
||||
"integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/helper-function-name": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz",
|
||||
"integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
|
||||
"integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/template": "^7.22.5",
|
||||
"@babel/types": "^7.22.5"
|
||||
"@babel/template": "^7.22.15",
|
||||
"@babel/types": "^7.23.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/template": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz",
|
||||
"integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==",
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
|
||||
"integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.22.5",
|
||||
"@babel/parser": "^7.22.5",
|
||||
"@babel/types": "^7.22.5"
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/parser": "^7.22.15",
|
||||
"@babel/types": "^7.22.15"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15258,9 +15258,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@babel/helper-validator-identifier": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz",
|
||||
"integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
|
||||
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/helper-validator-option": {
|
||||
@@ -15318,20 +15318,20 @@
|
||||
}
|
||||
},
|
||||
"@babel/highlight": {
|
||||
"version": "7.22.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz",
|
||||
"integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
|
||||
"integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.22.5",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"chalk": "^2.4.2",
|
||||
"js-tokens": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"@babel/parser": {
|
||||
"version": "7.22.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz",
|
||||
"integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
|
||||
"integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
|
||||
@@ -16160,30 +16160,30 @@
|
||||
}
|
||||
},
|
||||
"@babel/traverse": {
|
||||
"version": "7.22.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.10.tgz",
|
||||
"integrity": "sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==",
|
||||
"version": "7.23.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
|
||||
"integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.22.10",
|
||||
"@babel/generator": "^7.22.10",
|
||||
"@babel/helper-environment-visitor": "^7.22.5",
|
||||
"@babel/helper-function-name": "^7.22.5",
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/generator": "^7.23.0",
|
||||
"@babel/helper-environment-visitor": "^7.22.20",
|
||||
"@babel/helper-function-name": "^7.23.0",
|
||||
"@babel/helper-hoist-variables": "^7.22.5",
|
||||
"@babel/helper-split-export-declaration": "^7.22.6",
|
||||
"@babel/parser": "^7.22.10",
|
||||
"@babel/types": "^7.22.10",
|
||||
"@babel/parser": "^7.23.0",
|
||||
"@babel/types": "^7.23.0",
|
||||
"debug": "^4.1.0",
|
||||
"globals": "^11.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/generator": {
|
||||
"version": "7.22.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz",
|
||||
"integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
|
||||
"integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.22.10",
|
||||
"@babel/types": "^7.23.0",
|
||||
"@jridgewell/gen-mapping": "^0.3.2",
|
||||
"@jridgewell/trace-mapping": "^0.3.17",
|
||||
"jsesc": "^2.5.1"
|
||||
@@ -16212,13 +16212,13 @@
|
||||
}
|
||||
},
|
||||
"@babel/types": {
|
||||
"version": "7.22.10",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz",
|
||||
"integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
|
||||
"integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-string-parser": "^7.22.5",
|
||||
"@babel/helper-validator-identifier": "^7.22.5",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -5,21 +5,23 @@
|
||||
<mat-icon class="console-app__widget-icon">call</mat-icon>
|
||||
<h1 class="console-app__widget-title">Contact Support</h1>
|
||||
<h4 class="secondary-text text-center">
|
||||
View Google Registry support email and phone information
|
||||
Let us know if you have any questions
|
||||
</h4>
|
||||
</div>
|
||||
<div class="console-app__widget_right">
|
||||
<button mat-button color="primary" class="console-app__widget-link">
|
||||
Give us a Call
|
||||
</button>
|
||||
<div class="console-app__widget-section-header">Give us a Call</div>
|
||||
<p class="secondary-text">
|
||||
Call Google Registry support at <b>+1 (404) 978 8419</b>
|
||||
Call {{ userDataService.userData?.productName }} support at
|
||||
<a href="tel:{{ userDataService.userData?.supportPhoneNumber }}">{{
|
||||
userDataService.userData?.supportPhoneNumber
|
||||
}}</a>
|
||||
</p>
|
||||
<button mat-button color="primary" class="console-app__widget-link">
|
||||
Send us an Email
|
||||
</button>
|
||||
<div class="console-app__widget-section-header">Send us an Email</div>
|
||||
<p class="secondary-text">
|
||||
Email Google Registry at <b>support@google.com</b>
|
||||
Email {{ userDataService.userData?.productName }} at
|
||||
<a href="mailto:{{ userDataService.userData?.supportEmail }}">{{
|
||||
userDataService.userData?.supportEmail
|
||||
}}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -13,11 +13,12 @@
|
||||
// limitations under the License.
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { UserDataService } from 'src/app/shared/services/userData.service';
|
||||
|
||||
@Component({
|
||||
selector: '[app-contact-widget]',
|
||||
templateUrl: './contact-widget.component.html',
|
||||
})
|
||||
export class ContactWidgetComponent {
|
||||
constructor() {}
|
||||
constructor(public userDataService: UserDataService) {}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,11 @@ import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { GlobalLoader, GlobalLoaderService } from './globalLoader.service';
|
||||
|
||||
export interface UserData {
|
||||
isAdmin: boolean;
|
||||
globalRole: string;
|
||||
isAdmin: boolean;
|
||||
productName: string;
|
||||
supportEmail: string;
|
||||
supportPhoneNumber: string;
|
||||
technicalDocsUrl: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,10 @@ body {
|
||||
min-width: auto !important;
|
||||
height: min-content !important;
|
||||
}
|
||||
&-section-header {
|
||||
font-weight: 500;
|
||||
color: var(--primary) !important;
|
||||
}
|
||||
&-title {
|
||||
color: var(--primary) !important;
|
||||
text-align: center;
|
||||
|
||||
@@ -43,6 +43,12 @@ public class BatchModule {
|
||||
public static final String PARAM_DRY_RUN = "dryRun";
|
||||
public static final String PARAM_FAST = "fast";
|
||||
|
||||
@Provides
|
||||
@Parameter("url")
|
||||
static String provideUrl(HttpServletRequest req) {
|
||||
return extractRequiredParameter(req, "url");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("jobName")
|
||||
static Optional<String> provideJobName(HttpServletRequest req) {
|
||||
|
||||
@@ -14,26 +14,20 @@
|
||||
|
||||
package google.registry.batch;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.util.RegistrarUtils.normalizeRegistrarId;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.groups.GmailClient;
|
||||
import google.registry.groups.GroupsConnection;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.UrlConnectionService;
|
||||
import google.registry.request.UrlConnectionUtils;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.EmailMessage;
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
import java.net.URL;
|
||||
import javax.inject.Inject;
|
||||
import javax.mail.internet.AddressException;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
/**
|
||||
* Action that executes a canned script specified by the caller.
|
||||
@@ -50,88 +44,45 @@ import javax.mail.internet.InternetAddress;
|
||||
@Action(
|
||||
service = Action.Service.BACKEND,
|
||||
path = "/_dr/task/executeCannedScript",
|
||||
method = POST,
|
||||
method = {POST, GET},
|
||||
automaticallyPrintOk = true,
|
||||
auth = Auth.AUTH_API_ADMIN)
|
||||
public class CannedScriptExecutionAction implements Runnable {
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private final GroupsConnection groupsConnection;
|
||||
private final GmailClient gmailClient;
|
||||
|
||||
private final InternetAddress senderAddress;
|
||||
|
||||
private final InternetAddress recipientAddress;
|
||||
|
||||
private final String gSuiteDomainName;
|
||||
@Inject UrlConnectionService urlConnectionService;
|
||||
@Inject Response response;
|
||||
|
||||
@Inject
|
||||
CannedScriptExecutionAction(
|
||||
GroupsConnection groupsConnection,
|
||||
GmailClient gmailClient,
|
||||
@Config("projectId") String projectId,
|
||||
@Config("gSuiteDomainName") String gSuiteDomainName,
|
||||
@Config("newAlertRecipientEmailAddress") InternetAddress recipientAddress) {
|
||||
this.groupsConnection = groupsConnection;
|
||||
this.gmailClient = gmailClient;
|
||||
this.gSuiteDomainName = gSuiteDomainName;
|
||||
try {
|
||||
this.senderAddress = new InternetAddress(String.format("%s@%s", projectId, gSuiteDomainName));
|
||||
} catch (AddressException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
this.recipientAddress = recipientAddress;
|
||||
logger.atInfo().log("Sender:%s; Recipient: %s.", this.senderAddress, this.recipientAddress);
|
||||
}
|
||||
@Parameter("url")
|
||||
String url;
|
||||
|
||||
@Inject
|
||||
CannedScriptExecutionAction() {}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Integer responseCode = null;
|
||||
String responseContent = null;
|
||||
try {
|
||||
// Invoke canned scripts here.
|
||||
checkGroupApi();
|
||||
EmailMessage message = createEmail();
|
||||
this.gmailClient.sendEmail(message);
|
||||
logger.atInfo().log("Finished running scripts.");
|
||||
} catch (Throwable t) {
|
||||
logger.atWarning().withCause(t).log("Error executing scripts.");
|
||||
throw new RuntimeException("Execution failed.");
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if Directory and GroupSettings still work after GWorkspace changes.
|
||||
void checkGroupApi() {
|
||||
ImmutableList<Registrar> registrars =
|
||||
Streams.stream(Registrar.loadAllCached())
|
||||
.filter(registrar -> registrar.isLive() && registrar.getType() == Registrar.Type.REAL)
|
||||
.collect(toImmutableList());
|
||||
logger.atInfo().log("Found %s registrars.", registrars.size());
|
||||
for (Registrar registrar : registrars) {
|
||||
for (final RegistrarPoc.Type type : RegistrarPoc.Type.values()) {
|
||||
String groupKey =
|
||||
String.format(
|
||||
"%s-%s-contacts@%s",
|
||||
normalizeRegistrarId(registrar.getRegistrarId()),
|
||||
type.getDisplayName(),
|
||||
gSuiteDomainName);
|
||||
try {
|
||||
Set<String> currentMembers = groupsConnection.getMembersOfGroup(groupKey);
|
||||
logger.atInfo().log("%s has %s members.", groupKey, currentMembers.size());
|
||||
// One success is enough for validation.
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
logger.atWarning().withCause(e).log("Failed to check %s", groupKey);
|
||||
}
|
||||
logger.atInfo().log("Connecting to: %s", url);
|
||||
HttpsURLConnection connection =
|
||||
(HttpsURLConnection) urlConnectionService.createConnection(new URL(url));
|
||||
responseCode = connection.getResponseCode();
|
||||
logger.atInfo().log("Code: %d", responseCode);
|
||||
logger.atInfo().log("Headers: %s", connection.getHeaderFields());
|
||||
responseContent = new String(UrlConnectionUtils.getResponseBytes(connection), UTF_8);
|
||||
logger.atInfo().log("Response: %s", responseContent);
|
||||
} catch (Exception e) {
|
||||
logger.atWarning().withCause(e).log("Connection to %s failed", url);
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (responseCode != null) {
|
||||
response.setStatus(responseCode);
|
||||
}
|
||||
if (responseContent != null) {
|
||||
response.setPayload(responseContent);
|
||||
}
|
||||
}
|
||||
logger.atInfo().log("Finished checking GroupApis.");
|
||||
}
|
||||
|
||||
EmailMessage createEmail() {
|
||||
return EmailMessage.newBuilder()
|
||||
.setFrom(senderAddress)
|
||||
.setSubject("Test: Please ignore<eom>.")
|
||||
.setRecipients(ImmutableList.of(recipientAddress))
|
||||
.setBody("Sent from Nomulus through Google Workspace.")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
-9
@@ -59,13 +59,4 @@
|
||||
</description>
|
||||
<schedule>7 3 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/wipeOutCloudSql]]></url>
|
||||
<name>wipeOutCloudSql</name>
|
||||
<description>
|
||||
This job runs an action that deletes all data in Cloud SQL.
|
||||
</description>
|
||||
<schedule>7 3 * * 6</schedule>
|
||||
</task>
|
||||
</entries>
|
||||
|
||||
@@ -305,14 +305,14 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
new CacheLoader<VKey<AllocationToken>, Optional<AllocationToken>>() {
|
||||
@Override
|
||||
public Optional<AllocationToken> load(VKey<AllocationToken> key) {
|
||||
return tm().transact(() -> tm().loadByKeyIfPresent(key));
|
||||
return tm().reTransact(() -> tm().loadByKeyIfPresent(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<VKey<AllocationToken>, Optional<AllocationToken>> loadAll(
|
||||
Iterable<? extends VKey<AllocationToken>> keys) {
|
||||
ImmutableSet<VKey<AllocationToken>> keySet = ImmutableSet.copyOf(keys);
|
||||
return tm().transact(
|
||||
return tm().reTransact(
|
||||
() ->
|
||||
keySet.stream()
|
||||
.collect(
|
||||
|
||||
@@ -200,7 +200,11 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
|
||||
/** A caching {@link Supplier} of a registrarId to {@link Registrar} map. */
|
||||
private static final Supplier<ImmutableMap<String, Registrar>> CACHE_BY_REGISTRAR_ID =
|
||||
memoizeWithShortExpiration(() -> Maps.uniqueIndex(loadAll(), Registrar::getRegistrarId));
|
||||
memoizeWithShortExpiration(
|
||||
() ->
|
||||
Maps.uniqueIndex(
|
||||
tm().reTransact(() -> tm().loadAllOf(Registrar.class)),
|
||||
Registrar::getRegistrarId));
|
||||
|
||||
/**
|
||||
* Unique registrar client id. Must conform to "clIDType" as defined in RFC5730.
|
||||
|
||||
@@ -28,7 +28,7 @@ public class SignedMarkRevocationListDao {
|
||||
/** Loads the {@link SignedMarkRevocationList}. */
|
||||
static SignedMarkRevocationList load() {
|
||||
Optional<SignedMarkRevocationList> smdrl =
|
||||
tm().transact(
|
||||
tm().reTransact(
|
||||
() -> {
|
||||
Long revisionId =
|
||||
tm().query("SELECT MAX(revisionId) FROM SignedMarkRevocationList", Long.class)
|
||||
|
||||
@@ -233,7 +233,8 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
new CacheLoader<String, Tld>() {
|
||||
@Override
|
||||
public Tld load(final String tld) {
|
||||
return tm().transact(() -> tm().loadByKeyIfPresent(createVKey(tld))).orElse(null);
|
||||
return tm().reTransact(() -> tm().loadByKeyIfPresent(createVKey(tld)))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -241,7 +242,7 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
ImmutableMap<String, VKey<Tld>> keysMap =
|
||||
toMap(ImmutableSet.copyOf(tlds), Tld::createVKey);
|
||||
Map<VKey<? extends Tld>, Tld> entities =
|
||||
tm().transact(() -> tm().loadByKeysIfPresent(keysMap.values()));
|
||||
tm().reTransact(() -> tm().loadByKeysIfPresent(keysMap.values()));
|
||||
return Maps.transformEntries(keysMap, (k, v) -> entities.getOrDefault(v, null));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -56,7 +56,7 @@ public final class Tlds {
|
||||
private static Supplier<ImmutableMap<String, TldType>> createFreshCache() {
|
||||
return memoizeWithShortExpiration(
|
||||
() ->
|
||||
tm().transact(
|
||||
tm().reTransact(
|
||||
() -> {
|
||||
EntityManager entityManager = tm().getEntityManager();
|
||||
Stream<?> resultStream =
|
||||
|
||||
@@ -43,8 +43,6 @@ import google.registry.rde.JSchModule;
|
||||
import google.registry.request.Modules.GsonModule;
|
||||
import google.registry.request.Modules.NetHttpTransportModule;
|
||||
import google.registry.request.Modules.UrlConnectionServiceModule;
|
||||
import google.registry.request.Modules.UrlFetchServiceModule;
|
||||
import google.registry.request.Modules.UrlFetchTransportModule;
|
||||
import google.registry.request.Modules.UserServiceModule;
|
||||
import google.registry.request.auth.AuthModule;
|
||||
import google.registry.util.UtilsModule;
|
||||
@@ -80,8 +78,6 @@ import javax.inject.Singleton;
|
||||
SheetsServiceModule.class,
|
||||
StackdriverModule.class,
|
||||
UrlConnectionServiceModule.class,
|
||||
UrlFetchServiceModule.class,
|
||||
UrlFetchTransportModule.class,
|
||||
UserServiceModule.class,
|
||||
VoidDnsWriterModule.class,
|
||||
UtilsModule.class
|
||||
|
||||
@@ -14,22 +14,26 @@
|
||||
|
||||
package google.registry.rdap;
|
||||
|
||||
import static com.google.api.client.http.HttpStatusCodes.STATUS_CODE_OK;
|
||||
import static com.google.common.net.HttpHeaders.ACCEPT_ENCODING;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.api.client.http.GenericUrl;
|
||||
import com.google.api.client.http.HttpRequest;
|
||||
import com.google.api.client.http.HttpResponse;
|
||||
import com.google.api.client.http.HttpTransport;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.HttpException.InternalServerErrorException;
|
||||
import google.registry.request.UrlConnectionService;
|
||||
import google.registry.request.UrlConnectionUtils;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.UrlConnectionException;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.security.GeneralSecurityException;
|
||||
import javax.inject.Inject;
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVParser;
|
||||
@@ -41,9 +45,9 @@ import org.apache.commons.csv.CSVRecord;
|
||||
* <p>This will update ALL the REAL registrars. If a REAL registrar doesn't have an RDAP entry in
|
||||
* MoSAPI, we'll delete any BaseUrls it has.
|
||||
*
|
||||
* <p>The ICANN base website that provides this information can be found at
|
||||
* https://www.iana.org/assignments/registrar-ids/registrar-ids.xhtml. The provided CSV endpoint
|
||||
* requires no authentication.
|
||||
* <p>The ICANN base website that provides this information can be found at <a
|
||||
* href=https://www.iana.org/assignments/registrar-ids/registrar-ids.xhtml>here</a>. The provided
|
||||
* CSV endpoint requires no authentication.
|
||||
*/
|
||||
@Action(
|
||||
service = Action.Service.BACKEND,
|
||||
@@ -52,22 +56,26 @@ import org.apache.commons.csv.CSVRecord;
|
||||
auth = Auth.AUTH_API_ADMIN)
|
||||
public final class UpdateRegistrarRdapBaseUrlsAction implements Runnable {
|
||||
|
||||
private static final GenericUrl RDAP_IDS_URL =
|
||||
new GenericUrl("https://www.iana.org/assignments/registrar-ids/registrar-ids-1.csv");
|
||||
private static final String RDAP_IDS_URL =
|
||||
"https://www.iana.org/assignments/registrar-ids/registrar-ids-1.csv";
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
@Inject HttpTransport httpTransport;
|
||||
@Inject UrlConnectionService urlConnectionService;
|
||||
|
||||
@Inject
|
||||
UpdateRegistrarRdapBaseUrlsAction() {}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ImmutableMap<String, String> ianaIdsToUrls = getIanaIdsToUrls();
|
||||
tm().transact(() -> processAllRegistrars(ianaIdsToUrls));
|
||||
try {
|
||||
ImmutableMap<String, String> ianaIdsToUrls = getIanaIdsToUrls();
|
||||
tm().transact(() -> processAllRegistrars(ianaIdsToUrls));
|
||||
} catch (Exception e) {
|
||||
throw new InternalServerErrorException("Error when retrieving RDAP base URL CSV file", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void processAllRegistrars(ImmutableMap<String, String> ianaIdsToUrls) {
|
||||
private static void processAllRegistrars(ImmutableMap<String, String> ianaIdsToUrls) {
|
||||
int nonUpdatedRegistrars = 0;
|
||||
for (Registrar registrar : Registrar.loadAll()) {
|
||||
// Only update REAL registrars
|
||||
@@ -95,23 +103,28 @@ public final class UpdateRegistrarRdapBaseUrlsAction implements Runnable {
|
||||
logger.atInfo().log("No change in RDAP base URLs for %d registrars", nonUpdatedRegistrars);
|
||||
}
|
||||
|
||||
private ImmutableMap<String, String> getIanaIdsToUrls() {
|
||||
private ImmutableMap<String, String> getIanaIdsToUrls()
|
||||
throws IOException, GeneralSecurityException {
|
||||
CSVParser csv;
|
||||
HttpURLConnection connection = urlConnectionService.createConnection(new URL(RDAP_IDS_URL));
|
||||
// Explictly set the accepted encoding, as we know Brotli causes us problems when talking to
|
||||
// ICANN.
|
||||
connection.setRequestProperty(ACCEPT_ENCODING, "gzip");
|
||||
String csvString;
|
||||
try {
|
||||
HttpRequest request = httpTransport.createRequestFactory().buildGetRequest(RDAP_IDS_URL);
|
||||
// AppEngine might insert accept-encodings for us if we use the default gzip, so remove it
|
||||
request.getHeaders().setAcceptEncoding(null);
|
||||
HttpResponse response = request.execute();
|
||||
String csvString = new String(ByteStreams.toByteArray(response.getContent()), UTF_8);
|
||||
csv =
|
||||
CSVFormat.Builder.create(CSVFormat.DEFAULT)
|
||||
.setHeader()
|
||||
.setSkipHeaderRecord(true)
|
||||
.build()
|
||||
.parse(new StringReader(csvString));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Error when retrieving RDAP base URL CSV file", e);
|
||||
if (connection.getResponseCode() != STATUS_CODE_OK) {
|
||||
throw new UrlConnectionException("Failed to load RDAP base URLs from ICANN", connection);
|
||||
}
|
||||
csvString = new String(UrlConnectionUtils.getResponseBytes(connection), UTF_8);
|
||||
} finally {
|
||||
connection.disconnect();
|
||||
}
|
||||
csv =
|
||||
CSVFormat.Builder.create(CSVFormat.DEFAULT)
|
||||
.setHeader()
|
||||
.setSkipHeaderRecord(true)
|
||||
.build()
|
||||
.parse(new StringReader(csvString));
|
||||
ImmutableMap.Builder<String, String> result = new ImmutableMap.Builder<>();
|
||||
for (CSVRecord record : csv) {
|
||||
String ianaIdentifierString = record.get("ID");
|
||||
|
||||
@@ -14,25 +14,23 @@
|
||||
|
||||
package google.registry.rde;
|
||||
|
||||
import static com.google.appengine.api.urlfetch.FetchOptions.Builder.validateCertificate;
|
||||
import static com.google.appengine.api.urlfetch.HTTPMethod.PUT;
|
||||
import static com.google.common.io.BaseEncoding.base64;
|
||||
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
||||
import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
|
||||
import static com.google.api.client.http.HttpStatusCodes.STATUS_CODE_BAD_REQUEST;
|
||||
import static com.google.api.client.http.HttpStatusCodes.STATUS_CODE_OK;
|
||||
import static google.registry.request.UrlConnectionUtils.getResponseBytes;
|
||||
import static google.registry.request.UrlConnectionUtils.setBasicAuth;
|
||||
import static google.registry.request.UrlConnectionUtils.setPayload;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
|
||||
import com.google.appengine.api.urlfetch.HTTPHeader;
|
||||
import com.google.appengine.api.urlfetch.HTTPRequest;
|
||||
import com.google.api.client.http.HttpMethods;
|
||||
import com.google.appengine.api.urlfetch.HTTPResponse;
|
||||
import com.google.appengine.api.urlfetch.URLFetchService;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.keyring.api.KeyModule.Key;
|
||||
import google.registry.request.HttpException.InternalServerErrorException;
|
||||
import google.registry.util.Retrier;
|
||||
import google.registry.request.UrlConnectionService;
|
||||
import google.registry.util.UrlConnectionException;
|
||||
import google.registry.xjc.XjcXmlTransformer;
|
||||
import google.registry.xjc.iirdea.XjcIirdeaResponseElement;
|
||||
import google.registry.xjc.iirdea.XjcIirdeaResult;
|
||||
@@ -40,10 +38,11 @@ import google.registry.xjc.rdeheader.XjcRdeHeader;
|
||||
import google.registry.xjc.rdereport.XjcRdeReportReport;
|
||||
import google.registry.xml.XmlException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.security.GeneralSecurityException;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
@@ -59,49 +58,47 @@ public class RdeReporter {
|
||||
* @see <a href="http://tools.ietf.org/html/draft-lozano-icann-registry-interfaces-05#section-4">
|
||||
* ICANN Registry Interfaces - Interface details</a>
|
||||
*/
|
||||
private static final String REPORT_MIME = "text/xml";
|
||||
private static final MediaType MEDIA_TYPE = MediaType.XML_UTF_8;
|
||||
|
||||
@Inject Retrier retrier;
|
||||
@Inject URLFetchService urlFetchService;
|
||||
@Inject UrlConnectionService urlConnectionService;
|
||||
|
||||
@Inject @Config("rdeReportUrlPrefix") String reportUrlPrefix;
|
||||
@Inject @Key("icannReportingPassword") String password;
|
||||
@Inject RdeReporter() {}
|
||||
|
||||
/** Uploads {@code reportBytes} to ICANN. */
|
||||
public void send(byte[] reportBytes) throws XmlException {
|
||||
XjcRdeReportReport report = XjcXmlTransformer.unmarshal(
|
||||
XjcRdeReportReport.class, new ByteArrayInputStream(reportBytes));
|
||||
public void send(byte[] reportBytes) throws XmlException, GeneralSecurityException, IOException {
|
||||
XjcRdeReportReport report =
|
||||
XjcXmlTransformer.unmarshal(
|
||||
XjcRdeReportReport.class, new ByteArrayInputStream(reportBytes));
|
||||
XjcRdeHeader header = report.getHeader().getValue();
|
||||
|
||||
// Send a PUT request to ICANN's HTTPS server.
|
||||
URL url = makeReportUrl(header.getTld(), report.getId());
|
||||
String username = header.getTld() + "_ry";
|
||||
String token = base64().encode(String.format("%s:%s", username, password).getBytes(UTF_8));
|
||||
final HTTPRequest req = new HTTPRequest(url, PUT, validateCertificate().setDeadline(60d));
|
||||
req.addHeader(new HTTPHeader(CONTENT_TYPE, REPORT_MIME));
|
||||
req.addHeader(new HTTPHeader(AUTHORIZATION, "Basic " + token));
|
||||
req.setPayload(reportBytes);
|
||||
logger.atInfo().log("Sending report:\n%s", new String(reportBytes, UTF_8));
|
||||
HTTPResponse rsp =
|
||||
retrier.callWithRetry(
|
||||
() -> {
|
||||
HTTPResponse rsp1 = urlFetchService.fetch(req);
|
||||
int responseCode = rsp1.getResponseCode();
|
||||
if (responseCode != SC_OK && responseCode != SC_BAD_REQUEST) {
|
||||
logger.atSevere().log(
|
||||
"Failure when trying to PUT RDE report to ICANN server: %d\n%s",
|
||||
responseCode, Arrays.toString(rsp1.getContent()));
|
||||
throw new RuntimeException("Error uploading deposits to ICANN");
|
||||
}
|
||||
return rsp1;
|
||||
},
|
||||
SocketTimeoutException.class);
|
||||
HttpURLConnection connection = urlConnectionService.createConnection(url);
|
||||
connection.setRequestMethod(HttpMethods.PUT);
|
||||
setBasicAuth(connection, username, password);
|
||||
setPayload(connection, reportBytes, MEDIA_TYPE.toString());
|
||||
int responseCode;
|
||||
byte[] responseBytes;
|
||||
|
||||
// Ensure the XML response is valid. The EPP result code would not be 1000 if we get an
|
||||
// SC_BAD_REQUEST as the HTTP response code.
|
||||
XjcIirdeaResult result = parseResult(rsp.getContent());
|
||||
if (result.getCode().getValue() != 1000) {
|
||||
try {
|
||||
responseCode = connection.getResponseCode();
|
||||
if (responseCode != STATUS_CODE_OK && responseCode != STATUS_CODE_BAD_REQUEST) {
|
||||
logger.atWarning().log("Connection to RDE report server failed: %d", responseCode);
|
||||
throw new UrlConnectionException("PUT failed", connection);
|
||||
}
|
||||
responseBytes = getResponseBytes(connection);
|
||||
} finally {
|
||||
connection.disconnect();
|
||||
}
|
||||
|
||||
// We know that an HTTP 200 response can only contain a result code of
|
||||
// 1000 (i. e. success), there is no need to parse it.
|
||||
if (responseCode != STATUS_CODE_OK) {
|
||||
XjcIirdeaResult result = parseResult(responseBytes);
|
||||
logger.atWarning().log(
|
||||
"Rejected when trying to PUT RDE report to ICANN server: %d %s\n%s",
|
||||
result.getCode().getValue(), result.getMsg(), result.getDescription());
|
||||
@@ -116,10 +113,11 @@ public class RdeReporter {
|
||||
* href="http://tools.ietf.org/html/draft-lozano-icann-registry-interfaces-05#section-4.1">
|
||||
* ICANN Registry Interfaces - IIRDEA Result Object</a>
|
||||
*/
|
||||
private XjcIirdeaResult parseResult(byte[] responseBytes) throws XmlException {
|
||||
private static XjcIirdeaResult parseResult(byte[] responseBytes) throws XmlException {
|
||||
logger.atInfo().log("Received response:\n%s", new String(responseBytes, UTF_8));
|
||||
XjcIirdeaResponseElement response = XjcXmlTransformer.unmarshal(
|
||||
XjcIirdeaResponseElement.class, new ByteArrayInputStream(responseBytes));
|
||||
XjcIirdeaResponseElement response =
|
||||
XjcXmlTransformer.unmarshal(
|
||||
XjcIirdeaResponseElement.class, new ByteArrayInputStream(responseBytes));
|
||||
return response.getResult();
|
||||
}
|
||||
|
||||
|
||||
@@ -14,33 +14,31 @@
|
||||
|
||||
package google.registry.reporting.icann;
|
||||
|
||||
import static com.google.api.client.http.HttpStatusCodes.STATUS_CODE_BAD_REQUEST;
|
||||
import static com.google.api.client.http.HttpStatusCodes.STATUS_CODE_OK;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.net.MediaType.CSV_UTF_8;
|
||||
import static google.registry.model.tld.Tlds.assertTldExists;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.api.client.http.ByteArrayContent;
|
||||
import com.google.api.client.http.GenericUrl;
|
||||
import com.google.api.client.http.HttpHeaders;
|
||||
import com.google.api.client.http.HttpRequest;
|
||||
import com.google.api.client.http.HttpResponse;
|
||||
import com.google.api.client.http.HttpResponseException;
|
||||
import com.google.api.client.http.HttpStatusCodes;
|
||||
import com.google.api.client.http.HttpTransport;
|
||||
import com.google.api.client.http.HttpMethods;
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.keyring.api.KeyModule.Key;
|
||||
import google.registry.reporting.icann.IcannReportingModule.ReportType;
|
||||
import google.registry.request.UrlConnectionService;
|
||||
import google.registry.request.UrlConnectionUtils;
|
||||
import google.registry.xjc.XjcXmlTransformer;
|
||||
import google.registry.xjc.iirdea.XjcIirdeaResponseElement;
|
||||
import google.registry.xjc.iirdea.XjcIirdeaResult;
|
||||
import google.registry.xml.XmlException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.YearMonth;
|
||||
@@ -62,78 +60,64 @@ public class IcannHttpReporter {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
@Inject HttpTransport httpTransport;
|
||||
@Inject @Key("icannReportingPassword") String password;
|
||||
@Inject @Config("icannTransactionsReportingUploadUrl") String icannTransactionsUrl;
|
||||
@Inject @Config("icannActivityReportingUploadUrl") String icannActivityUrl;
|
||||
@Inject IcannHttpReporter() {}
|
||||
@Inject UrlConnectionService urlConnectionService;
|
||||
|
||||
@Inject
|
||||
@Key("icannReportingPassword")
|
||||
String password;
|
||||
|
||||
@Inject
|
||||
@Config("icannTransactionsReportingUploadUrl")
|
||||
String icannTransactionsUrl;
|
||||
|
||||
@Inject
|
||||
@Config("icannActivityReportingUploadUrl")
|
||||
String icannActivityUrl;
|
||||
|
||||
@Inject
|
||||
IcannHttpReporter() {}
|
||||
|
||||
/** Uploads {@code reportBytes} to ICANN, returning whether or not it succeeded. */
|
||||
public boolean send(byte[] reportBytes, String reportFilename) throws XmlException, IOException {
|
||||
public boolean send(byte[] reportBytes, String reportFilename)
|
||||
throws GeneralSecurityException, XmlException, IOException {
|
||||
validateReportFilename(reportFilename);
|
||||
GenericUrl uploadUrl = new GenericUrl(makeUrl(reportFilename));
|
||||
HttpRequest request =
|
||||
httpTransport
|
||||
.createRequestFactory()
|
||||
.buildPutRequest(uploadUrl, new ByteArrayContent(CSV_UTF_8.toString(), reportBytes));
|
||||
|
||||
HttpHeaders headers = request.getHeaders();
|
||||
headers.setBasicAuthentication(getTld(reportFilename) + "_ry", password);
|
||||
headers.setContentType(CSV_UTF_8.toString());
|
||||
request.setHeaders(headers);
|
||||
request.setFollowRedirects(false);
|
||||
request.setThrowExceptionOnExecuteError(false);
|
||||
|
||||
HttpResponse response = null;
|
||||
URL uploadUrl = makeUrl(reportFilename);
|
||||
logger.atInfo().log(
|
||||
"Sending report to %s with content length %d.",
|
||||
uploadUrl, request.getContent().getLength());
|
||||
boolean success = true;
|
||||
"Sending report to %s with content length %d.", uploadUrl, reportBytes.length);
|
||||
HttpURLConnection connection = urlConnectionService.createConnection(uploadUrl);
|
||||
connection.setRequestMethod(HttpMethods.PUT);
|
||||
UrlConnectionUtils.setBasicAuth(connection, getTld(reportFilename) + "_ry", password);
|
||||
UrlConnectionUtils.setPayload(connection, reportBytes, CSV_UTF_8.toString());
|
||||
connection.setInstanceFollowRedirects(false);
|
||||
|
||||
int responseCode;
|
||||
byte[] content;
|
||||
try {
|
||||
response = request.execute();
|
||||
// Only responses with a 200 or 400 status have a body. For everything else, throw so that
|
||||
// the caller catches it and prints the stack trace.
|
||||
if (response.getStatusCode() != HttpStatusCodes.STATUS_CODE_OK
|
||||
&& response.getStatusCode() != HttpStatusCodes.STATUS_CODE_BAD_REQUEST) {
|
||||
throw new HttpResponseException(response);
|
||||
}
|
||||
byte[] content;
|
||||
try {
|
||||
content = ByteStreams.toByteArray(response.getContent());
|
||||
} finally {
|
||||
response.getContent().close();
|
||||
}
|
||||
logger.atInfo().log(
|
||||
"Received response code %d\n\n"
|
||||
+ "Response headers: %s\n\n"
|
||||
+ "Response content in UTF-8: %s\n\n"
|
||||
+ "Response content in HEX: %s",
|
||||
response.getStatusCode(),
|
||||
response.getHeaders(),
|
||||
new String(content, UTF_8),
|
||||
BaseEncoding.base16().encode(content));
|
||||
// For reasons unclear at the moment, when we parse the response content using UTF-8 we get
|
||||
// garbled texts. Since we know that an HTTP 200 response can only contain a result code of
|
||||
// 1000 (i. e. success), there is no need to parse it.
|
||||
if (response.getStatusCode() == HttpStatusCodes.STATUS_CODE_BAD_REQUEST) {
|
||||
success = false;
|
||||
XjcIirdeaResult result = parseResult(content);
|
||||
logger.atWarning().log(
|
||||
"PUT rejected, status code %s:\n%s\n%s",
|
||||
result.getCode().getValue(), result.getMsg(), result.getDescription());
|
||||
responseCode = connection.getResponseCode();
|
||||
// Only responses with a 200 or 400 status have a body. For everything else, we can return
|
||||
// false early.
|
||||
if (responseCode != STATUS_CODE_OK && responseCode != STATUS_CODE_BAD_REQUEST) {
|
||||
logger.atWarning().log("Connection to ICANN server failed", connection);
|
||||
return false;
|
||||
}
|
||||
content = UrlConnectionUtils.getResponseBytes(connection);
|
||||
} finally {
|
||||
if (response != null) {
|
||||
response.disconnect();
|
||||
} else {
|
||||
success = false;
|
||||
logger.atWarning().log("Received null response from ICANN server at %s", uploadUrl);
|
||||
}
|
||||
connection.disconnect();
|
||||
}
|
||||
return success;
|
||||
// We know that an HTTP 200 response can only contain a result code of
|
||||
// 1000 (i. e. success), there is no need to parse it.
|
||||
// See: https://tools.ietf.org/html/draft-lozano-icann-registry-interfaces-13#page-16
|
||||
if (responseCode != STATUS_CODE_OK) {
|
||||
XjcIirdeaResult result = parseResult(content);
|
||||
logger.atWarning().log(
|
||||
"PUT rejected, status code %s:\n%s\n%s",
|
||||
result.getCode().getValue(), result.getMsg(), result.getDescription());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private XjcIirdeaResult parseResult(byte[] content) throws XmlException {
|
||||
private static XjcIirdeaResult parseResult(byte[] content) throws XmlException {
|
||||
XjcIirdeaResponseElement response =
|
||||
XjcXmlTransformer.unmarshal(
|
||||
XjcIirdeaResponseElement.class, new ByteArrayInputStream(content));
|
||||
@@ -141,7 +125,7 @@ public class IcannHttpReporter {
|
||||
}
|
||||
|
||||
/** Verifies a given report filename matches the pattern tld-reportType-yyyyMM.csv. */
|
||||
private void validateReportFilename(String filename) {
|
||||
private static void validateReportFilename(String filename) {
|
||||
checkArgument(
|
||||
filename.matches("[a-z0-9.\\-]+-((activity)|(transactions))-[0-9]{6}\\.csv"),
|
||||
"Expected file format: tld-reportType-yyyyMM.csv, got %s instead",
|
||||
@@ -149,12 +133,12 @@ public class IcannHttpReporter {
|
||||
assertTldExists(getTld(filename));
|
||||
}
|
||||
|
||||
private String getTld(String filename) {
|
||||
private static String getTld(String filename) {
|
||||
// Extract the TLD, up to second-to-last hyphen in the filename (works with international TLDs)
|
||||
return filename.substring(0, filename.lastIndexOf('-', filename.lastIndexOf('-') - 1));
|
||||
}
|
||||
|
||||
private String makeUrl(String filename) {
|
||||
private URL makeUrl(String filename) throws MalformedURLException {
|
||||
// Filename is in the format tld-reportType-yearMonth.csv
|
||||
String tld = getTld(filename);
|
||||
// Remove the tld- prefix and csv suffix
|
||||
@@ -164,7 +148,7 @@ public class IcannHttpReporter {
|
||||
// Re-add hyphen between year and month, because ICANN is inconsistent between filename and URL
|
||||
String yearMonth =
|
||||
YearMonth.parse(elements.get(1), DateTimeFormat.forPattern("yyyyMM")).toString("yyyy-MM");
|
||||
return String.format("%s/%s/%s", getUrlPrefix(reportType), tld, yearMonth);
|
||||
return new URL(String.format("%s/%s/%s", getUrlPrefix(reportType), tld, yearMonth));
|
||||
}
|
||||
|
||||
private String getUrlPrefix(ReportType reportType) {
|
||||
|
||||
@@ -14,20 +14,18 @@
|
||||
|
||||
package google.registry.request;
|
||||
|
||||
import com.google.api.client.extensions.appengine.http.UrlFetchTransport;
|
||||
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
|
||||
import com.google.api.client.http.HttpTransport;
|
||||
import com.google.api.client.http.javanet.NetHttpTransport;
|
||||
import com.google.api.client.json.JsonFactory;
|
||||
import com.google.api.client.json.gson.GsonFactory;
|
||||
import com.google.appengine.api.urlfetch.URLFetchService;
|
||||
import com.google.appengine.api.urlfetch.URLFetchServiceFactory;
|
||||
import com.google.appengine.api.users.UserService;
|
||||
import com.google.appengine.api.users.UserServiceFactory;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import java.net.HttpURLConnection;
|
||||
import javax.inject.Singleton;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
/** Dagger modules for App Engine services and other vendor classes. */
|
||||
public final class Modules {
|
||||
@@ -37,18 +35,16 @@ public final class Modules {
|
||||
public static final class UrlConnectionServiceModule {
|
||||
@Provides
|
||||
static UrlConnectionService provideUrlConnectionService() {
|
||||
return url -> (HttpURLConnection) url.openConnection();
|
||||
}
|
||||
}
|
||||
|
||||
/** Dagger module for {@link URLFetchService}. */
|
||||
@Module
|
||||
public static final class UrlFetchServiceModule {
|
||||
private static final URLFetchService fetchService = URLFetchServiceFactory.getURLFetchService();
|
||||
|
||||
@Provides
|
||||
static URLFetchService provideUrlFetchService() {
|
||||
return fetchService;
|
||||
return url -> {
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
if (connection instanceof HttpsURLConnection) {
|
||||
HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;
|
||||
SSLContext tls13Context = SSLContext.getInstance("TLSv1.3");
|
||||
tls13Context.init(null, null, null);
|
||||
httpsConnection.setSSLSocketFactory(tls13Context.getSocketFactory());
|
||||
}
|
||||
return connection;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,17 +68,6 @@ public final class Modules {
|
||||
}
|
||||
}
|
||||
|
||||
/** Dagger module that causes the App Engine's URL fetcher to be used for Google APIs requests. */
|
||||
@Module
|
||||
public static final class UrlFetchTransportModule {
|
||||
private static final UrlFetchTransport HTTP_TRANSPORT = new UrlFetchTransport();
|
||||
|
||||
@Provides
|
||||
static HttpTransport provideHttpTransport() {
|
||||
return HTTP_TRANSPORT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dagger module that provides standard {@link NetHttpTransport}. Used in non App Engine
|
||||
* environment.
|
||||
|
||||
@@ -166,6 +166,7 @@ public class RequestHandler<C> {
|
||||
} catch (Exception e) {
|
||||
rsp.setStatus(SC_INTERNAL_SERVER_ERROR);
|
||||
rsp.getWriter().write("Internal server error, please try again later");
|
||||
logger.atSevere().withCause(e).log("Encountered internal server error");
|
||||
} finally {
|
||||
requestMetrics.record(
|
||||
new Duration(startTime, clock.nowUtc()),
|
||||
|
||||
@@ -27,15 +27,20 @@ import com.google.common.io.ByteStreams;
|
||||
import com.google.common.net.MediaType;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Random;
|
||||
|
||||
/** Utilities for common functionality relating to {@link java.net.URLConnection}s. */
|
||||
public class UrlConnectionUtils {
|
||||
/** Utilities for common functionality relating to {@link URLConnection}s. */
|
||||
public final class UrlConnectionUtils {
|
||||
|
||||
private UrlConnectionUtils() {}
|
||||
|
||||
/** Retrieves the response from the given connection as a byte array. */
|
||||
public static byte[] getResponseBytes(URLConnection connection) throws IOException {
|
||||
return ByteStreams.toByteArray(connection.getInputStream());
|
||||
try (InputStream is = connection.getInputStream()) {
|
||||
return ByteStreams.toByteArray(is);
|
||||
}
|
||||
}
|
||||
|
||||
/** Sets auth on the given connection with the given username/password. */
|
||||
|
||||
@@ -71,6 +71,11 @@ public class ConfigureTldCommand extends MutatingCommand {
|
||||
+ " configuration in the database.")
|
||||
boolean breakglass;
|
||||
|
||||
@Parameter(
|
||||
names = {"-d", "--dryrun"},
|
||||
description = "Does not execute the entity mutation")
|
||||
boolean dryrun;
|
||||
|
||||
@Inject ObjectMapper mapper;
|
||||
|
||||
@Inject
|
||||
@@ -126,6 +131,9 @@ public class ConfigureTldCommand extends MutatingCommand {
|
||||
|
||||
@Override
|
||||
protected boolean dontRunCommand() {
|
||||
if (dryrun) {
|
||||
return true;
|
||||
}
|
||||
if (!newDiff) {
|
||||
if (oldTldInBreakglass && !breakglass) {
|
||||
// Run command to remove breakglass mode
|
||||
|
||||
@@ -39,7 +39,6 @@ import google.registry.privileges.secretmanager.SecretManagerModule;
|
||||
import google.registry.rde.RdeModule;
|
||||
import google.registry.request.Modules.GsonModule;
|
||||
import google.registry.request.Modules.UrlConnectionServiceModule;
|
||||
import google.registry.request.Modules.UrlFetchServiceModule;
|
||||
import google.registry.request.Modules.UserServiceModule;
|
||||
import google.registry.tools.AuthModule.LocalCredentialModule;
|
||||
import google.registry.util.UtilsModule;
|
||||
@@ -77,7 +76,6 @@ import javax.inject.Singleton;
|
||||
SecretManagerKeyringModule.class,
|
||||
SecretManagerModule.class,
|
||||
UrlConnectionServiceModule.class,
|
||||
UrlFetchServiceModule.class,
|
||||
UserServiceModule.class,
|
||||
UtilsModule.class,
|
||||
VoidDnsWriterModule.class,
|
||||
|
||||
@@ -19,6 +19,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.Iterables.getLast;
|
||||
import static google.registry.dns.DnsUtils.requestDomainDnsRefresh;
|
||||
import static google.registry.model.tld.Tlds.assertTldsExist;
|
||||
import static google.registry.persistence.PersistenceModule.TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.RequestParameters.PARAM_TLDS;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
@@ -82,13 +83,16 @@ public class RefreshDnsForAllDomainsAction implements Runnable {
|
||||
public void run() {
|
||||
assertTldsExist(tlds);
|
||||
checkArgument(batchSize > 0, "Must specify a positive number for batch size");
|
||||
int smearMinutes = tm().transact(this::calculateSmearMinutes);
|
||||
int smearMinutes = tm().transact(this::calculateSmearMinutes, TRANSACTION_REPEATABLE_READ);
|
||||
|
||||
ImmutableList<String> domainsBatch;
|
||||
@Nullable String lastInPreviousBatch = null;
|
||||
do {
|
||||
Optional<String> lastInPreviousBatchOpt = Optional.ofNullable(lastInPreviousBatch);
|
||||
domainsBatch = tm().transact(() -> refreshBatch(lastInPreviousBatchOpt, smearMinutes));
|
||||
domainsBatch =
|
||||
tm().transact(
|
||||
() -> refreshBatch(lastInPreviousBatchOpt, smearMinutes),
|
||||
TRANSACTION_REPEATABLE_READ);
|
||||
lastInPreviousBatch = domainsBatch.isEmpty() ? null : getLast(domainsBatch);
|
||||
} while (domainsBatch.size() == batchSize);
|
||||
}
|
||||
|
||||
@@ -40,15 +40,24 @@ public class ConsoleUserDataAction implements JsonGetAction {
|
||||
|
||||
private final AuthResult authResult;
|
||||
private final Response response;
|
||||
private final String productName;
|
||||
private final String supportPhoneNumber;
|
||||
private final String supportEmail;
|
||||
private final String technicalDocsUrl;
|
||||
|
||||
@Inject
|
||||
public ConsoleUserDataAction(
|
||||
AuthResult authResult,
|
||||
Response response,
|
||||
@Config("productName") String productName,
|
||||
@Config("supportEmail") String supportEmail,
|
||||
@Config("supportPhoneNumber") String supportPhoneNumber,
|
||||
@Config("technicalDocsUrl") String technicalDocsUrl) {
|
||||
this.response = response;
|
||||
this.authResult = authResult;
|
||||
this.productName = productName;
|
||||
this.supportEmail = supportEmail;
|
||||
this.supportPhoneNumber = supportPhoneNumber;
|
||||
this.technicalDocsUrl = technicalDocsUrl;
|
||||
}
|
||||
|
||||
@@ -74,6 +83,10 @@ public class ConsoleUserDataAction implements JsonGetAction {
|
||||
// auth checks.
|
||||
"isAdmin", user.getUserRoles().isAdmin(),
|
||||
"globalRole", user.getUserRoles().getGlobalRole(),
|
||||
// Include static contact resources in this call to minimize round trips
|
||||
"productName", productName,
|
||||
"supportEmail", supportEmail,
|
||||
"supportPhoneNumber", supportPhoneNumber,
|
||||
// Is used by UI to construct a link to registry resources
|
||||
"technicalDocsUrl", technicalDocsUrl));
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ package google.registry.ui.server.console.settings;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
|
||||
import avro.shaded.com.google.common.collect.ImmutableList;
|
||||
import com.google.api.client.http.HttpStatusCodes;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.flows.certs.CertificateChecker;
|
||||
@@ -103,42 +102,31 @@ public class SecurityAction implements JsonGetAction {
|
||||
.asBuilder()
|
||||
.setIpAddressAllowList(registrarParameter.getIpAddressAllowList());
|
||||
|
||||
boolean hasInvalidCerts =
|
||||
ImmutableList.of(
|
||||
registrarParameter.getClientCertificate(),
|
||||
registrarParameter.getFailoverClientCertificate())
|
||||
.stream()
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.anyMatch(
|
||||
cert -> {
|
||||
try {
|
||||
certificateChecker.validateCertificate(cert);
|
||||
return false;
|
||||
} catch (InsecureCertificateException e) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (hasInvalidCerts) {
|
||||
try {
|
||||
if (!savedRegistrar
|
||||
.getClientCertificate()
|
||||
.equals(registrarParameter.getClientCertificate())) {
|
||||
if (registrarParameter.getClientCertificate().isPresent()) {
|
||||
String newClientCert = registrarParameter.getClientCertificate().get();
|
||||
certificateChecker.validateCertificate(newClientCert);
|
||||
updatedRegistrar.setClientCertificate(newClientCert, tm().getTransactionTime());
|
||||
}
|
||||
}
|
||||
if (!savedRegistrar
|
||||
.getFailoverClientCertificate()
|
||||
.equals(registrarParameter.getFailoverClientCertificate())) {
|
||||
if (registrarParameter.getFailoverClientCertificate().isPresent()) {
|
||||
String newFailoverCert = registrarParameter.getFailoverClientCertificate().get();
|
||||
certificateChecker.validateCertificate(newFailoverCert);
|
||||
updatedRegistrar.setFailoverClientCertificate(newFailoverCert, tm().getTransactionTime());
|
||||
}
|
||||
}
|
||||
} catch (InsecureCertificateException e) {
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
||||
response.setPayload("Insecure Certificate in parameter");
|
||||
response.setPayload("Invalid certificate in parameter");
|
||||
return;
|
||||
}
|
||||
|
||||
registrarParameter
|
||||
.getClientCertificate()
|
||||
.ifPresent(
|
||||
newClientCert ->
|
||||
updatedRegistrar.setClientCertificate(newClientCert, tm().getTransactionTime()));
|
||||
|
||||
registrarParameter
|
||||
.getFailoverClientCertificate()
|
||||
.ifPresent(
|
||||
failoverCert ->
|
||||
updatedRegistrar.setFailoverClientCertificate(
|
||||
failoverCert, tm().getTransactionTime()));
|
||||
|
||||
tm().put(updatedRegistrar.build());
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_OK);
|
||||
}
|
||||
|
||||
+45
-61
@@ -18,20 +18,27 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.loadRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.persistSimpleResource;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.api.client.http.HttpResponseException;
|
||||
import com.google.api.client.http.HttpStatusCodes;
|
||||
import com.google.api.client.http.LowLevelHttpRequest;
|
||||
import com.google.api.client.testing.http.MockHttpTransport;
|
||||
import com.google.api.client.testing.http.MockLowLevelHttpRequest;
|
||||
import com.google.api.client.testing.http.MockLowLevelHttpResponse;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarAddress;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.request.HttpException.InternalServerErrorException;
|
||||
import google.registry.testing.FakeUrlConnectionService;
|
||||
import google.registry.util.UrlConnectionException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
@@ -61,44 +68,26 @@ public final class UpdateRegistrarRdapBaseUrlsActionTest {
|
||||
public JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
|
||||
private static class TestHttpTransport extends MockHttpTransport {
|
||||
private MockLowLevelHttpRequest requestSent;
|
||||
private MockLowLevelHttpResponse response;
|
||||
|
||||
void setResponse(MockLowLevelHttpResponse response) {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
MockLowLevelHttpRequest getRequestSent() {
|
||||
return requestSent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LowLevelHttpRequest buildRequest(String method, String url) {
|
||||
assertThat(method).isEqualTo("GET");
|
||||
MockLowLevelHttpRequest httpRequest = new MockLowLevelHttpRequest(url);
|
||||
httpRequest.setResponse(response);
|
||||
requestSent = httpRequest;
|
||||
return httpRequest;
|
||||
}
|
||||
}
|
||||
|
||||
private TestHttpTransport httpTransport;
|
||||
private final HttpURLConnection connection = mock(HttpURLConnection.class);
|
||||
private final FakeUrlConnectionService urlConnectionService =
|
||||
new FakeUrlConnectionService(connection);
|
||||
private UpdateRegistrarRdapBaseUrlsAction action;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
void beforeEach() throws Exception {
|
||||
action = new UpdateRegistrarRdapBaseUrlsAction();
|
||||
httpTransport = new TestHttpTransport();
|
||||
action.httpTransport = httpTransport;
|
||||
setValidResponse();
|
||||
action.urlConnectionService = urlConnectionService;
|
||||
when(connection.getResponseCode()).thenReturn(SC_OK);
|
||||
when(connection.getInputStream())
|
||||
.thenReturn(new ByteArrayInputStream(CSV_REPLY.getBytes(StandardCharsets.UTF_8)));
|
||||
createTld("tld");
|
||||
}
|
||||
|
||||
private void assertCorrectRequestSent() {
|
||||
assertThat(httpTransport.getRequestSent().getUrl())
|
||||
.isEqualTo("https://www.iana.org/assignments/registrar-ids/registrar-ids-1.csv");
|
||||
assertThat(httpTransport.getRequestSent().getHeaders().get("accept-encoding")).isNull();
|
||||
private void assertCorrectRequestSent() throws Exception {
|
||||
assertThat(urlConnectionService.getConnectedUrls())
|
||||
.containsExactly(
|
||||
new URL("https://www.iana.org/assignments/registrar-ids/registrar-ids-1.csv"));
|
||||
verify(connection).setRequestProperty("Accept-Encoding", "gzip");
|
||||
}
|
||||
|
||||
private static void persistRegistrar(
|
||||
@@ -119,14 +108,8 @@ public final class UpdateRegistrarRdapBaseUrlsActionTest {
|
||||
.build());
|
||||
}
|
||||
|
||||
private void setValidResponse() {
|
||||
MockLowLevelHttpResponse csvResponse = new MockLowLevelHttpResponse();
|
||||
csvResponse.setContent(CSV_REPLY);
|
||||
httpTransport.setResponse(csvResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnknownIana_cleared() {
|
||||
void testUnknownIana_cleared() throws Exception {
|
||||
// The IANA ID isn't in the CSV reply
|
||||
persistRegistrar("someRegistrar", 4123L, Registrar.Type.REAL, "http://rdap.example/blah");
|
||||
action.run();
|
||||
@@ -135,7 +118,7 @@ public final class UpdateRegistrarRdapBaseUrlsActionTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testKnownIana_changed() {
|
||||
void testKnownIana_changed() throws Exception {
|
||||
// The IANA ID is in the CSV reply
|
||||
persistRegistrar("someRegistrar", 1448L, Registrar.Type.REAL, "http://rdap.example/blah");
|
||||
action.run();
|
||||
@@ -145,7 +128,7 @@ public final class UpdateRegistrarRdapBaseUrlsActionTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testKnownIana_notReal_noChange() {
|
||||
void testKnownIana_notReal_noChange() throws Exception {
|
||||
// The IANA ID is in the CSV reply
|
||||
persistRegistrar("someRegistrar", 9999L, Registrar.Type.INTERNAL, "http://rdap.example/blah");
|
||||
// Real registrars should actually change
|
||||
@@ -159,7 +142,7 @@ public final class UpdateRegistrarRdapBaseUrlsActionTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testKnownIana_notReal_nullIANA_noChange() {
|
||||
void testKnownIana_notReal_nullIANA_noChange() throws Exception {
|
||||
persistRegistrar("someRegistrar", null, Registrar.Type.TEST, "http://rdap.example/blah");
|
||||
action.run();
|
||||
assertCorrectRequestSent();
|
||||
@@ -168,29 +151,30 @@ public final class UpdateRegistrarRdapBaseUrlsActionTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_serverErrorResponse() {
|
||||
MockLowLevelHttpResponse badResponse = new MockLowLevelHttpResponse();
|
||||
badResponse.setZeroContent();
|
||||
badResponse.setStatusCode(HttpStatusCodes.STATUS_CODE_SERVER_ERROR);
|
||||
httpTransport.setResponse(badResponse);
|
||||
|
||||
RuntimeException thrown = assertThrows(RuntimeException.class, action::run);
|
||||
void testFailure_serverErrorResponse() throws Exception {
|
||||
when(connection.getResponseCode()).thenReturn(SC_INTERNAL_SERVER_ERROR);
|
||||
when(connection.getInputStream())
|
||||
.thenReturn(new ByteArrayInputStream("".getBytes(StandardCharsets.UTF_8)));
|
||||
InternalServerErrorException thrown =
|
||||
assertThrows(InternalServerErrorException.class, action::run);
|
||||
verify(connection, times(0)).getInputStream();
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Error when retrieving RDAP base URL CSV file");
|
||||
Throwable cause = thrown.getCause();
|
||||
assertThat(cause).isInstanceOf(HttpResponseException.class);
|
||||
assertThat(cause).isInstanceOf(UrlConnectionException.class);
|
||||
assertThat(cause)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("500\nGET https://www.iana.org/assignments/registrar-ids/registrar-ids-1.csv");
|
||||
.contains("https://www.iana.org/assignments/registrar-ids/registrar-ids-1.csv");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_invalidCsv() {
|
||||
MockLowLevelHttpResponse csvResponse = new MockLowLevelHttpResponse();
|
||||
csvResponse.setContent("foo,bar\nbaz,foo");
|
||||
httpTransport.setResponse(csvResponse);
|
||||
void testFailure_invalidCsv() throws Exception {
|
||||
when(connection.getInputStream())
|
||||
.thenReturn(new ByteArrayInputStream("foo,bar\nbaz,foo".getBytes(StandardCharsets.UTF_8)));
|
||||
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, action::run);
|
||||
InternalServerErrorException thrown =
|
||||
assertThrows(InternalServerErrorException.class, action::run);
|
||||
assertThat(thrown)
|
||||
.hasCauseThat()
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Mapping for ID not found, expected one of [foo, bar]");
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
|
||||
package google.registry.rde;
|
||||
|
||||
import static com.google.appengine.api.urlfetch.HTTPMethod.PUT;
|
||||
import static com.google.api.client.http.HttpStatusCodes.STATUS_CODE_BAD_REQUEST;
|
||||
import static com.google.api.client.http.HttpStatusCodes.STATUS_CODE_OK;
|
||||
import static com.google.api.client.http.HttpStatusCodes.STATUS_CODE_UNAUTHORIZED;
|
||||
import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.common.Cursor.CursorType.RDE_REPORT;
|
||||
@@ -24,25 +26,17 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.loadByKey;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static org.joda.time.Duration.standardDays;
|
||||
import static org.joda.time.Duration.standardSeconds;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.appengine.api.urlfetch.HTTPHeader;
|
||||
import com.google.appengine.api.urlfetch.HTTPRequest;
|
||||
import com.google.appengine.api.urlfetch.HTTPResponse;
|
||||
import com.google.appengine.api.urlfetch.URLFetchService;
|
||||
import com.google.cloud.storage.BlobId;
|
||||
import com.google.cloud.storage.contrib.nio.testing.LocalStorageHelper;
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.io.ByteSource;
|
||||
import google.registry.gcs.GcsUtils;
|
||||
import google.registry.model.common.Cursor;
|
||||
@@ -53,25 +47,23 @@ import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationT
|
||||
import google.registry.request.HttpException.InternalServerErrorException;
|
||||
import google.registry.request.HttpException.NoContentException;
|
||||
import google.registry.testing.BouncyCastleProviderExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeKeyringModule;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.testing.FakeSleeper;
|
||||
import google.registry.util.Retrier;
|
||||
import google.registry.testing.FakeUrlConnectionService;
|
||||
import google.registry.util.UrlConnectionException;
|
||||
import google.registry.xjc.XjcXmlTransformer;
|
||||
import google.registry.xjc.rdereport.XjcRdeReportReport;
|
||||
import google.registry.xml.XmlException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
/** Unit tests for {@link RdeReportAction}. */
|
||||
public class RdeReportActionTest {
|
||||
@@ -89,9 +81,11 @@ public class RdeReportActionTest {
|
||||
|
||||
private final FakeResponse response = new FakeResponse();
|
||||
private final EscrowTaskRunner runner = mock(EscrowTaskRunner.class);
|
||||
private final URLFetchService urlFetchService = mock(URLFetchService.class);
|
||||
private final ArgumentCaptor<HTTPRequest> request = ArgumentCaptor.forClass(HTTPRequest.class);
|
||||
private final HTTPResponse httpResponse = mock(HTTPResponse.class);
|
||||
private final HttpURLConnection httpUrlConnection = mock(HttpURLConnection.class);
|
||||
private final FakeUrlConnectionService urlConnectionService =
|
||||
new FakeUrlConnectionService(httpUrlConnection);
|
||||
private final ByteArrayOutputStream connectionOutputStream = new ByteArrayOutputStream();
|
||||
|
||||
private final PGPPublicKey encryptKey =
|
||||
new FakeKeyringModule().get().getRdeStagingEncryptionKey();
|
||||
private final GcsUtils gcsUtils = new GcsUtils(LocalStorageHelper.getOptions());
|
||||
@@ -102,9 +96,8 @@ public class RdeReportActionTest {
|
||||
private RdeReportAction createAction() {
|
||||
RdeReporter reporter = new RdeReporter();
|
||||
reporter.reportUrlPrefix = "https://rde-report.example";
|
||||
reporter.urlFetchService = urlFetchService;
|
||||
reporter.urlConnectionService = urlConnectionService;
|
||||
reporter.password = "foo";
|
||||
reporter.retrier = new Retrier(new FakeSleeper(new FakeClock()), 3);
|
||||
RdeReportAction action = new RdeReportAction();
|
||||
action.gcsUtils = gcsUtils;
|
||||
action.response = response;
|
||||
@@ -126,6 +119,9 @@ public class RdeReportActionTest {
|
||||
persistResource(Cursor.createScoped(RDE_UPLOAD, DateTime.parse("2006-06-07TZ"), registry));
|
||||
gcsUtils.createFromBytes(reportFile, Ghostryde.encode(REPORT_XML.read(), encryptKey));
|
||||
tm().transact(() -> RdeRevision.saveRevision("test", DateTime.parse("2006-06-06TZ"), FULL, 0));
|
||||
when(httpUrlConnection.getOutputStream()).thenReturn(connectionOutputStream);
|
||||
when(httpUrlConnection.getResponseCode()).thenReturn(STATUS_CODE_OK);
|
||||
when(httpUrlConnection.getInputStream()).thenReturn(IIRDEA_GOOD_XML.openBufferedStream());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -142,24 +138,20 @@ public class RdeReportActionTest {
|
||||
|
||||
@Test
|
||||
void testRunWithLock() throws Exception {
|
||||
when(httpResponse.getResponseCode()).thenReturn(SC_OK);
|
||||
when(httpResponse.getContent()).thenReturn(IIRDEA_GOOD_XML.read());
|
||||
when(urlFetchService.fetch(request.capture())).thenReturn(httpResponse);
|
||||
createAction().runWithLock(loadRdeReportCursor());
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
|
||||
assertThat(response.getPayload()).isEqualTo("OK test 2006-06-06T00:00:00.000Z\n");
|
||||
|
||||
// Verify the HTTP request was correct.
|
||||
assertThat(request.getValue().getMethod()).isSameInstanceAs(PUT);
|
||||
assertThat(request.getValue().getURL().getProtocol()).isEqualTo("https");
|
||||
assertThat(request.getValue().getURL().getPath()).endsWith("/test/20101017001");
|
||||
Map<String, String> headers = mapifyHeaders(request.getValue().getHeaders());
|
||||
assertThat(headers).containsEntry("CONTENT_TYPE", "text/xml");
|
||||
assertThat(headers).containsEntry("AUTHORIZATION", "Basic dGVzdF9yeTpmb28=");
|
||||
verify(httpUrlConnection).setRequestMethod("PUT");
|
||||
assertThat(httpUrlConnection.getURL().getProtocol()).isEqualTo("https");
|
||||
assertThat(httpUrlConnection.getURL().getPath()).endsWith("/test/20101017001");
|
||||
verify(httpUrlConnection).setRequestProperty("Content-Type", "text/xml; charset=utf-8");
|
||||
verify(httpUrlConnection).setRequestProperty("Authorization", "Basic dGVzdF9yeTpmb28=");
|
||||
|
||||
// Verify the payload XML was the same as what's in testdata/report.xml.
|
||||
XjcRdeReportReport report = parseReport(request.getValue().getPayload());
|
||||
XjcRdeReportReport report = parseReport(connectionOutputStream.toByteArray());
|
||||
assertThat(report.getId()).isEqualTo("20101017001");
|
||||
assertThat(report.getCrDate()).isEqualTo(DateTime.parse("2010-10-17T00:15:00.0Z"));
|
||||
assertThat(report.getWatermark()).isEqualTo(DateTime.parse("2010-10-17T00:00:00Z"));
|
||||
@@ -167,9 +159,6 @@ public class RdeReportActionTest {
|
||||
|
||||
@Test
|
||||
void testRunWithLock_withPrefix() throws Exception {
|
||||
when(httpResponse.getResponseCode()).thenReturn(SC_OK);
|
||||
when(httpResponse.getContent()).thenReturn(IIRDEA_GOOD_XML.read());
|
||||
when(urlFetchService.fetch(request.capture())).thenReturn(httpResponse);
|
||||
RdeReportAction action = createAction();
|
||||
action.runWithLock(loadRdeReportCursor());
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
@@ -177,15 +166,14 @@ public class RdeReportActionTest {
|
||||
assertThat(response.getPayload()).isEqualTo("OK test 2006-06-06T00:00:00.000Z\n");
|
||||
|
||||
// Verify the HTTP request was correct.
|
||||
assertThat(request.getValue().getMethod()).isSameInstanceAs(PUT);
|
||||
assertThat(request.getValue().getURL().getProtocol()).isEqualTo("https");
|
||||
assertThat(request.getValue().getURL().getPath()).endsWith("/test/20101017001");
|
||||
Map<String, String> headers = mapifyHeaders(request.getValue().getHeaders());
|
||||
assertThat(headers).containsEntry("CONTENT_TYPE", "text/xml");
|
||||
assertThat(headers).containsEntry("AUTHORIZATION", "Basic dGVzdF9yeTpmb28=");
|
||||
verify(httpUrlConnection).setRequestMethod("PUT");
|
||||
assertThat(httpUrlConnection.getURL().getProtocol()).isEqualTo("https");
|
||||
assertThat(httpUrlConnection.getURL().getPath()).endsWith("/test/20101017001");
|
||||
verify(httpUrlConnection).setRequestProperty("Content-Type", "text/xml; charset=utf-8");
|
||||
verify(httpUrlConnection).setRequestProperty("Authorization", "Basic dGVzdF9yeTpmb28=");
|
||||
|
||||
// Verify the payload XML was the same as what's in testdata/report.xml.
|
||||
XjcRdeReportReport report = parseReport(request.getValue().getPayload());
|
||||
XjcRdeReportReport report = parseReport(connectionOutputStream.toByteArray());
|
||||
assertThat(report.getId()).isEqualTo("20101017001");
|
||||
assertThat(report.getCrDate()).isEqualTo(DateTime.parse("2010-10-17T00:15:00.0Z"));
|
||||
assertThat(report.getWatermark()).isEqualTo(DateTime.parse("2010-10-17T00:00:00Z"));
|
||||
@@ -200,9 +188,6 @@ public class RdeReportActionTest {
|
||||
|
||||
@Test
|
||||
void testRunWithLock_withoutPrefix() throws Exception {
|
||||
when(httpResponse.getResponseCode()).thenReturn(SC_OK);
|
||||
when(httpResponse.getContent()).thenReturn(IIRDEA_GOOD_XML.read());
|
||||
when(urlFetchService.fetch(request.capture())).thenReturn(httpResponse);
|
||||
RdeReportAction action = createAction();
|
||||
action.prefix = Optional.empty();
|
||||
gcsUtils.delete(reportFile);
|
||||
@@ -225,15 +210,14 @@ public class RdeReportActionTest {
|
||||
assertThat(response.getPayload()).isEqualTo("OK test 2006-06-06T00:00:00.000Z\n");
|
||||
|
||||
// Verify the HTTP request was correct.
|
||||
assertThat(request.getValue().getMethod()).isSameInstanceAs(PUT);
|
||||
assertThat(request.getValue().getURL().getProtocol()).isEqualTo("https");
|
||||
assertThat(request.getValue().getURL().getPath()).endsWith("/test/20101017001");
|
||||
Map<String, String> headers = mapifyHeaders(request.getValue().getHeaders());
|
||||
assertThat(headers).containsEntry("CONTENT_TYPE", "text/xml");
|
||||
assertThat(headers).containsEntry("AUTHORIZATION", "Basic dGVzdF9yeTpmb28=");
|
||||
verify(httpUrlConnection).setRequestMethod("PUT");
|
||||
assertThat(httpUrlConnection.getURL().getProtocol()).isEqualTo("https");
|
||||
assertThat(httpUrlConnection.getURL().getPath()).endsWith("/test/20101017001");
|
||||
verify(httpUrlConnection).setRequestProperty("Content-Type", "text/xml; charset=utf-8");
|
||||
verify(httpUrlConnection).setRequestProperty("Authorization", "Basic dGVzdF9yeTpmb28=");
|
||||
|
||||
// Verify the payload XML was the same as what's in testdata/report.xml.
|
||||
XjcRdeReportReport report = parseReport(request.getValue().getPayload());
|
||||
XjcRdeReportReport report = parseReport(connectionOutputStream.toByteArray());
|
||||
assertThat(report.getId()).isEqualTo("20101017001");
|
||||
assertThat(report.getCrDate()).isEqualTo(DateTime.parse("2010-10-17T00:15:00.0Z"));
|
||||
assertThat(report.getWatermark()).isEqualTo(DateTime.parse("2010-10-17T00:00:00Z"));
|
||||
@@ -246,9 +230,6 @@ public class RdeReportActionTest {
|
||||
PGPPublicKey encryptKey = new FakeKeyringModule().get().getRdeStagingEncryptionKey();
|
||||
gcsUtils.createFromBytes(newReport, Ghostryde.encode(REPORT_XML.read(), encryptKey));
|
||||
tm().transact(() -> RdeRevision.saveRevision("test", DateTime.parse("2006-06-06TZ"), FULL, 1));
|
||||
when(httpResponse.getResponseCode()).thenReturn(SC_OK);
|
||||
when(httpResponse.getContent()).thenReturn(IIRDEA_GOOD_XML.read());
|
||||
when(urlFetchService.fetch(request.capture())).thenReturn(httpResponse);
|
||||
createAction().runWithLock(loadRdeReportCursor());
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
@@ -281,9 +262,8 @@ public class RdeReportActionTest {
|
||||
|
||||
@Test
|
||||
void testRunWithLock_badRequest_throws500WithErrorInfo() throws Exception {
|
||||
when(httpResponse.getResponseCode()).thenReturn(SC_BAD_REQUEST);
|
||||
when(httpResponse.getContent()).thenReturn(IIRDEA_BAD_XML.read());
|
||||
when(urlFetchService.fetch(request.capture())).thenReturn(httpResponse);
|
||||
when(httpUrlConnection.getResponseCode()).thenReturn(STATUS_CODE_BAD_REQUEST);
|
||||
when(httpUrlConnection.getInputStream()).thenReturn(IIRDEA_BAD_XML.openBufferedStream());
|
||||
InternalServerErrorException thrown =
|
||||
assertThrows(
|
||||
InternalServerErrorException.class,
|
||||
@@ -292,38 +272,19 @@ public class RdeReportActionTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRunWithLock_fetchFailed_throwsRuntimeException() throws Exception {
|
||||
class ExpectedThrownException extends RuntimeException {}
|
||||
when(urlFetchService.fetch(any(HTTPRequest.class))).thenThrow(new ExpectedThrownException());
|
||||
assertThrows(
|
||||
ExpectedThrownException.class, () -> createAction().runWithLock(loadRdeReportCursor()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRunWithLock_socketTimeout_doesRetry() throws Exception {
|
||||
when(httpResponse.getResponseCode()).thenReturn(SC_OK);
|
||||
when(httpResponse.getContent()).thenReturn(IIRDEA_GOOD_XML.read());
|
||||
when(urlFetchService.fetch(request.capture()))
|
||||
.thenThrow(new SocketTimeoutException())
|
||||
.thenReturn(httpResponse);
|
||||
createAction().runWithLock(loadRdeReportCursor());
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getContentType()).isEqualTo(PLAIN_TEXT_UTF_8);
|
||||
assertThat(response.getPayload()).isEqualTo("OK test 2006-06-06T00:00:00.000Z\n");
|
||||
void testRunWithLock_notAuthorized() throws Exception {
|
||||
when(httpUrlConnection.getResponseCode()).thenReturn(STATUS_CODE_UNAUTHORIZED);
|
||||
UrlConnectionException thrown =
|
||||
assertThrows(
|
||||
UrlConnectionException.class, () -> createAction().runWithLock(loadRdeReportCursor()));
|
||||
verify(httpUrlConnection, times(0)).getInputStream();
|
||||
assertThat(thrown).hasMessageThat().contains("PUT failed");
|
||||
}
|
||||
|
||||
private DateTime loadRdeReportCursor() {
|
||||
return loadByKey(Cursor.createScopedVKey(RDE_REPORT, registry)).getCursorTime();
|
||||
}
|
||||
|
||||
private static ImmutableMap<String, String> mapifyHeaders(Iterable<HTTPHeader> headers) {
|
||||
ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<>();
|
||||
for (HTTPHeader header : headers) {
|
||||
builder.put(Ascii.toUpperCase(header.getName().replace('-', '_')), header.getValue());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private static XjcRdeReportReport parseReport(byte[] data) {
|
||||
try {
|
||||
return XjcXmlTransformer.unmarshal(XjcRdeReportReport.class, new ByteArrayInputStream(data));
|
||||
|
||||
@@ -14,28 +14,27 @@
|
||||
|
||||
package google.registry.reporting.icann;
|
||||
|
||||
import static com.google.common.net.MediaType.CSV_UTF_8;
|
||||
import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
|
||||
import static com.google.api.client.http.HttpStatusCodes.STATUS_CODE_BAD_REQUEST;
|
||||
import static com.google.api.client.http.HttpStatusCodes.STATUS_CODE_OK;
|
||||
import static com.google.api.client.http.HttpStatusCodes.STATUS_CODE_SERVER_ERROR;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.api.client.http.HttpResponseException;
|
||||
import com.google.api.client.http.HttpStatusCodes;
|
||||
import com.google.api.client.http.LowLevelHttpRequest;
|
||||
import com.google.api.client.http.LowLevelHttpResponse;
|
||||
import com.google.api.client.testing.http.MockHttpTransport;
|
||||
import com.google.api.client.testing.http.MockLowLevelHttpRequest;
|
||||
import com.google.api.client.testing.http.MockLowLevelHttpResponse;
|
||||
import com.google.api.client.util.Base64;
|
||||
import com.google.api.client.util.StringUtils;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import com.google.common.io.ByteSource;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import google.registry.testing.FakeUrlConnectionService;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
@@ -46,103 +45,75 @@ class IcannHttpReporterTest {
|
||||
private static final ByteSource IIRDEA_GOOD_XML = ReportingTestData.loadBytes("iirdea_good.xml");
|
||||
private static final ByteSource IIRDEA_BAD_XML = ReportingTestData.loadBytes("iirdea_bad.xml");
|
||||
private static final byte[] FAKE_PAYLOAD = "test,csv\n1,2".getBytes(UTF_8);
|
||||
private static final IcannHttpReporter reporter = new IcannHttpReporter();
|
||||
|
||||
private MockLowLevelHttpRequest mockRequest;
|
||||
private final HttpURLConnection connection = mock(HttpURLConnection.class);
|
||||
private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
private final FakeUrlConnectionService urlConnectionService =
|
||||
new FakeUrlConnectionService(connection);
|
||||
|
||||
@RegisterExtension
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
|
||||
private MockHttpTransport createMockTransport(
|
||||
int statusCode, final ByteSource iirdeaResponse) {
|
||||
return new MockHttpTransport() {
|
||||
@Override
|
||||
public LowLevelHttpRequest buildRequest(String method, String url) {
|
||||
mockRequest =
|
||||
new MockLowLevelHttpRequest() {
|
||||
@Override
|
||||
public LowLevelHttpResponse execute() throws IOException {
|
||||
MockLowLevelHttpResponse response = new MockLowLevelHttpResponse();
|
||||
response.setStatusCode(statusCode);
|
||||
response.setContentType(PLAIN_TEXT_UTF_8.toString());
|
||||
response.setContent(iirdeaResponse.read());
|
||||
return response;
|
||||
}
|
||||
};
|
||||
mockRequest.setUrl(url);
|
||||
return mockRequest;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private MockHttpTransport createMockTransport(final ByteSource iirdeaResponse) {
|
||||
return createMockTransport(HttpStatusCodes.STATUS_CODE_OK, iirdeaResponse);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
void beforeEach() throws Exception {
|
||||
createTld("test");
|
||||
createTld("xn--abc123");
|
||||
}
|
||||
|
||||
private IcannHttpReporter createReporter() {
|
||||
IcannHttpReporter reporter = new IcannHttpReporter();
|
||||
reporter.httpTransport = createMockTransport(IIRDEA_GOOD_XML);
|
||||
when(connection.getOutputStream()).thenReturn(outputStream);
|
||||
when(connection.getResponseCode()).thenReturn(STATUS_CODE_OK);
|
||||
when(connection.getInputStream()).thenReturn(IIRDEA_GOOD_XML.openBufferedStream());
|
||||
reporter.urlConnectionService = urlConnectionService;
|
||||
reporter.password = "fakePass";
|
||||
reporter.icannTransactionsUrl = "https://fake-transactions.url";
|
||||
reporter.icannActivityUrl = "https://fake-activity.url";
|
||||
return reporter;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess() throws Exception {
|
||||
IcannHttpReporter reporter = createReporter();
|
||||
reporter.send(FAKE_PAYLOAD, "test-transactions-201706.csv");
|
||||
assertThat(reporter.send(FAKE_PAYLOAD, "test-transactions-201706.csv")).isTrue();
|
||||
|
||||
assertThat(mockRequest.getUrl()).isEqualTo("https://fake-transactions.url/test/2017-06");
|
||||
Map<String, List<String>> headers = mockRequest.getHeaders();
|
||||
assertThat(urlConnectionService.getConnectedUrls())
|
||||
.containsExactly(new URL("https://fake-transactions.url/test/2017-06"));
|
||||
String userPass = "test_ry:fakePass";
|
||||
String expectedAuth =
|
||||
String.format("Basic %s", Base64.encodeBase64String(StringUtils.getBytesUtf8(userPass)));
|
||||
assertThat(headers.get("authorization")).containsExactly(expectedAuth);
|
||||
assertThat(headers.get("content-type")).containsExactly(CSV_UTF_8.toString());
|
||||
String.format("Basic %s", BaseEncoding.base64().encode(StringUtils.getBytesUtf8(userPass)));
|
||||
verify(connection).setRequestProperty("Authorization", expectedAuth);
|
||||
verify(connection).setRequestProperty("Content-Type", "text/csv; charset=utf-8");
|
||||
assertThat(outputStream.toByteArray()).isEqualTo(FAKE_PAYLOAD);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_internationalTld() throws Exception {
|
||||
IcannHttpReporter reporter = createReporter();
|
||||
reporter.send(FAKE_PAYLOAD, "xn--abc123-transactions-201706.csv");
|
||||
assertThat(reporter.send(FAKE_PAYLOAD, "xn--abc123-transactions-201706.csv")).isTrue();
|
||||
|
||||
assertThat(mockRequest.getUrl()).isEqualTo("https://fake-transactions.url/xn--abc123/2017-06");
|
||||
Map<String, List<String>> headers = mockRequest.getHeaders();
|
||||
assertThat(urlConnectionService.getConnectedUrls())
|
||||
.containsExactly(new URL("https://fake-transactions.url/xn--abc123/2017-06"));
|
||||
String userPass = "xn--abc123_ry:fakePass";
|
||||
String expectedAuth =
|
||||
String.format("Basic %s", Base64.encodeBase64String(StringUtils.getBytesUtf8(userPass)));
|
||||
assertThat(headers.get("authorization")).containsExactly(expectedAuth);
|
||||
assertThat(headers.get("content-type")).containsExactly(CSV_UTF_8.toString());
|
||||
String.format("Basic %s", BaseEncoding.base64().encode(StringUtils.getBytesUtf8(userPass)));
|
||||
verify(connection).setRequestProperty("Authorization", expectedAuth);
|
||||
verify(connection).setRequestProperty("Content-Type", "text/csv; charset=utf-8");
|
||||
assertThat(outputStream.toByteArray()).isEqualTo(FAKE_PAYLOAD);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFail_BadIirdeaResponse() throws Exception {
|
||||
IcannHttpReporter reporter = createReporter();
|
||||
reporter.httpTransport =
|
||||
createMockTransport(HttpStatusCodes.STATUS_CODE_BAD_REQUEST, IIRDEA_BAD_XML);
|
||||
when(connection.getInputStream()).thenReturn(IIRDEA_BAD_XML.openBufferedStream());
|
||||
when(connection.getResponseCode()).thenReturn(STATUS_CODE_BAD_REQUEST);
|
||||
assertThat(reporter.send(FAKE_PAYLOAD, "test-transactions-201706.csv")).isFalse();
|
||||
verify(connection).getInputStream();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFail_transportException() {
|
||||
IcannHttpReporter reporter = createReporter();
|
||||
reporter.httpTransport =
|
||||
createMockTransport(HttpStatusCodes.STATUS_CODE_FORBIDDEN, ByteSource.empty());
|
||||
assertThrows(
|
||||
HttpResponseException.class,
|
||||
() -> reporter.send(FAKE_PAYLOAD, "test-transactions-201706.csv"));
|
||||
void testFail_OtherBadHttpResponse() throws Exception {
|
||||
when(connection.getResponseCode()).thenReturn(STATUS_CODE_SERVER_ERROR);
|
||||
assertThat(reporter.send(FAKE_PAYLOAD, "test-transactions-201706.csv")).isFalse();
|
||||
verify(connection, times(0)).getInputStream();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFail_invalidFilename_nonSixDigitYearMonth() {
|
||||
IcannHttpReporter reporter = createReporter();
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
@@ -156,7 +127,6 @@ class IcannHttpReporterTest {
|
||||
|
||||
@Test
|
||||
void testFail_invalidFilename_notActivityOrTransactions() {
|
||||
IcannHttpReporter reporter = createReporter();
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
@@ -169,7 +139,6 @@ class IcannHttpReporterTest {
|
||||
|
||||
@Test
|
||||
void testFail_invalidFilename_invalidTldName() {
|
||||
IcannHttpReporter reporter = createReporter();
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
@@ -183,7 +152,6 @@ class IcannHttpReporterTest {
|
||||
|
||||
@Test
|
||||
void testFail_invalidFilename_tldDoesntExist() {
|
||||
IcannHttpReporter reporter = createReporter();
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.testing;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.request.UrlConnectionService;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
@@ -26,15 +27,10 @@ import java.util.List;
|
||||
public class FakeUrlConnectionService implements UrlConnectionService {
|
||||
|
||||
private final HttpURLConnection mockConnection;
|
||||
private final List<URL> connectedUrls;
|
||||
private final List<URL> connectedUrls = new ArrayList<>();
|
||||
|
||||
public FakeUrlConnectionService(HttpURLConnection mockConnection) {
|
||||
this(mockConnection, new ArrayList<>());
|
||||
}
|
||||
|
||||
public FakeUrlConnectionService(HttpURLConnection mockConnection, List<URL> connectedUrls) {
|
||||
this.mockConnection = mockConnection;
|
||||
this.connectedUrls = connectedUrls;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -43,4 +39,8 @@ public class FakeUrlConnectionService implements UrlConnectionService {
|
||||
when(mockConnection.getURL()).thenReturn(url);
|
||||
return mockConnection;
|
||||
}
|
||||
|
||||
public ImmutableList<URL> getConnectedUrls() {
|
||||
return ImmutableList.copyOf(connectedUrls);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,7 @@ import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeUrlConnectionService;
|
||||
import google.registry.testing.TestCacheExtension;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
@@ -55,9 +53,8 @@ abstract class TmchActionTestCase {
|
||||
final Marksdb marksdb = new Marksdb();
|
||||
|
||||
protected final HttpURLConnection httpUrlConnection = mock(HttpURLConnection.class);
|
||||
protected final ArrayList<URL> connectedUrls = new ArrayList<>();
|
||||
protected FakeUrlConnectionService urlConnectionService =
|
||||
new FakeUrlConnectionService(httpUrlConnection, connectedUrls);
|
||||
new FakeUrlConnectionService(httpUrlConnection);
|
||||
|
||||
@BeforeEach
|
||||
public void beforeEachTmchActionTestCase() throws Exception {
|
||||
|
||||
@@ -56,7 +56,8 @@ class TmchCrlActionTest extends TmchActionTestCase {
|
||||
readResourceBytes(TmchCertificateAuthority.class, "icann-tmch-pilot.crl").read()));
|
||||
newTmchCrlAction(TmchCaMode.PILOT).run();
|
||||
verify(httpUrlConnection).getInputStream();
|
||||
assertThat(connectedUrls).containsExactly(new URL("https://sloth.lol/tmch.crl"));
|
||||
assertThat(urlConnectionService.getConnectedUrls())
|
||||
.containsExactly(new URL("https://sloth.lol/tmch.crl"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -49,7 +49,10 @@ class TmchDnlActionTest extends TmchActionTestCase {
|
||||
.thenReturn(new ByteArrayInputStream(TmchTestData.loadBytes("dnl/dnl-latest.sig").read()));
|
||||
newTmchDnlAction().run();
|
||||
verify(httpUrlConnection, times(2)).getInputStream();
|
||||
assertThat(connectedUrls.stream().map(URL::toString).collect(toImmutableList()))
|
||||
assertThat(
|
||||
urlConnectionService.getConnectedUrls().stream()
|
||||
.map(URL::toString)
|
||||
.collect(toImmutableList()))
|
||||
.containsExactly(MARKSDB_URL + "/dnl/dnl-latest.csv", MARKSDB_URL + "/dnl/dnl-latest.sig");
|
||||
|
||||
// Make sure the contents of testdata/dnl-latest.csv got inserted into the database.
|
||||
|
||||
@@ -50,7 +50,10 @@ class TmchSmdrlActionTest extends TmchActionTestCase {
|
||||
.thenReturn(new ByteArrayInputStream(loadBytes("smdrl/smdrl-latest.sig").read()));
|
||||
newTmchSmdrlAction().run();
|
||||
verify(httpUrlConnection, times(2)).getInputStream();
|
||||
assertThat(connectedUrls.stream().map(URL::toString).collect(toImmutableList()))
|
||||
assertThat(
|
||||
urlConnectionService.getConnectedUrls().stream()
|
||||
.map(URL::toString)
|
||||
.collect(toImmutableList()))
|
||||
.containsExactly(
|
||||
MARKSDB_URL + "/smdrl/smdrl-latest.csv", MARKSDB_URL + "/smdrl/smdrl-latest.sig");
|
||||
smdrl = SignedMarkRevocationList.get();
|
||||
|
||||
@@ -41,6 +41,7 @@ import com.google.common.io.Files;
|
||||
import com.google.common.testing.TestLogHandler;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldNotFoundException;
|
||||
import google.registry.model.tld.label.PremiumList;
|
||||
import google.registry.model.tld.label.PremiumListDao;
|
||||
import java.io.File;
|
||||
@@ -570,4 +571,23 @@ public class ConfigureTldCommandTest extends CommandTestCase<ConfigureTldCommand
|
||||
"Changes can not be applied since TLD is in breakglass mode but the breakglass flag"
|
||||
+ " was not used");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_dryRunOnCreate_noChanges() throws Exception {
|
||||
File tldFile = tmpDir.resolve("tld.yaml").toFile();
|
||||
Files.asCharSink(tldFile, UTF_8).write(loadFile(getClass(), "tld.yaml"));
|
||||
runCommandForced("--input=" + tldFile, "--dryrun");
|
||||
assertThrows(TldNotFoundException.class, () -> Tld.get("tld"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_dryRunOnUpdate_noChanges() throws Exception {
|
||||
Tld tld = createTld("tld");
|
||||
assertThat(tld.getCreateBillingCost()).isEqualTo(Money.of(USD, 13));
|
||||
File tldFile = tmpDir.resolve("tld.yaml").toFile();
|
||||
Files.asCharSink(tldFile, UTF_8).write(loadFile(getClass(), "tld.yaml"));
|
||||
runCommandForced("--input=" + tldFile, "-d");
|
||||
Tld notUpdatedTld = Tld.get("tld");
|
||||
assertThat(notUpdatedTld.getCreateBillingCost()).isEqualTo(Money.of(USD, 13));
|
||||
}
|
||||
}
|
||||
|
||||
+15
-2
@@ -54,7 +54,19 @@ class ConsoleUserDataActionTest {
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_OK);
|
||||
Map jsonObject = GSON.fromJson(response.getPayload(), Map.class);
|
||||
assertThat(jsonObject)
|
||||
.containsExactly("isAdmin", false, "technicalDocsUrl", "test", "globalRole", "FTE");
|
||||
.containsExactly(
|
||||
"isAdmin",
|
||||
false,
|
||||
"technicalDocsUrl",
|
||||
"test",
|
||||
"globalRole",
|
||||
"FTE",
|
||||
"productName",
|
||||
"Nomulus",
|
||||
"supportPhoneNumber",
|
||||
"+1 (212) 867 5309",
|
||||
"supportEmail",
|
||||
"support@example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -71,6 +83,7 @@ class ConsoleUserDataActionTest {
|
||||
}
|
||||
|
||||
private ConsoleUserDataAction createAction(AuthResult authResult) throws IOException {
|
||||
return new ConsoleUserDataAction(authResult, response, "test");
|
||||
return new ConsoleUserDataAction(
|
||||
authResult, response, "Nomulus", "support@example.com", "+1 (212) 867 5309", "test");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ PATH CLASS
|
||||
/_dr/task/deleteExpiredDomains DeleteExpiredDomainsAction GET n API APP ADMIN
|
||||
/_dr/task/deleteLoadTestData DeleteLoadTestDataAction POST n API APP ADMIN
|
||||
/_dr/task/deleteProberData DeleteProberDataAction POST n API APP ADMIN
|
||||
/_dr/task/executeCannedScript CannedScriptExecutionAction POST y API APP ADMIN
|
||||
/_dr/task/executeCannedScript CannedScriptExecutionAction POST,GET y API APP ADMIN
|
||||
/_dr/task/expandBillingRecurrences ExpandBillingRecurrencesAction GET n API APP ADMIN
|
||||
/_dr/task/exportDomainLists ExportDomainListsAction POST n API APP ADMIN
|
||||
/_dr/task/exportPremiumTerms ExportPremiumTermsAction POST n API APP ADMIN
|
||||
|
||||
@@ -91,6 +91,35 @@ steps:
|
||||
--format="get(digest)" --filter="tags = ${TAG_NAME}")
|
||||
sed -i s/'prober_cert_updater:latest'/prober_cert_updater@$digest/g \
|
||||
release/cloudbuild-renew-prober-certs-*.yaml
|
||||
# Build the tld_updater image and upload it to GCR. This image extends
|
||||
# from the `builder` and the nomulus.jar built earlier.
|
||||
- name: 'gcr.io/cloud-builders/docker'
|
||||
entrypoint: /bin/bash
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
# The nomulus jar is not under the working dir. Must be copied over.
|
||||
cp ../../output/nomulus.jar .
|
||||
docker build -t gcr.io/${PROJECT_ID}/tld_updater:${TAG_NAME} \
|
||||
--build-arg TAG_NAME=${TAG_NAME} --build-arg PROJECT_ID=${PROJECT_ID} .
|
||||
docker tag gcr.io/${PROJECT_ID}/tld_updater:${TAG_NAME} \
|
||||
gcr.io/${PROJECT_ID}/tld_updater:latest
|
||||
docker push gcr.io/${PROJECT_ID}/tld_updater:latest
|
||||
docker push gcr.io/${PROJECT_ID}/tld_updater:${TAG_NAME}
|
||||
dir: 'release/tld-updater/'
|
||||
# Update the tld_updater image digest in relevant GCB files.
|
||||
- name: 'gcr.io/${PROJECT_ID}/builder:latest'
|
||||
entrypoint: /bin/bash
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
digest=$(gcloud container images list-tags \
|
||||
gcr.io/${PROJECT_ID}/tld_updater \
|
||||
--format="get(digest)" --filter="tags = ${TAG_NAME}")
|
||||
sed -i s/'tld_updater:latest'/tld_updater@$digest/g \
|
||||
release/cloudbuild-tld-sync-*.yaml
|
||||
# Build and stage Dataflow Flex templates.
|
||||
- name: 'gcr.io/${PROJECT_ID}/builder:latest'
|
||||
entrypoint: /bin/bash
|
||||
@@ -161,6 +190,7 @@ artifacts:
|
||||
- 'release/cloudbuild-renew-prober-certs-*.yaml'
|
||||
- 'release/cloudbuild-schema-deploy-*.yaml'
|
||||
- 'release/cloudbuild-schema-verify-*.yaml'
|
||||
- 'release/cloudbuild-tld-sync-*.yaml'
|
||||
|
||||
timeout: 7200s
|
||||
options:
|
||||
|
||||
@@ -139,12 +139,18 @@ steps:
|
||||
gcloud container images list-tags \
|
||||
gcr.io/${PROJECT_ID}/prober_cert_updater \
|
||||
--format='get(digest)' --filter='tags = ${TAG_NAME}')
|
||||
tld_updater_digest=$( \
|
||||
gcloud container images list-tags \
|
||||
gcr.io/${PROJECT_ID}/tld_updater \
|
||||
--format='get(digest)' --filter='tags = ${TAG_NAME}')
|
||||
sed -i s/builder:latest/builder@$builder_digest/g \
|
||||
release/cloudbuild-schema-deploy.yaml
|
||||
sed -i s/builder:latest/builder@$builder_digest/g \
|
||||
release/cloudbuild-schema-verify.yaml
|
||||
sed -i s/builder:latest/builder@$builder_digest/g \
|
||||
release/cloudbuild-renew-prober-certs.yaml
|
||||
sed -i s/builder:latest/builder@$builder_digest/g \
|
||||
release/cloudbuild-tld-sync.yaml
|
||||
sed -i s/schema_deployer:latest/schema_deployer@$schema_deployer_digest/g \
|
||||
release/cloudbuild-schema-deploy.yaml
|
||||
sed -i s/schema_verifier:latest/schema_verifier@$schema_verifier_digest/g \
|
||||
@@ -157,6 +163,8 @@ steps:
|
||||
> release/cloudbuild-schema-verify-${environment}.yaml
|
||||
sed s/'$${_ENV}'/${environment}/g release/cloudbuild-renew-prober-certs.yaml \
|
||||
> release/cloudbuild-renew-prober-certs-${environment}.yaml
|
||||
sed s/'$${_ENV}'/${environment}/g release/cloudbuild-tld-sync.yaml \
|
||||
> release/cloudbuild-tld-sync-${environment}.yaml
|
||||
done
|
||||
# Upload the gradle binary to GCS if it does not exist and point URL in gradle wrapper to it.
|
||||
- name: 'gcr.io/cloud-builders/gsutil'
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# This will sync the Tld configurations in the internal repo with the Tld objects in the database.
|
||||
#
|
||||
# To manually trigger a build on GCB, run:
|
||||
# gcloud builds submit --config cloudbuild-tld-sync.yaml --substitutions \
|
||||
# _INTERNAL_REPO_URL=[URL] ..
|
||||
#
|
||||
# To trigger a build automatically, follow the instructions below and add a trigger:
|
||||
# https://cloud.google.com/cloud-build/docs/running-builds/automate-builds
|
||||
#
|
||||
# Note that the release process hardens the tags and variables in this file:
|
||||
# - The 'latest' tag on docker images will be replaced by their image digests.
|
||||
# - The ${_ENV} pattern will be replaced by the actual environment name.
|
||||
# Please refer to ./cloudbuild-release.yaml for more details.
|
||||
#
|
||||
steps:
|
||||
# Check out the internal repo.
|
||||
- name: 'gcr.io/cloud-builders/git'
|
||||
entrypoint: /bin/bash
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
git clone https://gerrit.googlesource.com/gcompute-tools
|
||||
./gcompute-tools/git-cookie-authdaemon
|
||||
git clone ${_INTERNAL_REPO_URL} nomulus-internal
|
||||
# Download and decrypt the nomulus tool credential
|
||||
- name: 'gcr.io/$PROJECT_ID/builder:latest'
|
||||
entrypoint: /bin/bash
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
gcloud secrets versions access latest \
|
||||
--secret nomulus-tool-cloudbuild-credential \
|
||||
> nomulus_tool_credential.json
|
||||
# Configure the TLDs using the stored configuration files in the internal repo
|
||||
- name: 'gcr.io/$PROJECT_ID/tld_updater:latest'
|
||||
args:
|
||||
- ${_ENV}
|
||||
- ./nomulus_tool_credential.json
|
||||
- nomulus-internal/core/src/main/java/google/registry/config/files/tld/
|
||||
|
||||
timeout: 7200s
|
||||
options:
|
||||
machineType: 'E2_HIGHCPU_32'
|
||||
@@ -0,0 +1,22 @@
|
||||
# Copyright 2023 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.
|
||||
|
||||
ARG PROJECT_ID
|
||||
ARG TAG_NAME
|
||||
FROM gcr.io/${PROJECT_ID}/builder:${TAG_NAME}
|
||||
|
||||
COPY nomulus.jar /
|
||||
COPY sync_tlds.sh /usr/local/bin
|
||||
|
||||
ENTRYPOINT [ "bash", "sync_tlds.sh" ]
|
||||
@@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2023 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.
|
||||
|
||||
# Sync the TLD configuration files from the internal repo with the Tld object
|
||||
# in the database. Loops through the Tld configuration files and runs the configure_tld command
|
||||
# with the file.
|
||||
|
||||
# - env: The Nomulus environment, production, sandbox, etc.
|
||||
# - tools_credential: The credential (.json) needed to run the nomulus command.
|
||||
# - config_file_directory: The internal directory storing the TLD config files.
|
||||
|
||||
set -e
|
||||
if [ "$#" -ne 3 ]; then
|
||||
echo "Expecting three parameters in order: env tools_credential config_file_directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
nomulus_env="${1}"
|
||||
tools_credential="${2}"
|
||||
config_file_directory="${3}"
|
||||
|
||||
echo ${config_file_directory}
|
||||
|
||||
for FILE in ${config_file_directory}/${nomulus_env}/*; do
|
||||
echo $FILE
|
||||
java -jar /nomulus.jar -e "${nomulus_env}" \
|
||||
--credential "${tools_credential}" \
|
||||
configure_tld -i $FILE --force
|
||||
done
|
||||
Reference in New Issue
Block a user