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);
        }
Example #3
0
        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));
        }
Example #4
0
        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);
        }
Example #5
0
 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('/'));
 }