|
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using NuGet.Common;
namespace NuGet.Configuration
{
public class PackageSourceProvider : IPackageSourceProvider
{
public ISettings Settings { get; private set; }
internal const int MaxSupportedProtocolVersion = 3;
private readonly IReadOnlyList<PackageSource> _configurationDefaultSources;
private readonly IReadOnlyList<PackageSource> _configurationDefaultAuditSources;
public PackageSourceProvider(
ISettings settings) : this(settings, ConfigurationDefaults.Instance.DefaultPackageSources, ConfigurationDefaults.Instance.DefaultAuditSources, enablePackageSourcesChangedEvent: true)
{
}
[Obsolete("https://github.com/NuGet/Home/issues/8479")]
public PackageSourceProvider(
ISettings settings,
bool enablePackageSourcesChangedEvent)
: this(settings, ConfigurationDefaults.Instance.DefaultPackageSources, ConfigurationDefaults.Instance.DefaultAuditSources, enablePackageSourcesChangedEvent)
{
}
public PackageSourceProvider(
ISettings settings,
IEnumerable<PackageSource> configurationDefaultSources)
: this(settings, configurationDefaultSources, ConfigurationDefaults.Instance.DefaultAuditSources, enablePackageSourcesChangedEvent: true)
{
}
[Obsolete("https://github.com/NuGet/Home/issues/8479")]
public PackageSourceProvider(
ISettings settings,
IEnumerable<PackageSource> configurationDefaultSources,
bool enablePackageSourcesChangedEvent)
: this(settings, configurationDefaultSources, ConfigurationDefaults.Instance.DefaultAuditSources, enablePackageSourcesChangedEvent)
{
}
public PackageSourceProvider(
ISettings settings,
ConfigurationDefaults configurationDefaults)
: this(settings, configurationDefaults.DefaultPackageSources, configurationDefaults.DefaultAuditSources, enablePackageSourcesChangedEvent: true)
{
}
[Obsolete("https://github.com/NuGet/Home/issues/8479")]
public PackageSourceProvider(
ISettings settings,
ConfigurationDefaults configurationDefaults,
bool enablePackageSourcesChangedEvent)
: this(settings, configurationDefaults.DefaultPackageSources, configurationDefaults.DefaultAuditSources, enablePackageSourcesChangedEvent)
{
}
private PackageSourceProvider(
ISettings settings,
IEnumerable<PackageSource> configurationDefaultSources,
IReadOnlyList<PackageSource> configurationDefaultAuditSources,
bool enablePackageSourcesChangedEvent)
{
Settings = settings ?? throw new ArgumentNullException(nameof(settings));
if (enablePackageSourcesChangedEvent)
{
Settings.SettingsChanged += (_, __) => { OnPackageSourcesChanged(); };
}
if (configurationDefaultSources is null)
{
throw new ArgumentNullException(nameof(configurationDefaultSources));
}
_configurationDefaultSources = LoadConfigurationDefaultSources(configurationDefaultSources);
if (configurationDefaultAuditSources is null)
{
throw new ArgumentNullException(nameof(configurationDefaultAuditSources));
}
_configurationDefaultAuditSources = LoadConfigurationDefaultSources(configurationDefaultAuditSources);
}
private static IReadOnlyList<PackageSource> LoadConfigurationDefaultSources(IEnumerable<PackageSource> configurationDefaultSources)
{
#if !IS_CORECLR
// Global default NuGet source doesn't make sense on Mono
if (Common.RuntimeEnvironmentHelper.IsMono)
{
return Array.Empty<PackageSource>();
}
#endif
var packageSourceLookup = new Dictionary<string, IndexedPackageSource>(StringComparer.OrdinalIgnoreCase);
var packageIndex = 0;
foreach (var packageSource in configurationDefaultSources)
{
AddOrUpdateIndexedSource(packageSourceLookup, packageSource, ref packageIndex);
}
List<PackageSource> defaultSources = new(packageSourceLookup.Count);
defaultSources.AddRange(packageSourceLookup.Values
.OrderBy(source => source.Index)
.Select(source => source.PackageSource));
return defaultSources.AsReadOnly();
}
private static List<PackageSource> GetPackageSourceFromSettings(ISettings settings, string sectionName, IEnvironmentVariableReader environmentVariableReader)
{
var packageSourcesSection = settings.GetSection(sectionName);
var sourcesItems = packageSourcesSection?.Items.OfType<SourceItem>();
// Order the list so that the closer to the user appear first
IList<string> configFilePaths = settings.GetConfigFilePaths();
#pragma warning disable CS8604 // Possible null reference argument.
// netfx and netstandard BCLs are missing nullability annotations.
var sources = sourcesItems?.OrderBy(i => configFilePaths.IndexOf(i.Origin?.ConfigFilePath)); //lower index => higher priority => closer to user.
#pragma warning restore CS8604 // Possible null reference argument.
List<PackageSource> packageSources;
if (sources != null)
{
// get list of disabled packages
var disabledSourcesSection = settings.GetSection(ConfigurationConstants.DisabledPackageSources);
var disabledSourcesSettings = disabledSourcesSection?.Items.OfType<AddItem>();
var disabledSources = new HashSet<string>(disabledSourcesSettings?.GroupBy(setting => setting.Key).Select(group => group.First().Key) ?? Enumerable.Empty<string>());
var packageSourceLookup = new Dictionary<string, IndexedPackageSource>(StringComparer.OrdinalIgnoreCase);
var packageIndex = 0;
foreach (var setting in sources)
{
var name = setting.Key;
var isEnabled = !disabledSources.Contains(name);
var packageSource = ReadPackageSource(setting, isEnabled, settings, environmentVariableReader);
AddOrUpdateIndexedSource(packageSourceLookup, packageSource, ref packageIndex);
}
packageSources = new(capacity: packageSourceLookup.Count);
packageSources.AddRange(packageSourceLookup.Values
.OrderBy(psi => psi.Index).
Select(psi => psi.PackageSource));
}
else
{
packageSources = new List<PackageSource>();
}
return packageSources;
}
/// <summary>
/// Returns PackageSources specified in the config file merged with any default sources specified in the
/// constructor.
/// </summary>
public IEnumerable<PackageSource> LoadPackageSources()
{
return LoadPackageSources(EnvironmentVariableWrapper.Instance);
}
internal IEnumerable<PackageSource> LoadPackageSources(IEnvironmentVariableReader environmentVariableReader)
{
return LoadPackageSources(Settings, ConfigurationConstants.PackageSources, _configurationDefaultSources, environmentVariableReader);
}
public IReadOnlyList<PackageSource> LoadAuditSources()
{
return LoadAuditSources(EnvironmentVariableWrapper.Instance);
}
internal IReadOnlyList<PackageSource> LoadAuditSources(IEnvironmentVariableReader environmentVariableReader)
{
return LoadPackageSources(Settings, ConfigurationConstants.AuditSources, _configurationDefaultAuditSources, environmentVariableReader);
}
/// <summary>
/// Returns PackageSources if specified in the settings object, combined with the default sources from the default configuration.
/// </summary>
public static IEnumerable<PackageSource> LoadPackageSources(ISettings settings)
{
return LoadPackageSources(settings, ConfigurationConstants.PackageSources, ConfigurationDefaults.Instance.DefaultPackageSources, EnvironmentVariableWrapper.Instance);
}
private static List<PackageSource> LoadPackageSources(ISettings settings, string sectionName, IEnumerable<PackageSource> defaultSources, IEnvironmentVariableReader environmentVariableReader)
{
List<PackageSource> loadedPackageSources = GetPackageSourceFromSettings(settings, sectionName, environmentVariableReader);
if (defaultSources != null && defaultSources.Any())
{
AddDefaultPackageSources(loadedPackageSources, defaultSources);
}
return loadedPackageSources;
}
// This adds package sources defined in the machine-wide NuGetDefaults.config
// which as per our docs specifies, always get added, even if a repo nuget.config
// uses a <clear />
private static void AddDefaultPackageSources(List<PackageSource> loadedPackageSources, IEnumerable<PackageSource> defaultPackageSources)
{
var defaultPackageSourcesToBeAdded = new List<PackageSource>();
foreach (var packageSource in defaultPackageSources.NoAllocEnumerate())
{
var sourceMatching = loadedPackageSources.Any(p => p.Source.Equals(packageSource.Source, StringComparison.OrdinalIgnoreCase));
var feedNameMatching = loadedPackageSources.Any(p => p.Name.Equals(packageSource.Name, StringComparison.OrdinalIgnoreCase));
if (!sourceMatching && !feedNameMatching)
{
defaultPackageSourcesToBeAdded.Add(packageSource);
}
}
var defaultSourcesInsertIndex = loadedPackageSources.FindIndex(source => source.IsMachineWide);
if (defaultSourcesInsertIndex == -1)
{
defaultSourcesInsertIndex = loadedPackageSources.Count;
}
loadedPackageSources.InsertRange(defaultSourcesInsertIndex, defaultPackageSourcesToBeAdded);
}
/// <summary>
/// Create a package source from the package source or audit source setting.
/// </summary>
internal static PackageSource ReadPackageSource(SourceItem setting, bool isEnabled, ISettings settings, IEnvironmentVariableReader environmentVariableReader)
{
var name = setting.Key;
var packageSource = new PackageSource(setting.GetValueAsPath(), name, isEnabled)
{
IsMachineWide = setting.Origin?.IsMachineWide ?? false,
MaxHttpRequestsPerSource = SettingsUtility.GetMaxHttpRequest(settings)
};
var credentials = ReadCredential(name, settings, environmentVariableReader);
if (credentials != null)
{
packageSource.Credentials = credentials;
}
var clientCertificateProvider = new ClientCertificateProvider(settings);
var clientCertificateItem = clientCertificateProvider.GetClientCertificate(name);
if (clientCertificateItem != null)
{
packageSource.ClientCertificates = clientCertificateItem.Search().ToList();
}
packageSource.ProtocolVersion = ReadProtocolVersion(setting);
packageSource.AllowInsecureConnections = ReadAllowInsecureConnections(setting);
packageSource.DisableTLSCertificateValidation = ReadDisableTLSCertificateValidation(setting);
return packageSource;
}
private static int ReadProtocolVersion(SourceItem setting)
{
if (int.TryParse(setting.ProtocolVersion, out var protocolVersion))
{
return protocolVersion;
}
return PackageSource.DefaultProtocolVersion;
}
private static bool ReadDisableTLSCertificateValidation(SourceItem setting)
{
if (bool.TryParse(setting.DisableTLSCertificateValidation, out var disableTLSCertificateValidation))
{
return disableTLSCertificateValidation;
}
return PackageSource.DefaultDisableTLSCertificateValidation;
}
private static bool ReadAllowInsecureConnections(SourceItem setting)
{
if (bool.TryParse(setting.AllowInsecureConnections, out var allowInsecureConnections))
{
return allowInsecureConnections;
}
return PackageSource.DefaultAllowInsecureConnections;
}
private static void AddOrUpdateIndexedSource(
Dictionary<string, IndexedPackageSource> packageSourceLookup,
PackageSource packageSource,
ref int packageIndex)
{
if (!packageSourceLookup.TryGetValue(packageSource.Name, out var previouslyAddedSource))
{
packageSourceLookup[packageSource.Name] = new IndexedPackageSource
{
PackageSource = packageSource,
Index = packageIndex++
};
}
else if (previouslyAddedSource.PackageSource.ProtocolVersion < packageSource.ProtocolVersion
&&
packageSource.ProtocolVersion <= MaxSupportedProtocolVersion)
{
// Pick the package source with the highest supported protocol version
previouslyAddedSource.PackageSource = packageSource;
}
}
private static PackageSourceCredential? ReadCredential(string sourceName, ISettings settings, IEnvironmentVariableReader environmentVariableReader)
{
var environmentCredentials = ReadCredentialFromEnvironment(sourceName, environmentVariableReader);
if (environmentCredentials != null)
{
return environmentCredentials;
}
var credentialsSection = settings.GetSection(ConfigurationConstants.CredentialsSectionName);
var credentialsItem = credentialsSection?.Items.OfType<CredentialsItem>().FirstOrDefault(s => string.Equals(s.ElementName, sourceName, StringComparison.Ordinal));
if (credentialsItem != null && !credentialsItem.IsEmpty())
{
return new PackageSourceCredential(
sourceName,
credentialsItem.Username,
credentialsItem.Password,
credentialsItem.IsPasswordClearText,
credentialsItem.ValidAuthenticationTypes);
}
return null;
}
private static PackageSourceCredential? ReadCredentialFromEnvironment(string sourceName, IEnvironmentVariableReader environmentVariableReader)
{
var rawCredentials = environmentVariableReader.GetEnvironmentVariable("NuGetPackageSourceCredentials_" + sourceName);
if (string.IsNullOrEmpty(rawCredentials))
{
return null;
}
var match = Regex.Match(rawCredentials!.Trim(), @"^Username=(?<user>.*?);\s*Password=(?<pass>.*?)(?:;ValidAuthenticationTypes=(?<authTypes>.*?))?$", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
if (!match.Success)
{
return null;
}
return new PackageSourceCredential(
sourceName,
match.Groups["user"].Value,
match.Groups["pass"].Value,
isPasswordClearText: true,
validAuthenticationTypesText: match.Groups["authTypes"].Value);
}
public PackageSource? GetPackageSourceByName(string name)
{
return GetPackageSourceByName(name, EnvironmentVariableWrapper.Instance);
}
internal PackageSource? GetPackageSourceByName(string name, IEnvironmentVariableReader environmentVariableReader)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException(Resources.Argument_Cannot_Be_Null_Or_Empty, nameof(name));
}
List<PackageSource> packageSources = LoadPackageSources(Settings, ConfigurationConstants.PackageSources, _configurationDefaultSources, environmentVariableReader);
foreach (var packageSource in packageSources)
{
if (string.Equals(name, packageSource.Name, StringComparison.OrdinalIgnoreCase))
{
return packageSource;
}
}
return null;
}
public HashSet<string> GetPackageSourceNamesMatchingNamePrefix(string namePrefix)
{
return GetPackageSourceNamesMatchingNamePrefix(namePrefix, EnvironmentVariableWrapper.Instance);
}
internal HashSet<string> GetPackageSourceNamesMatchingNamePrefix(string namePrefix, IEnvironmentVariableReader environmentVariableReader)
{
var names = new HashSet<string>();
List<PackageSource> packageSources = LoadPackageSources(Settings, ConfigurationConstants.PackageSources, _configurationDefaultSources, environmentVariableReader);
foreach (PackageSource packageSource in packageSources)
{
if (packageSource.Name.StartsWith(namePrefix, StringComparison.OrdinalIgnoreCase))
{
names.Add(packageSource.Name);
}
}
return names;
}
public PackageSource? GetPackageSourceBySource(string source)
{
return GetPackageSourceBySource(source, EnvironmentVariableWrapper.Instance);
}
internal PackageSource? GetPackageSourceBySource(string source, IEnvironmentVariableReader environmentVariableReader)
{
if (string.IsNullOrEmpty(source))
{
throw new ArgumentException(Resources.Argument_Cannot_Be_Null_Or_Empty, nameof(source));
}
List<PackageSource> packageSources = LoadPackageSources(Settings, ConfigurationConstants.PackageSources, _configurationDefaultSources, environmentVariableReader);
foreach (var packageSource in packageSources)
{
if (string.Equals(source, packageSource.Source, StringComparison.OrdinalIgnoreCase))
{
return packageSource;
}
}
return null;
}
public void RemovePackageSource(string name)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException(Resources.Argument_Cannot_Be_Null_Or_Empty, nameof(name));
}
var isDirty = false;
RemovePackageSource(name, shouldSkipSave: false, isDirty: ref isDirty);
}
private void RemovePackageSource(string name, bool shouldSkipSave, ref bool isDirty)
{
// get list of sources
var packageSourcesSection = Settings.GetSection(ConfigurationConstants.PackageSources);
var sourcesSettings = packageSourcesSection?.Items.OfType<SourceItem>();
// get list of credentials for sources
var sourceCredentialsSection = Settings.GetSection(ConfigurationConstants.CredentialsSectionName);
var sourceCredentialsSettings = sourceCredentialsSection?.Items.OfType<CredentialsItem>();
var sourcesToRemove = sourcesSettings?.Where(s => string.Equals(s.Key, name, StringComparison.OrdinalIgnoreCase));
var credentialsToRemove = sourceCredentialsSettings?.Where(s => string.Equals(s.ElementName, name, StringComparison.OrdinalIgnoreCase));
if (sourcesToRemove != null)
{
foreach (var source in sourcesToRemove)
{
try
{
Settings.Remove(ConfigurationConstants.PackageSources, source);
isDirty = true;
}
catch { }
}
}
RemoveDisabledSource(name, shouldSkipSave: true, isDirty: ref isDirty);
if (credentialsToRemove != null)
{
foreach (var credentials in credentialsToRemove)
{
try
{
Settings.Remove(ConfigurationConstants.CredentialsSectionName, credentials);
isDirty = true;
}
catch { }
}
}
if (!shouldSkipSave && isDirty)
{
Settings.SaveToDisk();
OnPackageSourcesChanged();
isDirty = false;
}
}
public void DisablePackageSource(string name)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException(Resources.Argument_Cannot_Be_Null_Or_Empty, nameof(name));
}
var isDirty = false;
AddDisabledSource(name, shouldSkipSave: false, isDirty: ref isDirty);
}
private void AddDisabledSource(string name, bool shouldSkipSave, ref bool isDirty)
{
var settingsLookup = GetExistingSettingsLookup();
var addedInSameFileAsCurrentSource = false;
if (settingsLookup.TryGetValue(name, out var sourceSetting))
{
try
{
if (sourceSetting.Origin != null && Settings is Settings castSettings)
{
castSettings.AddOrUpdate(sourceSetting.Origin, ConfigurationConstants.DisabledPackageSources, new AddItem(name, "true"));
isDirty = true;
addedInSameFileAsCurrentSource = true;
}
}
// We ignore any errors since this means the current source file could not be edited
catch { }
}
if (!addedInSameFileAsCurrentSource)
{
Settings.AddOrUpdate(ConfigurationConstants.DisabledPackageSources, new AddItem(name, "true"));
isDirty = true;
}
if (!shouldSkipSave && isDirty)
{
Settings.SaveToDisk();
OnPackageSourcesChanged();
isDirty = false;
}
}
public void EnablePackageSource(string name)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException(Resources.Argument_Cannot_Be_Null_Or_Empty, nameof(name));
}
var isDirty = false;
RemoveDisabledSource(name, shouldSkipSave: false, isDirty: ref isDirty);
}
private void RemoveDisabledSource(string name, bool shouldSkipSave, ref bool isDirty)
{
// get list of disabled sources
var disabledSourcesSection = Settings.GetSection(ConfigurationConstants.DisabledPackageSources);
var disabledSourcesSettings = disabledSourcesSection?.Items.OfType<AddItem>();
var disableSourcesToRemove = disabledSourcesSettings?.Where(s => string.Equals(s.Key, name, StringComparison.OrdinalIgnoreCase));
if (disableSourcesToRemove != null)
{
foreach (var disabledSource in disableSourcesToRemove)
{
Settings.Remove(ConfigurationConstants.DisabledPackageSources, disabledSource);
isDirty = true;
}
}
if (!shouldSkipSave && isDirty)
{
Settings.SaveToDisk();
OnPackageSourcesChanged();
isDirty = false;
}
}
public void UpdatePackageSource(PackageSource source, bool updateCredentials, bool updateEnabled)
{
UpdatePackageSource(source, updateCredentials, updateEnabled, EnvironmentVariableWrapper.Instance);
}
internal void UpdatePackageSource(PackageSource source, bool updateCredentials, bool updateEnabled, IEnvironmentVariableReader environmentVariableReader)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
var packageSources = GetExistingSettingsLookup();
packageSources.TryGetValue(source.Name, out var sourceToUpdate);
if (sourceToUpdate != null)
{
AddItem? disabledSourceItem = null;
CredentialsItem? credentialsSettingsItem = null;
if (updateEnabled)
{
// get list of disabled packages
var disabledSourcesSection = Settings.GetSection(ConfigurationConstants.DisabledPackageSources);
disabledSourceItem = disabledSourcesSection?.GetFirstItemWithAttribute<AddItem>(ConfigurationConstants.KeyAttribute, sourceToUpdate.ElementName);
}
if (updateCredentials)
{
// get list of credentials for sources
var credentialsSection = Settings.GetSection(ConfigurationConstants.CredentialsSectionName);
credentialsSettingsItem = credentialsSection?.Items.OfType<CredentialsItem>().FirstOrDefault(s => string.Equals(s.ElementName, sourceToUpdate.Key, StringComparison.OrdinalIgnoreCase));
}
var oldPackageSource = ReadPackageSource(sourceToUpdate, disabledSourceItem == null, Settings, environmentVariableReader);
var isDirty = false;
UpdatePackageSource(
source,
oldPackageSource,
disabledSourceItem,
credentialsSettingsItem,
updateEnabled,
updateCredentials,
shouldSkipSave: false,
isDirty: ref isDirty);
}
}
private void UpdateAuditSource(
PackageSource newSource,
PackageSource existingSource,
bool shouldSkipSave,
ref bool isDirty)
{
if (string.Equals(newSource.Name, existingSource.Name, StringComparison.OrdinalIgnoreCase))
{
if ((!string.Equals(newSource.Source, existingSource.Source, StringComparison.OrdinalIgnoreCase) ||
newSource.ProtocolVersion != existingSource.ProtocolVersion ||
newSource.AllowInsecureConnections != existingSource.AllowInsecureConnections ||
newSource.DisableTLSCertificateValidation != existingSource.DisableTLSCertificateValidation) && newSource.IsPersistable)
{
Settings.AddOrUpdate(ConfigurationConstants.AuditSources, newSource.AsSourceItem());
isDirty = true;
}
if (!shouldSkipSave && isDirty)
{
Settings.SaveToDisk();
OnPackageSourcesChanged();
isDirty = false;
}
}
}
private void UpdatePackageSource(
PackageSource newSource,
PackageSource existingSource,
AddItem? existingDisabledSourceItem,
CredentialsItem? existingCredentialsItem,
bool updateEnabled,
bool updateCredentials,
bool shouldSkipSave,
ref bool isDirty)
{
if (string.Equals(newSource.Name, existingSource.Name, StringComparison.OrdinalIgnoreCase))
{
if ((!string.Equals(newSource.Source, existingSource.Source, StringComparison.OrdinalIgnoreCase) ||
newSource.ProtocolVersion != existingSource.ProtocolVersion ||
newSource.AllowInsecureConnections != existingSource.AllowInsecureConnections ||
newSource.DisableTLSCertificateValidation != existingSource.DisableTLSCertificateValidation) && newSource.IsPersistable)
{
Settings.AddOrUpdate(ConfigurationConstants.PackageSources, newSource.AsSourceItem());
isDirty = true;
}
if (updateEnabled)
{
if (newSource.IsEnabled && existingDisabledSourceItem != null)
{
Settings.Remove(ConfigurationConstants.DisabledPackageSources, existingDisabledSourceItem);
isDirty = true;
}
if (!newSource.IsEnabled && existingDisabledSourceItem == null)
{
AddDisabledSource(newSource.Name, shouldSkipSave: true, isDirty: ref isDirty);
}
}
if (updateCredentials && newSource.Credentials != existingSource.Credentials)
{
if (existingCredentialsItem != null)
{
if (newSource.Credentials == null)
{
Settings.Remove(ConfigurationConstants.CredentialsSectionName, existingCredentialsItem);
isDirty = true;
}
else
{
Settings.AddOrUpdate(ConfigurationConstants.CredentialsSectionName, newSource.Credentials.AsCredentialsItem());
isDirty = true;
}
}
else if (newSource.Credentials != null && newSource.Credentials.IsValid())
{
Settings.AddOrUpdate(ConfigurationConstants.CredentialsSectionName, newSource.Credentials.AsCredentialsItem());
isDirty = true;
}
}
if (!shouldSkipSave && isDirty)
{
Settings.SaveToDisk();
OnPackageSourcesChanged();
isDirty = false;
}
}
}
public void AddPackageSource(PackageSource source)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
var isDirty = false;
AddPackageSource(source, shouldSkipSave: false, isDirty: ref isDirty);
}
private void AddAuditSource(PackageSource source, bool shouldSkipSave, ref bool isDirty)
{
if (source.IsPersistable)
{
Settings.AddOrUpdate(ConfigurationConstants.AuditSources, source.AsSourceItem());
isDirty = true;
}
if (!shouldSkipSave && isDirty)
{
Settings.SaveToDisk();
OnPackageSourcesChanged();
isDirty = false;
}
}
private void AddPackageSource(PackageSource source, bool shouldSkipSave, ref bool isDirty)
{
if (source.IsPersistable)
{
Settings.AddOrUpdate(ConfigurationConstants.PackageSources, source.AsSourceItem());
isDirty = true;
}
if (source.IsEnabled)
{
RemoveDisabledSource(source.Name, shouldSkipSave: true, isDirty: ref isDirty);
}
else
{
AddDisabledSource(source.Name, shouldSkipSave: true, isDirty: ref isDirty);
}
if (source.Credentials != null && source.Credentials.IsValid())
{
Settings.AddOrUpdate(ConfigurationConstants.CredentialsSectionName, source.Credentials.AsCredentialsItem());
isDirty = true;
}
if (!shouldSkipSave && isDirty)
{
Settings.SaveToDisk();
OnPackageSourcesChanged();
isDirty = false;
}
}
public void SavePackageSources(IEnumerable<PackageSource> sources)
{
SavePackageSources(sources, EnvironmentVariableWrapper.Instance);
}
internal void SavePackageSources(IEnumerable<PackageSource> sources, IEnvironmentVariableReader environmentVariableReader)
{
if (sources == null)
{
throw new ArgumentNullException(nameof(sources));
}
var isDirty = false;
var existingSettingsLookup = GetExistingSettingsLookup();
var disabledSourcesSection = Settings.GetSection(ConfigurationConstants.DisabledPackageSources);
var existingDisabledSources = disabledSourcesSection?.Items.OfType<AddItem>();
Dictionary<string, AddItem>? existingDisabledSourcesLookup = null;
try
{
existingDisabledSourcesLookup = existingDisabledSources?.ToDictionary(setting => setting.Key, StringComparer.OrdinalIgnoreCase);
}
catch (ArgumentException e)
{
AddItem duplicatedKey = existingDisabledSources!
.GroupBy(s => s.Key, StringComparer.OrdinalIgnoreCase)
.Where(g => g.Count() > 1)
.Select(g => g.First())
.First();
string filePath = duplicatedKey.Origin?.ConfigFilePath ?? Resources.ShowError_UnknownOrigin;
string message = string.Format(CultureInfo.CurrentCulture, Resources.ShowError_ConfigDuplicateDisabledSources, duplicatedKey.Key, filePath);
throw new NuGetConfigurationException(message, e);
}
var credentialsSection = Settings.GetSection(ConfigurationConstants.CredentialsSectionName);
var existingCredentials = credentialsSection?.Items.OfType<CredentialsItem>();
var existingCredentialsLookup = existingCredentials?.ToDictionary(setting => setting.ElementName, StringComparer.OrdinalIgnoreCase);
foreach (var source in sources)
{
AddItem? existingDisabledSourceItem = null;
SourceItem? existingSourceItem = null;
CredentialsItem? existingCredentialsItem = null;
var existingSourceIsEnabled = existingDisabledSourcesLookup == null || existingDisabledSourcesLookup.TryGetValue(source.Name, out existingDisabledSourceItem);
if (existingSettingsLookup.TryGetValue(source.Name, out existingSourceItem))
{
var oldPackageSource = ReadPackageSource(existingSourceItem, existingSourceIsEnabled, Settings, environmentVariableReader);
existingCredentialsLookup?.TryGetValue(source.Name, out existingCredentialsItem);
UpdatePackageSource(
source,
oldPackageSource,
existingDisabledSourceItem,
existingCredentialsItem,
updateEnabled: true,
updateCredentials: true,
shouldSkipSave: true,
isDirty: ref isDirty);
}
else
{
AddPackageSource(source, shouldSkipSave: true, isDirty: ref isDirty);
}
if (existingSourceItem != null)
{
existingSettingsLookup.Remove(source.Name);
}
}
if (existingSettingsLookup != null)
{
// get list of credentials for sources
var sourceCredentialsSection = Settings.GetSection(ConfigurationConstants.CredentialsSectionName);
var sourceCredentialsSettings = sourceCredentialsSection?.Items.OfType<CredentialsItem>();
var existingsourceCredentialsLookup = sourceCredentialsSettings?.ToDictionary(setting => setting.ElementName, StringComparer.OrdinalIgnoreCase);
foreach (var sourceItem in existingSettingsLookup)
{
if (existingDisabledSourcesLookup != null && existingDisabledSourcesLookup.TryGetValue(sourceItem.Value.Key, out var existingDisabledSourceItem))
{
Settings.Remove(ConfigurationConstants.DisabledPackageSources, existingDisabledSourceItem);
isDirty = true;
}
if (existingsourceCredentialsLookup != null && existingsourceCredentialsLookup.TryGetValue(sourceItem.Value.Key, out var existingSourceCredentialItem))
{
Settings.Remove(ConfigurationConstants.CredentialsSectionName, existingSourceCredentialItem);
isDirty = true;
}
Settings.Remove(ConfigurationConstants.PackageSources, sourceItem.Value);
isDirty = true;
}
}
if (isDirty)
{
Settings.SaveToDisk();
OnPackageSourcesChanged();
isDirty = false;
}
}
public void SaveAuditSources(IEnumerable<PackageSource> sources)
{
SaveAuditSources(sources, EnvironmentVariableWrapper.Instance);
}
internal void SaveAuditSources(IEnumerable<PackageSource> sources, IEnvironmentVariableReader environmentVariableReader)
{
if (sources == null)
{
throw new ArgumentNullException(nameof(sources));
}
if (environmentVariableReader == null)
{
throw new ArgumentNullException(nameof(environmentVariableReader));
}
var isDirty = false;
var existingSettingsLookup = GetExistingSettingsLookup(ConfigurationConstants.AuditSources);
foreach (var source in sources)
{
SourceItem? existingSourceItem = null;
if (existingSettingsLookup.TryGetValue(source.Name, out existingSourceItem))
{
var oldPackageSource = ReadPackageSource(existingSourceItem, isEnabled: true, Settings, environmentVariableReader);
UpdateAuditSource(
source,
oldPackageSource,
shouldSkipSave: true,
isDirty: ref isDirty);
}
else
{
AddAuditSource(source, shouldSkipSave: true, isDirty: ref isDirty);
}
if (existingSourceItem != null)
{
existingSettingsLookup.Remove(source.Name);
}
}
if (existingSettingsLookup != null)
{
foreach (var sourceItem in existingSettingsLookup)
{
Settings.Remove(ConfigurationConstants.AuditSources, sourceItem.Value);
isDirty = true;
}
}
if (isDirty)
{
Settings.SaveToDisk();
OnPackageSourcesChanged();
isDirty = false;
}
}
private Dictionary<string, SourceItem> GetExistingSettingsLookup()
{
return GetExistingSettingsLookup(ConfigurationConstants.PackageSources);
}
private Dictionary<string, SourceItem> GetExistingSettingsLookup(string sectionName)
{
SettingSection? sourcesSection = Settings.GetSection(sectionName);
List<SourceItem>? existingSettings = sourcesSection?.Items.OfType<SourceItem>().Where(
c => !(c.Origin == null || c.Origin.IsReadOnly || c.Origin.IsMachineWide))
.ToList();
var existingSettingsLookup = new Dictionary<string, SourceItem>(StringComparer.OrdinalIgnoreCase);
if (existingSettings != null)
{
foreach (var setting in existingSettings)
{
if (existingSettingsLookup.TryGetValue(setting.Key, out var previouslyAddedSetting) &&
ReadProtocolVersion(previouslyAddedSetting) < ReadProtocolVersion(setting) &&
ReadProtocolVersion(setting) <= MaxSupportedProtocolVersion)
{
existingSettingsLookup.Remove(setting.Key);
}
existingSettingsLookup[setting.Key] = setting;
}
}
return existingSettingsLookup;
}
/// <summary>
/// Fires event PackageSourcesChanged
/// </summary>
private void OnPackageSourcesChanged()
{
PackageSourcesChanged?.Invoke(this, EventArgs.Empty);
}
public string? DefaultPushSource
{
get
{
var source = SettingsUtility.GetDefaultPushSource(Settings);
if (string.IsNullOrEmpty(source))
{
source = ConfigurationDefaults.Instance.DefaultPushSource;
}
return source;
}
}
public bool IsPackageSourceEnabled(string name)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException(Resources.Argument_Cannot_Be_Null_Or_Empty, nameof(name));
}
var disabledSources = Settings.GetSection(ConfigurationConstants.DisabledPackageSources);
var value = disabledSources?.GetFirstItemWithAttribute<AddItem>(ConfigurationConstants.KeyAttribute, name);
// It doesn't matter what value it is.
// As long as the package source name is persisted in the <disabledPackageSources> section, the source is disabled.
return value == null;
}
/// <summary>
/// Gets the name of the ActivePackageSource from NuGet.Config
/// </summary>
public string? ActivePackageSourceName
{
get
{
var activeSourceSection = Settings.GetSection(ConfigurationConstants.ActivePackageSourceSectionName);
return activeSourceSection?.Items.OfType<AddItem>().FirstOrDefault()?.Key;
}
}
/// <summary>
/// Saves the <paramref name="source" /> as the active source.
/// </summary>
/// <param name="source"></param>
public void SaveActivePackageSource(PackageSource source)
{
if (source is null)
{
throw new ArgumentNullException(nameof(source));
}
try
{
var activePackageSourceSection = Settings.GetSection(ConfigurationConstants.ActivePackageSourceSectionName);
if (activePackageSourceSection != null)
{
foreach (var activePackageSource in activePackageSourceSection.Items)
{
Settings.Remove(ConfigurationConstants.ActivePackageSourceSectionName, activePackageSource);
}
}
Settings.AddOrUpdate(ConfigurationConstants.ActivePackageSourceSectionName,
new AddItem(source.Name, source.Source));
Settings.SaveToDisk();
}
catch (Exception)
{
// we want to ignore all errors here.
}
}
private class IndexedPackageSource
{
public required int Index { get; init; }
public required PackageSource PackageSource { get; set; }
}
public event EventHandler? PackageSourcesChanged;
}
}
|