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 = input.GetRemoteUri(); // 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, input.UserName)); }
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 // Perform auto-detection network probe and remember the result // _context.Trace.WriteLine("Performing auto-detection of host provider."); var uri = input.GetRemoteUri(); if (uri is null) { throw new Exception("Unable to detect host provider without a remote URL"); } 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 IHostProvider match = await MatchProviderAsync(HostProviderPriority.High) ?? await MatchProviderAsync(HostProviderPriority.Normal) ?? await MatchProviderAsync(HostProviderPriority.Low) ?? throw new Exception("No host provider available to service this request."); // If we ended up making a network call then set the host provider explicitly // to avoid future calls! if (probeResponse != null) { IGitConfiguration gitConfig = _context.Git.GetConfiguration(); var keyName = string.Format(CultureInfo.InvariantCulture, "{0}.{1}.{2}", Constants.GitConfiguration.Credential.SectionName, uri.ToString().TrimEnd('/'), Constants.GitConfiguration.Credential.Provider); try { _context.Trace.WriteLine($"Remembering host provider for '{uri}' as '{match.Id}'..."); gitConfig.Set(GitConfigurationLevel.Global, keyName, match.Id); } catch (Exception ex) { _context.Trace.WriteLine("Failed to set host provider!"); _context.Trace.WriteException(ex); _context.Streams.Error.WriteLine("warning: failed to remember result of host provider detection!"); _context.Streams.Error.WriteLine($"warning: try setting this manually: `git config --global {keyName} {match.Id}`"); } } return(match); }
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('/')); }