diff --git a/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/AwsSecretManagerPersistence.kt b/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/AwsSecretManagerPersistence.kt index 78a2b3e8d61..2230bdd0f38 100644 --- a/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/AwsSecretManagerPersistence.kt +++ b/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/AwsSecretManagerPersistence.kt @@ -118,7 +118,7 @@ class AwsSecretManagerPersistence(private val awsClient: AwsClient, private val * * @param coordinate SecretCoordinate to delete. */ - private fun deleteSecret(coordinate: SecretCoordinate) { + override fun delete(coordinate: SecretCoordinate) { awsClient.client.deleteSecret( DeleteSecretRequest() .withSecretId(coordinate.coordinateBase) diff --git a/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/GoogleSecretManagerPersistence.kt b/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/GoogleSecretManagerPersistence.kt index 60bb62312f7..d638e6d56be 100644 --- a/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/GoogleSecretManagerPersistence.kt +++ b/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/GoogleSecretManagerPersistence.kt @@ -105,6 +105,13 @@ class GoogleSecretManagerPersistence( client.addSecretVersion(name, secretPayload) } } + + override fun delete(coordinate: SecretCoordinate) { + googleSecretManagerServiceClient.createClient().use { client -> + val secretName = SecretName.of(gcpProjectId, coordinate.fullCoordinate) + client.deleteSecret(secretName) + } + } } @Singleton diff --git a/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/LocalTestingSecretPersistence.kt b/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/LocalTestingSecretPersistence.kt index 83c69b9fdc8..b694da88faa 100644 --- a/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/LocalTestingSecretPersistence.kt +++ b/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/LocalTestingSecretPersistence.kt @@ -60,4 +60,8 @@ open class LocalTestingSecretPersistence( coordinate.fullCoordinate, ).execute() } + + override fun delete(coordinate: SecretCoordinate) { + return + } } diff --git a/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/NoOpSecretPersistence.kt b/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/NoOpSecretPersistence.kt index a66a9bdbd01..7b6ea560bf9 100644 --- a/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/NoOpSecretPersistence.kt +++ b/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/NoOpSecretPersistence.kt @@ -19,4 +19,8 @@ class NoOpSecretPersistence : SecretPersistence { ) { return } + + override fun delete(coordinate: SecretCoordinate) { + return + } } diff --git a/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/RuntimeSecretPersistence.kt b/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/RuntimeSecretPersistence.kt index 484e5cc7da1..4313cd502ae 100644 --- a/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/RuntimeSecretPersistence.kt +++ b/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/RuntimeSecretPersistence.kt @@ -77,6 +77,10 @@ class RuntimeSecretPersistence(private val secretPersistenceConfig: SecretPersis secretPersistence.write(coordinate, payload) } + override fun delete(coordinate: SecretCoordinate) { + return + } + private fun buildAwsSecretManager(configuration: Map): AwsSecretManagerPersistence { // We default to ACCESS_KEY auth val authType = configuration["auth_type"]?.uppercase() ?: AwsAuthType.ACCESS_KEY.value diff --git a/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/SecretPersistence.kt b/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/SecretPersistence.kt index c5f3f3719e5..d82f6e30962 100644 --- a/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/SecretPersistence.kt +++ b/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/SecretPersistence.kt @@ -46,4 +46,6 @@ interface SecretPersistence : ReadOnlySecretPersistence { // Default implementation does not support expiry. write(coordinate, payload) } + + fun delete(coordinate: SecretCoordinate) } diff --git a/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/VaultSecretPersistence.kt b/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/VaultSecretPersistence.kt index 41ff319e5b5..16a764010fb 100644 --- a/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/VaultSecretPersistence.kt +++ b/airbyte-config/config-secrets/src/main/kotlin/secrets/persistence/VaultSecretPersistence.kt @@ -57,6 +57,10 @@ class VaultSecretPersistence( } } + override fun delete(coordinate: SecretCoordinate) { + return + } + companion object { private const val SECRET_KEY = "value" } diff --git a/airbyte-config/config-secrets/src/test/kotlin/secrets/persistence/AwsSecretManagerPersistenceTest.kt b/airbyte-config/config-secrets/src/test/kotlin/secrets/persistence/AwsSecretManagerPersistenceTest.kt index 970e92b6959..579c1e53640 100644 --- a/airbyte-config/config-secrets/src/test/kotlin/secrets/persistence/AwsSecretManagerPersistenceTest.kt +++ b/airbyte-config/config-secrets/src/test/kotlin/secrets/persistence/AwsSecretManagerPersistenceTest.kt @@ -7,6 +7,8 @@ package io.airbyte.config.secrets.persistence import com.amazonaws.secretsmanager.caching.SecretCache import com.amazonaws.services.secretsmanager.AWSSecretsManager import com.amazonaws.services.secretsmanager.model.CreateSecretResult +import com.amazonaws.services.secretsmanager.model.DeleteSecretRequest +import com.amazonaws.services.secretsmanager.model.DeleteSecretResult import com.amazonaws.services.secretsmanager.model.DescribeSecretResult import com.amazonaws.services.secretsmanager.model.GetSecretValueResult import com.amazonaws.services.secretsmanager.model.ResourceNotFoundException @@ -174,4 +176,26 @@ class AwsSecretManagerPersistenceTest { verify { mockAwsClient.updateSecret(any()) } } + + @Test + fun `test deleting a secret via the client deletes the secret`() { + val secret = "secret value" + val coordinate = SecretCoordinate.fromFullCoordinate("secret_coordinate_v1") + val mockClient: AwsClient = mockk() + val mockCache: AwsCache = mockk() + val mockAwsCache: SecretCache = mockk() + val mockAwsClient: AWSSecretsManager = mockk() + val persistence = AwsSecretManagerPersistence(mockClient, mockCache) + every { mockAwsCache.getSecretString(any()) } returns secret + every { mockAwsClient.deleteSecret(any()) } returns mockk() + every { mockCache.cache } returns mockAwsCache + every { mockClient.client } returns mockAwsClient + every { mockClient.serializedConfig } returns null + every { mockClient.kmsKeyArn } returns null + every { mockClient.tags } returns emptyMap() + + persistence.delete(coordinate) + + verify { mockAwsClient.deleteSecret(any()) } + } } diff --git a/airbyte-config/config-secrets/src/test/kotlin/secrets/persistence/GoogleSecretManagerPersistenceTest.kt b/airbyte-config/config-secrets/src/test/kotlin/secrets/persistence/GoogleSecretManagerPersistenceTest.kt index 81cec8d7296..5209ce7b812 100644 --- a/airbyte-config/config-secrets/src/test/kotlin/secrets/persistence/GoogleSecretManagerPersistenceTest.kt +++ b/airbyte-config/config-secrets/src/test/kotlin/secrets/persistence/GoogleSecretManagerPersistenceTest.kt @@ -18,7 +18,9 @@ import com.google.protobuf.ByteString import io.airbyte.config.secrets.SecretCoordinate import io.airbyte.config.secrets.persistence.GoogleSecretManagerPersistence.Companion.replicationPolicy import io.grpc.Status +import io.mockk.Runs import io.mockk.every +import io.mockk.just import io.mockk.mockk import io.mockk.verify import org.junit.jupiter.api.Assertions @@ -164,4 +166,26 @@ class GoogleSecretManagerPersistenceTest { verify { mockGoogleClient.addSecretVersion(any(), any()) } } + + @Test + fun `test deleting a secret via the client deletes the secret`() { + val secret = "secret value" + val projectId = "test" + val coordinate = SecretCoordinate.fromFullCoordinate("secret_coordinate_v1") + val mockClient: GoogleSecretManagerServiceClient = mockk() + val mockGoogleClient: SecretManagerServiceClient = mockk() + val mockResponse: AccessSecretVersionResponse = mockk() + val mockPayload: SecretPayload = mockk() + val persistence = GoogleSecretManagerPersistence(projectId, mockClient) + + every { mockPayload.data } returns ByteString.copyFromUtf8(secret) + every { mockResponse.payload } returns mockPayload + every { mockClient.createClient() } returns mockGoogleClient + every { mockGoogleClient.deleteSecret(ofType(SecretName::class)) } just Runs + every { mockGoogleClient.close() } returns Unit + + persistence.delete(coordinate) + + verify { mockGoogleClient.deleteSecret(any()) } + } } diff --git a/airbyte-config/config-secrets/src/testFixtures/kotlin/secrets/MemorySecretPersistence.kt b/airbyte-config/config-secrets/src/testFixtures/kotlin/secrets/MemorySecretPersistence.kt index b5712e4770f..96e43b92b74 100644 --- a/airbyte-config/config-secrets/src/testFixtures/kotlin/secrets/MemorySecretPersistence.kt +++ b/airbyte-config/config-secrets/src/testFixtures/kotlin/secrets/MemorySecretPersistence.kt @@ -23,6 +23,10 @@ class MemorySecretPersistence : SecretPersistence { secretMap[coordinate] = payload } + override fun delete(coordinate: SecretCoordinate) { + secretMap.remove(coordinate) + } + val map: Map get() = secretMap.toMutableMap() }