diff --git a/Elsa.sln b/Elsa.sln index 2fc20c983..c16d086ad 100644 --- a/Elsa.sln +++ b/Elsa.sln @@ -353,6 +353,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Diagnostics.OpenTeleme EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Diagnostics.OpenTelemetry.IntegrationTests", "test\integration\Elsa.Diagnostics.OpenTelemetry.IntegrationTests\Elsa.Diagnostics.OpenTelemetry.IntegrationTests.csproj", "{B6A62D42-D9AA-47BE-BCBC-FCB866D346F0}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "secrets", "secrets", "{D3E5E9EF-DE26-41BF-A239-3241B5B5FD89}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1549,12 +1551,13 @@ Global {195FD304-EC3F-4350-93C8-AFE80C4E6896} = {78FD90A4-90A5-445F-97F2-74BA835AFA5D} {D8739449-22DC-42D4-85A4-4BA547B0B458} = {18453B51-25EB-4317-A4B3-B10518252E92} {93E9213A-694D-4AB4-870E-05E44F793133} = {1B8D5897-902E-4632-8698-E89CAF3DDF54} - {09B4B78B-FE02-44E2-8667-E182AF921C54} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79} {7D905CEC-B30B-4C99-B5F7-3052D33EC8E9} = {18453B51-25EB-4317-A4B3-B10518252E92} {81CFD2E0-2E5E-4810-ADB8-A08301199166} = {18453B51-25EB-4317-A4B3-B10518252E92} {8F4AD54E-8586-4D8C-82E6-69218DD4280F} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79} {60F844CC-6DA6-45EB-BA5E-B480B4E77624} = {18453B51-25EB-4317-A4B3-B10518252E92} {B6A62D42-D9AA-47BE-BCBC-FCB866D346F0} = {1B8D5897-902E-4632-8698-E89CAF3DDF54} + {D3E5E9EF-DE26-41BF-A239-3241B5B5FD89} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79} + {09B4B78B-FE02-44E2-8667-E182AF921C54} = {D3E5E9EF-DE26-41BF-A239-3241B5B5FD89} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D4B5CEAA-7D70-4FCB-A68E-B03FBE5E0E5E} diff --git a/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj b/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj index cbb44d3ba..725e606b5 100644 --- a/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj +++ b/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj @@ -19,6 +19,7 @@ + diff --git a/src/apps/Elsa.ModularServer.Web/appsettings.json b/src/apps/Elsa.ModularServer.Web/appsettings.json index d7e3f4a0f..e14366c4f 100644 --- a/src/apps/Elsa.ModularServer.Web/appsettings.json +++ b/src/apps/Elsa.ModularServer.Web/appsettings.json @@ -55,6 +55,9 @@ "ConsoleLogs": {}, "OpenTelemetry": {}, "Alterations": {}, + "Secrets": { + "EncryptionKey": "Q0hBTkdFX01FX1RPX0FfU0VDVVJFX1JBTkRPTV9LRVk=" + }, "SqliteAlterationsPersistence": { "ConnectionString": "Data Source=elsa_workflows.db;Cache=Shared" }, diff --git a/src/modules/Elsa.Secrets/Contracts/ISecretProvider.cs b/src/modules/Elsa.Secrets/Contracts/ISecretProvider.cs deleted file mode 100644 index 9e9d9a700..000000000 --- a/src/modules/Elsa.Secrets/Contracts/ISecretProvider.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Elsa.Secrets.Contracts; - -/// -/// Backward-compatible adapter surface for existing extension consumers. -/// -public interface ISecretProvider -{ - Task GetSecretAsync(string name, CancellationToken cancellationToken = default); -} diff --git a/src/modules/Elsa.Secrets/Extensions/ServiceCollectionExtensions.cs b/src/modules/Elsa.Secrets/Extensions/ServiceCollectionExtensions.cs index e72626846..5762d0977 100644 --- a/src/modules/Elsa.Secrets/Extensions/ServiceCollectionExtensions.cs +++ b/src/modules/Elsa.Secrets/Extensions/ServiceCollectionExtensions.cs @@ -20,7 +20,6 @@ public static class ServiceCollectionExtensions services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); - services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddEnumerable(ServiceDescriptor.Singleton()); diff --git a/src/modules/Elsa.Secrets/Services/SecretProviderAdapter.cs b/src/modules/Elsa.Secrets/Services/SecretProviderAdapter.cs deleted file mode 100644 index f6f592d3b..000000000 --- a/src/modules/Elsa.Secrets/Services/SecretProviderAdapter.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Security.Cryptography; -using Microsoft.Extensions.Logging; - -namespace Elsa.Secrets.Services; - -public class SecretProviderAdapter(ISecretResolver resolver, ILogger? logger = null) : ISecretProvider -{ - public async Task GetSecretAsync(string name, CancellationToken cancellationToken = default) - { - try - { - return await resolver.ResolveAsync(name, cancellationToken); - } - catch (KeyNotFoundException) - { - return null; - } - catch (InvalidOperationException e) - { - logger?.LogWarning(e, "Secret '{SecretName}' is unavailable.", name); - return null; - } - catch (CryptographicException e) - { - logger?.LogWarning(e, "Secret '{SecretName}' could not be decrypted.", name); - return null; - } - catch (FormatException e) - { - logger?.LogWarning(e, "Secret '{SecretName}' has a malformed encrypted payload.", name); - return null; - } - } -} diff --git a/test/unit/Elsa.Secrets.UnitTests/SecretResolverTests.cs b/test/unit/Elsa.Secrets.UnitTests/SecretResolverTests.cs index ae7d88ecd..d36801614 100644 --- a/test/unit/Elsa.Secrets.UnitTests/SecretResolverTests.cs +++ b/test/unit/Elsa.Secrets.UnitTests/SecretResolverTests.cs @@ -25,51 +25,7 @@ public class SecretResolverTests await Assert.ThrowsAsync(() => _fixture.Resolver.ResolveAsync(new SecretReference("api:key", SecretTypeNames.RsaKey))); } - - [Fact] - public async Task ProviderAdapter_ReturnsNull_WhenSecretIsUnavailable() - { - await _fixture.Manager.CreateAsync(new CreateSecretRequest { Name = "api:key", Value = "one" }); - await _fixture.Manager.RevokeAsync("api:key"); - var provider = new Elsa.Secrets.Services.SecretProviderAdapter(_fixture.Resolver); - - var value = await provider.GetSecretAsync("api:key"); - - Assert.Null(value); - } - - [Fact] - public async Task ProviderAdapter_ReturnsNull_WhenEncryptedPayloadCannotBeDecrypted() - { - var secret = await _fixture.Manager.CreateAsync(new CreateSecretRequest { Name = "api:key", Value = "one" }); - var version = secret.Versions.Single(); - version.Payload.Metadata["protectedValue"] = string.Join( - ".", - "v1", - Convert.ToBase64String(new byte[12]), - Convert.ToBase64String(new byte[16]), - Convert.ToBase64String(new byte[1])); - await _fixture.Repository.SaveAsync(secret); - var provider = new Elsa.Secrets.Services.SecretProviderAdapter(_fixture.Resolver); - - var value = await provider.GetSecretAsync("api:key"); - - Assert.Null(value); - } - - [Fact] - public async Task ProviderAdapter_ReturnsNull_WhenEncryptedPayloadIsMalformed() - { - var secret = await _fixture.Manager.CreateAsync(new CreateSecretRequest { Name = "api:key", Value = "one" }); - secret.Versions.Single().Payload.Metadata["protectedValue"] = "v1.not-base64.not-base64.not-base64"; - await _fixture.Repository.SaveAsync(secret); - var provider = new Elsa.Secrets.Services.SecretProviderAdapter(_fixture.Resolver); - - var value = await provider.GetSecretAsync("api:key"); - - Assert.Null(value); - } - + [Fact] public async Task TestAsync_ReturnsFailedResult_WhenEncryptedPayloadIsMalformed() {