Skip to content

Commit 97367f0

Browse files
committed
Split validation and key mapping into two methods.
1 parent 48b090c commit 97367f0

4 files changed

Lines changed: 51 additions & 13 deletions

File tree

src/Azure/AzureKeyVaultConfigBuilder.cs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,14 +143,33 @@ public override ICollection<KeyValuePair<string, string>> GetAllValues(string pr
143143
return d;
144144
}
145145

146+
/// <summary>
147+
/// Transform the given key to an intermediate format that will be used to look up values in backing store.
148+
/// </summary>
149+
/// <param name="key">The string to be mapped.</param>
150+
/// <returns>The key string to be used while looking up config values..</returns>
151+
public override string MapKey(string key)
152+
{
153+
if (String.IsNullOrEmpty(key))
154+
return key;
155+
156+
// Colons and underscores are common in appSettings keys, but not allowed in key vault key names.
157+
// It's likely that apps will want to lookup config values with these characters in their name in
158+
// key vault. More extensive key mapping can be done with subclasses. But let's handle the most
159+
// most common case here.
160+
key = key.Replace(':', '-');
161+
key = key.Replace('_', '-');
162+
return key;
163+
}
164+
146165
/// <summary>
147166
/// Makes a determination about whether the input key is valid for this builder and backing store.
148167
/// </summary>
149168
/// <param name="key">The string to be validated. May be partial.</param>
150169
/// <returns>True if the string is valid. False if the string is not a valid key.</returns>
151-
public override bool ValidateKey(ref string key)
170+
public override bool ValidateKey(string key)
152171
{
153-
// Key Vault only allows alphanumerics and '-'. This builder also allows for one '/'.
172+
// Key Vault only allows alphanumerics and '-'. This builder also allows for one '/' for versioning.
154173
return Regex.IsMatch(key, "^[a-zA-Z0-9-]+(/?[a-zA-Z0-9-]+)?$");
155174
}
156175

src/AzureAppConfig/AzureAppConfigurationBuilder.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Generic;
66
using System.Collections.Specialized;
77
using System.Linq;
8+
using System.Text.RegularExpressions;
89
using System.Threading;
910
using System.Threading.Tasks;
1011
using Microsoft.Azure.AppConfiguration.Azconfig;
@@ -100,10 +101,23 @@ protected override void LazyInitialize(string name, NameValueCollection config)
100101
/// </summary>
101102
/// <param name="key">The string to be validated. May be partial.</param>
102103
/// <returns>True if the string is valid. False if the string is not a valid key.</returns>
103-
public override bool ValidateKey(ref string key)
104+
public override bool ValidateKey(string key)
104105
{
105-
// Azure App Config does not restrict key names, although a couple characters have special meaning if not escaped.
106-
// We may want to restrict using those characters unescaped in a key name in the future.
106+
// From - https://docs.microsoft.com/en-us/azure/azure-app-configuration/concept-key-value
107+
// You can use any unicode character in key names entered into App Configuration except for *, ,, and \. These characters are
108+
// reserved.If you need to include a reserved character, you must escape it by using \{ Reserved Character}.
109+
if (String.IsNullOrWhiteSpace(key))
110+
return false;
111+
112+
if (key.Contains('*') || key.Contains(','))
113+
return false;
114+
115+
// We don't want to completely disallow '\' since it is used for escaping. But writing a full parser for someone elses
116+
// naming format could be error prone. If we see a '\' followed by a '{', just call it good. Don't bother with the Regex
117+
// if there aren't any backslashes though.
118+
if (key.Contains('\\'))
119+
return !Regex.IsMatch(key, @"\\[^{]");
120+
107121
return true;
108122
}
109123

src/Base/KeyValueConfigBuilder.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,18 @@ public abstract class KeyValueConfigBuilder : ConfigurationBuilder
6868
/// <returns>A collection of key/value pairs.</returns>
6969
public abstract ICollection<KeyValuePair<string, string>> GetAllValues(string prefix);
7070

71+
/// <summary>
72+
/// Transform the given key to an intermediate format that will be used to look up values in backing store.
73+
/// </summary>
74+
/// <param name="key">The string to be mapped.</param>
75+
/// <returns>The key string to be used while looking up config values..</returns>
76+
public virtual string MapKey(string key) { return key; }
7177
/// <summary>
7278
/// Makes a determination about whether the input key is valid for this builder and backing store.
7379
/// </summary>
7480
/// <param name="key">The string to be validated. May be partial.</param>
7581
/// <returns>True if the string is valid. False if the string is not a valid key.</returns>
76-
public virtual bool ValidateKey(ref string key) { return true; }
82+
public virtual bool ValidateKey(string key) { return true; }
7783
/// <summary>
7884
/// Transforms the raw key to a new string just before updating items in Strict and Greedy modes.
7985
/// </summary>
@@ -246,10 +252,10 @@ private void EnsureGreedyInitialized()
246252
// can't 'cache' them as we go along. Slurp them all up now. But only once. ;)
247253
if (!_greedyInitialized)
248254
{
249-
string prefix = KeyPrefix; // Do this outside the lock. It ensures _cachedValues is initialized.
255+
string prefix = MapKey(KeyPrefix); // Do this outside the lock. It ensures _cachedValues is initialized.
250256
lock (_cachedValues)
251257
{
252-
if (!_greedyInitialized && (String.IsNullOrEmpty(prefix) || ValidateKey(ref prefix)))
258+
if (!_greedyInitialized && (String.IsNullOrEmpty(prefix) || ValidateKey(prefix)))
253259
{
254260
foreach (KeyValuePair<string, string> kvp in GetAllValues(prefix))
255261
{
@@ -301,9 +307,9 @@ private string GetValueInternal(string key)
301307

302308
// Stripping Prefix in strict mode means from the source key. The static config file will have a prefix-less key to match.
303309
// ie <add key="MySetting" /> should only match the key/value (KeyPrefix + "MySetting") from the source.
304-
string sourceKey = (StripPrefix) ? KeyPrefix + key : key;
310+
string sourceKey = MapKey((StripPrefix) ? KeyPrefix + key : key);
305311

306-
if (!ValidateKey(ref sourceKey))
312+
if (!ValidateKey(sourceKey))
307313
return null;
308314

309315
return (_cachedValues.ContainsKey(sourceKey)) ? _cachedValues[sourceKey] : _cachedValues[sourceKey] = GetValue(sourceKey);

test/Microsoft.Configuration.ConfigurationBuilders.Test/BaseTests.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -786,10 +786,9 @@ public void SetTokenPattern(string newPattern)
786786

787787
class FakeKeyMappingConfigBuilder : FakeConfigBuilder
788788
{
789-
public override bool ValidateKey(ref string key)
789+
public override string MapKey(string key)
790790
{
791-
key = key.Replace(":", "_");
792-
return true;
791+
return key.Replace(":", "_");
793792
}
794793

795794
public override string UpdateKey(string rawKey)

0 commit comments

Comments
 (0)