@@ -17,7 +17,9 @@ namespace NuGet.PackageManagement.VisualStudio.Options
1717 [ Guid ( "15C605EC-4FD7-446B-BA4A-75ECF0C0B2D0" ) ]
1818 public class PackageSourcesPage : NuGetExternalSettingsProvider , IExternalSettingValidator
1919 {
20+ internal const bool DefaultNuGetAudit = false ;
2021 internal const string MonikerPackageSources = "packageSources" ;
22+ internal const string MonikerAuditSources = "auditSources" ;
2123 internal const string MonikerMachineWideSources = "machineWidePackageSources" ;
2224 internal const string MonikerPackageSourceId = "packageSourceId" ; // Unique identifier for the package source
2325 internal const string MonikerSourceName = "sourceName" ;
@@ -40,32 +42,49 @@ internal override void VsSettings_SettingsChanged(object sender, EventArgs e)
4042 base . VsSettings_SettingsChanged ( sender , e ) ;
4143 }
4244
43- private List < PackageSource > LoadPackageSources ( bool isMachineWide )
45+ private IReadOnlyList < PackageSource > LoadPackageSources ( bool isMachineWide )
4446 {
45- IEnumerable < PackageSource > all = _packageSourceProvider . LoadPackageSources ( ) ;
46- List < PackageSource > filteredPackageSources = all
47- . Where ( packageSource => packageSource . IsMachineWide == isMachineWide ) . ToList ( ) ;
47+ IReadOnlyList < PackageSource > filteredPackageSources = _packageSourceProvider . LoadPackageSources ( )
48+ . Where ( packageSource => packageSource . IsMachineWide == isMachineWide )
49+ . ToList ( )
50+ . AsReadOnly ( ) ;
4851 return filteredPackageSources ;
4952 }
5053
54+ private IReadOnlyList < PackageSource > LoadAuditSources ( )
55+ {
56+ var auditSources = _packageSourceProvider . LoadAuditSources ( ) ;
57+ return auditSources ;
58+ }
59+
5160 public override async Task < ExternalSettingOperationResult < T > > GetValueAsync < T > ( string moniker , CancellationToken cancellationToken )
5261 {
5362 switch ( moniker )
5463 {
5564 case MonikerPackageSources :
56- var packageSources = await Task . Run (
57- ( ) => LoadPackageSources ( isMachineWide : false ) ,
58- cancellationToken ) ;
65+ {
66+ var packageSources = await Task . Run (
67+ ( ) => LoadPackageSources ( isMachineWide : false ) ,
68+ cancellationToken ) ;
5969
60- return GetValuePackageSources < T > ( packageSources ) ;
70+ return GetValuePackageSources < T > ( packageSources ) ;
71+ }
72+ case MonikerAuditSources :
73+ {
74+ var auditSources = await Task . Run (
75+ ( ) => LoadAuditSources ( ) ,
76+ cancellationToken ) ;
6177
78+ return GetValuePackageSources < T > ( auditSources ) ;
79+ }
6280 case MonikerMachineWideSources :
63- var machineWidePackageSources = await Task . Run (
64- ( ) => LoadPackageSources ( isMachineWide : true ) ,
65- cancellationToken ) ;
66-
67- return GetValuePackageSources < T > ( machineWidePackageSources ) ;
81+ {
82+ var machineWidePackageSources = await Task . Run (
83+ ( ) => LoadPackageSources ( isMachineWide : true ) ,
84+ cancellationToken ) ;
6885
86+ return GetValuePackageSources < T > ( machineWidePackageSources ) ;
87+ }
6988 default : break ;
7089 }
7190
@@ -75,12 +94,6 @@ public override async Task<ExternalSettingOperationResult<T>> GetValueAsync<T>(s
7594
7695 public override async Task < ExternalSettingOperationResult > SetValueAsync < T > ( string moniker , T value , CancellationToken cancellationToken )
7796 {
78- var packageSourcesList = value as IReadOnlyList < IDictionary < string , object > > ;
79- if ( packageSourcesList is null )
80- {
81- throw new InvalidOperationException ( ) ;
82- }
83-
8497 bool hasAnyHiddenPropertyChanged = false ;
8598
8699 try
@@ -91,6 +104,7 @@ public override async Task<ExternalSettingOperationResult> SetValueAsync<T>(stri
91104 switch ( moniker )
92105 {
93106 case MonikerPackageSources :
107+ var packageSourcesList = ( IReadOnlyList < IDictionary < string , object > > ) value ;
94108 return await Task . Run (
95109 ( ) =>
96110 {
@@ -99,10 +113,20 @@ public override async Task<ExternalSettingOperationResult> SetValueAsync<T>(stri
99113 return savePackageSourcesResult . result ;
100114 } ,
101115 cancellationToken ) ;
102-
116+ case MonikerAuditSources :
117+ var auditSourceList = ( IReadOnlyList < IDictionary < string , object > > ) value ;
118+ return await Task . Run (
119+ ( ) =>
120+ {
121+ ( ExternalSettingOperationResult result , bool hasAnyHiddenPropertyChanged ) saveAuditSourcesResult = SaveAuditSources ( auditSourceList , cancellationToken ) ;
122+ hasAnyHiddenPropertyChanged = saveAuditSourcesResult . hasAnyHiddenPropertyChanged ;
123+ return saveAuditSourcesResult . result ;
124+ } ,
125+ cancellationToken ) ;
103126 case MonikerMachineWideSources :
127+ var machineWidePackageSourcesList = ( IReadOnlyList < IDictionary < string , object > > ) value ;
104128 return await Task . Run (
105- ( ) => SetIsEnabledOnMachineWidePackageSources ( packageSourcesList , cancellationToken ) ,
129+ ( ) => SetIsEnabledOnMachineWidePackageSources ( machineWidePackageSourcesList , cancellationToken ) ,
106130 cancellationToken ) ;
107131
108132 default :
@@ -178,7 +202,7 @@ private ExternalSettingOperationResult SetIsEnabledOnMachineWidePackageSources(
178202 try
179203 {
180204 List < PackageSource > packageSources = new List < PackageSource > ( capacity : packageSourceDictionaryList . Count ) ;
181- List < PackageSource > existingPackageSources = LoadPackageSources ( isMachineWide : false ) ;
205+ IReadOnlyList < PackageSource > existingPackageSources = LoadPackageSources ( isMachineWide : false ) ;
182206 bool hasAnyPackageSourceNameChanged = false ;
183207
184208 foreach ( Dictionary < string , object > packageSourceDictionary in packageSourceDictionaryList )
@@ -188,7 +212,7 @@ private ExternalSettingOperationResult SetIsEnabledOnMachineWidePackageSources(
188212 string name = packageSourceDictionary [ MonikerSourceName ] . ToString ( ) ;
189213 string lookupName ;
190214
191- // Package Sources that were pre-existing in the NuGet.Config when GetValueAsync was called will have a Package ID.
215+ // Package Sources that were pre-existing in the NuGet.Config when GetValueAsync was called will have an ID.
192216 if ( packageSourceDictionary . TryGetValue ( MonikerPackageSourceId , out object packageSourceIdObj ) )
193217 {
194218 lookupName = packageSourceIdObj . ToString ( ) ;
@@ -199,7 +223,7 @@ private ExternalSettingOperationResult SetIsEnabledOnMachineWidePackageSources(
199223 hasAnyPackageSourceNameChanged = true ;
200224 }
201225 }
202- else // Newly added Package Sources will not have a Package ID yet.
226+ else // Newly added Package Sources will not have an ID yet.
203227 {
204228 lookupName = name ;
205229 }
@@ -237,17 +261,85 @@ private ExternalSettingOperationResult SetIsEnabledOnMachineWidePackageSources(
237261 return ( result , hasAnyHiddenPropertyChanged ) ;
238262 }
239263
264+ private ( ExternalSettingOperationResult result , bool hasAnyHiddenPropertyChanged ) SaveAuditSources (
265+ IReadOnlyList < IDictionary < string , object > > auditSourceDictionaryList ,
266+ CancellationToken cancellationToken )
267+ {
268+ bool hasAnyHiddenPropertyChanged = false ;
269+ ExternalSettingOperationResult result ;
270+
271+ try
272+ {
273+ List < PackageSource > auditSources = new List < PackageSource > ( capacity : auditSourceDictionaryList . Count ) ;
274+ IReadOnlyList < PackageSource > existingAuditSources = LoadAuditSources ( ) ;
275+ bool hasAnyPackageSourceNameChanged = false ;
276+
277+ foreach ( Dictionary < string , object > packageSourceDictionary in auditSourceDictionaryList )
278+ {
279+ cancellationToken . ThrowIfCancellationRequested ( ) ;
280+
281+ string name = packageSourceDictionary [ MonikerSourceName ] . ToString ( ) ;
282+ string lookupName ;
283+
284+ // Package Sources that were pre-existing in the NuGet.Config when GetValueAsync was called will have an ID.
285+ if ( packageSourceDictionary . TryGetValue ( MonikerPackageSourceId , out object packageSourceIdObj ) )
286+ {
287+ lookupName = packageSourceIdObj . ToString ( ) ;
288+
289+ if ( ! string . Equals ( lookupName , name , StringComparison . CurrentCultureIgnoreCase ) )
290+ {
291+ // Changing the ID needs to refresh Unified Settings since the ID is a hidden property.
292+ hasAnyPackageSourceNameChanged = true ;
293+ }
294+ }
295+ else // Newly added Package Sources will not have an ID yet.
296+ {
297+ lookupName = name ;
298+ }
299+
300+ string source = packageSourceDictionary [ MonikerSourceUrl ] . ToString ( ) ;
301+
302+ PackageSource packageSource =
303+ PackageSourceValidator . FindExistingOrCreate (
304+ lookupName ,
305+ source ,
306+ name ,
307+ isEnabled : true ,
308+ allowInsecureConnections : false ,
309+ existingAuditSources ) ;
310+
311+ auditSources . Add ( packageSource ) ;
312+ }
313+
314+ _packageSourceProvider . SaveAuditSources ( auditSources ) ;
315+
316+ hasAnyHiddenPropertyChanged = hasAnyPackageSourceNameChanged ;
317+
318+ result = ExternalSettingOperationResult . Success . Instance ;
319+ }
320+ #pragma warning disable CA1031 // Do not catch general exception types
321+ catch ( Exception ex ) when ( ! ( ex is OperationCanceledException && cancellationToken . IsCancellationRequested ) )
322+ #pragma warning restore CA1031 // Do not catch general exception types
323+ {
324+ result = CreateSettingErrorResult ( ex . Message , isTransient : true ) ;
325+ ActivityLog . LogError ( ExceptionHelper . LogEntrySource , ex . ToString ( ) ) ;
326+ }
327+
328+ return ( result , hasAnyHiddenPropertyChanged ) ;
329+ }
330+
331+
240332 private static PackageSource ParsePackageSource ( IReadOnlyDictionary < string , object > packageSourceDictionary )
241333 {
242334 string name = packageSourceDictionary [ MonikerSourceName ] . ToString ( ) . Trim ( ) ;
243335 string ? lookupName ;
244336
245- // Package Sources that were pre-existing in the NuGet.Config when GetValueAsync was called will have a Package ID.
337+ // Package Sources that were pre-existing in the NuGet.Config when GetValueAsync was called will have an ID.
246338 if ( packageSourceDictionary . TryGetValue ( MonikerPackageSourceId , out object packageSourceIdObj ) )
247339 {
248340 lookupName = packageSourceIdObj . ToString ( ) . Trim ( ) ;
249341 }
250- else // Newly added Package Sources will not have a Package ID yet.
342+ else // Newly added Package Sources will not have an ID yet.
251343 {
252344 lookupName = name ;
253345 }
@@ -264,7 +356,29 @@ private static PackageSource ParsePackageSource(IReadOnlyDictionary<string, obje
264356 return packageSource ;
265357 }
266358
267- private static ExternalSettingOperationResult < T > GetValuePackageSources < T > ( List < PackageSource > packageSources )
359+ private static PackageSource ParseAuditSource ( IReadOnlyDictionary < string , object > auditSourceDictionary )
360+ {
361+ string name = auditSourceDictionary [ MonikerSourceName ] . ToString ( ) . Trim ( ) ;
362+ string ? lookupName ;
363+
364+ // Package Sources that were pre-existing in the NuGet.Config when GetValueAsync was called will have an ID.
365+ if ( auditSourceDictionary . TryGetValue ( MonikerPackageSourceId , out object packageSourceIdObj ) )
366+ {
367+ lookupName = packageSourceIdObj . ToString ( ) . Trim ( ) ;
368+ }
369+ else // Newly added Package Sources will not have an ID yet.
370+ {
371+ lookupName = name ;
372+ }
373+
374+ string source = auditSourceDictionary [ MonikerSourceUrl ] . ToString ( ) . Trim ( ) ;
375+
376+ var packageSource = new PackageSource ( source , lookupName , isEnabled : true ) ;
377+
378+ return packageSource ;
379+ }
380+
381+ private static ExternalSettingOperationResult < T > GetValuePackageSources < T > ( IReadOnlyList < PackageSource > packageSources )
268382 {
269383 ExternalSettingOperationResult < T > result ;
270384
@@ -317,7 +431,9 @@ public OneOrMany<SettingMessage> ValidateArrayItemProperty(
317431 {
318432 var settingMessages = new OneOrMany < SettingMessage > ( ) ;
319433
320- if ( arraySettingMoniker != MonikerPackageSources )
434+ bool isAuditSources = arraySettingMoniker == MonikerAuditSources ;
435+ bool isPackageSources = arraySettingMoniker == MonikerPackageSources ;
436+ if ( ! isPackageSources && ! isAuditSources )
321437 {
322438 return settingMessages ;
323439 }
@@ -336,7 +452,9 @@ public OneOrMany<SettingMessage> ValidateArrayItemProperty(
336452 case MonikerSourceUrl :
337453 {
338454 var packageSourceDictionary = arraySettingContent [ arrayItemIndex ] ;
339- var result = ParsePackageSource ( packageSourceDictionary ) ;
455+ PackageSource result = isPackageSources
456+ ? ParsePackageSource ( packageSourceDictionary )
457+ : ParseAuditSource ( packageSourceDictionary ) ;
340458
341459 var isValidSource = PackageSourceValidator . IsValidSource ( result ) ;
342460 if ( ! isValidSource )
0 commit comments