Skip to content

Commit

Permalink
Allow customization of GetSecretValueRequest (#77)
Browse files Browse the repository at this point in the history
  • Loading branch information
avalara-stephen-hickey authored and Kralizek committed Nov 8, 2022
1 parent fba2e43 commit 5a81fc3
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 6 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,21 @@ builder.AddSecretsManager(configurator: options =>

You can see an example [here](/samples/Sample5).

### Customizing the GetSecretValueRequest

Sometimes we may want to request a different version of the secret or be required to specify the version stage.

In this case, you can provide a function that updates the `GetSecretValueRequest` that is used to retrieve the secret from AWS before the request is sent.

As an example, here we are adding a `VersionStage` of `"AWSCURRENT"` to every `GetSecretValueRequest`.

```csharp
builder.AddSecretsManager(configurator: options =>
{
options.ConfigureSecretValueRequest = (request, context) => request.VersionStage = "AWSCURRENT";
});
```

### Customizing the AmazonSecretsManagerConfig, for example to use localstack

There are some situations where you might want to customize how the AmazonSecretsManagerConfig is built, for example when you want to use
Expand Down
19 changes: 17 additions & 2 deletions SecretsManager.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30711.63
# Visual Studio Version 17
VisualStudioVersion = 17.3.32929.385
MinimumVisualStudioVersion = 15.0.26124.0
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{84C43065-ED9C-45C7-84B3-AAB386BB79D1}"
EndProject
Expand All @@ -27,6 +27,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample6", "samples\Sample6\
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleWeb", "samples\SampleWeb\SampleWeb.csproj", "{713BFD7D-75C3-452E-A424-437348D6C5FF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample7", "samples\Sample7\Sample7.csproj", "{3A0AE313-30F6-4C0F-AEB8-D67E9401BA34}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -145,6 +147,18 @@ Global
{713BFD7D-75C3-452E-A424-437348D6C5FF}.Release|x64.Build.0 = Release|Any CPU
{713BFD7D-75C3-452E-A424-437348D6C5FF}.Release|x86.ActiveCfg = Release|Any CPU
{713BFD7D-75C3-452E-A424-437348D6C5FF}.Release|x86.Build.0 = Release|Any CPU
{3A0AE313-30F6-4C0F-AEB8-D67E9401BA34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3A0AE313-30F6-4C0F-AEB8-D67E9401BA34}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3A0AE313-30F6-4C0F-AEB8-D67E9401BA34}.Debug|x64.ActiveCfg = Debug|Any CPU
{3A0AE313-30F6-4C0F-AEB8-D67E9401BA34}.Debug|x64.Build.0 = Debug|Any CPU
{3A0AE313-30F6-4C0F-AEB8-D67E9401BA34}.Debug|x86.ActiveCfg = Debug|Any CPU
{3A0AE313-30F6-4C0F-AEB8-D67E9401BA34}.Debug|x86.Build.0 = Debug|Any CPU
{3A0AE313-30F6-4C0F-AEB8-D67E9401BA34}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3A0AE313-30F6-4C0F-AEB8-D67E9401BA34}.Release|Any CPU.Build.0 = Release|Any CPU
{3A0AE313-30F6-4C0F-AEB8-D67E9401BA34}.Release|x64.ActiveCfg = Release|Any CPU
{3A0AE313-30F6-4C0F-AEB8-D67E9401BA34}.Release|x64.Build.0 = Release|Any CPU
{3A0AE313-30F6-4C0F-AEB8-D67E9401BA34}.Release|x86.ActiveCfg = Release|Any CPU
{3A0AE313-30F6-4C0F-AEB8-D67E9401BA34}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -159,6 +173,7 @@ Global
{7756FF98-349B-4E15-82D6-523E8AA59D72} = {101F77F3-9063-4287-AB1D-20DD19F171A6}
{0C856672-08BA-4E06-B69E-0C4526FB8F54} = {101F77F3-9063-4287-AB1D-20DD19F171A6}
{713BFD7D-75C3-452E-A424-437348D6C5FF} = {101F77F3-9063-4287-AB1D-20DD19F171A6}
{3A0AE313-30F6-4C0F-AEB8-D67E9401BA34} = {101F77F3-9063-4287-AB1D-20DD19F171A6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A1CF3BDB-61B4-458C-960A-25CBC7E5EB83}
Expand Down
28 changes: 28 additions & 0 deletions samples/Sample7/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using Microsoft.Extensions.Configuration;

namespace Sample7
{
internal class Program
{
private static void Main(string[] _)
{
var builder = new ConfigurationBuilder();

/*
Uses default credentials
Uses default region
Uses options to customize the GetSecretValueRequest (e.g. specify VersionStage)
*/

builder.AddSecretsManager(configurator: options =>
{
options.ConfigureSecretValueRequest = (request, context) => request.VersionStage = "AWSCURRENT";
});

var configuration = builder.Build();

Console.WriteLine("Hello World!");
}
}
}
13 changes: 13 additions & 0 deletions samples/Sample7/Sample7.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<ProjectReference Include="..\..\src\Kralizek.Extensions.Configuration.AWSSecretsManager\Kralizek.Extensions.Configuration.AWSSecretsManager.csproj" />
</ItemGroup>

<PropertyGroup>
<OutputType>Exe</OutputType>
<IsPackable>false</IsPackable>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using Amazon.SecretsManager.Model;

namespace Kralizek.Extensions.Configuration.Internal
{
/// <summary>
/// A secret context when making a request to get a secret value.
/// </summary>
public class SecretValueContext
{
public SecretValueContext(SecretListEntry secret)
{
_ = secret ?? throw new ArgumentNullException(nameof(secret));

Name = secret.Name;
VersionsToStages = secret.SecretVersionsToStages;
}

/// <summary>
/// The secret name.
/// </summary>
public string Name { get; private set; }

/// <summary>
/// A list of all the secret's currently assigned SecretVersionStage staging levels and SecretVersionId attached to each one.
/// </summary>
public Dictionary<string, List<string>> VersionsToStages { get; private set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,9 @@ private async Task<IReadOnlyList<SecretListEntry>> FetchAllSecretsAsync(Cancella
{
if (!Options.SecretFilter(secret)) continue;

var secretValue = await Client.GetSecretValueAsync(new GetSecretValueRequest { SecretId = secret.ARN }, cancellationToken).ConfigureAwait(false);
var request = new GetSecretValueRequest { SecretId = secret.ARN };
Options.ConfigureSecretValueRequest?.Invoke(request, new SecretValueContext(secret));
var secretValue = await Client.GetSecretValueAsync(request, cancellationToken).ConfigureAwait(false);

var secretEntry = Options.AcceptedSecretArns.Count > 0
? new SecretListEntry
Expand All @@ -214,7 +216,7 @@ private async Task<IReadOnlyList<SecretListEntry>> FetchAllSecretsAsync(Cancella
if (secretString is null)
continue;

if (TryParseJson(secretString, out var jToken))
if (TryParseJson(secretString, out var jToken) && jToken is not null)
{
// [MaybeNullWhen(false)] attribute is available in .net standard since version 2.1
var values = ExtractValues(jToken, secretName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using Amazon.SecretsManager;
using Amazon.SecretsManager.Model;


namespace Kralizek.Extensions.Configuration.Internal
{
public class SecretsManagerConfigurationProviderOptions
Expand Down Expand Up @@ -62,13 +61,23 @@ public class SecretsManagerConfigurationProviderOptions
/// </example>
public Func<SecretListEntry, string, string> KeyGenerator { get; set; } = (secret, key) => key;

/// <summary>
/// Defines a function that can be used to customize the <see cref="GetSecretValueRequest"/> before it is sent.
/// </summary>
/// <example>
/// <code>
/// ConfigureSecretValueRequest = (request, context) => request.VersionStage = "AWSCURRENT";
/// </code>
/// </example>
public Action<GetSecretValueRequest, SecretValueContext> ConfigureSecretValueRequest { get; set; } = (_, _) => { };

/// <summary>
/// A function that can be used to configure the <see cref="AmazonSecretsManagerClient"/>
/// that's injected into the client.
/// </summary>
/// <example>
/// <code>
/// ConfigureSecretsManagerConfig = config => config.Timeout = TimeSpan.FromSeconds(5)
/// ConfigureSecretsManagerConfig = config => config.Timeout = TimeSpan.FromSeconds(5);
/// </code>
/// </example>
public Action<AmazonSecretsManagerConfig> ConfigureSecretsManagerConfig { get; set; } = _ => { };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,20 @@ public void Keys_should_be_case_insensitive([Frozen] SecretListEntry testEntry,
Assert.That(sut.Get(testEntry.Name.ToUpper()), Is.EqualTo(getSecretValueResponse.SecretString));
}

[Test, CustomAutoData]
public void Get_secret_value_request_can_be_customized_via_options([Frozen] SecretListEntry testEntry, ListSecretsResponse listSecretsResponse, GetSecretValueResponse getSecretValueResponse, string secretVersionStage, [Frozen] IAmazonSecretsManager secretsManager, [Frozen] SecretsManagerConfigurationProviderOptions options, SecretsManagerConfigurationProvider sut, IFixture fixture)
{
Mock.Get(secretsManager).Setup(p => p.ListSecretsAsync(It.IsAny<ListSecretsRequest>(), It.IsAny<CancellationToken>())).ReturnsAsync(listSecretsResponse);

Mock.Get(secretsManager).Setup(p => p.GetSecretValueAsync(It.IsAny<GetSecretValueRequest>(), It.IsAny<CancellationToken>())).ReturnsAsync(getSecretValueResponse);

options.ConfigureSecretValueRequest = (request, _) => request.VersionStage = secretVersionStage;

sut.Load();

Mock.Get(secretsManager).Verify(p => p.GetSecretValueAsync(It.Is<GetSecretValueRequest>(x => x.VersionStage == secretVersionStage), It.IsAny<CancellationToken>()), Times.Once);
}

[Test, CustomAutoData]
public void Should_throw_on_missing_secret_value([Frozen] SecretListEntry testEntry, ListSecretsResponse listSecretsResponse, [Frozen] IAmazonSecretsManager secretsManager, SecretsManagerConfigurationProvider sut, IFixture fixture)
{
Expand Down

0 comments on commit 5a81fc3

Please sign in to comment.