From 9acac1a6a4a4518fa7ebc60f5a0959948e905c7b Mon Sep 17 00:00:00 2001 From: gbrodman Date: Mon, 9 Nov 2020 13:47:42 -0500 Subject: [PATCH] Persist two singleton entities in SQL tables (#860) * Persist two singleton entities in SQL tables A table might not be the best place to store singleton entities, but by doing this we ensure we can easily inspect them later and use the same sort of persistence logic for these that we do elsewhere. ServerSecret is stored upon retrieval so that we make sure that the same secret is used in both Datastore and SQL (we wouldn't want to change it). * Responses to CR * Don't have a separate ID for the singleton entities * Rename secret UUID * Rename and regenerate --- .../model/common/CrossTldSingleton.java | 11 +- .../registry/model/server/ServerSecret.java | 115 ++++-- .../google/registry/model/tmch/TmchCrl.java | 77 +++- .../tmch/TmchCertificateAuthority.java | 18 +- .../main/resources/META-INF/persistence.xml | 2 + .../model/server/ServerSecretTest.java | 52 ++- .../registry/model/tmch/TmchCrlTest.java | 44 ++- .../auth/RequestAuthenticatorTest.java | 11 +- .../integration/SqlIntegrationTestSuite.java | 4 + .../sql/er_diagram/brief_er_diagram.html | 238 ++++++++++--- .../sql/er_diagram/full_er_diagram.html | 328 ++++++++++++++---- db/src/main/resources/sql/flyway.txt | 1 + .../sql/flyway/V73__singleton_entities.sql | 25 ++ .../sql/schema/db-schema.sql.generated | 12 + .../resources/sql/schema/nomulus.golden.sql | 36 ++ 15 files changed, 759 insertions(+), 215 deletions(-) create mode 100644 db/src/main/resources/sql/flyway/V73__singleton_entities.sql diff --git a/core/src/main/java/google/registry/model/common/CrossTldSingleton.java b/core/src/main/java/google/registry/model/common/CrossTldSingleton.java index 83315bc65..f92b6ba12 100644 --- a/core/src/main/java/google/registry/model/common/CrossTldSingleton.java +++ b/core/src/main/java/google/registry/model/common/CrossTldSingleton.java @@ -20,15 +20,16 @@ import com.googlecode.objectify.Key; import com.googlecode.objectify.annotation.Id; import com.googlecode.objectify.annotation.Parent; import google.registry.model.ImmutableObject; +import javax.persistence.MappedSuperclass; +import javax.persistence.Transient; /** A singleton entity in Datastore. */ +@MappedSuperclass public abstract class CrossTldSingleton extends ImmutableObject { - public static final long SINGLETON_ID = 1; // There is always exactly one of these. + public static final long SINGLETON_ID = 1; // There is always exactly one of these. - @Id - long id = SINGLETON_ID; + @Id @Transient long id = SINGLETON_ID; - @Parent - Key parent = getCrossTldKey(); + @Transient @Parent Key parent = getCrossTldKey(); } diff --git a/core/src/main/java/google/registry/model/server/ServerSecret.java b/core/src/main/java/google/registry/model/server/ServerSecret.java index 7011334f9..290dddf28 100644 --- a/core/src/main/java/google/registry/model/server/ServerSecret.java +++ b/core/src/main/java/google/registry/model/server/ServerSecret.java @@ -14,29 +14,43 @@ package google.registry.model.server; -import static google.registry.model.ofy.ObjectifyService.ofy; +import static google.registry.model.common.EntityGroupRoot.getCrossTldKey; +import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm; +import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import com.google.common.annotations.VisibleForTesting; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableList; import com.google.common.primitives.Longs; +import com.googlecode.objectify.Key; import com.googlecode.objectify.annotation.Entity; +import com.googlecode.objectify.annotation.Ignore; +import com.googlecode.objectify.annotation.OnLoad; import com.googlecode.objectify.annotation.Unindex; import google.registry.model.annotations.NotBackedUp; import google.registry.model.annotations.NotBackedUp.Reason; import google.registry.model.common.CrossTldSingleton; +import google.registry.persistence.VKey; +import google.registry.schema.replay.DatastoreEntity; +import google.registry.schema.replay.SqlEntity; import java.nio.ByteBuffer; import java.util.UUID; import java.util.concurrent.ExecutionException; +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.PostLoad; +import javax.persistence.Transient; /** A secret number used for generating tokens (such as XSRF tokens). */ @Entity +@javax.persistence.Entity @Unindex @NotBackedUp(reason = Reason.AUTO_GENERATED) // TODO(b/27427316): Replace this with an entry in KMSKeyring -public class ServerSecret extends CrossTldSingleton { +public class ServerSecret extends CrossTldSingleton implements DatastoreEntity, SqlEntity { /** * Cache of the singleton ServerSecret instance that creates it if not present. @@ -45,28 +59,34 @@ public class ServerSecret extends CrossTldSingleton { * Supplier that can be reset for testing purposes. */ private static final LoadingCache, ServerSecret> CACHE = - CacheBuilder.newBuilder().build( - new CacheLoader, ServerSecret>() { - @Override - public ServerSecret load(Class unused) { - // Fast path - non-transactional load to hit memcache. - ServerSecret secret = ofy().load().entity(new ServerSecret()).now(); - if (secret != null) { - return secret; - } - // Slow path - transactionally create a new ServerSecret (once per app setup). - return tm().transact(() -> { - // Check again for an existing secret within the transaction to avoid races. - ServerSecret secret1 = ofy().load().entity(new ServerSecret()).now(); - if (secret1 == null) { - UUID uuid = UUID.randomUUID(); - secret1 = create(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits()); - ofy().saveWithoutBackup().entity(secret1).now(); + CacheBuilder.newBuilder() + .build( + new CacheLoader, ServerSecret>() { + @Override + public ServerSecret load(Class unused) { + return retrieveAndSaveSecret(); } - return secret1; }); - } - }); + + private static ServerSecret retrieveAndSaveSecret() { + VKey key = + VKey.create( + ServerSecret.class, + SINGLETON_ID, + Key.create(getCrossTldKey(), ServerSecret.class, SINGLETON_ID)); + return tm().transact( + () -> { + // transactionally create a new ServerSecret (once per app setup) if necessary. + // return the ofy() result during Datastore-primary phase + ServerSecret secret = + ofyTm().maybeLoad(key).orElseGet(() -> create(UUID.randomUUID())); + // During a dual-write period, write it to both Datastore and SQL + // even if we didn't have to retrieve it from the DB + ofyTm().transact(() -> ofyTm().putWithoutBackup(secret)); + jpaTm().transact(() -> jpaTm().putWithoutBackup(secret)); + return secret; + }); + } /** Returns the global ServerSecret instance, creating it if one isn't already in Datastore. */ public static ServerSecret get() { @@ -77,23 +97,38 @@ public class ServerSecret extends CrossTldSingleton { } } - /** Most significant 8 bytes of the UUID value. */ - long mostSignificant; + /** Most significant 8 bytes of the UUID value (stored separately for legacy purposes). */ + @Transient long mostSignificant; - /** Least significant 8 bytes of the UUID value. */ - long leastSignificant; + /** Least significant 8 bytes of the UUID value (stored separately for legacy purposes). */ + @Transient long leastSignificant; - @VisibleForTesting - static ServerSecret create(long mostSignificant, long leastSignificant) { - ServerSecret secret = new ServerSecret(); - secret.mostSignificant = mostSignificant; - secret.leastSignificant = leastSignificant; - return secret; + /** The UUID value itself. */ + @Id + @Column(columnDefinition = "uuid") + @Ignore + UUID secret; + + /** Convert the Datastore representation to SQL. */ + @OnLoad + void onLoad() { + secret = new UUID(mostSignificant, leastSignificant); } - /** Returns the value of this ServerSecret as a UUID. */ - public UUID asUuid() { - return new UUID(mostSignificant, leastSignificant); + /** Convert the SQL representation to Datastore. */ + @PostLoad + void postLoad() { + mostSignificant = secret.getMostSignificantBits(); + leastSignificant = secret.getLeastSignificantBits(); + } + + @VisibleForTesting + static ServerSecret create(UUID uuid) { + ServerSecret secret = new ServerSecret(); + secret.mostSignificant = uuid.getMostSignificantBits(); + secret.leastSignificant = uuid.getLeastSignificantBits(); + secret.secret = uuid; + return secret; } /** Returns the value of this ServerSecret as a byte array. */ @@ -104,6 +139,16 @@ public class ServerSecret extends CrossTldSingleton { .array(); } + @Override + public ImmutableList toSqlEntities() { + return ImmutableList.of(); // dually-written + } + + @Override + public ImmutableList toDatastoreEntities() { + return ImmutableList.of(); // dually-written + } + @VisibleForTesting static void resetCache() { CACHE.invalidateAll(); diff --git a/core/src/main/java/google/registry/model/tmch/TmchCrl.java b/core/src/main/java/google/registry/model/tmch/TmchCrl.java index b212167f8..50cf4803d 100644 --- a/core/src/main/java/google/registry/model/tmch/TmchCrl.java +++ b/core/src/main/java/google/registry/model/tmch/TmchCrl.java @@ -15,31 +15,50 @@ package google.registry.model.tmch; import static com.google.common.base.Preconditions.checkNotNull; -import static google.registry.model.ofy.ObjectifyService.ofy; +import static google.registry.model.common.EntityGroupRoot.getCrossTldKey; +import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm; +import static google.registry.persistence.transaction.TransactionManagerFactory.ofyTm; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; +import com.google.common.collect.ImmutableList; +import com.googlecode.objectify.Key; import com.googlecode.objectify.annotation.Entity; import google.registry.model.annotations.NotBackedUp; import google.registry.model.annotations.NotBackedUp.Reason; import google.registry.model.common.CrossTldSingleton; -import javax.annotation.Nullable; +import google.registry.model.tmch.TmchCrl.TmchCrlId; +import google.registry.persistence.VKey; +import google.registry.schema.replay.DatastoreEntity; +import google.registry.schema.replay.SqlEntity; +import java.io.Serializable; +import java.util.Optional; import javax.annotation.concurrent.Immutable; +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.IdClass; import org.joda.time.DateTime; /** Datastore singleton for ICANN's TMCH CA certificate revocation list (CRL). */ @Entity +@javax.persistence.Entity @Immutable @NotBackedUp(reason = Reason.EXTERNALLY_SOURCED) -public final class TmchCrl extends CrossTldSingleton { +@IdClass(TmchCrlId.class) +public final class TmchCrl extends CrossTldSingleton implements DatastoreEntity, SqlEntity { - String crl; - DateTime updated; - String url; + @Id String crl; + + @Id DateTime updated; + + @Id String url; /** Returns the singleton instance of this entity, without memoization. */ - @Nullable - public static TmchCrl get() { - return ofy().load().entity(new TmchCrl()).now(); + public static Optional get() { + VKey key = + VKey.create( + TmchCrl.class, SINGLETON_ID, Key.create(getCrossTldKey(), TmchCrl.class, SINGLETON_ID)); + // return the ofy() result during Datastore-primary phase + return ofyTm().transact(() -> ofyTm().maybeLoad(key)); } /** @@ -47,16 +66,18 @@ public final class TmchCrl extends CrossTldSingleton { * *

Please do not call this function unless your CRL is properly formatted, signed by the root, * and actually newer than the one currently in Datastore. + * + *

During the dual-write period, we write to both Datastore and SQL */ public static void set(final String crl, final String url) { - tm() - .transactNew( + tm().transact( () -> { TmchCrl tmchCrl = new TmchCrl(); tmchCrl.updated = tm().getTransactionTime(); tmchCrl.crl = checkNotNull(crl, "crl"); tmchCrl.url = checkNotNull(url, "url"); - ofy().saveWithoutBackup().entity(tmchCrl); + ofyTm().transactNew(() -> ofyTm().putWithoutBackup(tmchCrl)); + jpaTm().transactNew(() -> jpaTm().putWithoutBackup(tmchCrl)); }); } @@ -74,4 +95,36 @@ public final class TmchCrl extends CrossTldSingleton { public final DateTime getUpdated() { return updated; } + + @Override + public ImmutableList toSqlEntities() { + return ImmutableList.of(); // dually-written + } + + @Override + public ImmutableList toDatastoreEntities() { + return ImmutableList.of(); // dually-written + } + + static class TmchCrlId implements Serializable { + + @Column(name = "certificateRevocations") + String crl; + + @Column(name = "updateTimestamp") + DateTime updated; + + String url; + + /** Hibernate requires this default constructor. */ + private TmchCrlId() {} + + static TmchCrlId create(String crl, DateTime updated, String url) { + TmchCrlId result = new TmchCrlId(); + result.crl = crl; + result.updated = updated; + result.url = url; + return result; + } + } } diff --git a/core/src/main/java/google/registry/tmch/TmchCertificateAuthority.java b/core/src/main/java/google/registry/tmch/TmchCertificateAuthority.java index 422a7a256..6100c0c7b 100644 --- a/core/src/main/java/google/registry/tmch/TmchCertificateAuthority.java +++ b/core/src/main/java/google/registry/tmch/TmchCertificateAuthority.java @@ -32,6 +32,7 @@ import java.security.GeneralSecurityException; import java.security.cert.CertificateParsingException; import java.security.cert.X509CRL; import java.security.cert.X509Certificate; +import java.util.Optional; import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.ThreadSafe; import javax.inject.Inject; @@ -82,14 +83,15 @@ public final class TmchCertificateAuthority { new CacheLoader() { @Override public X509CRL load(final TmchCaMode tmchCaMode) throws GeneralSecurityException { - TmchCrl storedCrl = TmchCrl.get(); - String crlContents; - if (storedCrl == null) { - String file = (tmchCaMode == PILOT) ? CRL_PILOT_FILE : CRL_FILE; - crlContents = readResourceUtf8(TmchCertificateAuthority.class, file); - } else { - crlContents = storedCrl.getCrl(); - } + Optional storedCrl = TmchCrl.get(); + String crlContents = + storedCrl + .map(TmchCrl::getCrl) + .orElseGet( + () -> { + String file = (tmchCaMode == PILOT) ? CRL_PILOT_FILE : CRL_FILE; + return readResourceUtf8(TmchCertificateAuthority.class, file); + }); X509CRL crl = X509Utils.loadCrl(crlContents); crl.verify(ROOT_CERTS.get(tmchCaMode).getPublicKey()); return crl; diff --git a/core/src/main/resources/META-INF/persistence.xml b/core/src/main/resources/META-INF/persistence.xml index 58ab1ec77..4edaed2de 100644 --- a/core/src/main/resources/META-INF/persistence.xml +++ b/core/src/main/resources/META-INF/persistence.xml @@ -63,8 +63,10 @@ google.registry.model.reporting.DomainTransactionRecord google.registry.model.reporting.Spec11ThreatMatch google.registry.model.server.KmsSecretRevision + google.registry.model.server.ServerSecret google.registry.model.smd.SignedMarkRevocationList google.registry.model.tmch.ClaimsListShard + google.registry.model.tmch.TmchCrl google.registry.persistence.transaction.TransactionEntity google.registry.schema.cursor.Cursor google.registry.schema.domain.RegistryLock diff --git a/core/src/test/java/google/registry/model/server/ServerSecretTest.java b/core/src/test/java/google/registry/model/server/ServerSecretTest.java index cc74780e1..7a104d427 100644 --- a/core/src/test/java/google/registry/model/server/ServerSecretTest.java +++ b/core/src/test/java/google/registry/model/server/ServerSecretTest.java @@ -15,21 +15,25 @@ package google.registry.model.server; import static com.google.common.truth.Truth.assertThat; +import static google.registry.model.common.CrossTldSingleton.SINGLETON_ID; +import static google.registry.model.common.EntityGroupRoot.getCrossTldKey; import static google.registry.model.ofy.ObjectifyService.ofy; +import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm; +import com.googlecode.objectify.Key; +import google.registry.model.EntityTestCase; import google.registry.model.ofy.RequestCapturingAsyncDatastoreService; -import google.registry.testing.AppEngineExtension; +import google.registry.persistence.VKey; import java.util.UUID; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; /** Unit tests for {@link ServerSecret}. */ -public class ServerSecretTest { +public class ServerSecretTest extends EntityTestCase { - @RegisterExtension - public final AppEngineExtension appEngine = - AppEngineExtension.builder().withDatastoreAndCloudSql().build(); + ServerSecretTest() { + super(JpaEntityCoverageCheck.ENABLED); + } @BeforeEach void beforeEach() { @@ -41,18 +45,20 @@ public class ServerSecretTest { ServerSecret secret = ServerSecret.get(); assertThat(secret).isNotNull(); assertThat(ofy().load().entity(new ServerSecret()).now()).isEqualTo(secret); + assertThat(loadFromSql()).isEqualTo(secret); } @Test void testGet_existingSecret_returned() { - ServerSecret secret = ServerSecret.create(123, 456); + ServerSecret secret = ServerSecret.create(new UUID(123, 456)); ofy().saveWithoutBackup().entity(secret).now(); assertThat(ServerSecret.get()).isEqualTo(secret); assertThat(ofy().load().entity(new ServerSecret()).now()).isEqualTo(secret); + assertThat(loadFromSql()).isEqualTo(secret); } @Test - void testGet_cachedSecret_returnedWithoutDatastoreRead() { + void testGet_cachedSecret() { int numInitialReads = RequestCapturingAsyncDatastoreService.getReads().size(); ServerSecret secret = ServerSecret.get(); int numReads = RequestCapturingAsyncDatastoreService.getReads().size(); @@ -62,16 +68,28 @@ public class ServerSecretTest { } @Test - void testAsUuid() { - UUID uuid = ServerSecret.create(123, 456).asUuid(); - assertThat(uuid.getMostSignificantBits()).isEqualTo(123); - assertThat(uuid.getLeastSignificantBits()).isEqualTo(456); + void testAsBytes() { + byte[] bytes = ServerSecret.create(new UUID(123, 0x456)).asBytes(); + assertThat(bytes).isEqualTo(new byte[] {0, 0, 0, 0, 0, 0, 0, 123, 0, 0, 0, 0, 0, 0, 0x4, 0x56}); } - @Test - void testAsBytes() { - byte[] bytes = ServerSecret.create(123, 0x456).asBytes(); - assertThat(bytes) - .isEqualTo(new byte[] {0, 0, 0, 0, 0, 0, 0, 123, 0, 0, 0, 0, 0, 0, 0x4, 0x56}); + private static ServerSecret loadFromSql() { + return jpaTm() + .transact( + () -> + jpaTm() + .getEntityManager() + .createQuery("FROM ServerSecret", ServerSecret.class) + .setMaxResults(1) + .getResultStream() + .findFirst() + .get()); + } + + private static VKey createKey() { + return VKey.create( + ServerSecret.class, + SINGLETON_ID, + Key.create(getCrossTldKey(), ServerSecret.class, SINGLETON_ID)); } } diff --git a/core/src/test/java/google/registry/model/tmch/TmchCrlTest.java b/core/src/test/java/google/registry/model/tmch/TmchCrlTest.java index 83d1bde55..3f75a5436 100644 --- a/core/src/test/java/google/registry/model/tmch/TmchCrlTest.java +++ b/core/src/test/java/google/registry/model/tmch/TmchCrlTest.java @@ -15,22 +15,48 @@ package google.registry.model.tmch; import static com.google.common.truth.Truth.assertThat; +import static google.registry.model.ofy.ObjectifyService.ofy; +import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm; -import google.registry.testing.AppEngineExtension; +import google.registry.model.EntityTestCase; +import java.util.Optional; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; /** Unit tests for {@link TmchCrl}. */ -public class TmchCrlTest { +public class TmchCrlTest extends EntityTestCase { - @RegisterExtension - public final AppEngineExtension appEngine = - AppEngineExtension.builder().withDatastoreAndCloudSql().build(); + TmchCrlTest() { + super(JpaEntityCoverageCheck.ENABLED); + } @Test void testSuccess() { - assertThat(TmchCrl.get()).isNull(); - TmchCrl.set("lolcat", "http://lol.cat"); - assertThat(TmchCrl.get().getCrl()).isEqualTo("lolcat"); + assertThat(TmchCrl.get()).isEqualTo(Optional.empty()); + TmchCrl.set("lolcat", "https://lol.cat"); + assertThat(TmchCrl.get().get().getCrl()).isEqualTo("lolcat"); + } + + @Test + void testDualWrite() { + TmchCrl expected = new TmchCrl(); + expected.crl = "lolcat"; + expected.url = "https://lol.cat"; + expected.updated = fakeClock.nowUtc(); + TmchCrl.set("lolcat", "https://lol.cat"); + assertThat(ofy().load().entity(new TmchCrl()).now()).isEqualTo(expected); + assertThat(loadFromSql()).isEqualTo(expected); + } + + private static TmchCrl loadFromSql() { + return jpaTm() + .transact( + () -> + jpaTm() + .getEntityManager() + .createQuery("FROM TmchCrl", TmchCrl.class) + .setMaxResults(1) + .getResultStream() + .findFirst() + .get()); } } diff --git a/core/src/test/java/google/registry/request/auth/RequestAuthenticatorTest.java b/core/src/test/java/google/registry/request/auth/RequestAuthenticatorTest.java index e56751820..0cb443cbd 100644 --- a/core/src/test/java/google/registry/request/auth/RequestAuthenticatorTest.java +++ b/core/src/test/java/google/registry/request/auth/RequestAuthenticatorTest.java @@ -43,12 +43,13 @@ import org.junit.jupiter.api.extension.RegisterExtension; /** Unit tests for {@link RequestAuthenticator}. */ class RequestAuthenticatorTest { - @RegisterExtension final AppEngineExtension appEngine = AppEngineExtension.builder().build(); + @RegisterExtension + final AppEngineExtension appEngine = + AppEngineExtension.builder().withDatastoreAndCloudSql().build(); - private static final AuthSettings AUTH_NONE = AuthSettings.create( - ImmutableList.of(AuthMethod.INTERNAL), - AuthLevel.NONE, - UserPolicy.IGNORED); + private static final AuthSettings AUTH_NONE = + AuthSettings.create( + ImmutableList.of(AuthMethod.INTERNAL), AuthLevel.NONE, UserPolicy.IGNORED); private static final AuthSettings AUTH_INTERNAL_OR_ADMIN = AuthSettings.create( ImmutableList.of(AuthMethod.INTERNAL), diff --git a/core/src/test/java/google/registry/schema/integration/SqlIntegrationTestSuite.java b/core/src/test/java/google/registry/schema/integration/SqlIntegrationTestSuite.java index a717bd6f3..59ae333cb 100644 --- a/core/src/test/java/google/registry/schema/integration/SqlIntegrationTestSuite.java +++ b/core/src/test/java/google/registry/schema/integration/SqlIntegrationTestSuite.java @@ -30,8 +30,10 @@ import google.registry.model.registry.RegistryTest; import google.registry.model.registry.label.ReservedListSqlDaoTest; import google.registry.model.reporting.Spec11ThreatMatchTest; import google.registry.model.server.KmsSecretRevisionSqlDaoTest; +import google.registry.model.server.ServerSecretTest; import google.registry.model.smd.SignedMarkRevocationListDaoTest; import google.registry.model.tmch.ClaimsListDaoTest; +import google.registry.model.tmch.TmchCrlTest; import google.registry.persistence.transaction.JpaEntityCoverageExtension; import google.registry.persistence.transaction.JpaTestRules.JpaIntegrationWithCoverageExtension; import google.registry.schema.cursor.CursorDaoTest; @@ -95,8 +97,10 @@ import org.junit.runner.RunWith; RegistryTest.class, ReservedListSqlDaoTest.class, RegistryLockDaoTest.class, + ServerSecretTest.class, SignedMarkRevocationListDaoTest.class, Spec11ThreatMatchTest.class, + TmchCrlTest.class, // AfterSuiteTest must be the last entry. See class javadoc for details. AfterSuiteTest.class }) diff --git a/db/src/main/resources/sql/er_diagram/brief_er_diagram.html b/db/src/main/resources/sql/er_diagram/brief_er_diagram.html index 657d1d06e..4e2791b44 100644 --- a/db/src/main/resources/sql/er_diagram/brief_er_diagram.html +++ b/db/src/main/resources/sql/er_diagram/brief_er_diagram.html @@ -261,19 +261,19 @@ td.section { generated on - 2020-11-04 16:40:52.942256 + 2020-11-09 17:11:19.905881 last flyway file - V72__add_missing_foreign_keys.sql + V73__singleton_entities.sql

 

 

- + SchemaCrawler_Diagram - + generated by @@ -284,7 +284,7 @@ td.section { generated on - 2020-11-04 16:40:52.942256 + 2020-11-09 17:11:19.905881 @@ -1352,7 +1352,7 @@ td.section { fk_domain_transfer_losing_registrar_id - + tld_f1fa57e2 @@ -2956,97 +2956,153 @@ td.section { fkgq03rk0bt1hb915dnyvd3vnfc - + + serversecret_6cc90f09 + + + public.ServerSecret + + + + [table] + + + secret + + + + + uuid not null + + + + signedmarkrevocationentry_99c39721 - - + + public.SignedMarkRevocationEntry - - + + [table] - + revision_id - + - + int8 not null - + smd_id - + - + text not null - + - + signedmarkrevocationlist_c5d968fb - - + + public.SignedMarkRevocationList - - + + [table] - + revision_id - + - + bigserial not null - + - + auto-incremented - + signedmarkrevocationentry_99c39721:w->signedmarkrevocationlist_c5d968fb:e - - - - - - - - + + + + + + + + fk5ivlhvs3121yx2li5tqh54u4 - - - transaction_d50389d4 - - - public.Transaction + + + tmchcrl_d282355 + + + public.TmchCrl - - + + [table] - + + certificate_revocations + + + + + text not null + + + update_timestamp + + + + + timestamptz not null + + + url + + + + + text not null + + + + + transaction_d50389d4 + + + public.Transaction + + + + [table] + + id - + - + bigserial not null - + - + auto-incremented - + @@ -6461,6 +6517,36 @@ td.section {

 

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
public.ServerSecret [table] +
secretuuid not null
Primary Key
ServerSecret_pkey[primary key]
secret
+

 

@@ -6741,6 +6827,56 @@ td.section {
public.SignedMarkRevocationEntry [table]

 

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
public.TmchCrl [table] +
certificate_revocationstext not null
update_timestamptimestamptz not null
urltext not null
Primary Key
TmchCrl_pkey[primary key]
certificate_revocations
update_timestamp
url
+

 

diff --git a/db/src/main/resources/sql/er_diagram/full_er_diagram.html b/db/src/main/resources/sql/er_diagram/full_er_diagram.html index 0c7224a31..b7c709b60 100644 --- a/db/src/main/resources/sql/er_diagram/full_er_diagram.html +++ b/db/src/main/resources/sql/er_diagram/full_er_diagram.html @@ -261,32 +261,32 @@ td.section { - + - +
public.Transaction [table]
generated on2020-11-04 16:40:50.9033912020-11-09 17:11:18.25328
last flyway fileV72__add_missing_foreign_keys.sqlV73__singleton_entities.sql

 

 

- + SchemaCrawler_Diagram - - + + generated by - + SchemaCrawler 16.10.1 - + generated on - - 2020-11-04 16:40:50.903391 + + 2020-11-09 17:11:18.25328 - + allocationtoken_a08ccbef @@ -1577,7 +1577,7 @@ td.section { billingevent_a57d1815:w->registrar_6e1503e3:e - + @@ -3048,7 +3048,7 @@ td.section { fk_domain_transfer_losing_registrar_id - + tld_f1fa57e2 @@ -5124,7 +5124,7 @@ td.section { - + fk_spec11_threat_match_tld @@ -5385,14 +5385,14 @@ td.section { domaintransactionrecord_6e77ff61:w->tld_f1fa57e2:e - + - - - - - + + + + + fk_domain_transaction_record_tld @@ -6068,121 +6068,177 @@ td.section { fkgq03rk0bt1hb915dnyvd3vnfc - + + serversecret_6cc90f09 + + + public.ServerSecret + + + + [table] + + + secret + + + + + uuid not null + + + + signedmarkrevocationentry_99c39721 - - + + public.SignedMarkRevocationEntry - - + + [table] - + revision_id - + - + int8 not null - + revocation_time - + - + timestamptz not null - + smd_id - + - + text not null - + - + signedmarkrevocationlist_c5d968fb - - + + public.SignedMarkRevocationList - - + + [table] - + revision_id - + - + bigserial not null - + - + auto-incremented - + creation_time - + - + timestamptz - + signedmarkrevocationentry_99c39721:w->signedmarkrevocationlist_c5d968fb:e - - - - - - - - + + + + + + + + fk5ivlhvs3121yx2li5tqh54u4 - - - transaction_d50389d4 - - - public.Transaction + + + tmchcrl_d282355 + + + public.TmchCrl - - + + [table] - + + certificate_revocations + + + + + text not null + + + update_timestamp + + + + + timestamptz not null + + + url + + + + + text not null + + + + + transaction_d50389d4 + + + public.Transaction + + + + [table] + + id - + - + bigserial not null - + - + auto-incremented - + contents - + - + bytea - + @@ -12531,6 +12587,54 @@ td.section {

 

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
public.ServerSecret [table] +
secretuuid not null
Primary Key
ServerSecret_pkey[primary key]
secret
Indexes
ServerSecret_pkey[unique index]
secretascending
+

 

@@ -13129,6 +13233,84 @@ td.section {
public.SignedMarkRevocationEntry [table]

 

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
public.TmchCrl [table] +
certificate_revocationstext not null
update_timestamptimestamptz not null
urltext not null
Primary Key
TmchCrl_pkey[primary key]
certificate_revocations
update_timestamp
url
Indexes
TmchCrl_pkey[unique index]
certificate_revocationsascending
update_timestampascending
urlascending
+

 

diff --git a/db/src/main/resources/sql/flyway.txt b/db/src/main/resources/sql/flyway.txt index 6e1602c67..cd6151565 100644 --- a/db/src/main/resources/sql/flyway.txt +++ b/db/src/main/resources/sql/flyway.txt @@ -70,3 +70,4 @@ V69__change_primary_key_and_add_history_table_for_delegation_signer.sql V70__signed_mark_revocation_list.sql V71__create_kms_secret.sql V72__add_missing_foreign_keys.sql +V73__singleton_entities.sql diff --git a/db/src/main/resources/sql/flyway/V73__singleton_entities.sql b/db/src/main/resources/sql/flyway/V73__singleton_entities.sql new file mode 100644 index 000000000..08e735770 --- /dev/null +++ b/db/src/main/resources/sql/flyway/V73__singleton_entities.sql @@ -0,0 +1,25 @@ +-- Copyright 2020 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 "ServerSecret" ( + secret uuid NOT NULL, + PRIMARY KEY (secret) +); + +CREATE TABLE "TmchCrl" ( + certificate_revocations text NOT NULL, + update_timestamp timestamptz NOT NULL, + url text NOT NULL, + PRIMARY KEY (certificate_revocations, update_timestamp, url) +); diff --git a/db/src/main/resources/sql/schema/db-schema.sql.generated b/db/src/main/resources/sql/schema/db-schema.sql.generated index 1bdd0556d..25f6a3c0e 100644 --- a/db/src/main/resources/sql/schema/db-schema.sql.generated +++ b/db/src/main/resources/sql/schema/db-schema.sql.generated @@ -626,6 +626,11 @@ primary key (revision_id) ); + create table "ServerSecret" ( + secret uuid not null, + primary key (secret) + ); + create table "SignedMarkRevocationEntry" ( revision_id int8 not null, revocation_time timestamptz not null, @@ -692,6 +697,13 @@ primary key (tld_name) ); + create table "TmchCrl" ( + certificate_revocations text not null, + update_timestamp timestamptz not null, + url text not null, + primary key (certificate_revocations, update_timestamp, url) + ); + create table "Transaction" ( id bigserial not null, contents bytea, diff --git a/db/src/main/resources/sql/schema/nomulus.golden.sql b/db/src/main/resources/sql/schema/nomulus.golden.sql index e95350d55..ae0637542 100644 --- a/db/src/main/resources/sql/schema/nomulus.golden.sql +++ b/db/src/main/resources/sql/schema/nomulus.golden.sql @@ -899,6 +899,15 @@ CREATE SEQUENCE public."SafeBrowsingThreat_id_seq" ALTER SEQUENCE public."SafeBrowsingThreat_id_seq" OWNED BY public."Spec11ThreatMatch".id; +-- +-- Name: ServerSecret; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public."ServerSecret" ( + secret uuid NOT NULL +); + + -- -- Name: SignedMarkRevocationEntry; Type: TABLE; Schema: public; Owner: - -- @@ -985,6 +994,17 @@ CREATE TABLE public."Tld" ( ); +-- +-- Name: TmchCrl; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public."TmchCrl" ( + certificate_revocations text NOT NULL, + update_timestamp timestamp with time zone NOT NULL, + url text NOT NULL +); + + -- -- Name: Transaction; Type: TABLE; Schema: public; Owner: - -- @@ -1302,6 +1322,14 @@ ALTER TABLE ONLY public."Spec11ThreatMatch" ADD CONSTRAINT "SafeBrowsingThreat_pkey" PRIMARY KEY (id); +-- +-- Name: ServerSecret ServerSecret_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public."ServerSecret" + ADD CONSTRAINT "ServerSecret_pkey" PRIMARY KEY (secret); + + -- -- Name: SignedMarkRevocationEntry SignedMarkRevocationEntry_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -1326,6 +1354,14 @@ ALTER TABLE ONLY public."Tld" ADD CONSTRAINT "Tld_pkey" PRIMARY KEY (tld_name); +-- +-- Name: TmchCrl TmchCrl_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public."TmchCrl" + ADD CONSTRAINT "TmchCrl_pkey" PRIMARY KEY (certificate_revocations, update_timestamp, url); + + -- -- Name: Transaction Transaction_pkey; Type: CONSTRAINT; Schema: public; Owner: - --
public.Transaction [table]