1
0
mirror of https://github.com/google/nomulus synced 2026-05-26 01:30:36 +00:00

Compare commits

..

10 Commits

Author SHA1 Message Date
Weimin Yu
b5e131ecba Add BSA schema (#2204)
* Add BSA schema

Also lock down flyway due to java8 compatiblity
2023-11-02 15:38:23 -04:00
Pavlo Tkach
87e99f59bc Replace node.js installation method in build.sh (#2206) 2023-11-02 14:17:18 -04:00
Weimin Yu
30accea383 Add keyring support for BSA API key (#2208)
* Add keyring support for BSA API key

Also removing JSON_CREDENTIAL. It is an exported service account key,
which we no longer use.
2023-11-02 14:08:50 -04:00
Lai Jiang
72e0101746 Delete unused actions (#2197)
Both actions have not been used for a while (the wipe out action
actually caused problems when it ran unintentionally and wiped out QA).
Keeping them around is a burden when refactoring efforts have to take
them into consideration.

It is always possible to resurrect them form git history should the need
arises.
2023-11-02 11:41:03 -04:00
Lai Jiang
3090df9a78 Upgrade to Java 17 runtime (#2201)
We finally fixed Spinnaker (I hope) to deploy bundled services with Java
17 runtime. Note that the bytecodes are still targeting Java 8. The only
change this PR introduces is to switch the runtime environment to Java
17.

TESTED=deployed to crash.
2023-11-02 10:08:14 -04:00
gbrodman
7332b1fa38 Add TypeAdapters for VKey objects (#2194)
GSON doesn't allow for clean (de)serialization of Class or Serializable
objects which we'll need for converting VKeys to/from JSON.
2023-10-31 15:14:41 -04:00
Lai Jiang
9330e3a50d Move truely public endpoints to a separate Auth (#2200)
This allows us to more easily refactor public endpoints that still use
the legacy auth mechanism to identify logged-in users (for the legacy
console).
2023-10-31 13:58:45 -04:00
gbrodman
1d6b119340 Add a console action to retrieve a paged list of domains (#2193)
In the future we'll want to add searching capability but for now we can
go with straightforward pagination.
2023-10-30 17:01:31 -04:00
Weimin Yu
8158f761c8 Add BSA configurations (#2202) 2023-10-30 16:44:28 -04:00
Pavlo Tkach
08838e091f Enable BACKEND service to route external traffic through VPC on Sandbox (#2199) 2023-10-30 13:36:04 -04:00
69 changed files with 3589 additions and 2523 deletions

View File

@@ -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.");
}
}
}
}
}

View File

@@ -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)

View File

@@ -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;
}
}

View File

@@ -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://"

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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"/>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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. */

View File

@@ -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

View File

@@ -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. */

View File

@@ -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);
}
/**

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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();

View File

@@ -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() {}

View File

@@ -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.
*

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;

View File

@@ -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,

View File

@@ -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;
}
}
}

View File

@@ -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";

View File

@@ -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;

View File

@@ -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();

View File

@@ -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);
}
}

View File

@@ -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";

View File

@@ -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>

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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));
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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;

View File

@@ -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: -
--

View File

@@ -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,)',

View File

@@ -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

View File

@@ -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