33
44using System ;
55using System . Data . Entity . Infrastructure ;
6+ using System . Diagnostics ;
7+ using System . IO ;
8+ using System . Net ;
69using System . Net . Http ;
10+ using System . Threading ;
711using System . Threading . Tasks ;
812using Microsoft . Extensions . Logging ;
913using NuGet . Jobs . Validation . PackageSigning . Messages ;
1014using NuGet . Jobs . Validation . PackageSigning . Storage ;
15+ using NuGet . Packaging ;
1116using NuGet . Services . ServiceBus ;
1217using NuGet . Services . Validation ;
13- using NuGet . Versioning ;
1418
1519namespace NuGet . Jobs . Validation . PackageSigning . ExtractAndValidateSignature
1620{
@@ -23,22 +27,27 @@ namespace NuGet.Jobs.Validation.PackageSigning.ExtractAndValidateSignature
2327 public class SignatureValidationMessageHandler
2428 : IMessageHandler < SignatureValidationMessage >
2529 {
30+ private const int BufferSize = 8192 ;
31+
32+ private readonly HttpClient _httpClient ;
2633 private readonly IValidatorStateService _validatorStateService ;
2734 private readonly IPackageSigningStateService _packageSigningStateService ;
2835 private readonly ILogger < SignatureValidationMessageHandler > _logger ;
2936
3037 /// <summary>
3138 /// Instantiate's a new package signatures validator.
3239 /// </summary>
33- /// <param name="validationContext">The persisted validation context.</param>
34- /// <param name="certificateStore">The persisted certificate store.</param>
40+ /// <param name="httpClient">The HTTP client used to download packages.</param>
3541 /// <param name="validatorStateService">The service used to retrieve and persist this validator's state.</param>
3642 /// <param name="packageSigningStateService">The service used to retrieve and persist package signing state.</param>
43+ /// <param name="logger">The logger that should be used.</param>
3744 public SignatureValidationMessageHandler (
45+ HttpClient httpClient ,
3846 IValidatorStateService validatorStateService ,
3947 IPackageSigningStateService packageSigningStateService ,
4048 ILogger < SignatureValidationMessageHandler > logger )
4149 {
50+ _httpClient = httpClient ?? throw new ArgumentNullException ( nameof ( httpClient ) ) ;
4251 _validatorStateService = validatorStateService ?? throw new ArgumentNullException ( nameof ( validatorStateService ) ) ;
4352 _packageSigningStateService = packageSigningStateService ?? throw new ArgumentNullException ( nameof ( packageSigningStateService ) ) ;
4453 _logger = logger ?? throw new ArgumentNullException ( nameof ( logger ) ) ;
@@ -83,8 +92,7 @@ public async Task<bool> HandleAsync(SignatureValidationMessage message)
8392 }
8493
8594 // Validate package
86- // TODO: consume actual client nupkg's containing missing signing APIs
87- if ( ! IsSigned ( message . PackageVersion ) )
95+ if ( ! await IsSigned ( message . NupkgUri , CancellationToken . None ) )
8896 {
8997 return await HandleUnsignedPackageAsync ( validation , message ) ;
9098 }
@@ -95,10 +103,13 @@ public async Task<bool> HandleAsync(SignatureValidationMessage message)
95103 }
96104 }
97105
98- private bool IsSigned ( string packageVersion )
106+ private async Task < bool > IsSigned ( Uri packageUri , CancellationToken cancellationToken )
99107 {
100- var nugetVersion = NuGetVersion . Parse ( packageVersion ) ;
101- return nugetVersion . IsPrerelease && string . Equals ( nugetVersion . Release , "signed" , StringComparison . OrdinalIgnoreCase ) ;
108+ using ( var packageStream = await DownloadPackageAsync ( packageUri , cancellationToken ) )
109+ using ( var package = new PackageArchiveReader ( packageStream , leaveStreamOpen : false ) )
110+ {
111+ return await package . IsSignedAsync ( cancellationToken ) ;
112+ }
102113 }
103114
104115 private async Task < bool > HandleUnsignedPackageAsync ( ValidatorStatus validation , SignatureValidationMessage message )
@@ -171,5 +182,67 @@ private async Task<bool> BlockSignedPackageAsync(ValidatorStatus validation, Sig
171182 // Consume the message if successfully saved state.
172183 return saveStateResult == SaveStatusResult . Success ;
173184 }
185+
186+ private async Task < Stream > DownloadPackageAsync ( Uri packageUri , CancellationToken cancellationToken )
187+ {
188+ _logger . LogInformation ( "Attempting to download package from {PackageUri}..." , packageUri ) ;
189+
190+ Stream packageStream = null ;
191+ var stopwatch = Stopwatch . StartNew ( ) ;
192+
193+ try
194+ {
195+ // Download the package from the network to a temporary file.
196+ using ( var response = await _httpClient . GetAsync ( packageUri , HttpCompletionOption . ResponseHeadersRead ) )
197+ {
198+ _logger . LogInformation (
199+ "Received response {StatusCode}: {ReasonPhrase} of type {ContentType} for request {PackageUri}" ,
200+ response . StatusCode ,
201+ response . ReasonPhrase ,
202+ response . Content . Headers . ContentType ,
203+ packageUri ) ;
204+
205+ if ( response . StatusCode != HttpStatusCode . OK )
206+ {
207+ throw new InvalidOperationException ( $ "Expected status code { HttpStatusCode . OK } for package download, actual: { response . StatusCode } ") ;
208+ }
209+
210+ using ( var networkStream = await response . Content . ReadAsStreamAsync ( ) )
211+ {
212+ packageStream = new FileStream (
213+ Path . GetTempFileName ( ) ,
214+ FileMode . Create ,
215+ FileAccess . ReadWrite ,
216+ FileShare . None ,
217+ BufferSize ,
218+ FileOptions . DeleteOnClose | FileOptions . Asynchronous ) ;
219+
220+ await networkStream . CopyToAsync ( packageStream , BufferSize , cancellationToken ) ;
221+ }
222+ }
223+
224+ packageStream . Position = 0 ;
225+
226+ _logger . LogInformation (
227+ "Downloaded {PackageSizeInBytes} bytes in {DownloadElapsedTime} seconds for request {PackageUri}" ,
228+ packageStream . Length ,
229+ stopwatch . Elapsed . TotalSeconds ,
230+ packageUri ) ;
231+
232+ return packageStream ;
233+ }
234+ catch ( Exception e )
235+ {
236+ _logger . LogError (
237+ Error . ValidateSignatureFailedToDownloadPackageStatus ,
238+ e ,
239+ "Exception thrown when trying to download package from {PackageUri}" ,
240+ packageUri ) ;
241+
242+ packageStream ? . Dispose ( ) ;
243+
244+ throw ;
245+ }
246+ }
174247 }
175248}
0 commit comments