Skip to content

Commit e104c18

Browse files
committed
input: parse and surface wwwauth[] Git input args
Add the new `wwwauth[]` credential property to the input arguments surfaced to providers and commands.
1 parent 6d44635 commit e104c18

File tree

4 files changed

+72
-24
lines changed

4 files changed

+72
-24
lines changed

src/shared/Core.Tests/InputArgumentsTests.cs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,25 @@ public class InputArgumentsTests
99
[Fact]
1010
public void InputArguments_Ctor_Null_ThrowsArgNullException()
1111
{
12-
Assert.Throws<ArgumentNullException>(() => new InputArguments(null));
12+
Assert.Throws<ArgumentNullException>(() => new InputArguments((IDictionary<string, string>)null));
13+
Assert.Throws<ArgumentNullException>(() => new InputArguments((IDictionary<string, IList<string>>)null));
1314
}
1415

1516
[Fact]
1617
public void InputArguments_CommonArguments_ValuePresent_ReturnsValues()
1718
{
18-
var dict = new Dictionary<string, string>
19+
var dict = new Dictionary<string, IList<string>>
1920
{
20-
["protocol"] = "https",
21-
["host"] = "example.com",
22-
["path"] = "an/example/path",
23-
["username"] = "john.doe",
24-
["password"] = "password123"
21+
["protocol"] = new[] { "https" },
22+
["host"] = new[] { "example.com" },
23+
["path"] = new[] { "an/example/path" },
24+
["username"] = new[] { "john.doe" },
25+
["password"] = new[] { "password123" },
26+
["wwwauth"] = new[]
27+
{
28+
"basic realm=\"example.com\"",
29+
"bearer authorize_uri=https://id.example.com p=1 q=0"
30+
}
2531
};
2632

2733
var inputArgs = new InputArguments(dict);
@@ -31,10 +37,16 @@ public void InputArguments_CommonArguments_ValuePresent_ReturnsValues()
3137
Assert.Equal("an/example/path", inputArgs.Path);
3238
Assert.Equal("john.doe", inputArgs.UserName);
3339
Assert.Equal("password123", inputArgs.Password);
40+
Assert.Equal(new[]
41+
{
42+
"basic realm=\"example.com\"",
43+
"bearer authorize_uri=https://id.example.com p=1 q=0"
44+
},
45+
inputArgs.WwwAuth);
3446
}
3547

3648
[Fact]
37-
public void InputArguments_CommonArguments_ValueMissing_ReturnsNull()
49+
public void InputArguments_CommonArguments_ValueMissing_ReturnsNullOrEmptyCollection()
3850
{
3951
var dict = new Dictionary<string, string>();
4052

@@ -45,20 +57,23 @@ public void InputArguments_CommonArguments_ValueMissing_ReturnsNull()
4557
Assert.Null(inputArgs.Path);
4658
Assert.Null(inputArgs.UserName);
4759
Assert.Null(inputArgs.Password);
60+
Assert.Empty(inputArgs.WwwAuth);
4861
}
4962

5063
[Fact]
5164
public void InputArguments_OtherArguments()
5265
{
53-
var dict = new Dictionary<string, string>
66+
var dict = new Dictionary<string, IList<string>>
5467
{
55-
["foo"] = "bar"
68+
["foo"] = new[] { "bar" },
69+
["multi"] = new[] { "val1", "val2", "val3" },
5670
};
5771

5872
var inputArgs = new InputArguments(dict);
5973

6074
Assert.Equal("bar", inputArgs["foo"]);
6175
Assert.Equal("bar", inputArgs.GetArgumentOrDefault("foo"));
76+
Assert.Equal(new[] { "val1", "val2", "val3" }, inputArgs.GetMultiArgumentOrDefault("multi"));
6277
}
6378

6479
[Fact]

src/shared/Core/Commands/GitCommandBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ internal async Task ExecuteAsync()
3333

3434
// Parse standard input arguments
3535
// git-credential treats the keys as case-sensitive; so should we.
36-
IDictionary<string, string> inputDict = await Context.Streams.In.ReadDictionaryAsync(StringComparer.Ordinal);
36+
IDictionary<string, IList<string>> inputDict = await Context.Streams.In.ReadMultiDictionaryAsync(StringComparer.Ordinal);
3737
var input = new InputArguments(inputDict);
3838

