diff --git a/src/Dapr.Extensions.Configuration/DaprSecretDescriptor.cs b/src/Dapr.Extensions.Configuration/DaprSecretDescriptor.cs
index e708ad712..6d86dc046 100644
--- a/src/Dapr.Extensions.Configuration/DaprSecretDescriptor.cs
+++ b/src/Dapr.Extensions.Configuration/DaprSecretDescriptor.cs
@@ -1,4 +1,4 @@
-// ------------------------------------------------------------------------
+// ------------------------------------------------------------------------
// Copyright 2021 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -21,8 +21,11 @@ namespace Dapr.Extensions.Configuration
public class DaprSecretDescriptor
{
///
- /// Gets or sets the secret name.
+ /// The name of the secret to retrieve from the Dapr secret store.
///
+ ///
+ /// If the is not specified, this value will also be used as the key to retrieve the secret from the associated source secret store.
+ ///
public string SecretName { get; }
///
@@ -31,20 +34,39 @@ public class DaprSecretDescriptor
public IReadOnlyDictionary Metadata { get; }
///
- /// Secret Descriptor Construcutor
+ /// A value indicating whether to throw an exception if the secret is not found in the source secret store.
+ ///
+ ///
+ /// Setting this value to will suppress the exception; otherwise, will not.
+ ///
+ public bool IsRequired { get; }
+
+ ///
+ /// The secret key that maps to the to retrieve from the source secret store.
+ ///
+ ///
+ /// Use this property when the does not match the key used to retrieve the secret from the source secret store.
+ ///
+ public string SecretKey { get; }
+
+ ///
+ /// Secret Descriptor Constructor
///
- public DaprSecretDescriptor(string secretName) : this(secretName, new Dictionary())
+ public DaprSecretDescriptor(string secretName, bool isRequired = true, string secretKey = "")
+ : this(secretName, new Dictionary(), isRequired, secretKey)
{
}
///
- /// Secret Descriptor Construcutor
+ /// Secret Descriptor Constructor
///
- public DaprSecretDescriptor(string secretName, IReadOnlyDictionary metadata)
+ public DaprSecretDescriptor(string secretName, IReadOnlyDictionary metadata, bool isRequired = true, string secretKey = "")
{
SecretName = secretName;
Metadata = metadata;
+ IsRequired = isRequired;
+ SecretKey = string.IsNullOrEmpty(secretKey) ? secretName : secretKey;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationProvider.cs b/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationProvider.cs
index da5349c30..5991a7dad 100644
--- a/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationProvider.cs
+++ b/src/Dapr.Extensions.Configuration/DaprSecretStoreConfigurationProvider.cs
@@ -1,4 +1,4 @@
-// ------------------------------------------------------------------------
+// ------------------------------------------------------------------------
// Copyright 2021 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -54,7 +54,7 @@ public DaprSecretStoreConfigurationProvider(
bool normalizeKey,
IEnumerable secretDescriptors,
DaprClient client) : this(store, normalizeKey, null, secretDescriptors, client, DefaultSidecarWaitTimeout)
- {
+ {
}
///
@@ -181,6 +181,10 @@ private string NormalizeKey(string key)
return key;
}
+ ///
+ /// Loads the configuration by calling the asynchronous LoadAsync method and blocking the calling
+ /// thread until the operation is completed.
+ ///
public override void Load() => LoadAsync().ConfigureAwait(false).GetAwaiter().GetResult();
private async Task LoadAsync()
@@ -197,16 +201,34 @@ private async Task LoadAsync()
{
foreach (var secretDescriptor in secretDescriptors)
{
- var result = await client.GetSecretAsync(store, secretDescriptor.SecretName, secretDescriptor.Metadata).ConfigureAwait(false);
+
+ Dictionary result;
+
+ try
+ {
+ result = await client
+ .GetSecretAsync(store, secretDescriptor.SecretKey, secretDescriptor.Metadata)
+ .ConfigureAwait(false);
+ }
+ catch (DaprException)
+ {
+ if (secretDescriptor.IsRequired)
+ {
+ throw;
+ }
+ result = new Dictionary();
+ }
foreach (var key in result.Keys)
{
if (data.ContainsKey(key))
{
- throw new InvalidOperationException($"A duplicate key '{key}' was found in the secret store '{store}'. Please remove any duplicates from your secret store.");
+ throw new InvalidOperationException(
+ $"A duplicate key '{key}' was found in the secret store '{store}'. Please remove any duplicates from your secret store.");
}
- data.Add(normalizeKey ? NormalizeKey(key) : key, result[key]);
+ data.Add(normalizeKey ? NormalizeKey(secretDescriptor.SecretName) : secretDescriptor.SecretName,
+ result[key]);
}
}
diff --git a/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj b/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj
index 2e4523582..7d11d5c40 100644
--- a/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj
+++ b/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj
@@ -7,6 +7,7 @@
+
all
diff --git a/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs b/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs
index 488c94983..d35275dd1 100644
--- a/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs
+++ b/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs
@@ -1,4 +1,4 @@
-// ------------------------------------------------------------------------
+// ------------------------------------------------------------------------
// Copyright 2021 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
using FluentAssertions;
using Grpc.Net.Client;
using Microsoft.Extensions.Configuration;
+using Moq;
using Xunit;
using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
@@ -142,23 +143,23 @@ public void AddDaprSecretStore_UsingDescriptors_DuplicateSecret_ReportsError()
[Fact]
public void LoadSecrets_FromSecretStoreThatReturnsOneValue()
{
- // Configure Client
- var httpClient = new TestHttpClient()
+ var storeName = "store";
+ var secretKey = "secretName";
+ var secretValue = "secret";
+
+ var secretDescriptors = new[]
{
- Handler = async (entry) =>
- {
- var secrets = new Dictionary() { { "secretName", "secret" } };
- await SendResponseWithSecrets(secrets, entry);
- }
+ new DaprSecretDescriptor(secretKey),
};
- var daprClient = new DaprClientBuilder()
- .UseHttpClientFactory(() => httpClient)
- .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
- .Build();
+ var daprClient = new Mock();
+
+ daprClient.Setup(c => c.GetSecretAsync(storeName, secretKey,
+ It.IsAny>(), default))
+ .ReturnsAsync(new Dictionary { { secretKey, secretValue } });
var config = CreateBuilder()
- .AddDaprSecretStore("store", new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient)
+ .AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object)
.Build();
config["secretName"].Should().Be("secret");
@@ -167,32 +168,127 @@ public void LoadSecrets_FromSecretStoreThatReturnsOneValue()
[Fact]
public void LoadSecrets_FromSecretStoreThatCanReturnsMultipleValues()
{
- // Configure Client
- var httpClient = new TestHttpClient()
+ var storeName = "store";
+ var firstSecretKey = "first_secret";
+ var secondSecretKey = "second_secret";
+ var firstSecretValue = "secret1";
+ var secondSecretValue = "secret2";
+
+ var secretDescriptors = new[]
{
- Handler = async (entry) =>
- {
- var secrets = new Dictionary() {
- { "first_secret", "secret1" },
- { "second_secret", "secret2" }};
- await SendResponseWithSecrets(secrets, entry);
- }
+ new DaprSecretDescriptor(firstSecretKey),
+ new DaprSecretDescriptor(secondSecretKey),
+ };
+
+ var daprClient = new Mock();
+
+ daprClient.Setup(c => c.GetSecretAsync(storeName, firstSecretKey,
+ It.IsAny>(), default))
+ .ReturnsAsync(new Dictionary { { firstSecretKey, firstSecretValue } });
+
+ daprClient.Setup(c => c.GetSecretAsync(storeName, secondSecretKey,
+ It.IsAny>(), default))
+ .ReturnsAsync(new Dictionary { { secondSecretKey, secondSecretValue } });
+
+ var config = CreateBuilder()
+ .AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object)
+ .Build();
+
+ config[firstSecretKey].Should().Be(firstSecretValue);
+ config[secondSecretKey].Should().Be(secondSecretValue);
+ }
+
+ [Fact]
+ public void LoadSecrets_FromSecretStoreWithADifferentSecretKeyAndName()
+ {
+ var storeName = "store";
+ var secretKey = "Microsservice-DatabaseConnStr";
+ var secretName = "ConnectionStrings:DatabaseConnStr";
+ var secretValue = "secret1";
+
+ var secretDescriptors = new[]
+ {
+ new DaprSecretDescriptor(secretName, new Dictionary(), true,
+ secretKey)
+ };
+
+ var daprClient = new Mock();
+
+ daprClient.Setup(c => c.GetSecretAsync(storeName, secretKey,
+ It.IsAny>(), default))
+ .ReturnsAsync(new Dictionary { { secretKey, secretValue } });
+
+ var config = CreateBuilder()
+ .AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object)
+ .Build();
+
+ config[secretName].Should().Be(secretValue);
+ }
+
+ [Fact]
+ public void LoadSecrets_FromSecretStoreNotRequiredAndDoesNotExist_ShouldNotThrowException()
+ {
+ var storeName = "store";
+ var secretName = "ConnectionStrings:DatabaseConnStr";
+
+ var secretDescriptors = new[]
+ {
+ new DaprSecretDescriptor(secretName, new Dictionary(), false)
+ };
+
+ var httpClient = new TestHttpClient
+ {
+ Handler = async entry =>
+ {
+ await SendEmptyResponse(entry);
+ }
};
var daprClient = new DaprClientBuilder()
.UseHttpClientFactory(() => httpClient)
- .UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
.Build();
var config = CreateBuilder()
- .AddDaprSecretStore("store", new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName") }, daprClient)
+ .AddDaprSecretStore(storeName, secretDescriptors, daprClient)
+ .Build();
+
+ config[secretName].Should().BeNull();
+ }
+
+ [Fact]
+ public void LoadSecrets_FromSecretStoreRequiredAndDoesNotExist_ShouldThrowException()
+ {
+ var storeName = "store";
+ var secretName = "ConnectionStrings:DatabaseConnStr";
+
+ var secretDescriptors = new[]
+ {
+ new DaprSecretDescriptor(secretName, new Dictionary(), true)
+ };
+
+ var httpClient = new TestHttpClient
+ {
+ Handler = async entry =>
+ {
+ await SendEmptyResponse(entry);
+ }
+ };
+
+ var daprClient = new DaprClientBuilder()
+ .UseHttpClientFactory(() => httpClient)
+ .Build();
+
+ var ex = Assert.Throws(() =>
+ {
+ var config = CreateBuilder()
+ .AddDaprSecretStore(storeName, secretDescriptors, daprClient)
.Build();
+ });
- config["first_secret"].Should().Be("secret1");
- config["second_secret"].Should().Be("secret2");
+ Assert.Contains("Secret", ex.Message);
}
- //Here
+
[Fact]
public void AddDaprSecretStore_WithoutStore_ReportsError()
{
@@ -565,7 +661,7 @@ await SendResponseWithSecrets(new Dictionary()
["otherSecretName≡value"] = "secret",
}, entry);
}
- }
+ }
}
};