From e40bc8b3130640199d71e6b9fb2211b842b08353 Mon Sep 17 00:00:00 2001 From: Weimin Yu Date: Mon, 9 Feb 2026 21:07:45 +0000 Subject: [PATCH] Simplify SQL credential store The current SQL credential store was designed to support automatic password rotation without any disruption to the applications. For that goal, the credentials are stored with one level of indirection, and the secret name of the actual credential data may change automatically. The automatic password rotation feature has been dropped. In the meantime, the need arises that we use sidecar SQL proxy to get around the Enterprise Plus edition's post-maintenance reconnection failures by the socket factory library. This is hampered by the indirection in storage. This PR removes the indirection. This change is transparent to the rest of the code base. We will manually populate the secret manager with the new secrets in all environments after submissiion of this PR. --- .../secretmanager/SqlCredentialStore.java | 76 +++---------------- .../secretmanager/SqlCredentialStoreTest.java | 30 +++----- 2 files changed, 20 insertions(+), 86 deletions(-) diff --git a/core/src/main/java/google/registry/privileges/secretmanager/SqlCredentialStore.java b/core/src/main/java/google/registry/privileges/secretmanager/SqlCredentialStore.java index 4e7ad14422d..3a71b00e5cb 100644 --- a/core/src/main/java/google/registry/privileges/secretmanager/SqlCredentialStore.java +++ b/core/src/main/java/google/registry/privileges/secretmanager/SqlCredentialStore.java @@ -14,28 +14,12 @@ package google.registry.privileges.secretmanager; -import com.google.cloud.secretmanager.v1.SecretVersionName; import google.registry.config.RegistryConfig.Config; -import google.registry.privileges.secretmanager.SecretManagerClient.NoSuchSecretResourceException; import jakarta.inject.Inject; import java.util.Optional; /** * Storage of SQL users' login credentials, backed by Cloud Secret Manager. - * - *

A user's credential is stored with one level of indirection using two secret IDs: Each version - * of the credential data is stored as follows: its secret ID is determined by {@link - * #getCredentialDataSecretId(SqlUser, String dbInstance)}, and the value of each version is a - * {@link SqlCredential}, serialized using {@link SqlCredential#toFormattedString}. The 'live' - * version of the credential is saved under the 'live pointer' secret explained below. - * - *