3939
// Validate minimum arguments are present

src/shared/Core/InputArguments.cs

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,24 @@ namespace GitCredentialManager
1515
/// </remarks>
1616
public class InputArguments
1717
{
18-
private readonly IReadOnlyDictionary<string, string> _dict;
18+
private readonly IReadOnlyDictionary<string, IList<string>> _dict;
1919

2020
public InputArguments(IDictionary<string, string> dict)
2121
{
22-
if (dict == null)
23-
{
24-
throw new ArgumentNullException(nameof(dict));
25-
}
22+
EnsureArgument.NotNull(dict, nameof(dict));
23+
24+
// Transform input from 1:1 to 1:n and store as readonly
25+
_dict = new ReadOnlyDictionary<string, IList<string>>(
26+
dict.ToDictionary(x => x.Key, x => (IList<string>)new[] { x.Value })
27+
);
28+
}
29+
30+
public InputArguments(IDictionary<string, IList<string>> dict)
31+
{
32+
EnsureArgument.NotNull(dict, nameof(dict));
2633

2734
// Wrap the dictionary internally as readonly
28-
_dict = new ReadOnlyDictionary<string, string>(dict);
35+
_dict = new ReadOnlyDictionary<string, IList<string>>(dict);
2936
}
3037

3138
#region Common Arguments
@@ -35,6 +42,7 @@ public InputArguments(IDictionary<string, string> dict)
3542
public string Path => GetArgumentOrDefault("path");
3643
public string UserName => GetArgumentOrDefault("username");
3744
public string Password => GetArgumentOrDefault("password");
45+
public IList<string> WwwAuth => GetMultiArgumentOrDefault("wwwauth");
3846

3947
#endregion
4048

@@ -50,9 +58,33 @@ public string GetArgumentOrDefault(string key)
5058
return TryGetArgument(key, out string value) ? value : null;
5159
}
5260

61+
public IList<string> GetMultiArgumentOrDefault(string key)
62+
{
63+
return TryGetMultiArgument(key, out IList<string> values) ? values : Array.Empty<string>();
64+
}
65+
5366
public bool TryGetArgument(string key, out string value)
5467
{
55-
return _dict.TryGetValue(key, out value);
68+
if (_dict.TryGetValue(key, out IList<string> values))
69+
{
70+
value = values.FirstOrDefault();
71+
return value != null;
72+
}
73+
74+
value = null;
75+
return false;
76+
}
77+
78+
public bool TryGetMultiArgument(string key, out IList<string> value)
79+
{
80+
if (_dict.TryGetValue(key, out IList<string> values))
81+
{
82+
value = values;
83+
return true;
84+
}
85+
86+
value = null;
87+
return false;
5688
}
5789

5890
public bool TryGetHostAndPort(out string host, out int? port)

src/shared/Core/Trace.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -217,24 +217,25 @@ public void WriteDictionarySecrets<TKey, TValue>(
217217
bool isSecretEntry = !(secretKeys is null) &&
218218
secretKeys.Contains(entry.Key, keyComparer ?? EqualityComparer<TKey>.Default);
219219

220-
void WriteSecretLine(object value)
220+
void WriteSecretLine(string keySuffix, object value)
221221
{
222222
var message = isSecretEntry && !IsSecretTracingEnabled
223-
? $"\t{entry.Key}={SecretMask}"
224-
: $"\t{entry.Key}={value}";
223+
? $"\t{entry.Key}{keySuffix}={SecretMask}"
224+
: $"\t{entry.Key}{keySuffix}={value}";
225225
WriteLine(message, filePath, lineNumber, memberName);
226226
}
227227

228228
if (entry.Value is IEnumerable<string> values)
229229
{
230-
foreach (string value in values)
230+
List<string> valueList = values.ToList();
231+
foreach (string value in valueList)
231232
{
232-
WriteSecretLine(value);
233+
WriteSecretLine(valueList.Count > 1 ? "[]" : string.Empty, value);
233234
}
234235
}
235236
else
236237
{
237-
WriteSecretLine(entry.Value);
238+
WriteSecretLine(string.Empty, entry.Value);
238239
}
239240
}
240241
}

0 commit comments

Comments
 (0)