Skip to content

Commit e578646

Browse files
StephenMolloyHongGit
authored andcommitted
Key per file (#28)
* Add KeyPerFile config builder, similar in concept/behavior as the KeyPerFile config source in .Net Core. * Add Json.NET to sample app via nuget reference. * Update Readme.md with KeyPerFile. * Remove documentation about msbuild magic to grab UserSecretsId, as this is no longer a feature. * Add KeyPerFile to sample app. * Add example secrets.xml in Readme.md * Update Readme language.
1 parent 003df05 commit e578646

21 files changed

Lines changed: 436 additions & 12 deletions

MicrosoftConfigurationBuilders.sln

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Environment", "src\Environm
5151
{F382FBF8-146D-4968-A199-90D37F9EF9A7} = {F382FBF8-146D-4968-A199-90D37F9EF9A7}
5252
EndProjectSection
5353
EndProject
54+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeyPerFile", "src\KeyPerFile\KeyPerFile.csproj", "{60C31149-44ED-4789-B5C3-AAA5D3B2FCF1}"
55+
ProjectSection(ProjectDependencies) = postProject
56+
{F382FBF8-146D-4968-A199-90D37F9EF9A7} = {F382FBF8-146D-4968-A199-90D37F9EF9A7}
57+
EndProjectSection
58+
EndProject
5459
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleWebApp", "samples\SampleWebApp\SampleWebApp.csproj", "{590892DD-F842-4E7C-9400-4C6451C16B1A}"
5560
EndProject
5661
Global
@@ -87,6 +92,10 @@ Global
8792
{C6530E81-D8D8-47A8-912E-D2939F801835}.Debug|Any CPU.Build.0 = Debug|Any CPU
8893
{C6530E81-D8D8-47A8-912E-D2939F801835}.Release|Any CPU.ActiveCfg = Release|Any CPU
8994
{C6530E81-D8D8-47A8-912E-D2939F801835}.Release|Any CPU.Build.0 = Release|Any CPU
95+
{60C31149-44ED-4789-B5C3-AAA5D3B2FCF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
96+
{60C31149-44ED-4789-B5C3-AAA5D3B2FCF1}.Debug|Any CPU.Build.0 = Debug|Any CPU
97+
{60C31149-44ED-4789-B5C3-AAA5D3B2FCF1}.Release|Any CPU.ActiveCfg = Release|Any CPU
98+
{60C31149-44ED-4789-B5C3-AAA5D3B2FCF1}.Release|Any CPU.Build.0 = Release|Any CPU
9099
{590892DD-F842-4E7C-9400-4C6451C16B1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
91100
{590892DD-F842-4E7C-9400-4C6451C16B1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
92101
{590892DD-F842-4E7C-9400-4C6451C16B1A}.Release|Any CPU.ActiveCfg = Release|Any CPU

README.md

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,25 @@ builder, the json format for Core secrets is technically an implementation detai
8787
There are three additional configuration attributes for this config builder:
8888
* `userSecretsId` - This is the preferred method for identifying an xml secrets file. It works similar to .Net Core, which uses a 'UserSecretsId' project
8989
property to store this identifier. (The string does not have to be a Guid. Just unique. The VS "Manage User Secrets" experience produces a Guid.) With this
90-
attribute, the `UserSecretsConfigBuilder` will look in a well-known local location for a secrets file belonging to this identifier. In MSBuild environments,
91-
the value of this attribute will be replaced with the project property $(UserSecretsId) in the output directory iff the initial value is '${UserSecretsId}'.
92-
One of this attribute or the 'userSecretsFile' attribute is required.
90+
attribute, the `UserSecretsConfigBuilder` will look in a well-known local location (%APPDATA%\Microsoft\UserSecrets\<userSecretsId>\secrets.xml in
91+
Windows environments) for a secrets file belonging to this identifier.
9392
* `userSecretsFile` - An optional attribute specifying the file containing the secrets. The '~' character can be used at the start to reference the app root.
9493
One of this attribute or the 'userSecretsId' attribute is required. If both are specified, 'userSecretsFile' takes precedence.
9594
* `optional` - A simple boolean to avoid throwing exceptions if the secrets file cannot be found. The default is `true`.
9695

96+
The next Visual Studio update will include "Manage User Secrets..." support for WebForms projects. When using this feature, Visual Studio will create an
97+
empty secrets file outside of the solution folder and allow editing the raw content to add/remove secrets. This is similar to the .Net Core experience,
98+
and currently exposes the format of the file - which as mentioned above - should be considered an implementation detail. A non-empty secrets file would look like this:
99+
100+
```xml
101+
<?xml version="1.0" encoding="utf-8" ?>
102+
<root>
103+
<secrets ver="1.0">
104+
<secret name="secretFoo" value="valueBar" />
105+
</secrets>
106+
</root>
107+
```
108+
97109
### AzureKeyVaultConfigBuilder
98110
```xml
99111
<add name="AzureKeyVault"
@@ -117,6 +129,26 @@ up connection information from the execution environment if possible, but you ca
117129
this attribute to 'false', and secrets will be retrieved one at a time. This could also be useful if the vault allows "Get" access but not
118130
"List" access. (NOTE: Disabling preload is incompatible with Greedy mode.)
119131

132+
### KeyPerFileConfigBuilder
133+
```xml
134+
<add name="KeyPerFile"
135+
[mode|prefix|stripPrefix|tokenPattern]
136+
(directoryPath="PathToSourceDirectory")
137+
[ignorePrefix="ignore."]
138+
[keyDelimiter=":"]
139+
[optional="false"]
140+
type="Microsoft.Configuration.ConfigurationBuilders.KeyPerFileConfigBuilder, Microsoft.Configuration.ConfigurationBuilders.KeyPerFile" />
141+
```
142+
This is a simple config builder that uses a directory's files as a source of values. A file's name is the key, and the contents are the value. This
143+
config builder can be useful when running in an orchestrated container environment, as systems like Docker Swarm and Kubernetes provide 'secrets' to
144+
their orchestrated windows containers in this key-per-file manner.
145+
* `directoryPath` - This is a required attribute. It specifies a path to the source directory to look in for values. Docker for Windows secrets
146+
are stored in the 'C:\ProgramData\Docker\secrets' directory by default.
147+
* `ignorePrefix` - Files that start with this prefix will be excluded. Defaults to "ignore.".
148+
* `keyDelimiter` - If specified, the config builder will traverse multiple levels of the directory, building key names up with this delimeter. If
149+
this value is left `null` however, the config builder only looks at the top-level of the directory. `null` is the default.
150+
* `optional` - Specifies whether the config builder should cause errors if the source directory doesn't exist. The default is `false`.
151+
120152
### SimpleJsonConfigBuilder
121153
```xml
122154
<add name="SimpleJson"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
I might have been put here by a container orchestrator.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
I am definitely just a test secret.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This might be a subfeature setting.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This one should be ignored.

samples/SampleWebApp/SampleWebApp.csproj

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@
4848
<HintPath>..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.8\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll</HintPath>
4949
</Reference>
5050
<Reference Include="Microsoft.CSharp" />
51+
<Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
52+
<HintPath>..\..\packages\Newtonsoft.Json.11.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
53+
</Reference>
5154
<Reference Include="System.Web.DynamicData" />
5255
<Reference Include="System.Web.Entity" />
5356
<Reference Include="System.Web.ApplicationServices" />
@@ -78,16 +81,14 @@
7881
<ItemGroup>
7982
<Content Include="App_Data\secrets.xml" />
8083
<Content Include="default.aspx" />
81-
<Content Include="Web.config" />
84+
<Content Include="Web.config">
85+
<SubType>Designer</SubType>
86+
</Content>
8287
</ItemGroup>
8388
<ItemGroup>
8489
<Compile Include="Properties\AssemblyInfo.cs" />
8590
</ItemGroup>
8691
<ItemGroup>
87-
<ProjectReference Include="..\..\src\Azure\Azure.csproj">
88-
<Project>{345c5437-4990-45dc-be34-6e37aa05d8d2}</Project>
89-
<Name>Azure</Name>
90-
</ProjectReference>
9192
<ProjectReference Include="..\..\src\Base\Base.csproj">
9293
<Project>{f382fbf8-146d-4968-a199-90d37f9ef9a7}</Project>
9394
<Name>Base</Name>
@@ -100,6 +101,10 @@
100101
<Project>{84e0ce5d-4af2-414f-a940-22b3f93fc727}</Project>
101102
<Name>Json</Name>
102103
</ProjectReference>
104+
<ProjectReference Include="..\..\src\KeyPerFile\KeyPerFile.csproj">
105+
<Project>{60c31149-44ed-4789-b5c3-aaa5d3b2fcf1}</Project>
106+
<Name>KeyPerFile</Name>
107+
</ProjectReference>
103108
<ProjectReference Include="..\..\src\UserSecrets\UserSecrets.csproj">
104109
<Project>{c60d6cbb-d513-4692-81a6-0be5d45e4702}</Project>
105110
<Name>UserSecrets</Name>
@@ -116,7 +121,7 @@
116121
<VisualStudio>
117122
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
118123
<WebProjectProperties>
119-
<UseIIS>True</UseIIS>
124+
<UseIIS>False</UseIIS>
120125
<AutoAssignPort>True</AutoAssignPort>
121126
<DevelopmentServerPort>58254</DevelopmentServerPort>
122127
<DevelopmentServerVPath>/</DevelopmentServerVPath>

samples/SampleWebApp/Web.config

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@
1414
<!-- <add name="Environment" mode="Expand" type="Microsoft.Configuration.ConfigurationBuilders.EnvironmentConfigBuilder, Microsoft.Configuration.ConfigurationBuilders.Environment, Version=1.0.0.0, Culture=neutral"/> -->
1515
<add name="Secrets" userSecretsFile="~/App_Data/secrets.xml" mode="Greedy" type="Microsoft.Configuration.ConfigurationBuilders.UserSecretsConfigBuilder, Microsoft.Configuration.ConfigurationBuilders.UserSecrets, Version=1.0.0.0, Culture=neutral" />
1616
<add name="SimpleJson" jsonFile="~/App_Data/settings.json" ignoreMissingFile="true" type="Microsoft.Configuration.ConfigurationBuilders.SimpleJsonConfigBuilder, Microsoft.Configuration.ConfigurationBuilders.Json, Version=1.0.0.0, Culture=neutral" />
17+
<add name="KeyPerFile" directoryPath="~/../KeyPerFileSampleRoot" mode="Greedy" keyDelimiter="--" type="Microsoft.Configuration.ConfigurationBuilders.KeyPerFileConfigBuilder, Microsoft.Configuration.ConfigurationBuilders.KeyPerFile, Version=1.0.0.0, Culture=neutral" />
1718
</builders>
1819
</configBuilders>
1920

20-
<appSettings configBuilders="Environment,SimpleJson,Secrets">
21+
<appSettings configBuilders="Environment,SimpleJson,Secrets,KeyPerFile">
2122
<add key="WINDIR" value="Will be replaced by 'Environment' in Strict and Greedy modes." />
2223
<add key="SYSTEMDRIVE" value="Will initally be replaced by 'Environment' in Strict and Greedy modes... but then superceded by 'Secrets' in the same modes." />
2324
<add key="Value_Replaced_By_Environment_In_Expand_Mode" value="${WINDIR}" />

samples/SampleWebApp/packages.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
<packages>
33
<package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="1.0.8" targetFramework="net471" />
44
<package id="Microsoft.Net.Compilers" version="2.8.0" targetFramework="net471" developmentDependency="true" />
5+
<package id="Newtonsoft.Json" version="11.0.1" targetFramework="net471" />
56
</packages>

src/Base/KeyValueConfigBuilder.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ private string TrimPrefix(string fullString)
251251

252252
private string GetValueInternal(string key)
253253
{
254+
if (String.IsNullOrEmpty(key)) { return null; }
255+
254256
try
255257
{
256258
return GetValue(key);

0 commit comments

Comments
 (0)