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/flows/domain/DomainCheckFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainCheckFlowTest.java index 295903ff385..ef3a6e514f1 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainCheckFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainCheckFlowTest.java @@ -1151,7 +1151,6 @@ void testFeeExtension_invalid_multipleCurrencies_std_v1() { .marshalsToXml(); } - @Test void testSuccess_eapFeeCheck_std_v1() throws Exception { runEapFeeCheckTestWithXmlInputOutput( 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"); } }