public virtual Task EraseCredentialAsync(InputArguments input) { // Try to locate an existing credential with the computed key string credentialKey = GetCredentialKey(input); Context.Trace.WriteLine($"Looking for existing credential in store with key '{credentialKey}'..."); ICredential credential = Context.CredentialStore.Get(credentialKey); if (credential == null) { Context.Trace.WriteLine("No stored credential was found."); return(Task.CompletedTask); } else { Context.Trace.WriteLine("Existing credential found."); } // If we've been given a specific username and/or password we should only proceed // to erase the stored credential if they match exactly if (!string.IsNullOrWhiteSpace(input.UserName) && !StringComparer.Ordinal.Equals(input.UserName, credential.UserName)) { Context.Trace.WriteLine("Stored username does not match specified username - not erasing credential."); Context.Trace.WriteLine($"\tInput username={input.UserName}"); Context.Trace.WriteLine($"\tStored username={credential.UserName}"); return(Task.CompletedTask); } if (!string.IsNullOrWhiteSpace(input.Password) && !StringComparer.Ordinal.Equals(input.Password, credential.Password)) { Context.Trace.WriteLine("Stored password does not match specified password - not erasing credential."); Context.Trace.WriteLineSecrets("\tInput password={0}", new object[] { input.Password }); Context.Trace.WriteLineSecrets("\tStored password={0}", new object[] { credential.Password }); return(Task.CompletedTask); } Context.Trace.WriteLine("Erasing stored credential..."); if (Context.CredentialStore.Remove(credentialKey)) { Context.Trace.WriteLine("Credential was successfully erased."); } else { Context.Trace.WriteLine("Credential erase failed."); } return(Task.CompletedTask); }
public virtual Task EraseCredentialAsync(InputArguments input) { string service = GetServiceName(input); // Try to locate an existing credential Context.Trace.WriteLine($"Erasing stored credential in store with service={service} account={input.UserName}..."); if (Context.CredentialStore.Remove(service, input.UserName)) { Context.Trace.WriteLine("Credential was successfully erased."); } else { Context.Trace.WriteLine("No credential was erased."); } return(Task.CompletedTask); }
public virtual Task StoreCredentialAsync(InputArguments input) { string service = GetServiceName(input); // WIA-authentication is signaled to Git as an empty username/password pair // and we will get called to 'store' these WIA credentials. // We avoid storing empty credentials. if (string.IsNullOrWhiteSpace(input.UserName) && string.IsNullOrWhiteSpace(input.Password)) { Context.Trace.WriteLine("Not storing empty credential."); } else { // Add or update the credential in the store. Context.Trace.WriteLine($"Storing credential with service={service} account={input.UserName}..."); Context.CredentialStore.AddOrUpdate(service, input.UserName, input.Password); Context.Trace.WriteLine("Credential was successfully stored."); } return(Task.CompletedTask); }
public override async Task <ICredential> GenerateCredentialAsync(InputArguments input) { ThrowIfDisposed(); Uri uri = GetUriFromInput(input); // Determine the if the host supports Windows Integration Authentication (WIA) if (IsWindowsAuthAllowed) { if (PlatformUtils.IsWindows()) { Context.Trace.WriteLine($"Checking host '{uri.AbsoluteUri}' for Windows Integrated Authentication..."); bool isWiaSupported = await _winAuth.GetIsSupportedAsync(uri); if (!isWiaSupported) { Context.Trace.WriteLine("Host does not support WIA."); } else { Context.Trace.WriteLine("Host supports WIA - generating empty credential..."); // WIA is signaled to Git using an empty username/password return(new GitCredential(string.Empty, string.Empty)); } } else { string osType = PlatformUtils.GetPlatformInformation().OperatingSystemType; Context.Trace.WriteLine($"Skipping check for Windows Integrated Authentication on {osType}."); } } else { Context.Trace.WriteLine("Windows Integrated Authentication detection has been disabled."); } Context.Trace.WriteLine("Prompting for basic credentials..."); return(_basicAuth.GetCredentials(uri.AbsoluteUri, uri.UserInfo)); }
public override string GetCredentialKey(InputArguments input) { return($"git:{GetUriFromInput(input).AbsoluteUri}"); }
public override bool IsSupported(InputArguments input) { return(input != null && (StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http") || StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "https"))); }
public abstract bool IsSupported(InputArguments input);
/// <summary> /// Create a new credential used for accessing the remote Git repository on this hosting service. /// </summary> /// <param name="input">Input arguments of a Git credential query.</param> /// <returns>A credential Git can use to authenticate to the remote repository.</returns> public abstract Task <ICredential> GenerateCredentialAsync(InputArguments input);
/// <summary> /// Return a string that uniquely identifies the service that a credential should be stored against. /// </summary> /// <remarks> /// <para> /// This key forms part of the identifier used to retrieve and store credentials from the OS secure /// credential storage system. It is important the returned value is stable over time to avoid any /// potential re-authentication requests. /// </para> /// <para> /// The default implementation returns the absolute URI formed by from the <see cref="InputArguments"/> /// without any userinfo component. Any trailing slashes are trimmed. /// </para> /// </remarks> /// <param name="input">Input arguments of a Git credential query.</param> /// <returns>Credential service name.</returns> public virtual string GetServiceName(InputArguments input) { // By default we assume the service name will be the absolute URI based on the // input arguments from Git, without any userinfo part. return(input.GetRemoteUri(includeUser: false).AbsoluteUri.TrimEnd('/')); }
public async Task <IHostProvider> GetProviderAsync(InputArguments input) { IHostProvider provider; // // Try and locate a specified provider // if (_context.Settings.ProviderOverride is string providerId) { _context.Trace.WriteLine($"Host provider override was set id='{providerId}'"); if (!StringComparer.OrdinalIgnoreCase.Equals(Constants.ProviderIdAuto, providerId)) { provider = _hostProviders .SelectMany(x => x.Value) .FirstOrDefault(x => StringComparer.OrdinalIgnoreCase.Equals(x.Id, providerId)); if (provider is null) { _context.Trace.WriteLine($"No host provider was found with ID '{providerId}'.. falling back to auto-detection."); _context.Streams.Error.WriteLine($"warning: a host provider override was set but no such provider '{providerId}' was found. Falling back to auto-detection."); } else { return(provider); } } } // // Try and locate a provider by supported authorities // else if (_context.Settings.LegacyAuthorityOverride is string authority) { _context.Trace.WriteLine($"Host provider authority override was set authority='{authority}'"); _context.Streams.Error.WriteLine("warning: the `credential.authority` and `GCM_AUTHORITY` settings are deprecated."); _context.Streams.Error.WriteLine($"warning: see {Constants.HelpUrls.GcmAuthorityDeprecated} for more information."); if (!StringComparer.OrdinalIgnoreCase.Equals(Constants.AuthorityIdAuto, authority)) { provider = _hostProviders .SelectMany(x => x.Value) .FirstOrDefault(x => x.SupportedAuthorityIds.Contains(authority, StringComparer.OrdinalIgnoreCase)); if (provider is null) { _context.Trace.WriteLine($"No host provider was found with authority '{authority}'.. falling back to auto-detection."); _context.Streams.Error.WriteLine($"warning: a supported authority override was set but no such provider supporting authority '{authority}' was found. Falling back to auto-detection."); } else { return(provider); } } } // // Auto-detection // _context.Trace.WriteLine("Performing auto-detection of host provider."); var uri = input.GetRemoteUri(); var queryResponse = new Lazy <Task <HttpResponseMessage> >(() => { _context.Trace.WriteLine("Querying remote URL for host provider auto-detection."); return(HttpClient.HeadAsync(uri)); }); async Task <IHostProvider> MatchProviderAsync(HostProviderPriority priority) { if (_hostProviders.TryGetValue(priority, out ICollection <IHostProvider> providers)) { _context.Trace.WriteLine($"Checking against {providers.Count} host providers registered with priority '{priority}'."); // Try matching using the static Git input arguments first (cheap) if (providers.TryGetFirst(x => x.IsSupported(input), out IHostProvider match)) { return(match); } HttpResponseMessage response = await queryResponse.Value; // Try matching using the HTTP response from a query to the remote URL (expensive) if (providers.TryGetFirst(x => x.IsSupported(response), out match)) { return(match); } } return(null); } // Match providers starting with the highest priority return(await MatchProviderAsync(HostProviderPriority.High) ?? await MatchProviderAsync(HostProviderPriority.Normal) ?? await MatchProviderAsync(HostProviderPriority.Low) ?? throw new Exception("No host provider available to service this request.")); }
public async Task <IHostProvider> GetProviderAsync(InputArguments input) { IHostProvider provider; // // Try and locate a specified provider // if (_context.Settings.ProviderOverride is string providerId) { _context.Trace.WriteLine($"Host provider override was set id='{providerId}'"); if (!StringComparer.OrdinalIgnoreCase.Equals(Constants.ProviderIdAuto, providerId)) { provider = _hostProviders .SelectMany(x => x.Value) .FirstOrDefault(x => StringComparer.OrdinalIgnoreCase.Equals(x.Id, providerId)); if (provider is null) { _context.Trace.WriteLine($"No host provider was found with ID '{providerId}'.. falling back to auto-detection."); _context.Streams.Error.WriteLine($"warning: a host provider override was set but no such provider '{providerId}' was found. Falling back to auto-detection."); } else { return(provider); } } } // // Try and locate a provider by supported authorities // else if (_context.Settings.LegacyAuthorityOverride is string authority) { _context.Trace.WriteLine($"Host provider authority override was set authority='{authority}'"); _context.Streams.Error.WriteLine("warning: the `credential.authority` and `GCM_AUTHORITY` settings are deprecated."); _context.Streams.Error.WriteLine($"warning: see {Constants.HelpUrls.GcmAuthorityDeprecated} for more information."); if (!StringComparer.OrdinalIgnoreCase.Equals(Constants.AuthorityIdAuto, authority)) { provider = _hostProviders .SelectMany(x => x.Value) .FirstOrDefault(x => x.SupportedAuthorityIds.Contains(authority, StringComparer.OrdinalIgnoreCase)); if (provider is null) { _context.Trace.WriteLine($"No host provider was found with authority '{authority}'.. falling back to auto-detection."); _context.Streams.Error.WriteLine($"warning: a supported authority override was set but no such provider supporting authority '{authority}' was found. Falling back to auto-detection."); } else { return(provider); } } } // // Auto-detection // _context.Trace.WriteLine("Performing auto-detection of host provider."); var uri = input.GetRemoteUri(); var probeTimeout = TimeSpan.FromMilliseconds(_context.Settings.AutoDetectProviderTimeout); _context.Trace.WriteLine($"Auto-detect probe timeout is {probeTimeout.TotalSeconds} ms."); HttpResponseMessage probeResponse = null; async Task <IHostProvider> MatchProviderAsync(HostProviderPriority priority) { if (_hostProviders.TryGetValue(priority, out ICollection <IHostProvider> providers)) { _context.Trace.WriteLine($"Checking against {providers.Count} host providers registered with priority '{priority}'."); // Try matching using the static Git input arguments first (cheap) if (providers.TryGetFirst(x => x.IsSupported(input), out IHostProvider match)) { return(match); } // Try matching using the HTTP response from a query to the remote URL (expensive). // The user may have disabled this feature with a zero or negative timeout for performance reasons. // We only probe the remote once and reuse the same response for all providers. if (probeTimeout.TotalMilliseconds > 0) { if (probeResponse is null) { _context.Trace.WriteLine("Querying remote URL for host provider auto-detection."); using (HttpClient client = _context.HttpClientFactory.CreateClient()) { client.Timeout = probeTimeout; try { probeResponse = await client.HeadAsync(uri); } catch (TaskCanceledException) { _context.Streams.Error.WriteLine($"warning: auto-detection of host provider took too long (>{probeTimeout.TotalMilliseconds}ms)"); _context.Streams.Error.WriteLine($"warning: see {Constants.HelpUrls.GcmAutoDetect} for more information."); } catch (Exception ex) { // The auto detect probing failed for some other reason. // We don't particular care why, but we should not crash! _context.Streams.Error.WriteLine($"warning: failed to probe '{uri}' to detect provider"); _context.Streams.Error.WriteLine($"warning: {ex.Message}"); _context.Streams.Error.WriteLine($"warning: see {Constants.HelpUrls.GcmAutoDetect} for more information."); } } } if (providers.TryGetFirst(x => x.IsSupported(probeResponse), out match)) { return(match); } } } return(null); } // Match providers starting with the highest priority return(await MatchProviderAsync(HostProviderPriority.High) ?? await MatchProviderAsync(HostProviderPriority.Normal) ?? await MatchProviderAsync(HostProviderPriority.Low) ?? throw new Exception("No host provider available to service this request.")); }
/// <summary> /// Return a key that uniquely represents the given Git credential query arguments. /// </summary> /// <remarks> /// This key forms part of the identifier used to retrieve and store credentials from the OS secure /// credential storage system. It is important the returned value is stable over time to avoid any /// potential re-authentication requests. /// </remarks> /// <param name="input">Input arguments of a Git credential query.</param> /// <returns>Stable credential key.</returns> public abstract string GetCredentialKey(InputArguments input);
public IHostProvider GetProvider(InputArguments input) { IHostProvider provider; // // Try and locate a specified provider // if (_context.Settings.ProviderOverride is string providerId) { _context.Trace.WriteLine($"Host provider override was set id='{providerId}'"); if (!StringComparer.OrdinalIgnoreCase.Equals(Constants.ProviderIdAuto, providerId)) { provider = _hostProviders.FirstOrDefault(x => StringComparer.OrdinalIgnoreCase.Equals(x.Id, providerId)); if (provider is null) { _context.Trace.WriteLine($"No host provider was found with ID '{providerId}'.. falling back to auto-detection."); _context.Streams.Error.WriteLine($"warning: a host provider override was set but no such provider '{providerId}' was found. Falling back to auto-detection."); } else { return(provider); } } } // // Try and locate a provider by supported authorities // else if (_context.Settings.LegacyAuthorityOverride is string authority) { _context.Trace.WriteLine($"Host provider authority override was set authority='{authority}'"); _context.Streams.Error.WriteLine("warning: the `credential.authority` and `GCM_AUTHORITY` settings are deprecated."); _context.Streams.Error.WriteLine($"warning: see {Constants.HelpUrls.GcmAuthorityDeprecated} for more information."); if (!StringComparer.OrdinalIgnoreCase.Equals(Constants.AuthorityIdAuto, authority)) { provider = _hostProviders.FirstOrDefault(x => x.SupportedAuthorityIds.Contains(authority, StringComparer.OrdinalIgnoreCase)); if (provider is null) { _context.Trace.WriteLine($"No host provider was found with authority '{authority}'.. falling back to auto-detection."); _context.Streams.Error.WriteLine($"warning: a supported authority override was set but no such provider supporting authority '{authority}' was found. Falling back to auto-detection."); } else { return(provider); } } } // // Auto-detection // _context.Trace.WriteLine("Performing auto-detection of host provider."); provider = _hostProviders.FirstOrDefault(x => x.IsSupported(input)); if (provider is null) { throw new Exception("No host provider available to service this request."); } return(provider); }