The pointer to the 'live' version of the credential data is stored as follows: its secret ID - * is determined by {@link #getLiveLabelSecretId(SqlUser, String dbInstance)}; and the value of each - * version is a {@link SecretVersionName} in String form, pointing to a version of the credential - * data. Only the 'latest' version of this secret should be used. It is guaranteed to be valid. - * - *

The indirection in credential storage makes it easy to handle failures in the credential - * change process. */ public class SqlCredentialStore { private final SecretManagerClient csmClient; @@ -49,61 +33,19 @@ public class SqlCredentialStore { } public SqlCredential getCredential(SqlUser user) { - SecretVersionName credentialName = getLiveCredentialSecretVersion(user); - return SqlCredential.fromFormattedString( - csmClient.getSecretData( - credentialName.getSecret(), Optional.of(credentialName.getSecretVersion()))); + var secretId = getSecretIdForUserPassword(user); + var secretData = csmClient.getSecretData(secretId, Optional.empty()); + return SqlCredential.fromFormattedString(secretData); } public void createOrUpdateCredential(SqlUser user, String password) { - SecretVersionName dataName = saveCredentialData(user, password); - saveLiveLabel(user, dataName); + var secretId = getSecretIdForUserPassword(user); + csmClient.createSecretIfAbsent(secretId); + csmClient.addSecretVersion( + secretId, SqlCredential.create(user.geUserName(), password).toFormattedString()); } - public void deleteCredential(SqlUser user) { - try { - csmClient.deleteSecret(getCredentialDataSecretId(user, dbInstance)); - } catch (NoSuchSecretResourceException e) { - // ok - } - try { - csmClient.deleteSecret(getLiveLabelSecretId(user, dbInstance)); - } catch (NoSuchSecretResourceException e) { - // ok. - } - } - - private SecretVersionName saveCredentialData(SqlUser user, String password) { - String credentialDataSecretId = getCredentialDataSecretId(user, dbInstance); - csmClient.createSecretIfAbsent(credentialDataSecretId); - String credentialVersion = - csmClient.addSecretVersion( - credentialDataSecretId, - SqlCredential.create(createDatabaseLoginName(user), password).toFormattedString()); - return SecretVersionName.of(csmClient.getProject(), credentialDataSecretId, credentialVersion); - } - - private void saveLiveLabel(SqlUser user, SecretVersionName dataVersionName) { - String liveLabelSecretId = getLiveLabelSecretId(user, dbInstance); - csmClient.createSecretIfAbsent(liveLabelSecretId); - csmClient.addSecretVersion(liveLabelSecretId, dataVersionName.toString()); - } - - private SecretVersionName getLiveCredentialSecretVersion(SqlUser user) { - return SecretVersionName.parse( - csmClient.getSecretData(getLiveLabelSecretId(user, dbInstance), Optional.empty())); - } - - private static String getLiveLabelSecretId(SqlUser user, String dbInstance) { - return String.format("sql-cred-live-label-%s-%s", user.geUserName(), dbInstance); - } - - private static String getCredentialDataSecretId(SqlUser user, String dbInstance) { - return String.format("sql-cred-data-%s-%s", user.geUserName(), dbInstance); - } - - // WIP: when b/170230882 is complete, login will be versioned. - private static String createDatabaseLoginName(SqlUser user) { - return user.geUserName(); + private String getSecretIdForUserPassword(SqlUser user) { + return String.format("sql-password-for-%s-on-%s", user.geUserName(), this.dbInstance); } } diff --git a/core/src/test/java/google/registry/privileges/secretmanager/SqlCredentialStoreTest.java b/core/src/test/java/google/registry/privileges/secretmanager/SqlCredentialStoreTest.java index 5eba4dcd4f5..e752d0ee130 100644 --- a/core/src/test/java/google/registry/privileges/secretmanager/SqlCredentialStoreTest.java +++ b/core/src/test/java/google/registry/privileges/secretmanager/SqlCredentialStoreTest.java @@ -16,7 +16,6 @@ import static com.google.common.truth.Truth.assertThat; -import com.google.cloud.secretmanager.v1.SecretVersionName; import google.registry.privileges.secretmanager.SqlUser.RobotId; import google.registry.privileges.secretmanager.SqlUser.RobotUser; import java.util.Optional; @@ -32,32 +31,25 @@ public class SqlCredentialStoreTest { @Test void createSecret() { credentialStore.createOrUpdateCredential(user, "password"); - assertThat(client.secretExists("sql-cred-live-label-nomulus-db")).isTrue(); - assertThat( - SecretVersionName.parse( - client.getSecretData("sql-cred-live-label-nomulus-db", Optional.empty())) - .getSecret()) - .isEqualTo("sql-cred-data-nomulus-db"); - assertThat(client.secretExists("sql-cred-data-nomulus-db")).isTrue(); - assertThat(client.getSecretData("sql-cred-data-nomulus-db", Optional.empty())) + assertThat(client.secretExists("sql-password-for-nomulus-on-db")).isTrue(); + assertThat(client.getSecretData("sql-password-for-nomulus-on-db", Optional.empty())) .isEqualTo("nomulus password"); } @Test - void getCredential() { + void updateSecret() { credentialStore.createOrUpdateCredential(user, "password"); - SqlCredential credential = credentialStore.getCredential(user); - assertThat(credential.login()).isEqualTo("nomulus"); - assertThat(credential.password()).isEqualTo("password"); + credentialStore.createOrUpdateCredential(user, "new-password"); + assertThat(client.secretExists("sql-password-for-nomulus-on-db")).isTrue(); + assertThat(client.getSecretData("sql-password-for-nomulus-on-db", Optional.empty())) + .isEqualTo("nomulus new-password"); } @Test - void deleteCredential() { + void getCredential() { credentialStore.createOrUpdateCredential(user, "password"); - assertThat(client.secretExists("sql-cred-live-label-nomulus-db")).isTrue(); - assertThat(client.secretExists("sql-cred-data-nomulus-db")).isTrue(); - credentialStore.deleteCredential(user); - assertThat(client.secretExists("sql-cred-live-label-nomulus-db")).isFalse(); - assertThat(client.secretExists("sql-cred-data-nomulus-db")).isFalse(); + SqlCredential credential = credentialStore.getCredential(user); + assertThat(credential.login()).isEqualTo("nomulus"); + assertThat(credential.password()).isEqualTo("password"); } }