mirror of
https://github.com/google/nomulus
synced 2026-05-26 01:30:36 +00:00
Compare commits
10 Commits
nomulus-20
...
nomulus-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5e131ecba | ||
|
|
87e99f59bc | ||
|
|
30accea383 | ||
|
|
72e0101746 | ||
|
|
3090df9a78 | ||
|
|
7332b1fa38 | ||
|
|
9330e3a50d | ||
|
|
1d6b119340 | ||
|
|
8158f761c8 | ||
|
|
08838e091f |
@@ -1,161 +0,0 @@
|
||||
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.batch;
|
||||
|
||||
import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.persistence.PersistenceModule.SchemaManagerConnection;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.Retrier;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.function.Supplier;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Wipes out all Cloud SQL data in a Nomulus GCP environment.
|
||||
*
|
||||
* <p>This class is created for the QA environment, where migration testing with production data
|
||||
* will happen. A regularly scheduled wipeout is a prerequisite to using production data there.
|
||||
*/
|
||||
@Action(
|
||||
service = Action.Service.BACKEND,
|
||||
path = "/_dr/task/wipeOutCloudSql",
|
||||
auth = Auth.AUTH_API_ADMIN)
|
||||
public class WipeOutCloudSqlAction implements Runnable {
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private static final ImmutableSet<RegistryEnvironment> FORBIDDEN_ENVIRONMENTS =
|
||||
ImmutableSet.of(RegistryEnvironment.PRODUCTION, RegistryEnvironment.SANDBOX);
|
||||
|
||||
private final Supplier<Connection> connectionSupplier;
|
||||
private final Response response;
|
||||
private final Retrier retrier;
|
||||
|
||||
@Inject
|
||||
WipeOutCloudSqlAction(
|
||||
@SchemaManagerConnection Supplier<Connection> connectionSupplier,
|
||||
Response response,
|
||||
Retrier retrier) {
|
||||
this.connectionSupplier = connectionSupplier;
|
||||
this.response = response;
|
||||
this.retrier = retrier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
response.setContentType(PLAIN_TEXT_UTF_8);
|
||||
|
||||
if (FORBIDDEN_ENVIRONMENTS.contains(RegistryEnvironment.get())) {
|
||||
response.setStatus(SC_FORBIDDEN);
|
||||
response.setPayload("Wipeout is not allowed in " + RegistryEnvironment.get());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
retrier.callWithRetry(
|
||||
() -> {
|
||||
try (Connection conn = connectionSupplier.get()) {
|
||||
dropAllTables(conn, listTables(conn));
|
||||
dropAllSequences(conn, listSequences(conn));
|
||||
}
|
||||
return null;
|
||||
},
|
||||
e -> !(e instanceof SQLException));
|
||||
response.setStatus(SC_OK);
|
||||
response.setPayload("Wiped out Cloud SQL in " + RegistryEnvironment.get());
|
||||
} catch (RuntimeException e) {
|
||||
logger.atSevere().withCause(e).log("Failed to wipe out Cloud SQL data.");
|
||||
response.setStatus(SC_INTERNAL_SERVER_ERROR);
|
||||
response.setPayload("Failed to wipe out Cloud SQL in " + RegistryEnvironment.get());
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a list of all tables in the public schema of a Postgresql database. */
|
||||
static ImmutableList<String> listTables(Connection connection) throws SQLException {
|
||||
try (ResultSet resultSet =
|
||||
connection.getMetaData().getTables(null, null, null, new String[] {"TABLE"})) {
|
||||
ImmutableList.Builder<String> tables = new ImmutableList.Builder<>();
|
||||
while (resultSet.next()) {
|
||||
String schema = resultSet.getString("TABLE_SCHEM");
|
||||
if (schema == null || !schema.equalsIgnoreCase("public")) {
|
||||
continue;
|
||||
}
|
||||
String tableName = resultSet.getString("TABLE_NAME");
|
||||
tables.add("public.\"" + tableName + "\"");
|
||||
}
|
||||
return tables.build();
|
||||
}
|
||||
}
|
||||
|
||||
static void dropAllTables(Connection conn, ImmutableList<String> tables) throws SQLException {
|
||||
if (tables.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (Statement statement = conn.createStatement()) {
|
||||
for (String table : tables) {
|
||||
statement.addBatch(String.format("DROP TABLE IF EXISTS %s CASCADE;", table));
|
||||
}
|
||||
for (int code : statement.executeBatch()) {
|
||||
if (code == Statement.EXECUTE_FAILED) {
|
||||
throw new RuntimeException("Failed to drop some tables. Please check.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a list of all sequences in a Postgresql database. */
|
||||
static ImmutableList<String> listSequences(Connection conn) throws SQLException {
|
||||
try (Statement statement = conn.createStatement();
|
||||
ResultSet resultSet =
|
||||
statement.executeQuery("SELECT c.relname FROM pg_class c WHERE c.relkind = 'S';")) {
|
||||
ImmutableList.Builder<String> sequences = new ImmutableList.Builder<>();
|
||||
while (resultSet.next()) {
|
||||
sequences.add('\"' + resultSet.getString(1) + '\"');
|
||||
}
|
||||
return sequences.build();
|
||||
}
|
||||
}
|
||||
|
||||
static void dropAllSequences(Connection conn, ImmutableList<String> sequences)
|
||||
throws SQLException {
|
||||
if (sequences.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (Statement statement = conn.createStatement()) {
|
||||
for (String sequence : sequences) {
|
||||
statement.addBatch(String.format("DROP SEQUENCE IF EXISTS %s CASCADE;", sequence));
|
||||
}
|
||||
for (int code : statement.executeBatch()) {
|
||||
if (code == Statement.EXECUTE_FAILED) {
|
||||
throw new RuntimeException("Failed to drop some sequences. Please check.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import com.google.common.base.Ascii;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import dagger.Module;
|
||||
@@ -1390,6 +1391,24 @@ public final class RegistryConfig {
|
||||
return config.bulkPricingPackageMonitoring.bulkPricingPackageDomainLimitUpgradeEmailBody;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("bsaAuthUrl")
|
||||
public static String provideBsaAuthUrl(RegistryConfigSettings config) {
|
||||
return config.bsa.authUrl;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("bsaAuthTokenExpiry")
|
||||
public static Duration provideBsaAuthTokenExpiry(RegistryConfigSettings config) {
|
||||
return Duration.standardSeconds(config.bsa.authTokenExpirySeconds);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("bsaDataUrls")
|
||||
public static ImmutableMap<String, String> provideBsaDataUrls(RegistryConfigSettings config) {
|
||||
return ImmutableMap.copyOf(config.bsa.dataUrls);
|
||||
}
|
||||
|
||||
private static String formatComments(String text) {
|
||||
return Splitter.on('\n').omitEmptyStrings().trimResults().splitToList(text).stream()
|
||||
.map(s -> "# " + s)
|
||||
|
||||
@@ -43,6 +43,7 @@ public class RegistryConfigSettings {
|
||||
public ContactHistory contactHistory;
|
||||
public DnsUpdate dnsUpdate;
|
||||
public BulkPricingPackageMonitoring bulkPricingPackageMonitoring;
|
||||
public Bsa bsa;
|
||||
|
||||
/** Configuration options that apply to the entire GCP project. */
|
||||
public static class GcpProject {
|
||||
@@ -261,4 +262,11 @@ public class RegistryConfigSettings {
|
||||
public String bulkPricingPackageDomainLimitUpgradeEmailSubject;
|
||||
public String bulkPricingPackageDomainLimitUpgradeEmailBody;
|
||||
}
|
||||
|
||||
/** Configurations for integration with Brand Safety Alliance (BSA) API. */
|
||||
public static class Bsa {
|
||||
public String authUrl;
|
||||
public int authTokenExpirySeconds;
|
||||
public Map<String, String> dataUrls;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -598,3 +598,14 @@ bulkPricingPackageMonitoring:
|
||||
Registrar: %3$s
|
||||
Active Domain Limit: %4$s
|
||||
Current Active Domains: %5$s
|
||||
|
||||
# Configurations for integration with Brand Safety Alliance (BSA) API
|
||||
bsa:
|
||||
# Http endpoint for acquiring Auth tokens.
|
||||
authUrl: "https://"
|
||||
# Auth token expiry.
|
||||
authTokenExpirySeconds: 1800
|
||||
# Http endpoints for downloading data
|
||||
dataUrls:
|
||||
"BLOCK": "https://"
|
||||
"BLOCK_PLUS": "https://"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>backend</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>default</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>pubapi</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>tools</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>backend</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>default</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>pubapi</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>tools</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>backend</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>default</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>pubapi</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>tools</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>backend</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>default</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<manual-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>pubapi</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<manual-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>tools</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>backend</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>default</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>F4_1G</instance-class>
|
||||
<automatic-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>pubapi</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>tools</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>backend</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
@@ -22,6 +22,12 @@
|
||||
<include path="/*.html" expiration="1d"/>
|
||||
</static-files>
|
||||
|
||||
<!-- Enable external traffic to go through VPC, required for static ip -->
|
||||
<vpc-access-connector>
|
||||
<name>projects/domain-registry-sandbox/locations/us-central1/connectors/appengine-connector</name>
|
||||
<egress-setting>all-traffic</egress-setting>
|
||||
</vpc-access-connector>
|
||||
|
||||
<!-- Prevent uncaught servlet errors from leaking a stack trace. -->
|
||||
<static-error-handlers>
|
||||
<handler file="error.html"/>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>default</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<manual-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>pubapi</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<manual-scaling>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
|
||||
<runtime>java8</runtime>
|
||||
<runtime>java17</runtime>
|
||||
<service>tools</service>
|
||||
<threadsafe>true</threadsafe>
|
||||
<app-engine-apis>true</app-engine-apis>
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4</instance-class>
|
||||
<basic-scaling>
|
||||
|
||||
@@ -38,7 +38,7 @@ public final class InMemoryKeyring implements Keyring {
|
||||
private final String marksdbDnlLoginAndPassword;
|
||||
private final String marksdbLordnPassword;
|
||||
private final String marksdbSmdrlLoginAndPassword;
|
||||
private final String jsonCredential;
|
||||
private final String bsaApiKey;
|
||||
|
||||
public InMemoryKeyring(
|
||||
PGPKeyPair rdeStagingKey,
|
||||
@@ -53,9 +53,9 @@ public final class InMemoryKeyring implements Keyring {
|
||||
String marksdbDnlLoginAndPassword,
|
||||
String marksdbLordnPassword,
|
||||
String marksdbSmdrlLoginAndPassword,
|
||||
String jsonCredential,
|
||||
String cloudSqlPassword,
|
||||
String toolsCloudSqlPassword) {
|
||||
String toolsCloudSqlPassword,
|
||||
String bsaApiKey) {
|
||||
checkArgument(PgpHelper.isSigningKey(rdeSigningKey.getPublicKey()),
|
||||
"RDE signing key must support signing: %s", rdeSigningKey.getKeyID());
|
||||
checkArgument(rdeStagingKey.getPublicKey().isEncryptionKey(),
|
||||
@@ -80,7 +80,7 @@ public final class InMemoryKeyring implements Keyring {
|
||||
this.marksdbLordnPassword = checkNotNull(marksdbLordnPassword, "marksdbLordnPassword");
|
||||
this.marksdbSmdrlLoginAndPassword =
|
||||
checkNotNull(marksdbSmdrlLoginAndPassword, "marksdbSmdrlLoginAndPassword");
|
||||
this.jsonCredential = checkNotNull(jsonCredential, "jsonCredential");
|
||||
this.bsaApiKey = checkNotNull(bsaApiKey, "bsaApiKey");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -149,8 +149,8 @@ public final class InMemoryKeyring implements Keyring {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJsonCredential() {
|
||||
return jsonCredential;
|
||||
public String getBsaApiKey() {
|
||||
return bsaApiKey;
|
||||
}
|
||||
|
||||
/** Does nothing. */
|
||||
|
||||
@@ -145,11 +145,8 @@ public interface Keyring extends AutoCloseable {
|
||||
*/
|
||||
String getMarksdbSmdrlLoginAndPassword();
|
||||
|
||||
/**
|
||||
* Returns the credentials for a service account on the Google AppEngine project downloaded from
|
||||
* the Cloud Console dashboard in JSON format.
|
||||
*/
|
||||
String getJsonCredential();
|
||||
/** Returns the API_KEY for authentication with the BSA portal. */
|
||||
String getBsaApiKey();
|
||||
|
||||
// Don't throw so try-with-resources works better.
|
||||
@Override
|
||||
|
||||
@@ -58,8 +58,8 @@ public class SecretManagerKeyring implements Keyring {
|
||||
/** Key labels for string secrets. */
|
||||
enum StringKeyLabel {
|
||||
SAFE_BROWSING_API_KEY,
|
||||
BSA_API_KEY_STRING,
|
||||
ICANN_REPORTING_PASSWORD_STRING,
|
||||
JSON_CREDENTIAL_STRING,
|
||||
MARKSDB_DNL_LOGIN_STRING,
|
||||
MARKSDB_LORDN_PASSWORD_STRING,
|
||||
MARKSDB_SMDRL_LOGIN_STRING,
|
||||
@@ -143,10 +143,9 @@ public class SecretManagerKeyring implements Keyring {
|
||||
return getString(StringKeyLabel.MARKSDB_SMDRL_LOGIN_STRING);
|
||||
}
|
||||
|
||||
// TODO(b/237305940): remove this method and all supports, including entry in secretmanager
|
||||
@Override
|
||||
public String getJsonCredential() {
|
||||
return getString(StringKeyLabel.JSON_CREDENTIAL_STRING);
|
||||
public String getBsaApiKey() {
|
||||
return getString(StringKeyLabel.BSA_API_KEY_STRING);
|
||||
}
|
||||
|
||||
/** No persistent resources are maintained for this Keyring implementation. */
|
||||
|
||||
@@ -24,8 +24,8 @@ import static google.registry.keyring.secretmanager.SecretManagerKeyring.PublicK
|
||||
import static google.registry.keyring.secretmanager.SecretManagerKeyring.PublicKeyLabel.RDE_RECEIVER_PUBLIC;
|
||||
import static google.registry.keyring.secretmanager.SecretManagerKeyring.PublicKeyLabel.RDE_SIGNING_PUBLIC;
|
||||
import static google.registry.keyring.secretmanager.SecretManagerKeyring.PublicKeyLabel.RDE_STAGING_PUBLIC;
|
||||
import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel.BSA_API_KEY_STRING;
|
||||
import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel.ICANN_REPORTING_PASSWORD_STRING;
|
||||
import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel.JSON_CREDENTIAL_STRING;
|
||||
import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel.MARKSDB_DNL_LOGIN_STRING;
|
||||
import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel.MARKSDB_LORDN_PASSWORD_STRING;
|
||||
import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel.MARKSDB_SMDRL_LOGIN_STRING;
|
||||
@@ -120,8 +120,8 @@ public final class SecretManagerKeyringUpdater {
|
||||
return setString(login, MARKSDB_SMDRL_LOGIN_STRING);
|
||||
}
|
||||
|
||||
public SecretManagerKeyringUpdater setJsonCredential(String credential) {
|
||||
return setString(credential, JSON_CREDENTIAL_STRING);
|
||||
public SecretManagerKeyringUpdater setBsaApiKey(String credential) {
|
||||
return setString(credential, BSA_API_KEY_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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.
|
||||
|
||||
package google.registry.model.adapters;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
* Adapter factory that allows for (de)serialization of Class objects in GSON.
|
||||
*
|
||||
* <p>GSON's built-in adapter for Class objects throws an exception, but there are situations where
|
||||
* we want to (de)serialize these, such as in VKeys. This instructs GSON to look for our custom
|
||||
* {@link ClassTypeAdapter} rather than the default.
|
||||
*/
|
||||
public class ClassProcessingTypeAdapterFactory implements TypeAdapterFactory {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
|
||||
if (Class.class.isAssignableFrom(typeToken.getRawType())) {
|
||||
// in this case, T is a class object
|
||||
return (TypeAdapter<T>) new ClassTypeAdapter();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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.
|
||||
|
||||
package google.registry.model.adapters;
|
||||
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* TypeAdapter for {@link Class} objects.
|
||||
*
|
||||
* <p>GSON's default adapter doesn't allow this, but we want to allow for (de)serialization of Class
|
||||
* objects for containers like VKeys using the full name of the class.
|
||||
*/
|
||||
public class ClassTypeAdapter extends TypeAdapter<Class<?>> {
|
||||
|
||||
@Override
|
||||
public void write(JsonWriter out, Class value) throws IOException {
|
||||
out.value(value.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> read(JsonReader reader) throws IOException {
|
||||
String stringValue = reader.nextString();
|
||||
if (stringValue.equals("null")) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Class.forName(stringValue);
|
||||
} catch (ClassNotFoundException e) {
|
||||
// this should not happen...
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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.
|
||||
|
||||
package google.registry.model.adapters;
|
||||
|
||||
import google.registry.util.StringBaseTypeAdapter;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* TypeAdapter for {@link Serializable} objects.
|
||||
*
|
||||
* <p>VKey keys (primary keys in SQL) are usually represented by either a long or a String. There
|
||||
* are a couple situations (CursorId, HistoryEntryId) where the Serializable in question is a
|
||||
* complex object, but we do not need to worry about (de)serializing those objects to/from JSON.
|
||||
*/
|
||||
public class SerializableJsonTypeAdapter extends StringBaseTypeAdapter<Serializable> {
|
||||
|
||||
@Override
|
||||
protected Serializable fromString(String stringValue) throws IOException {
|
||||
try {
|
||||
return Long.parseLong(stringValue);
|
||||
} catch (NumberFormatException e) {
|
||||
return stringValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,6 @@ import google.registry.batch.RelockDomainAction;
|
||||
import google.registry.batch.ResaveAllEppResourcesPipelineAction;
|
||||
import google.registry.batch.ResaveEntityAction;
|
||||
import google.registry.batch.SendExpiringCertificateNotificationEmailAction;
|
||||
import google.registry.batch.WipeOutCloudSqlAction;
|
||||
import google.registry.batch.WipeOutContactHistoryPiiAction;
|
||||
import google.registry.cron.CronModule;
|
||||
import google.registry.cron.TldFanoutAction;
|
||||
@@ -178,8 +177,6 @@ interface BackendRequestComponent {
|
||||
|
||||
UpdateRegistrarRdapBaseUrlsAction updateRegistrarRdapBaseUrlsAction();
|
||||
|
||||
WipeOutCloudSqlAction wipeOutCloudSqlAction();
|
||||
|
||||
WipeOutContactHistoryPiiAction wipeOutContactHistoryPiiAction();
|
||||
|
||||
@Subcomponent.Builder
|
||||
|
||||
@@ -26,6 +26,7 @@ import google.registry.request.RequestComponentBuilder;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.request.RequestScope;
|
||||
import google.registry.ui.server.console.ConsoleDomainGetAction;
|
||||
import google.registry.ui.server.console.ConsoleDomainListAction;
|
||||
import google.registry.ui.server.console.ConsoleUserDataAction;
|
||||
import google.registry.ui.server.console.RegistrarsAction;
|
||||
import google.registry.ui.server.console.settings.ContactAction;
|
||||
@@ -55,6 +56,8 @@ import google.registry.ui.server.registrar.RegistryLockVerifyAction;
|
||||
interface FrontendRequestComponent {
|
||||
ConsoleDomainGetAction consoleDomainGetAction();
|
||||
|
||||
ConsoleDomainListAction consoleDomainListAction();
|
||||
|
||||
ConsoleOteSetupAction consoleOteSetupAction();
|
||||
ConsoleRegistrarCreatorAction consoleRegistrarCreatorAction();
|
||||
ConsoleUiAction consoleUiAction();
|
||||
|
||||
@@ -57,7 +57,7 @@ public class VKey<T> extends ImmutableObject implements Serializable {
|
||||
// The primary key for the referenced entity.
|
||||
@Expose Serializable key;
|
||||
|
||||
Class<? extends T> kind;
|
||||
@Expose Class<? extends T> kind;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
VKey() {}
|
||||
|
||||
@@ -30,16 +30,14 @@ public enum Auth {
|
||||
* <p>If a user is logged in, will authenticate (and return) them. Otherwise, access is still
|
||||
* granted, but NOT_AUTHENTICATED is returned.
|
||||
*
|
||||
* <p>This is used for public HTML endpoints like RDAP, the check API, and web WHOIS.
|
||||
*
|
||||
* <p>User-facing legacy console endpoints (those that extend {@link HtmlAction}) also use it.
|
||||
* They need to allow requests from signed-out users so that they can redirect users to the login
|
||||
* page. After a user is logged in, they check if the user actually has access to the specific
|
||||
* console using {@link AuthenticatedRegistrarAccessor}.
|
||||
* <p>User-facing legacy console endpoints (those that extend {@link HtmlAction}) use it. They
|
||||
* need to allow requests from signed-out users so that they can redirect users to the login page.
|
||||
* After a user is logged in, they check if the user actually has access to the specific console
|
||||
* using {@link AuthenticatedRegistrarAccessor}.
|
||||
*
|
||||
* @see HtmlAction
|
||||
*/
|
||||
AUTH_PUBLIC(
|
||||
AUTH_PUBLIC_LEGACY(
|
||||
ImmutableList.of(AuthMethod.API, AuthMethod.LEGACY), AuthLevel.NONE, UserPolicy.PUBLIC),
|
||||
|
||||
/**
|
||||
@@ -52,6 +50,13 @@ public enum Auth {
|
||||
AUTH_PUBLIC_LOGGED_IN(
|
||||
ImmutableList.of(AuthMethod.API, AuthMethod.LEGACY), AuthLevel.USER, UserPolicy.PUBLIC),
|
||||
|
||||
/**
|
||||
* Allows anyone to access.
|
||||
*
|
||||
* <p>This is used for public HTML endpoints like RDAP, the check API, and web WHOIS.
|
||||
*/
|
||||
AUTH_PUBLIC(ImmutableList.of(AuthMethod.API), AuthLevel.NONE, UserPolicy.PUBLIC),
|
||||
|
||||
/**
|
||||
* Allows only the app itself (via service accounts) or admins to access.
|
||||
*
|
||||
|
||||
@@ -64,15 +64,15 @@ final class GetKeyringSecretCommand implements Command {
|
||||
case BRDA_SIGNING_PUBLIC_KEY:
|
||||
out.write(KeySerializer.serializePublicKey(keyring.getBrdaSigningKey().getPublicKey()));
|
||||
break;
|
||||
case BSA_API_KEY:
|
||||
out.write(KeySerializer.serializeString(keyring.getBsaApiKey()));
|
||||
break;
|
||||
case ICANN_REPORTING_PASSWORD:
|
||||
out.write(KeySerializer.serializeString(keyring.getIcannReportingPassword()));
|
||||
break;
|
||||
case SAFE_BROWSING_API_KEY:
|
||||
out.write(KeySerializer.serializeString(keyring.getSafeBrowsingAPIKey()));
|
||||
break;
|
||||
case JSON_CREDENTIAL:
|
||||
out.write(KeySerializer.serializeString(keyring.getJsonCredential()));
|
||||
break;
|
||||
case MARKSDB_DNL_LOGIN_AND_PASSWORD:
|
||||
out.write(KeySerializer.serializeString(keyring.getMarksdbDnlLoginAndPassword()));
|
||||
break;
|
||||
|
||||
@@ -21,11 +21,14 @@ import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import google.registry.model.adapters.ClassProcessingTypeAdapterFactory;
|
||||
import google.registry.model.adapters.CurrencyJsonAdapter;
|
||||
import google.registry.model.adapters.SerializableJsonTypeAdapter;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import google.registry.util.CidrAddressBlock.CidrAddressBlockAdapter;
|
||||
import google.registry.util.DateTimeTypeAdapter;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -69,9 +72,11 @@ public class GsonUtils {
|
||||
|
||||
public static Gson provideGson() {
|
||||
return new GsonBuilder()
|
||||
.registerTypeAdapter(DateTime.class, new DateTimeTypeAdapter())
|
||||
.registerTypeAdapter(CidrAddressBlock.class, new CidrAddressBlockAdapter())
|
||||
.registerTypeAdapter(CurrencyUnit.class, new CurrencyJsonAdapter())
|
||||
.registerTypeAdapter(DateTime.class, new DateTimeTypeAdapter())
|
||||
.registerTypeAdapter(Serializable.class, new SerializableJsonTypeAdapter())
|
||||
.registerTypeAdapterFactory(new ClassProcessingTypeAdapterFactory())
|
||||
.registerTypeAdapterFactory(new GsonPostProcessableTypeAdapterFactory())
|
||||
.excludeFieldsWithoutExposeAnnotation()
|
||||
.create();
|
||||
|
||||
@@ -64,12 +64,12 @@ final class UpdateKeyringSecretCommand implements Command {
|
||||
throw new IllegalArgumentException(
|
||||
"Can't update BRDA_SIGNING_PUBLIC_KEY directly."
|
||||
+ " Must update public and private keys together using BRDA_SIGNING_KEY_PAIR.");
|
||||
case BSA_API_KEY:
|
||||
secretManagerKeyringUpdater.setBsaApiKey(deserializeString(input));
|
||||
break;
|
||||
case ICANN_REPORTING_PASSWORD:
|
||||
secretManagerKeyringUpdater.setIcannReportingPassword(deserializeString(input));
|
||||
break;
|
||||
case JSON_CREDENTIAL:
|
||||
secretManagerKeyringUpdater.setJsonCredential(deserializeString(input));
|
||||
break;
|
||||
case MARKSDB_DNL_LOGIN_AND_PASSWORD:
|
||||
secretManagerKeyringUpdater.setMarksdbDnlLoginAndPassword(deserializeString(input));
|
||||
break;
|
||||
|
||||
@@ -24,8 +24,8 @@ public enum KeyringKeyName {
|
||||
BRDA_RECEIVER_PUBLIC_KEY,
|
||||
BRDA_SIGNING_KEY_PAIR,
|
||||
BRDA_SIGNING_PUBLIC_KEY,
|
||||
BSA_API_KEY,
|
||||
ICANN_REPORTING_PASSWORD,
|
||||
JSON_CREDENTIAL,
|
||||
MARKSDB_DNL_LOGIN_AND_PASSWORD,
|
||||
MARKSDB_LORDN_PASSWORD,
|
||||
MARKSDB_SMDRL_LOGIN_AND_PASSWORD,
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
// 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.
|
||||
|
||||
package google.registry.ui.server.console;
|
||||
|
||||
import static google.registry.model.console.ConsolePermission.DOWNLOAD_DOMAINS;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.api.client.http.HttpStatusCodes;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.model.CreateAutoTimestamp;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.ui.server.registrar.JsonGetAction;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Returns a (paginated) list of domains for a particular registrar. */
|
||||
@Action(
|
||||
service = Action.Service.DEFAULT,
|
||||
path = ConsoleDomainListAction.PATH,
|
||||
method = Action.Method.GET,
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public class ConsoleDomainListAction implements JsonGetAction {
|
||||
|
||||
public static final String PATH = "/console-api/domain-list";
|
||||
|
||||
private static final int DEFAULT_RESULTS_PER_PAGE = 50;
|
||||
private static final String DOMAIN_QUERY_TEMPLATE =
|
||||
"FROM Domain WHERE currentSponsorRegistrarId = :registrarId AND deletionTime >"
|
||||
+ " :deletedAfterTime AND creationTime <= :createdBeforeTime";
|
||||
|
||||
private final AuthResult authResult;
|
||||
private final Response response;
|
||||
private final Gson gson;
|
||||
private final String registrarId;
|
||||
private final Optional<DateTime> checkpointTime;
|
||||
private final int pageNumber;
|
||||
private final int resultsPerPage;
|
||||
private final Optional<Long> totalResults;
|
||||
|
||||
@Inject
|
||||
public ConsoleDomainListAction(
|
||||
AuthResult authResult,
|
||||
Response response,
|
||||
Gson gson,
|
||||
@Parameter("registrarId") String registrarId,
|
||||
@Parameter("checkpointTime") Optional<DateTime> checkpointTime,
|
||||
@Parameter("pageNumber") Optional<Integer> pageNumber,
|
||||
@Parameter("resultsPerPage") Optional<Integer> resultsPerPage,
|
||||
@Parameter("totalResults") Optional<Long> totalResults) {
|
||||
this.authResult = authResult;
|
||||
this.response = response;
|
||||
this.gson = gson;
|
||||
this.registrarId = registrarId;
|
||||
this.checkpointTime = checkpointTime;
|
||||
this.pageNumber = pageNumber.orElse(0);
|
||||
this.resultsPerPage = resultsPerPage.orElse(DEFAULT_RESULTS_PER_PAGE);
|
||||
this.totalResults = totalResults;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
User user = authResult.userAuthInfo().get().consoleUser().get();
|
||||
if (!user.getUserRoles().hasPermission(registrarId, DOWNLOAD_DOMAINS)) {
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
if (resultsPerPage < 1 || resultsPerPage > 500) {
|
||||
writeBadRequest("Results per page must be between 1 and 500 inclusive");
|
||||
return;
|
||||
}
|
||||
if (pageNumber < 0) {
|
||||
writeBadRequest("Page number must be non-negative");
|
||||
return;
|
||||
}
|
||||
|
||||
tm().transact(this::runInTransaction);
|
||||
}
|
||||
|
||||
private void runInTransaction() {
|
||||
int numResultsToSkip = resultsPerPage * pageNumber;
|
||||
|
||||
// We have to use a constant checkpoint time in order to have stable pagination, since domains
|
||||
// can be constantly created or deleted
|
||||
DateTime checkpoint = checkpointTime.orElseGet(tm()::getTransactionTime);
|
||||
CreateAutoTimestamp checkpointTimestamp = CreateAutoTimestamp.create(checkpoint);
|
||||
// Don't compute the number of total results over and over if we don't need to
|
||||
long actualTotalResults =
|
||||
totalResults.orElseGet(
|
||||
() ->
|
||||
tm().query("SELECT COUNT(*) " + DOMAIN_QUERY_TEMPLATE, Long.class)
|
||||
.setParameter("registrarId", registrarId)
|
||||
.setParameter("createdBeforeTime", checkpointTimestamp)
|
||||
.setParameter("deletedAfterTime", checkpoint)
|
||||
.getSingleResult());
|
||||
List<Domain> domains =
|
||||
tm().query(DOMAIN_QUERY_TEMPLATE + " ORDER BY creationTime DESC", Domain.class)
|
||||
.setParameter("registrarId", registrarId)
|
||||
.setParameter("createdBeforeTime", checkpointTimestamp)
|
||||
.setParameter("deletedAfterTime", checkpoint)
|
||||
.setFirstResult(numResultsToSkip)
|
||||
.setMaxResults(resultsPerPage)
|
||||
.getResultList();
|
||||
response.setPayload(gson.toJson(new DomainListResult(domains, checkpoint, actualTotalResults)));
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_OK);
|
||||
}
|
||||
|
||||
private void writeBadRequest(String message) {
|
||||
response.setPayload(message);
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
||||
}
|
||||
|
||||
/** Container result class that allows for pagination. */
|
||||
@VisibleForTesting
|
||||
static final class DomainListResult {
|
||||
@Expose List<Domain> domains;
|
||||
@Expose DateTime checkpointTime;
|
||||
@Expose long totalResults;
|
||||
|
||||
private DomainListResult(List<Domain> domains, DateTime checkpointTime, long totalResults) {
|
||||
this.domains = domains;
|
||||
this.checkpointTime = checkpointTime;
|
||||
this.totalResults = totalResults;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ import javax.inject.Named;
|
||||
service = Action.Service.DEFAULT,
|
||||
path = ConsoleOteSetupAction.PATH,
|
||||
method = {Method.POST, Method.GET},
|
||||
auth = Auth.AUTH_PUBLIC)
|
||||
auth = Auth.AUTH_PUBLIC_LEGACY)
|
||||
public final class ConsoleOteSetupAction extends HtmlAction {
|
||||
|
||||
public static final String PATH = "/registrar-ote-setup";
|
||||
|
||||
@@ -63,7 +63,7 @@ import org.joda.money.CurrencyUnit;
|
||||
service = Service.DEFAULT,
|
||||
path = ConsoleRegistrarCreatorAction.PATH,
|
||||
method = {Method.POST, Method.GET},
|
||||
auth = Auth.AUTH_PUBLIC)
|
||||
auth = Auth.AUTH_PUBLIC_LEGACY)
|
||||
public final class ConsoleRegistrarCreatorAction extends HtmlAction {
|
||||
|
||||
private static final int PASSWORD_LENGTH = 16;
|
||||
|
||||
@@ -41,7 +41,10 @@ import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Action that serves Registrar Console single HTML page (SPA). */
|
||||
@Action(service = Action.Service.DEFAULT, path = ConsoleUiAction.PATH, auth = Auth.AUTH_PUBLIC)
|
||||
@Action(
|
||||
service = Action.Service.DEFAULT,
|
||||
path = ConsoleUiAction.PATH,
|
||||
auth = Auth.AUTH_PUBLIC_LEGACY)
|
||||
public final class ConsoleUiAction extends HtmlAction {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
@@ -30,6 +30,7 @@ import google.registry.request.OptionalJsonPayload;
|
||||
import google.registry.request.Parameter;
|
||||
import java.util.Optional;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Dagger module for the Registrar Console parameters. */
|
||||
@Module
|
||||
@@ -188,4 +189,28 @@ public final class RegistrarConsoleModule {
|
||||
Gson gson, @OptionalJsonPayload Optional<JsonElement> payload) {
|
||||
return payload.map(s -> gson.fromJson(s, Registrar.class));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("checkpointTime")
|
||||
public static Optional<DateTime> provideCheckpointTime(HttpServletRequest req) {
|
||||
return extractOptionalParameter(req, "checkpointTime").map(DateTime::parse);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("pageNumber")
|
||||
public static Optional<Integer> providePageNumber(HttpServletRequest req) {
|
||||
return extractOptionalIntParameter(req, "pageNumber");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("resultsPerPage")
|
||||
public static Optional<Integer> provideResultsPerPage(HttpServletRequest req) {
|
||||
return extractOptionalIntParameter(req, "resultsPerPage");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("totalResults")
|
||||
public static Optional<Long> provideTotalResults(HttpServletRequest req) {
|
||||
return extractOptionalParameter(req, "totalResults").map(Long::valueOf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import javax.inject.Inject;
|
||||
@Action(
|
||||
service = Action.Service.DEFAULT,
|
||||
path = RegistryLockVerifyAction.PATH,
|
||||
auth = Auth.AUTH_PUBLIC)
|
||||
auth = Auth.AUTH_PUBLIC_LEGACY)
|
||||
public final class RegistryLockVerifyAction extends HtmlAction {
|
||||
|
||||
public static final String PATH = "/registry-lock-verify";
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<delete>
|
||||
<domain:delete
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>%DOMAIN%</domain:name>
|
||||
</domain:delete>
|
||||
</delete>
|
||||
<extension>
|
||||
<metadata:metadata xmlns:metadata="urn:google:params:xml:ns:metadata-1.0">
|
||||
<metadata:reason>Non-renewing domain has reached expiration date.</metadata:reason>
|
||||
<metadata:requestedByRegistrar>false</metadata:requestedByRegistrar>
|
||||
</metadata:metadata>
|
||||
</extension>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
@@ -1,117 +0,0 @@
|
||||
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.batch;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.testing.FakeSleeper;
|
||||
import google.registry.util.Retrier;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
/** Unit tests for {@link WipeOutCloudSqlAction}. */
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class WipeOutCloudSqlActionTest {
|
||||
|
||||
@Mock private Statement stmt;
|
||||
@Mock private Connection conn;
|
||||
@Mock private DatabaseMetaData metaData;
|
||||
@Mock private ResultSet resultSet;
|
||||
|
||||
private FakeResponse response = new FakeResponse();
|
||||
private Retrier retrier = new Retrier(new FakeSleeper(new FakeClock()), 2);
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
lenient().when(conn.createStatement()).thenReturn(stmt);
|
||||
lenient().when(conn.getMetaData()).thenReturn(metaData);
|
||||
lenient()
|
||||
.when(
|
||||
metaData.getTables(
|
||||
nullable(String.class),
|
||||
nullable(String.class),
|
||||
nullable(String.class),
|
||||
nullable(String[].class)))
|
||||
.thenReturn(resultSet);
|
||||
lenient().when(stmt.executeQuery(anyString())).thenReturn(resultSet);
|
||||
lenient().when(resultSet.next()).thenReturn(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
void run_projectAllowed() throws Exception {
|
||||
WipeOutCloudSqlAction action = new WipeOutCloudSqlAction(() -> conn, response, retrier);
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
verify(stmt, times(1)).executeQuery(anyString());
|
||||
verify(stmt, times(1)).close();
|
||||
verifyNoMoreInteractions(stmt);
|
||||
}
|
||||
|
||||
@Test
|
||||
void run_projectNotAllowed() {
|
||||
try {
|
||||
RegistryEnvironment.SANDBOX.setup();
|
||||
WipeOutCloudSqlAction action = new WipeOutCloudSqlAction(() -> conn, response, retrier);
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_FORBIDDEN);
|
||||
verifyNoInteractions(stmt);
|
||||
} finally {
|
||||
RegistryEnvironment.UNITTEST.setup();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void run_nonRetrieableFailure() throws Exception {
|
||||
doThrow(new SQLException()).when(conn).getMetaData();
|
||||
WipeOutCloudSqlAction action = new WipeOutCloudSqlAction(() -> conn, response, retrier);
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_INTERNAL_SERVER_ERROR);
|
||||
verifyNoInteractions(stmt);
|
||||
}
|
||||
|
||||
@Test
|
||||
void run_retrieableFailure() throws Exception {
|
||||
when(conn.getMetaData()).thenThrow(new RuntimeException()).thenReturn(metaData);
|
||||
WipeOutCloudSqlAction action = new WipeOutCloudSqlAction(() -> conn, response, retrier);
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
verify(stmt, times(1)).executeQuery(anyString());
|
||||
verify(stmt, times(1)).close();
|
||||
verifyNoMoreInteractions(stmt);
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.batch;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.persistence.NomulusPostgreSql;
|
||||
import java.sql.Connection;
|
||||
import java.sql.Statement;
|
||||
import java.util.Properties;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.containers.PostgreSQLContainer;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
/** Tests the database wipeout mechanism used by {@link WipeOutCloudSqlAction}. */
|
||||
@Testcontainers
|
||||
public class WipeOutCloudSqlIntegrationTest {
|
||||
|
||||
@Container
|
||||
PostgreSQLContainer container = new PostgreSQLContainer(NomulusPostgreSql.getDockerTag());
|
||||
|
||||
private Connection getJdbcConnection() throws Exception {
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("user", container.getUsername());
|
||||
properties.setProperty("password", container.getPassword());
|
||||
return container.getJdbcDriverInstance().connect(container.getJdbcUrl(), properties);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
try (Connection conn = getJdbcConnection();
|
||||
Statement statement = conn.createStatement()) {
|
||||
statement.addBatch("CREATE TABLE public.\"Domain\" (value int);");
|
||||
statement.addBatch("CREATE SEQUENCE public.\"Domain_seq\"");
|
||||
statement.executeBatch();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void listTables() throws Exception {
|
||||
try (Connection conn = getJdbcConnection()) {
|
||||
ImmutableList<String> tables = WipeOutCloudSqlAction.listTables(conn);
|
||||
assertThat(tables).containsExactly("public.\"Domain\"");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void dropAllTables() throws Exception {
|
||||
try (Connection conn = getJdbcConnection()) {
|
||||
ImmutableList<String> tables = WipeOutCloudSqlAction.listTables(conn);
|
||||
assertThat(tables).isNotEmpty();
|
||||
WipeOutCloudSqlAction.dropAllTables(conn, tables);
|
||||
assertThat(WipeOutCloudSqlAction.listTables(conn)).isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void listAllSequences() throws Exception {
|
||||
try (Connection conn = getJdbcConnection()) {
|
||||
ImmutableList<String> sequences = WipeOutCloudSqlAction.listSequences(conn);
|
||||
assertThat(sequences).containsExactly("\"Domain_seq\"");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void dropAllSequences() throws Exception {
|
||||
try (Connection conn = getJdbcConnection()) {
|
||||
ImmutableList<String> sequences = WipeOutCloudSqlAction.listSequences(conn);
|
||||
assertThat(sequences).isNotEmpty();
|
||||
WipeOutCloudSqlAction.dropAllSequences(conn, sequences);
|
||||
assertThat(WipeOutCloudSqlAction.listSequences(conn)).isEmpty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,16 +51,16 @@ public class SecretManagerKeyringUpdaterTest {
|
||||
updater
|
||||
.setMarksdbDnlLoginAndPassword(secretPrefix + "marksdb")
|
||||
.setIcannReportingPassword(secretPrefix + "icann")
|
||||
.setJsonCredential(secretPrefix + "json")
|
||||
.setBsaApiKey(secretPrefix + "bsa")
|
||||
.update();
|
||||
|
||||
assertThat(keyring.getMarksdbDnlLoginAndPassword()).isEqualTo(secretPrefix + "marksdb");
|
||||
assertThat(keyring.getIcannReportingPassword()).isEqualTo(secretPrefix + "icann");
|
||||
assertThat(keyring.getJsonCredential()).isEqualTo(secretPrefix + "json");
|
||||
assertThat(keyring.getBsaApiKey()).isEqualTo(secretPrefix + "bsa");
|
||||
|
||||
verifyPersistedSecret("marksdb-dnl-login-string", secretPrefix + "marksdb");
|
||||
verifyPersistedSecret("icann-reporting-password-string", secretPrefix + "icann");
|
||||
verifyPersistedSecret("json-credential-string", secretPrefix + "json");
|
||||
verifyPersistedSecret("bsa-api-key-string", secretPrefix + "bsa");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -94,12 +94,12 @@ public class SecretManagerKeyringUpdaterTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void jsonCredential() {
|
||||
String secret = "jsonCredential";
|
||||
updater.setJsonCredential(secret).update();
|
||||
void bsaApiKey() {
|
||||
String secret = "bsaApiKey";
|
||||
updater.setBsaApiKey(secret).update();
|
||||
|
||||
assertThat(keyring.getJsonCredential()).isEqualTo(secret);
|
||||
verifyPersistedSecret("json-credential-string", secret);
|
||||
assertThat(keyring.getBsaApiKey()).isEqualTo(secret);
|
||||
verifyPersistedSecret("bsa-api-key-string", secret);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// 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.
|
||||
|
||||
package google.registry.model.adapters;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.tools.GsonUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Tests for {@link ClassTypeAdapter} and {@link SerializableJsonTypeAdapter}. */
|
||||
public class VKeyAdapterTest {
|
||||
|
||||
private static final Gson GSON = GsonUtils.provideGson();
|
||||
|
||||
@Test
|
||||
void testVKeyConversion_string() {
|
||||
VKey<Domain> vkey = VKey.create(Domain.class, "someRepoId");
|
||||
String vkeyJson = GSON.toJson(vkey);
|
||||
assertThat(vkeyJson)
|
||||
.isEqualTo(
|
||||
"{\"key\":\"someRepoId\",\"kind\":" + "\"google.registry.model.domain.Domain\"}");
|
||||
assertThat(GSON.fromJson(vkeyJson, VKey.class)).isEqualTo(vkey);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testVKeyConversion_number() {
|
||||
VKey<BillingEvent> vkey = VKey.create(BillingEvent.class, 203L);
|
||||
String vkeyJson = GSON.toJson(vkey);
|
||||
assertThat(vkeyJson)
|
||||
.isEqualTo("{\"key\":203,\"kind\":" + "\"google.registry.model.billing.BillingEvent\"}");
|
||||
assertThat(GSON.fromJson(vkeyJson, VKey.class)).isEqualTo(vkey);
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ public final class FakeKeyringModule {
|
||||
private static final String MARKSDB_DNL_LOGIN_AND_PASSWORD = "dnl:yolo";
|
||||
private static final String MARKSDB_LORDN_PASSWORD = "yolo";
|
||||
private static final String MARKSDB_SMDRL_LOGIN_AND_PASSWORD = "smdrl:yolo";
|
||||
private static final String JSON_CREDENTIAL = "json123";
|
||||
private static final String BSA_API_KEY = "bsaapikey";
|
||||
|
||||
@Provides
|
||||
public Keyring get() {
|
||||
@@ -127,8 +127,8 @@ public final class FakeKeyringModule {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getJsonCredential() {
|
||||
return JSON_CREDENTIAL;
|
||||
public String getBsaApiKey() {
|
||||
return BSA_API_KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -66,12 +66,16 @@ public class ConsoleDomainGetActionTest {
|
||||
assertThat(RESPONSE.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_OK);
|
||||
assertThat(RESPONSE.getPayload())
|
||||
.isEqualTo(
|
||||
"{\"domainName\":\"exists.tld\",\"adminContact\":{\"key\":\"3-ROID\"},\"techContact\":"
|
||||
+ "{\"key\":\"3-ROID\"},\"registrantContact\":{\"key\":\"3-ROID\"},\"registrationExpirationTime\":"
|
||||
+ "\"294247-01-10T04:00:54.775Z\",\"lastTransferTime\":\"null\",\"repoId\":\"2-TLD\","
|
||||
+ "\"currentSponsorRegistrarId\":\"TheRegistrar\",\"creationRegistrarId\":\"TheRegistrar\","
|
||||
+ "\"creationTime\":{\"creationTime\":\"1970-01-01T00:00:00.000Z\"},\"lastEppUpdateTime\":\"null\","
|
||||
+ "\"statuses\":[\"INACTIVE\"]}");
|
||||
"{\"domainName\":\"exists.tld\",\"adminContact\":{\"key\":\"3-ROID\",\"kind\":"
|
||||
+ "\"google.registry.model.contact.Contact\"},\"techContact\":{\"key\":\"3-ROID\","
|
||||
+ "\"kind\":\"google.registry.model.contact.Contact\"},\"registrantContact\":"
|
||||
+ "{\"key\":\"3-ROID\",\"kind\":\"google.registry.model.contact.Contact\"},"
|
||||
+ "\"registrationExpirationTime\":\"294247-01-10T04:00:54.775Z\","
|
||||
+ "\"lastTransferTime\":\"null\",\"repoId\":\"2-TLD\","
|
||||
+ "\"currentSponsorRegistrarId\":\"TheRegistrar\",\"creationRegistrarId\":"
|
||||
+ "\"TheRegistrar\",\"creationTime\":{\"creationTime\":"
|
||||
+ "\"1970-01-01T00:00:00.000Z\"},\"lastEppUpdateTime\":\"null\",\"statuses\":"
|
||||
+ "[\"INACTIVE\"]}");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
// 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.
|
||||
|
||||
package google.registry.ui.server.console;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistDomainAsDeleted;
|
||||
|
||||
import com.google.api.client.http.HttpStatusCodes;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.model.EppResourceUtils;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.tools.GsonUtils;
|
||||
import google.registry.ui.server.console.ConsoleDomainListAction.DomainListResult;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Tests for {@link ConsoleDomainListAction}. */
|
||||
public class ConsoleDomainListActionTest {
|
||||
|
||||
private static final Gson GSON = GsonUtils.provideGson();
|
||||
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2023-10-20T00:00:00.000Z"));
|
||||
|
||||
private FakeResponse response;
|
||||
|
||||
@RegisterExtension
|
||||
final JpaTestExtensions.JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension();
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTld("tld");
|
||||
for (int i = 0; i < 10; i++) {
|
||||
DatabaseHelper.persistActiveDomain(i + "exists.tld", clock.nowUtc());
|
||||
clock.advanceOneMilli();
|
||||
}
|
||||
DatabaseHelper.persistDeletedDomain("deleted.tld", clock.nowUtc().minusDays(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_allDomains() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar");
|
||||
action.run();
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains).hasSize(10);
|
||||
assertThat(result.totalResults).isEqualTo(10);
|
||||
assertThat(result.checkpointTime).isEqualTo(clock.nowUtc());
|
||||
assertThat(result.domains.stream().anyMatch(d -> d.getDomainName().equals("deleted.tld")))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_noDomains() {
|
||||
ConsoleDomainListAction action = createAction("NewRegistrar");
|
||||
action.run();
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains).hasSize(0);
|
||||
assertThat(result.totalResults).isEqualTo(0);
|
||||
assertThat(result.checkpointTime).isEqualTo(clock.nowUtc());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_pages() {
|
||||
// Two pages of results should go in reverse chronological order
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 5, null);
|
||||
action.run();
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains.stream().map(Domain::getDomainName).collect(toImmutableList()))
|
||||
.containsExactly("9exists.tld", "8exists.tld", "7exists.tld", "6exists.tld", "5exists.tld");
|
||||
assertThat(result.totalResults).isEqualTo(10);
|
||||
|
||||
// Now do the second page
|
||||
action = createAction("TheRegistrar", result.checkpointTime, 1, 5, 10L);
|
||||
action.run();
|
||||
result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains.stream().map(Domain::getDomainName).collect(toImmutableList()))
|
||||
.containsExactly("4exists.tld", "3exists.tld", "2exists.tld", "1exists.tld", "0exists.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_partialPage() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 1, 8, null);
|
||||
action.run();
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains.stream().map(Domain::getDomainName).collect(toImmutableList()))
|
||||
.containsExactly("1exists.tld", "0exists.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_checkpointTime_createdBefore() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 10, null);
|
||||
action.run();
|
||||
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains).hasSize(10);
|
||||
assertThat(result.totalResults).isEqualTo(10);
|
||||
|
||||
clock.advanceOneMilli();
|
||||
persistActiveDomain("newdomain.tld", clock.nowUtc());
|
||||
|
||||
// Even though we persisted a new domain, the old checkpoint should return no more results
|
||||
action = createAction("TheRegistrar", result.checkpointTime, 1, 10, null);
|
||||
action.run();
|
||||
result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains).isEmpty();
|
||||
assertThat(result.totalResults).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_checkpointTime_deletion() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 5, null);
|
||||
action.run();
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
|
||||
clock.advanceOneMilli();
|
||||
Domain toDelete =
|
||||
EppResourceUtils.loadByForeignKey(Domain.class, "0exists.tld", clock.nowUtc()).get();
|
||||
persistDomainAsDeleted(toDelete, clock.nowUtc());
|
||||
|
||||
// Second page should include the domain that is now deleted due to the checkpoint time
|
||||
action = createAction("TheRegistrar", result.checkpointTime, 1, 5, null);
|
||||
action.run();
|
||||
result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains.stream().map(Domain::getDomainName).collect(toImmutableList()))
|
||||
.containsExactly("4exists.tld", "3exists.tld", "2exists.tld", "1exists.tld", "0exists.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPartialSuccess_pastEnd() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 5, 5, null);
|
||||
action.run();
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_invalidResultsPerPage() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 0, null);
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo("Results per page must be between 1 and 500 inclusive");
|
||||
|
||||
action = createAction("TheRegistrar", null, 0, 501, null);
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo("Results per page must be between 1 and 500 inclusive");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_invalidPageNumber() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, -1, 10, null);
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
||||
assertThat(response.getPayload()).isEqualTo("Page number must be non-negative");
|
||||
}
|
||||
|
||||
private ConsoleDomainListAction createAction(String registrarId) {
|
||||
return createAction(registrarId, null, null, null, null);
|
||||
}
|
||||
|
||||
private ConsoleDomainListAction createAction(
|
||||
String registrarId,
|
||||
@Nullable DateTime checkpointTime,
|
||||
@Nullable Integer pageNumber,
|
||||
@Nullable Integer resultsPerPage,
|
||||
@Nullable Long totalResults) {
|
||||
response = new FakeResponse();
|
||||
AuthResult authResult =
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(
|
||||
new User.Builder()
|
||||
.setEmailAddress("email@email.example")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
|
||||
.build()));
|
||||
return new ConsoleDomainListAction(
|
||||
authResult,
|
||||
response,
|
||||
GSON,
|
||||
registrarId,
|
||||
Optional.ofNullable(checkpointTime),
|
||||
Optional.ofNullable(pageNumber),
|
||||
Optional.ofNullable(resultsPerPage),
|
||||
Optional.ofNullable(totalResults));
|
||||
}
|
||||
}
|
||||
@@ -35,5 +35,4 @@ PATH CLASS
|
||||
/_dr/task/tmchDnl TmchDnlAction POST y API APP ADMIN
|
||||
/_dr/task/tmchSmdrl TmchSmdrlAction POST y API APP ADMIN
|
||||
/_dr/task/updateRegistrarRdapBaseUrls UpdateRegistrarRdapBaseUrlsAction GET y API APP ADMIN
|
||||
/_dr/task/wipeOutCloudSql WipeOutCloudSqlAction GET n API APP ADMIN
|
||||
/_dr/task/wipeOutContactHistoryPii WipeOutContactHistoryPiiAction GET n API APP ADMIN
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
PATH CLASS METHODS OK AUTH_METHODS MIN USER_POLICY
|
||||
/_dr/epp EppTlsAction POST n API APP ADMIN
|
||||
/console-api/domain ConsoleDomainGetAction GET n API,LEGACY USER PUBLIC
|
||||
/console-api/domain-list ConsoleDomainListAction GET n API,LEGACY USER PUBLIC
|
||||
/console-api/registrars RegistrarsAction GET,POST n API,LEGACY USER PUBLIC
|
||||
/console-api/settings/contacts ContactAction GET,POST n API,LEGACY USER PUBLIC
|
||||
/console-api/settings/security SecurityAction POST n API,LEGACY USER PUBLIC
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
PATH CLASS METHODS OK AUTH_METHODS MIN USER_POLICY
|
||||
/_dr/whois WhoisAction POST n API APP ADMIN
|
||||
/check CheckApiAction GET n API,LEGACY NONE PUBLIC
|
||||
/rdap/autnum/(*) RdapAutnumAction GET,HEAD n API,LEGACY NONE PUBLIC
|
||||
/rdap/domain/(*) RdapDomainAction GET,HEAD n API,LEGACY NONE PUBLIC
|
||||
/rdap/domains RdapDomainSearchAction GET,HEAD n API,LEGACY NONE PUBLIC
|
||||
/rdap/entities RdapEntitySearchAction GET,HEAD n API,LEGACY NONE PUBLIC
|
||||
/rdap/entity/(*) RdapEntityAction GET,HEAD n API,LEGACY NONE PUBLIC
|
||||
/rdap/help(*) RdapHelpAction GET,HEAD n API,LEGACY NONE PUBLIC
|
||||
/rdap/ip/(*) RdapIpAction GET,HEAD n API,LEGACY NONE PUBLIC
|
||||
/rdap/nameserver/(*) RdapNameserverAction GET,HEAD n API,LEGACY NONE PUBLIC
|
||||
/rdap/nameservers RdapNameserverSearchAction GET,HEAD n API,LEGACY NONE PUBLIC
|
||||
/whois/(*) WhoisHttpAction GET n API,LEGACY NONE PUBLIC
|
||||
/check CheckApiAction GET n API NONE PUBLIC
|
||||
/rdap/autnum/(*) RdapAutnumAction GET,HEAD n API NONE PUBLIC
|
||||
/rdap/domain/(*) RdapDomainAction GET,HEAD n API NONE PUBLIC
|
||||
/rdap/domains RdapDomainSearchAction GET,HEAD n API NONE PUBLIC
|
||||
/rdap/entities RdapEntitySearchAction GET,HEAD n API NONE PUBLIC
|
||||
/rdap/entity/(*) RdapEntityAction GET,HEAD n API NONE PUBLIC
|
||||
/rdap/help(*) RdapHelpAction GET,HEAD n API NONE PUBLIC
|
||||
/rdap/ip/(*) RdapIpAction GET,HEAD n API NONE PUBLIC
|
||||
/rdap/nameserver/(*) RdapNameserverAction GET,HEAD n API NONE PUBLIC
|
||||
/rdap/nameservers RdapNameserverSearchAction GET,HEAD n API NONE PUBLIC
|
||||
/whois/(*) WhoisHttpAction GET n API NONE PUBLIC
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -145,3 +145,5 @@ V144__drop_database_migration_state_schedule_table.sql
|
||||
V145__add_breakglass_mode_to_tld_table.sql
|
||||
V146__last_update_time_via_epp.sql
|
||||
V147__drop_gaia_id_from_user.sql
|
||||
V148__add_bsa_download_and_label_tables.sql
|
||||
V149__add_bsa_domain_in_use_table.sql
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
-- 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.
|
||||
|
||||
CREATE TABLE "BsaDownload" (
|
||||
job_id bigserial not null,
|
||||
block_list_checksums text not null,
|
||||
creation_time timestamptz not null,
|
||||
stage text not null,
|
||||
update_timestamp timestamptz,
|
||||
primary key (job_id)
|
||||
);
|
||||
|
||||
CREATE TABLE "BsaLabel" (
|
||||
label text not null,
|
||||
creation_time timestamptz not null,
|
||||
primary key (label)
|
||||
);
|
||||
|
||||
CREATE INDEX IDXj874kw19bgdnkxo1rue45jwlw on "BsaDownload" (creation_time);
|
||||
@@ -0,0 +1,27 @@
|
||||
-- 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.
|
||||
|
||||
CREATE TABLE "BsaDomainInUse" (
|
||||
label text not null,
|
||||
tld text not null,
|
||||
creation_time timestamptz not null,
|
||||
reason text not null,
|
||||
primary key (label, tld)
|
||||
);
|
||||
|
||||
ALTER TABLE IF EXISTS "BsaDomainInUse"
|
||||
ADD CONSTRAINT FKbsadomaininuse2label
|
||||
FOREIGN KEY (label)
|
||||
REFERENCES "BsaLabel" (label)
|
||||
ON DELETE CASCADE;
|
||||
@@ -123,6 +123,60 @@ CREATE TABLE public."BillingRecurrence" (
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: BsaDomainInUse; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE public."BsaDomainInUse" (
|
||||
label text NOT NULL,
|
||||
tld text NOT NULL,
|
||||
creation_time timestamp with time zone NOT NULL,
|
||||
reason text NOT NULL
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: BsaDownload; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE public."BsaDownload" (
|
||||
job_id bigint NOT NULL,
|
||||
block_list_checksums text NOT NULL,
|
||||
creation_time timestamp with time zone NOT NULL,
|
||||
stage text NOT NULL,
|
||||
update_timestamp timestamp with time zone
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: BsaDownload_job_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE public."BsaDownload_job_id_seq"
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: BsaDownload_job_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE public."BsaDownload_job_id_seq" OWNED BY public."BsaDownload".job_id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: BsaLabel; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE public."BsaLabel" (
|
||||
label text NOT NULL,
|
||||
creation_time timestamp with time zone NOT NULL
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ClaimsEntry; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
@@ -1155,6 +1209,13 @@ CREATE SEQUENCE public.project_wide_unique_id_seq
|
||||
CACHE 10;
|
||||
|
||||
|
||||
--
|
||||
-- Name: BsaDownload job_id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public."BsaDownload" ALTER COLUMN job_id SET DEFAULT nextval('public."BsaDownload_job_id_seq"'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ClaimsList revision_id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
@@ -1257,6 +1318,30 @@ ALTER TABLE ONLY public."BillingRecurrence"
|
||||
ADD CONSTRAINT "BillingRecurrence_pkey" PRIMARY KEY (billing_recurrence_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: BsaDomainInUse BsaDomainInUse_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public."BsaDomainInUse"
|
||||
ADD CONSTRAINT "BsaDomainInUse_pkey" PRIMARY KEY (label, tld);
|
||||
|
||||
|
||||
--
|
||||
-- Name: BsaDownload BsaDownload_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public."BsaDownload"
|
||||
ADD CONSTRAINT "BsaDownload_pkey" PRIMARY KEY (job_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: BsaLabel BsaLabel_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public."BsaLabel"
|
||||
ADD CONSTRAINT "BsaLabel_pkey" PRIMARY KEY (label);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ClaimsEntry ClaimsEntry_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
@@ -1847,6 +1932,13 @@ CREATE INDEX idxj1mtx98ndgbtb1bkekahms18w ON public."GracePeriod" USING btree (d
|
||||
CREATE INDEX idxj77pfwhui9f0i7wjq6lmibovj ON public."HostHistory" USING btree (host_name);
|
||||
|
||||
|
||||
--
|
||||
-- Name: idxj874kw19bgdnkxo1rue45jwlw; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE INDEX idxj874kw19bgdnkxo1rue45jwlw ON public."BsaDownload" USING btree (creation_time);
|
||||
|
||||
|
||||
--
|
||||
-- Name: idxjny8wuot75b5e6p38r47wdawu; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
@@ -2622,6 +2714,14 @@ ALTER TABLE ONLY public."DomainHistoryHost"
|
||||
ADD CONSTRAINT fka9woh3hu8gx5x0vly6bai327n FOREIGN KEY (domain_history_domain_repo_id, domain_history_history_revision_id) REFERENCES public."DomainHistory"(domain_repo_id, history_revision_id) DEFERRABLE INITIALLY DEFERRED;
|
||||
|
||||
|
||||
--
|
||||
-- Name: BsaDomainInUse fkbsadomaininuse2label; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public."BsaDomainInUse"
|
||||
ADD CONSTRAINT fkbsadomaininuse2label FOREIGN KEY (label) REFERENCES public."BsaLabel"(label) ON DELETE CASCADE;
|
||||
|
||||
|
||||
--
|
||||
-- Name: DomainTransactionRecord fkcjqe54u72kha71vkibvxhjye7; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
@@ -210,7 +210,7 @@ ext {
|
||||
'org.apache.httpcomponents:httpcore:[4.4.13,)',
|
||||
'org.apache.tomcat:tomcat-annotations-api:[8.0.5,)',
|
||||
'com.fasterxml.jackson.core:jackson-databind:[2.11.2,)',
|
||||
'org.flywaydb:flyway-core:[5.2.4,)',
|
||||
'org.flywaydb:flyway-core:[5.2.4,10.0)!!',
|
||||
'org.glassfish.jaxb:jaxb-runtime:[2.3.0,)',
|
||||
'org.hamcrest:hamcrest:[2.2,)',
|
||||
'org.hamcrest:hamcrest-core:[2.2,)',
|
||||
|
||||
@@ -28,7 +28,7 @@ COPY go.sum ./
|
||||
COPY go.mod ./
|
||||
RUN go build -o /deployCloudSchedulerAndQueue
|
||||
|
||||
FROM marketplace.gcr.io/google/debian10
|
||||
FROM marketplace.gcr.io/google/debian11
|
||||
ENV DEBIAN_FRONTEND=noninteractive LANG=en_US.UTF-8
|
||||
# Add script for cloud scheduler and cloud tasks deployment
|
||||
COPY --from=deployCloudSchedulerAndQueueBuilder /deployCloudSchedulerAndQueue /usr/local/bin/deployCloudSchedulerAndQueue
|
||||
|
||||
@@ -24,7 +24,7 @@ apt-get install gnupg2 -y
|
||||
# Install Java
|
||||
apt-get install openjdk-11-jdk-headless -y
|
||||
# Install Python
|
||||
apt-get install python -y
|
||||
apt-get install python3 -y
|
||||
# As of March 2021 python3 is at v3.6. Get pip then install dataclasses
|
||||
# (introduced in 3.7) for nom_build
|
||||
apt-get install python3-pip -y
|
||||
@@ -32,8 +32,10 @@ python3 -m pip install dataclasses
|
||||
# Install curl.
|
||||
apt-get install curl -y
|
||||
# Install Node
|
||||
curl -sL https://deb.nodesource.com/setup_current.x | bash -
|
||||
apt-get install -y nodejs
|
||||
apt-get install npm -y
|
||||
npm cache clean -f
|
||||
npm install -g n
|
||||
n 16.19.0
|
||||
# Install gcloud
|
||||
# Cribbed from https://cloud.google.com/sdk/docs/quickstart-debian-ubuntu
|
||||
apt-get install lsb-release -y
|
||||
|
||||
Reference in New Issue
Block a user