chocolatey / choco

Chocolatey - the package manager for Windows
https://chocolatey.org
Other
10.28k stars 902 forks source link

Allow to reuse nuget credentials for choco #2221

Open tapika opened 3 years ago

tapika commented 3 years ago

It's possible to host nuget/choco packages on private feed in azure devops.

Private means that it also requires authentication to list, download or install any package from that feed.

Theoretically you can use choco's approach to configure credentials to azure devops -

https://docs.chocolatey.org/en-us/create/commands/api-key

and provide user name as login and azure devops Personal Authentication Token (PAT) as password.

From software developer perspective however - it's easier just to configure credentials once for both - nuget packages and for choco packages in one place and use them for all eternity (that is 1 year as long as PAT is valid).

You can configure and store nuget credentials in centralized manner - that is using %APPDATA%\NuGet\nuget.config folder.

(Instructions can be found from here for example: https://mallibone.com/post/private-nuget-feed-azure-devops )

It would simplify whole picture if choco could re-use same kind of credentials as nuget uses - that is same config file.

Based on my brief analysis there needs to be implemented two features:

  1. Url translation

https://....azure.com/.../nuget/v3/index.json => https://....azure.com/.../nuget/v2

Azure devops supports older communication protocols, which is used by choco - v2. Maybe choco can translate those url automatically (throw away v3... part and put v2 instead) - this is needed not only for command line, but also for url stored in nuget.config.

  1. Get user / password (PAT) from %APPDATA%\NuGet\nuget.config - if that file exists.

Using this approach it's possible to configure nuget and choco in centralized manner and use both as necessary.

tapika commented 3 years ago

Related tickets:

2146

1721

tapika commented 3 years ago

Patch should be trivial, e.g.

src/chocolatey/infrastructure.app/nuget/ChocolateyNugetCredentialProvider.cs

....

            // Fetch login / password from %APPDATA%\NuGet\nuget.config file
            if (source == null)
            {
                string nugetConfigDir = Environment.ExpandEnvironmentVariables(@"%APPDATA%\NuGet");
                string nugetConfig = System.IO.Path.Combine(nugetConfigDir, "nuget.config");

                if (System.IO.File.Exists(nugetConfig))
                {
                    try
                    {
                        var pfs = new PhysicalFileSystem(nugetConfigDir);
                        var settings = Settings.LoadDefaultSettings(pfs, nugetConfig, null);
                        var packageSourceProvider = new PackageSourceProvider(settings);
                        var sources = packageSourceProvider.LoadPackageSources().ToArray();

                        foreach (var configSource in sources)
                        {
                            string url = NugetCommon.UriBackwardsCompatibleSimplify(configSource.Source);
                            if (url == uri.ToString() && 
                                !String.IsNullOrEmpty(configSource.UserName) && 
                                !String.IsNullOrEmpty(configSource.Password))
                            { 
                                return new NetworkCredential(configSource.UserName, configSource.Password);
                            }
                        }
                    }
                    catch (Exception ex)
                    { 
                        this.Log().Debug($"Failed to load config file '{nugetConfig}': {ex.Message}");
                    }
                }
            }

            if (source == null)
            {
                this.Log().Debug("Asking user for credentials for '{0}'".format_with(uri.OriginalString));
                return get_credentials_from_user(uri, proxy, credentialType);
            }
            else
            {
                this.Log().Debug("Using saved credentials");
            }
....

src/chocolatey/infrastructure.app/nuget/NugetCommon.cs

       public static string UriBackwardsCompatibleSimplify(string originalUri)
        {
            var r = new System.Text.RegularExpressions.Regex("/v3/.*");
            if (!r.Match(originalUri).Success)
            {
                return originalUri;
            }

            var s = r.Replace(originalUri, "/v2");
            return s;
        }

...
                try
                {
                    var uri = new Uri(source);
                    if (uri.IsFile || uri.IsUnc)
                    {
                        repositories.Add(new ChocolateyLocalPackageRepository(uri.LocalPath) { Logger = nugetLogger });
                    }
                    else
                    {
                        //added line
                        uri = new Uri(UriBackwardsCompatibleSimplify(source));