-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
Copy pathSettings.cs
853 lines (729 loc) · 36.2 KB
/
Settings.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using KnownEnvars = GitCredentialManager.Constants.EnvironmentVariables;
using KnownGitCfg = GitCredentialManager.Constants.GitConfiguration;
using GitCredCfg = GitCredentialManager.Constants.GitConfiguration.Credential;
using GitHttpCfg = GitCredentialManager.Constants.GitConfiguration.Http;
namespace GitCredentialManager
{
/// <summary>
/// Component that represents settings for Git Credential Manager as found from the environment and Git configuration.
/// Setting values from Git configuration may be cached for performance reasons.
/// </summary>
public interface ISettings : IDisposable
{
/// <summary>
/// Try and get the value of a specified setting as specified in the environment and Git configuration,
/// with the environment taking precedence over Git.
/// </summary>
/// <param name="envarName">Optional environment variable name.</param>
/// <param name="section">Optional Git configuration section name.</param>
/// <param name="property">Git configuration property name. Required if <paramref name="section"/> is set, optional otherwise.</param>
/// <param name="value">Value of the requested setting.</param>
/// <returns>True if a setting value was found, false otherwise.</returns>
bool TryGetSetting(string envarName, string section, string property, out string value);
/// <summary>
/// Try and get the value of a specified setting as specified in the environment and Git configuration,
/// with the environment taking precedence over Git. If the value is pulled from the Git configuration,
/// it is returned as a canonical path.
/// </summary>
/// <param name="envarName">Optional environment variable name.</param>
/// <param name="section">Optional Git configuration section name.</param>
/// <param name="property">Git configuration property name. Required if <paramref name="section"/> is set, optional otherwise.</param>
/// <param name="value">Value of the requested setting as a canonical path.</param>
/// <returns>True if a setting value was found, false otherwise.</returns>
bool TryGetPathSetting(string envarName, string section, string property, out string value);
/// <summary>
/// Try and get the all values of a specified setting as specified in the environment and Git configuration,
/// in the correct order or precedence.
/// </summary>
/// <param name="envarName">Optional environment variable name.</param>
/// <param name="section">Optional Git configuration section name.</param>
/// <param name="property">Git configuration property name. Required if <paramref name="section"/> is set, optional otherwise.</param>
/// <param name="isPath">Whether the returned values should be transformed into canonical paths.</param>
/// <returns>All values for the specified setting, in order of precedence, or an empty collection if no such values are set.</returns>
IEnumerable<string> GetSettingValues(string envarName, string section, string property, bool isPath);
/// <summary>
/// Git remote address that setting lookup is scoped to, or null if no remote URL has been discovered.
/// </summary>
Uri RemoteUri { get; set; }
/// <summary>
/// True if debugging is enabled, false otherwise.
/// </summary>
bool IsDebuggingEnabled { get; }
/// <summary>
/// True if terminal prompting is enabled, false otherwise.
/// </summary>
bool IsTerminalPromptsEnabled { get; }
/// <summary>
/// True if GUI prompts are enabled, false otherwise.
/// </summary>
/// <remarks>
/// If GUI prompts are disabled but an equivalent terminal prompt is available, the latter will be used instead.
/// </remarks>
bool IsGuiPromptsEnabled { get; set; }
/// <summary>
/// True if it is permitted to interact with the user, false otherwise.
/// </summary>
/// <remarks>
/// If this value is false but interactivity is required to continue execution, the caller should
/// abort the operation and report failure.
/// </remarks>
bool IsInteractionAllowed { get; }
/// <summary>
/// Get if tracing has been enabled, returning trace setting value in the out parameter.
/// </summary>
/// <param name="value">Trace setting value.</param>
/// <returns>True if tracing is enabled, false otherwise.</returns>
bool GetTracingEnabled(out string value);
/// <summary>
/// True if tracing of secrets and sensitive information is enabled, false otherwise.
/// </summary>
bool IsSecretTracingEnabled { get; }
/// <summary>
/// True if MSAL tracing is enabled, false otherwise.
/// </summary>
bool IsMsalTracingEnabled { get; }
/// <summary>
/// Get the host provider configured to override auto-detection if set, null otherwise.
/// </summary>
string ProviderOverride { get; }
/// <summary>
/// Get the authority name configured to override host provider auto-detection if set, null otherwise.
/// </summary>
string LegacyAuthorityOverride { get; }
/// <summary>
/// True if Windows Integrated Authentication (NTLM, Kerberos) should be detected and used if available, false otherwise.
/// </summary>
bool IsWindowsIntegratedAuthenticationEnabled { get; }
/// <summary>
/// True if certificate verification should occur, false otherwise.
/// </summary>
bool IsCertificateVerificationEnabled { get; }
/// <summary>
/// Automatically send client TLS certificates.
/// </summary>
bool AutomaticallyUseClientCertificates { get; }
/// <summary>
/// Get the proxy setting if configured, or null otherwise.
/// </summary>
/// <returns>Proxy setting, or null if not configured.</returns>
ProxyConfiguration GetProxyConfiguration();
/// <summary>
/// The parent window handle/ID. Used to correctly position and parent dialogs generated by GCM.
/// </summary>
/// <remarks>This value is platform specific.</remarks>
string ParentWindowId { get; }
/// <summary>
/// Credential storage namespace prefix.
/// </summary>
/// <remarks>The default value is "git" if unset.</remarks>
string CredentialNamespace { get; }
/// <summary>
/// Credential backing store override.
/// </summary>
string CredentialBackingStore { get; }
/// <summary>
/// Optional path to a file containing one or more certificates that should
/// be used *exclusively* when verifying server certificate chains.
/// </summary>
/// <remarks>The default value is null if unset.</remarks>
string CustomCertificateBundlePath { get; }
/// <summary>
// Optional path to a file containing one or more cookies.
/// </summary>
/// <remarks>The default value is null if unset.</remarks>
string CustomCookieFilePath { get; }
/// <summary>
/// The SSL/TLS backend.
/// </summary>
TlsBackend TlsBackend { get; }
/// <summary>
/// True if, when using an schannel backend, using certificates from the
/// CustomCertificateBundlePath is allowed.
/// </summary>
/// <remarks>The default value is false if unset.</remarks>
bool UseCustomCertificateBundleWithSchannel { get; }
/// <summary>
/// Maximum number of milliseconds to wait for a network response when probing a remote URL for the purpose
/// of host provider auto-detection. Use a zero or negative value to disable probing.
/// </summary>
int AutoDetectProviderTimeout { get; }
/// <summary>
/// Automatically use the default/current operating system account if no other account information is given
/// for Microsoft Authentication.
/// </summary>
bool UseMsAuthDefaultAccount { get; }
/// <summary>
/// True if software rendering should be used for graphical user interfaces, false otherwise.
/// </summary>
bool UseSoftwareRendering { get; }
/// <summary>
/// Permit the use of unsafe remotes URLs such as regular HTTP.
/// </summary>
bool AllowUnsafeRemotes { get; }
/// <summary>
/// Get TRACE2 settings.
/// </summary>
/// <returns>TRACE2 settings object.</returns>
Trace2Settings GetTrace2Settings();
}
public class ProxyConfiguration
{
public ProxyConfiguration(
Uri proxyAddress,
string userName = null,
string password = null,
string noProxyRaw = null,
bool isDeprecatedSource = false)
{
Address = proxyAddress;
UserName = userName;
Password = password;
NoProxyRaw = noProxyRaw;
IsDeprecatedSource = isDeprecatedSource;
}
/// <summary>
/// True if the proxy configuration method is deprecated, false otherwise.
/// </summary>
public bool IsDeprecatedSource { get; }
/// <summary>
/// Configured proxy URI (proxy server address and optional user authentication information).
/// </summary>
public Uri Address { get; }
/// <summary>
/// User name to use to authenticate to the proxy address.
/// </summary>
public string UserName { get; }
/// <summary>
/// Password to use to authenticate to the proxy address.
/// </summary>
public string Password { get; }
/// <summary>
/// List of host names that should not be proxied.
/// </summary>
/// <remarks>
/// This is the raw value from the NO_PROXY setting. Values are expected to be in a libcurl compatible format.
/// <para/>
/// To convert the string in to a set of .NET regular expressions for use with proxy settings,
/// use the <see cref="ConvertToBypassRegexArray"/> method.
/// </remarks>
public string NoProxyRaw { get; }
/// <summary>
/// Convert a libcurl-format NO_PROXY string in to a set of equivalent .NET regular expressions.
/// </summary>
/// <param name="noProxy">NO_PROXY value in a libcurl-compatible format.</param>
/// <returns>Array of regular expressions.</returns>
public static IEnumerable<string> ConvertToBypassRegexArray(string noProxy)
{
if (string.IsNullOrWhiteSpace(noProxy))
{
yield break;
}
string[] split = noProxy.Split(new[] { ",", " " }, StringSplitOptions.RemoveEmptyEntries);
var normalized = new StringBuilder();
var regex = new StringBuilder();
foreach (string str in split)
{
// Normalize the domain search value
normalized.Clear();
normalized.Append(str);
// Strip leading subdomain wildcards: *.example.com => example.com
if (normalized.Length > 1 && normalized[0] == '*' && normalized[1] == '.')
{
normalized.Remove(0, 2);
}
// Strip all leading dots: .example.com => example.com
while (normalized.Length > 0 && normalized[0] == '.')
{
normalized.Remove(0, 1);
}
// Build the regular expression
regex.Clear();
// Only match (sub-)domains, not partial domain names.
// For example: "example.com" should match "http://example.com" and
// "http://www.example.com" but not "http://notanexample.com".
regex.Append(@"(\.|\:\/\/)");
// Add the escaped domain search value
regex.Append(Regex.Escape(normalized.ToString()));
// Ensure we only match the specified port and TLD
regex.Append('$');
yield return regex.ToString();
}
}
}
public enum TlsBackend
{
OpenSsl,
Schannel,
Other,
}
public class Settings : ISettings
{
private readonly IEnvironment _environment;
private readonly IGit _git;
private Dictionary<string,string> _configEntries;
public Settings(IEnvironment environment, IGit git)
{
EnsureArgument.NotNull(environment, nameof(environment));
EnsureArgument.NotNull(git, nameof(git));
_environment = environment;
_git = git;
}
public bool TryGetSetting(string envarName, string section, string property, out string value)
{
IEnumerable<string> allValues = GetSettingValues(envarName, section, property, false);
value = allValues.FirstOrDefault();
return value != null;
}
public bool TryGetPathSetting(string envarName, string section, string property, out string value)
{
IEnumerable<string> allValues = GetSettingValues(envarName, section, property, true);
value = allValues.FirstOrDefault();
return value != null;
}
public IEnumerable<string> GetSettingValues(string envarName, string section, string property, bool isPath)
{
string value;
if (envarName != null)
{
if (_environment.Variables.TryGetValue(envarName, out value))
{
yield return value;
}
}
if (section != null && property != null)
{
IGitConfiguration config = _git.GetConfiguration();
//
// Enumerate all configuration entries for all sections and property names and make a
// local copy of them here to avoid needing to call `TryGetValue` on the IGitConfiguration
// object multiple times in a loop below.
//
// This is a performance optimisation to avoid calling `TryGet` on the IGitConfiguration
// object multiple times in a loop below, or each time this entire method is called.
// The assumption is that the configuration entries will not change during a single invocation
// of Git Credential Manager, which is reasonable given process lifetime is typically less
// than a few seconds. For some entries (type=path), we still need to ask Git in order to
// expand the path correctly.
//
if (_configEntries is null)
{
_configEntries = new Dictionary<string, string>(GitConfigurationKeyComparer.Instance);
config.Enumerate(entry =>
{
_configEntries[entry.Key] = entry.Value;
// Continue the enumeration
return true;
});
}
if (RemoteUri != null)
{
/*
* Look for URL scoped "section" configuration entries, starting from the most specific
* down to the least specific (stopping before the TLD).
*
* In a divergence from standard Git configuration rules, we also consider matching URL scopes
* without a scheme ("protocol://").
*
* For each level of scope, we look for an entry with the scheme included (the default), and then
* also one without it specified. This allows you to have one configuration scope for both "http" and
* "https" without needing to repeat yourself, for example.
*
* For example, starting with "https://foo.example.com/bar/buzz" we have:
*
* 1a. [section "https://foo.example.com/bar/buzz"]
* property = value
*
* 1b. [section "foo.example.com/bar/buzz"]
* property = value
*
* 2a. [section "https://foo.example.com/bar"]
* property = value
*
* 2b. [section "foo.example.com/bar"]
* property = value
*
* 3a. [section "https://foo.example.com"]
* property = value
*
* 3b. [section "foo.example.com"]
* property = value
*
* 4a. [section "https://example.com"]
* property = value
*
* 4b. [section "example.com"]
* property = value
*
* It is also important to note that although the section and property names are NOT case
* sensitive, the "scope" part IS case sensitive! We must be careful when searching to ensure
* we follow Git's rules.
*
*/
foreach (string scope in RemoteUri.GetGitConfigurationScopes())
{
string queryName = $"{section}.{scope}.{property}";
// Look for a scoped entry that includes the scheme "protocol://example.com" first as
// this is more specific. If `isPath` is true, then re-get the value from the
// `GitConfiguration` with `isPath` specified.
if ((isPath && config.TryGet(queryName, true, out value)) ||
_configEntries.TryGetValue(queryName, out value))
{
yield return value;
}
// Now look for a scoped entry that omits the scheme "example.com" second as this is less
// specific. As above, if `isPath` is true, get the configuration setting again with
// `isPath` specified.
string scopeWithoutScheme = scope.TrimUntilIndexOf(Uri.SchemeDelimiter);
string queryWithSchemeName = $"{section}.{scopeWithoutScheme}.{property}";
if ((isPath && config.TryGet(queryWithSchemeName, true, out value)) ||
_configEntries.TryGetValue(queryWithSchemeName, out value))
{
yield return value;
}
// Check for an externally specified default value
if (TryGetExternalDefault(section, scope, property, out value))
{
yield return value;
}
}
}
/*
* Try to look for an un-scoped "section" property setting:
*
* [section]
* property = value
*
*/
string name = $"{section}.{property}";
if ((isPath && config.TryGet(name, true, out value)) ||
_configEntries.TryGetValue(name, out value))
{
yield return value;
}
// Check for an externally specified default value without a scope
if (TryGetExternalDefault(section, null, property, out value))
{
yield return value;
}
}
}
/// <summary>
/// Try to get the default value for a configuration setting.
/// This may come from external policies or the Operating System.
/// </summary>
/// <param name="section">Configuration section name.</param>
/// <param name="scope">Optional configuration scope.</param>
/// <param name="property">Configuration property name.</param>
/// <param name="value">Value of the configuration setting, or null.</param>
/// <returns>True if a default setting has been set, false otherwise.</returns>
protected internal virtual bool TryGetExternalDefault(string section, string scope, string property, out string value)
{
value = null;
return false;
}
public Uri RemoteUri { get; set; }
public bool IsDebuggingEnabled =>
TryGetSetting(KnownEnvars.GcmDebug,
KnownGitCfg.Credential.SectionName,
KnownGitCfg.Credential.Debug,
out string str) && str.IsTruthy();
public bool IsTerminalPromptsEnabled => _environment.Variables.GetBooleanyOrDefault(KnownEnvars.GitTerminalPrompts, true);
public bool IsGuiPromptsEnabled
{
get
{
const bool defaultValue = true;
if (TryGetSetting(
KnownEnvars.GcmGuiPromptsEnabled,
KnownGitCfg.Credential.SectionName,
KnownGitCfg.Credential.GuiPromptsEnabled,
out string str))
{
return str.ToBooleanyOrDefault(defaultValue);
}
return defaultValue;
}
set => _environment.SetEnvironmentVariable(KnownEnvars.GcmGuiPromptsEnabled, value ? bool.TrueString : bool.FalseString);
}
public bool IsInteractionAllowed
{
get
{
const bool defaultValue = true;
if (TryGetSetting(KnownEnvars.GcmInteractive, GitCredCfg.SectionName, GitCredCfg.Interactive, out string value))
{
/*
* COMPAT: In the previous GCM we accepted the values 'auto', 'never', and 'always'.
*
* We've slightly changed the behaviour of this setting in GCM to essentially
* remove the 'always' option. The table below outlines the changes:
*
* -------------------------------------------------------------
* | Value(s) | Old meaning | New meaning |
* |-----------------------------------------------------------|
* | auto | Prompt if required | [unchanged] |
* |-----------------------------------------------------------|
* | never | Never prompt ─ fail if | [unchanged] |
* | false | interaction is required | |
* |-----------------------------------------------------------|
* | always | Always prompt ─ don't use | Prompt if required |
* | force | cached credentials | |
* | true | | |
* -------------------------------------------------------------
*/
if (StringComparer.OrdinalIgnoreCase.Equals("never", value))
{
return false;
}
return value.ToBooleanyOrDefault(defaultValue);
}
return defaultValue;
}
}
public bool GetTracingEnabled(out string value) =>
TryGetSetting(KnownEnvars.GcmTrace,
KnownGitCfg.Credential.SectionName,
KnownGitCfg.Credential.Trace,
out value) && !value.IsFalsey();
public bool UseSoftwareRendering
{
get
{
// WORKAROUND: Some Windows ARM devices have a graphics driver issue that causes transparent windows
// when using hardware rendering. Until this is fixed, we will default to software rendering on these
// devices. Users can always override this setting back to HW-accelerated rendering if they wish.
bool defaultValue = PlatformUtils.IsWindows() && PlatformUtils.IsArm();
return TryGetSetting(KnownEnvars.GcmGuiSoftwareRendering,
KnownGitCfg.Credential.SectionName,
KnownGitCfg.Credential.GuiSoftwareRendering,
out string str) ? str.ToBooleanyOrDefault(defaultValue) : defaultValue;
}
}
public bool AllowUnsafeRemotes =>
TryGetSetting(KnownEnvars.GcmAllowUnsafeRemotes,
KnownGitCfg.Credential.SectionName,
KnownGitCfg.Credential.AllowUnsafeRemotes,
out string str) && str.ToBooleanyOrDefault(false);
public Trace2Settings GetTrace2Settings()
{
var settings = new Trace2Settings();
if (TryGetSetting(Constants.EnvironmentVariables.GitTrace2Event, KnownGitCfg.Trace2.SectionName,
Constants.GitConfiguration.Trace2.EventTarget, out string value))
{
settings.FormatTargetsAndValues.Add(Trace2FormatTarget.Event, value);
}
if (TryGetSetting(Constants.EnvironmentVariables.GitTrace2Normal, KnownGitCfg.Trace2.SectionName,
Constants.GitConfiguration.Trace2.NormalTarget, out value))
{
settings.FormatTargetsAndValues.Add(Trace2FormatTarget.Normal, value);
}
if (TryGetSetting(Constants.EnvironmentVariables.GitTrace2Performance, KnownGitCfg.Trace2.SectionName,
Constants.GitConfiguration.Trace2.PerformanceTarget, out value))
{
settings.FormatTargetsAndValues.Add(Trace2FormatTarget.Performance, value);
}
return settings;
}
public bool IsSecretTracingEnabled =>
TryGetSetting(KnownEnvars.GcmTraceSecrets,
KnownGitCfg.Credential.SectionName,
KnownGitCfg.Credential.TraceSecrets,
out string str) && str.IsTruthy();
public bool IsMsalTracingEnabled =>
TryGetSetting(KnownEnvars.GcmTraceMsAuth,
KnownGitCfg.Credential.SectionName,
KnownGitCfg.Credential.TraceMsAuth,
out string str) && str.IsTruthy();
public string ProviderOverride =>
TryGetSetting(KnownEnvars.GcmProvider, GitCredCfg.SectionName, GitCredCfg.Provider, out string providerId) ? providerId : null;
public string LegacyAuthorityOverride =>
TryGetSetting(KnownEnvars.GcmAuthority, GitCredCfg.SectionName, GitCredCfg.Authority, out string authority) ? authority : null;
public bool IsWindowsIntegratedAuthenticationEnabled =>
!TryGetSetting(KnownEnvars.GcmAllowWia, GitCredCfg.SectionName, GitCredCfg.AllowWia, out string value) || value.ToBooleanyOrDefault(true);
public bool IsCertificateVerificationEnabled
{
get
{
// Prefer environment variable
if (_environment.Variables.TryGetValue(KnownEnvars.GitSslNoVerify, out string envarValue))
{
return !envarValue.ToBooleanyOrDefault(false);
}
// Next try the equivalent Git configuration option
if (TryGetSetting(null, KnownGitCfg.Http.SectionName, KnownGitCfg.Http.SslVerify, out string cfgValue))
{
return cfgValue.ToBooleanyOrDefault(true);
}
// Safe default
return true;
}
}
public bool AutomaticallyUseClientCertificates =>
TryGetSetting(null, KnownGitCfg.Credential.SectionName, KnownGitCfg.Http.SslAutoClientCert, out string value) && value.ToBooleanyOrDefault(false);
public string CustomCertificateBundlePath =>
TryGetPathSetting(KnownEnvars.GitSslCaInfo, KnownGitCfg.Http.SectionName, KnownGitCfg.Http.SslCaInfo, out string value) ? value : null;
public string CustomCookieFilePath =>
TryGetPathSetting(null, KnownGitCfg.Http.SectionName, KnownGitCfg.Http.CookieFile, out string value) ? value : null;
public TlsBackend TlsBackend =>
TryGetSetting(null, KnownGitCfg.Http.SectionName, KnownGitCfg.Http.SslBackend, out string config)
? (Enum.TryParse(config, true, out TlsBackend backend) ? backend : GitCredentialManager.TlsBackend.Other)
: default(TlsBackend);
public bool UseCustomCertificateBundleWithSchannel =>
TryGetSetting(null, KnownGitCfg.Http.SectionName, KnownGitCfg.Http.SchannelUseSslCaInfo, out string schannelUseSslCaInfo) &&
schannelUseSslCaInfo.ToBooleanyOrDefault(false);
public int AutoDetectProviderTimeout
{
get
{
if (TryGetSetting(KnownEnvars.GcmAutoDetectTimeout,
KnownGitCfg.Credential.SectionName,
KnownGitCfg.Credential.AutoDetectTimeout,
out string valueStr) &&
ConvertUtils.TryToInt32(valueStr, out int value))
{
return value;
}
return Constants.DefaultAutoDetectProviderTimeoutMs;
}
}
public ProxyConfiguration GetProxyConfiguration()
{
bool TryGetUriSetting(string envarName, string section, string property, out Uri uri)
{
IEnumerable<string> allValues = GetSettingValues(envarName, section, property, false);
foreach (var value in allValues)
{
if (Uri.TryCreate(value, UriKind.Absolute, out uri))
{
return true;
}
else if (string.IsNullOrWhiteSpace(value))
{
// An empty string value means "no proxy"
return false;
}
}
uri = null;
return false;
}
char[] bypassListSeparators = {',', ' '};
ProxyConfiguration CreateConfiguration(Uri uri, bool isLegacy = false)
{
// Strip the userinfo, query, and fragment parts of the Uri retaining only the scheme, host, port, and path
Uri address = new UriBuilder(uri)
{
UserName = string.Empty,
Password = string.Empty,
Query = string.Empty,
Fragment = string.Empty,
}.Uri;
// Extract the username and password from the URI if present
uri.TryGetUserInfo(out string userName, out string password);
// Get the proxy bypass host names
// Check both lowercase "no_proxy" and uppercase "NO_PROXY" variants (preferring the former)
if (!_environment.Variables.TryGetValue(KnownEnvars.CurlNoProxy, out string noProxyStr))
{
_environment.Variables.TryGetValue(KnownEnvars.CurlNoProxyUpper, out noProxyStr);
}
return new ProxyConfiguration(address, userName, password, noProxyStr, isLegacy);
}
/*
* There are several different ways we support the configuration of a proxy.
*
* In order of preference:
*
* 1. GCM proxy Git configuration (deprecated)
* credential.httpsProxy
* credential.httpProxy
*
* 2. Standard Git configuration
* http.proxy
*
* 3. cURL environment variables
* https_proxy
* HTTPS_PROXY
* http_proxy (note that uppercase HTTP_PROXY is not supported by libcurl)
* all_proxy
* ALL_PROXY
*
* 4. GCM proxy environment variable (deprecated)
* GCM_HTTP_PROXY
*
* If the remote URI is HTTPS we check the HTTPS variants first, and fallback to the
* non-secure HTTP options if not found.
*
* For HTTP URIs we only check the HTTP variants.
*
* We also support the cURL "no_proxy" / "NO_PROXY" environment variables in conjunction with any
* of the above supported proxy address configurations. This comma separated list of
* host names (or host name wildcards) should be respected and the proxy should NOT
* be used for these addresses.
*
*/
bool isHttps = StringComparer.OrdinalIgnoreCase.Equals(Uri.UriSchemeHttps, RemoteUri?.Scheme);
Uri proxyUri;
// 1. GCM proxy Git configuration (deprecated)
if (isHttps && TryGetUriSetting(null, GitCredCfg.SectionName, GitCredCfg.HttpsProxy, out proxyUri) ||
TryGetUriSetting(null, GitCredCfg.SectionName, GitCredCfg.HttpProxy, out proxyUri))
{
return CreateConfiguration(proxyUri, isLegacy: true);
}
// 2. Standard Git configuration
if (TryGetUriSetting(null, GitHttpCfg.SectionName, GitHttpCfg.Proxy, out proxyUri))
{
return CreateConfiguration(proxyUri);
}
// 3. cURL environment variables (both lower- and uppercase variants)
// Prefer the lowercase variants as these are quasi-standard.
if (isHttps && TryGetUriSetting(KnownEnvars.CurlHttpsProxy, null, null, out proxyUri) ||
isHttps && TryGetUriSetting(KnownEnvars.CurlHttpsProxyUpper, null, null, out proxyUri) ||
TryGetUriSetting(KnownEnvars.CurlHttpProxy, null, null, out proxyUri) ||
// Note that the uppercase HTTP_PROXY is not recognized by libcurl
TryGetUriSetting(KnownEnvars.CurlAllProxy, null, null, out proxyUri) ||
TryGetUriSetting(KnownEnvars.CurlAllProxyUpper, null, null, out proxyUri))
{
return CreateConfiguration(proxyUri);
}
// 4. GCM proxy environment variable (deprecated)
if (TryGetUriSetting(KnownEnvars.GcmHttpProxy, null, null, out proxyUri))
{
return CreateConfiguration(proxyUri, isLegacy: true);
}
return null;
}
public string ParentWindowId => _environment.Variables.TryGetValue(KnownEnvars.GcmParentWindow, out string parentWindowId) ? parentWindowId : null;
public string CredentialNamespace =>
TryGetSetting(KnownEnvars.GcmCredNamespace,
KnownGitCfg.Credential.SectionName, KnownGitCfg.Credential.CredNamespace,
out string @namespace)
? @namespace
: Constants.DefaultCredentialNamespace;
public string CredentialBackingStore =>
TryGetSetting(
KnownEnvars.GcmCredentialStore,
KnownGitCfg.Credential.SectionName,
KnownGitCfg.Credential.CredentialStore,
out string credStore)
? credStore
: null;
public bool UseMsAuthDefaultAccount =>
TryGetSetting(
KnownEnvars.MsAuthUseDefaultAccount,
KnownGitCfg.Credential.SectionName,
KnownGitCfg.Credential.MsAuthUseDefaultAccount,
out string str)
? str.IsTruthy()
: PlatformUtils.IsDevBox(); // default to true in DevBox environment
#region IDisposable
public void Dispose()
{
// Do nothing
}
#endregion
}
}