Skip to content

Commit caefb88

Browse files
committed
Align key file resolution with the way the FileConfiguration* classes do.
1 parent b28851b commit caefb88

File tree

5 files changed

+111
-52
lines changed

5 files changed

+111
-52
lines changed

src/SecureStore.Contrib.Configuration/KeyType.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
namespace SecureStore.Contrib.Configuration
22
{
33
/// <summary>
4-
/// SecureStore key types.
4+
/// SecureStore key types.
55
/// </summary>
66
public enum KeyType
77
{
88
/// <summary>
9-
/// File key.
9+
/// File key.
1010
/// </summary>
1111
File,
1212

1313
/// <summary>
14-
/// Password key.
14+
/// Password key.
1515
/// </summary>
1616
Password
1717
}

src/SecureStore.Contrib.Configuration/SecureStore.Contrib.Configuration.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<TargetFramework>netstandard2.0</TargetFramework>
55
<LangVersion>latest</LangVersion>
66
<NoWarn>$(NoWarn);1591</NoWarn>
7-
<Version>1.0.0</Version>
7+
<Version>1.0.1</Version>
88
<Authors>Gowon Patterson</Authors>
99
<Description>A SecureStore configuration provider to use with Microsoft.Extensions.Configuration.</Description>
1010
<Copyright>© Gowon Patterson. All rights reserved.</Copyright>
Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,62 @@
11
namespace SecureStore.Contrib.Configuration
22
{
33
using System;
4-
using System.IO;
54
using Microsoft.Extensions.Configuration;
65
using Microsoft.Extensions.FileProviders;
76

87
/// <summary>
9-
/// Extension methods for adding <see cref="SecureStoreConfigurationProvider"/>.
8+
/// Extension methods for adding <see cref="SecureStoreConfigurationProvider" />.
109
/// </summary>
1110
public static class SecureStoreConfigurationExtensions
1211
{
1312
/// <summary>
14-
/// Adds the SecureStore configuration provider at <paramref name="path"/> to <paramref name="builder"/>.
13+
/// Adds the SecureStore configuration provider at <paramref name="path" /> to <paramref name="builder" />.
1514
/// </summary>
16-
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
17-
/// <param name="path">Path relative to the base path stored in
18-
/// <see cref="IConfigurationBuilder.Properties"/> of <paramref name="builder"/>.</param>
15+
/// <param name="builder">The <see cref="IConfigurationBuilder" /> to add to.</param>
16+
/// <param name="path">
17+
/// Path relative to the base path stored in
18+
/// <see cref="IConfigurationBuilder.Properties" /> of <paramref name="builder" />.
19+
/// </param>
1920
/// <param name="key">The SecureStore key.</param>
2021
/// <param name="keyType">The key type.</param>
21-
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
22+
/// <returns>The <see cref="IConfigurationBuilder" />.</returns>
2223
public static IConfigurationBuilder AddSecureStoreFile(this IConfigurationBuilder builder, string path,
2324
string key, KeyType keyType)
2425
{
25-
return AddSecureStoreFile(builder, path, key, keyType, false);
26+
return AddSecureStoreFile(builder, null, path, key, keyType, false, false);
2627
}
2728

2829
/// <summary>
29-
/// Adds the SecureStore configuration provider at <paramref name="path"/> to <paramref name="builder"/>.
30+
/// Adds the SecureStore configuration provider at <paramref name="path" /> to <paramref name="builder" />.
3031
/// </summary>
31-
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
32-
/// <param name="path">Path relative to the base path stored in
33-
/// <see cref="IConfigurationBuilder.Properties"/> of <paramref name="builder"/>.</param>
32+
/// <param name="builder">The <see cref="IConfigurationBuilder" /> to add to.</param>
33+
/// <param name="path">
34+
/// Path relative to the base path stored in
35+
/// <see cref="IConfigurationBuilder.Properties" /> of <paramref name="builder" />.
36+
/// </param>
3437
/// <param name="key">The SecureStore key.</param>
3538
/// <param name="keyType">The key type.</param>
3639
/// <param name="optional">Whether the file is optional.</param>
37-
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
40+
/// <returns>The <see cref="IConfigurationBuilder" />.</returns>
3841
public static IConfigurationBuilder AddSecureStoreFile(this IConfigurationBuilder builder, string path,
3942
string key, KeyType keyType, bool optional)
4043
{
41-
return AddSecureStoreFile(builder, path, key, keyType, optional, false);
44+
return AddSecureStoreFile(builder, null, path, key, keyType, optional, false);
4245
}
4346

4447
/// <summary>
45-
/// Adds the SecureStore configuration provider at <paramref name="path"/> to <paramref name="builder"/>.
48+
/// Adds the SecureStore configuration provider at <paramref name="path" /> to <paramref name="builder" />.
4649
/// </summary>
47-
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
48-
/// <param name="path">Path relative to the base path stored in
49-
/// <see cref="IConfigurationBuilder.Properties"/> of <paramref name="builder"/>.</param>
50+
/// <param name="builder">The <see cref="IConfigurationBuilder" /> to add to.</param>
51+
/// <param name="path">
52+
/// Path relative to the base path stored in
53+
/// <see cref="IConfigurationBuilder.Properties" /> of <paramref name="builder" />.
54+
/// </param>
5055
/// <param name="key">The SecureStore key.</param>
5156
/// <param name="keyType">The key type.</param>
5257
/// <param name="optional">Whether the file is optional.</param>
5358
/// <param name="reloadOnChange">Whether the configuration should be reloaded if the file changes.</param>
54-
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
59+
/// <returns>The <see cref="IConfigurationBuilder" />.</returns>
5560
public static IConfigurationBuilder AddSecureStoreFile(this IConfigurationBuilder builder, string path,
5661
string key, KeyType keyType, bool optional,
5762
bool reloadOnChange)
@@ -60,17 +65,19 @@ public static IConfigurationBuilder AddSecureStoreFile(this IConfigurationBuilde
6065
}
6166

6267
/// <summary>
63-
/// Adds a SecureStore configuration source to <paramref name="builder"/>.
68+
/// Adds a SecureStore configuration source to <paramref name="builder" />.
6469
/// </summary>
65-
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
66-
/// <param name="provider">The <see cref="IFileProvider"/> to use to access the file.</param>
67-
/// <param name="path">Path relative to the base path stored in
68-
/// <see cref="IConfigurationBuilder.Properties"/> of <paramref name="builder"/>.</param>
70+
/// <param name="builder">The <see cref="IConfigurationBuilder" /> to add to.</param>
71+
/// <param name="provider">The <see cref="IFileProvider" /> to use to access the file.</param>
72+
/// <param name="path">
73+
/// Path relative to the base path stored in
74+
/// <see cref="IConfigurationBuilder.Properties" /> of <paramref name="builder" />.
75+
/// </param>
6976
/// <param name="key">The SecureStore key.</param>
7077
/// <param name="keyType">The key type.</param>
7178
/// <param name="optional">Whether the file is optional.</param>
7279
/// <param name="reloadOnChange">Whether the configuration should be reloaded if the file changes.</param>
73-
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
80+
/// <returns>The <see cref="IConfigurationBuilder" />.</returns>
7481
public static IConfigurationBuilder AddSecureStoreFile(this IConfigurationBuilder builder,
7582
IFileProvider provider,
7683
string path, string key, KeyType keyType, bool optional, bool reloadOnChange)
@@ -90,12 +97,6 @@ public static IConfigurationBuilder AddSecureStoreFile(this IConfigurationBuilde
9097
throw new ArgumentException("File key/path must be a non-empty string.", nameof(key));
9198
}
9299

93-
if (provider == null && Path.IsPathRooted(path))
94-
{
95-
provider = new PhysicalFileProvider(Path.GetDirectoryName(path));
96-
path = Path.GetFileName(path);
97-
}
98-
99100
return builder.AddSecureStoreFile(source =>
100101
{
101102
source.FileProvider = provider;
@@ -104,16 +105,21 @@ public static IConfigurationBuilder AddSecureStoreFile(this IConfigurationBuilde
104105
source.KeyType = keyType;
105106
source.Optional = optional;
106107
source.ReloadOnChange = reloadOnChange;
108+
source.ResolveFileProvider();
109+
source.ResolveKeyFileProvider();
107110
});
108111
}
109112

110113
/// <summary>
111-
/// Adds a SecureStore configuration source to <paramref name="builder"/>.
114+
/// Adds a SecureStore configuration source to <paramref name="builder" />.
112115
/// </summary>
113-
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
116+
/// <param name="builder">The <see cref="IConfigurationBuilder" /> to add to.</param>
114117
/// <param name="configureSource">Configures the source.</param>
115-
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
116-
public static IConfigurationBuilder AddSecureStoreFile(this IConfigurationBuilder builder, Action<SecureStoreConfigurationSource> configureSource)
117-
=> builder.Add(configureSource);
118+
/// <returns>The <see cref="IConfigurationBuilder" />.</returns>
119+
public static IConfigurationBuilder AddSecureStoreFile(this IConfigurationBuilder builder,
120+
Action<SecureStoreConfigurationSource> configureSource)
121+
{
122+
return builder.Add(configureSource);
123+
}
118124
}
119125
}

src/SecureStore.Contrib.Configuration/SecureStoreConfigurationProvider.cs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,52 @@
33
using System;
44
using System.Collections.Generic;
55
using System.IO;
6+
using System.Text;
67
using Microsoft.Extensions.Configuration;
78
using NeoSmart.SecureStore;
89

910
/// <summary>
10-
/// A SecureStore file based <see cref="FileConfigurationProvider"/>.
11+
/// A SecureStore file based <see cref="FileConfigurationProvider" />.
1112
/// </summary>
1213
public class SecureStoreConfigurationProvider : FileConfigurationProvider
1314
{
1415
/// <summary>
15-
/// Initializes a new instance with the specified source.
16+
/// Initializes a new instance with the specified source.
1617
/// </summary>
1718
/// <param name="source">The source settings.</param>
1819
public SecureStoreConfigurationProvider(SecureStoreConfigurationSource source) : base(source)
1920
{
2021
}
2122

2223
/// <summary>
23-
/// Loads the SecureStore data from a stream.
24+
/// Loads the SecureStore data from a stream.
2425
/// </summary>
2526
/// <param name="stream">The stream to read.</param>
2627
public override void Load(Stream stream)
2728
{
28-
var source = (SecureStoreConfigurationSource)Source;
29-
var dictionary = new Dictionary<string, string>();
29+
var source = (SecureStoreConfigurationSource) Source;
30+
var dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
31+
3032
using (var manager = SecretsManager.LoadStore(stream))
3133
{
3234
switch (source.KeyType)
3335
{
3436
case KeyType.File:
35-
manager.LoadKeyFromFile(source.Key);
37+
// ref: https://github.com/aspnet/Configuration/blob/master/src/Config.FileExtensions/FileConfigurationProvider.cs#L48
38+
var file = source.KeyFileProvider?.GetFileInfo(source.Key);
39+
if (file == null || !file.Exists)
40+
{
41+
var error = new StringBuilder(
42+
$"The configuration key file '{source.Key}' was not found and is not optional.");
43+
if (!string.IsNullOrEmpty(file?.PhysicalPath))
44+
{
45+
error.Append($" The physical path is '{file.PhysicalPath}'.");
46+
}
47+
48+
throw new FileNotFoundException(error.ToString());
49+
}
50+
51+
manager.LoadKeyFromFile(file.PhysicalPath);
3652
break;
3753
case KeyType.Password:
3854
manager.LoadKeyFromPassword(source.Key);
Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,68 @@
11
namespace SecureStore.Contrib.Configuration
22
{
3+
using System.IO;
34
using Microsoft.Extensions.Configuration;
5+
using Microsoft.Extensions.FileProviders;
46

57
/// <summary>
6-
/// Represents a SecureStore file as an <see cref="IConfigurationSource"/>.
8+
/// Represents a SecureStore file as an <see cref="IConfigurationSource" />.
79
/// </summary>
810
public class SecureStoreConfigurationSource : FileConfigurationSource
911
{
1012
/// <summary>
11-
/// The key to decrypt the SecureStore file.
13+
/// Used to access the contents of the key file.
14+
/// </summary>
15+
public IFileProvider KeyFileProvider { get; set; }
16+
17+
18+
/// <summary>
19+
/// The key to decrypt the SecureStore file.
1220
/// </summary>
1321
public string Key { get; set; }
1422

1523
/// <summary>
16-
/// Determines if the key is a password or a key file.
24+
/// Determines if the key is a password or a key file.
1725
/// </summary>
1826
public KeyType KeyType { get; set; }
1927

2028
/// <summary>
21-
/// Builds the <see cref="SecureStoreConfigurationProvider"/> for this source.
29+
/// Builds the <see cref="SecureStoreConfigurationProvider" /> for this source.
2230
/// </summary>
23-
/// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
24-
/// <returns>A <see cref="SecureStoreConfigurationProvider"/></returns>
31+
/// <param name="builder">The <see cref="IConfigurationBuilder" />.</param>
32+
/// <returns>A <see cref="SecureStoreConfigurationProvider" /></returns>
2533
public override IConfigurationProvider Build(IConfigurationBuilder builder)
2634
{
2735
EnsureDefaults(builder);
36+
// ref: https://github.com/aspnet/Configuration/blob/master/src/Config.FileExtensions/FileConfigurationSource.cs#L59
37+
KeyFileProvider ??= builder.GetFileProvider();
2838
return new SecureStoreConfigurationProvider(this);
2939
}
40+
41+
/// <summary>
42+
/// If no file provider has been set, for absolute Key path, this will creates a physical file provider
43+
/// for the nearest existing directory.
44+
/// </summary>
45+
public void ResolveKeyFileProvider()
46+
{
47+
// ref: https://github.com/aspnet/Configuration/blob/master/src/Config.FileExtensions/FileConfigurationSource.cs#L67
48+
if (KeyFileProvider == null &&
49+
!string.IsNullOrEmpty(Key) &&
50+
System.IO.Path.IsPathRooted(Key))
51+
{
52+
var directory = System.IO.Path.GetDirectoryName(Key);
53+
var pathToFile = System.IO.Path.GetFileName(Key);
54+
while (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
55+
{
56+
pathToFile = System.IO.Path.Combine(System.IO.Path.GetFileName(directory), pathToFile);
57+
directory = System.IO.Path.GetDirectoryName(directory);
58+
}
59+
60+
if (Directory.Exists(directory))
61+
{
62+
KeyFileProvider = new PhysicalFileProvider(directory);
63+
Key = pathToFile;
64+
}
65+
}
66+
}
3067
}
3168
}

0 commit comments

Comments
 (0)