@@ -17,6 +17,7 @@ use config::Config;
1717use  futures_util:: TryStreamExt ; 
1818use  sea_orm:: TransactionTrait ; 
1919use  std:: str:: FromStr ; 
20+ use  tokio_util:: io:: StreamReader ; 
2021use  trustify_auth:: { CreateAdvisory ,  DeleteAdvisory ,  ReadAdvisory ,  authorizer:: Require } ; 
2122use  trustify_common:: { 
2223    db:: { Database ,  query:: Query } , 
@@ -26,9 +27,10 @@ use trustify_common::{
2627} ; 
2728use  trustify_entity:: labels:: Labels ; 
2829use  trustify_module_ingestor:: service:: { Format ,  Ingest ,  IngestorService } ; 
30+ use  trustify_module_signature:: model:: VerificationResult ; 
2931use  trustify_module_signature:: { 
3032    model:: Signature , 
31-     service:: { DocumentType ,  SignatureService } , 
33+     service:: { DocumentType ,  SignatureService ,   TrustAnchorService } , 
3234} ; 
3335use  trustify_module_storage:: service:: StorageBackend ; 
3436use  utoipa:: IntoParams ; 
@@ -40,18 +42,21 @@ pub fn configure(
4042)  { 
4143    let  advisory_service = AdvisoryService :: new ( db. clone ( ) ) ; 
4244    let  purl_service = PurlService :: new ( ) ; 
45+     let  signature_service = SignatureService :: new ( ) ; 
4346
4447    config
4548        . app_data ( web:: Data :: new ( db) ) 
4649        . app_data ( web:: Data :: new ( advisory_service) ) 
4750        . app_data ( web:: Data :: new ( purl_service) ) 
51+         . app_data ( web:: Data :: new ( signature_service) ) 
4852        . app_data ( web:: Data :: new ( Config  {  upload_limit } ) ) 
4953        . service ( all) 
5054        . service ( get) 
5155        . service ( delete) 
5256        . service ( upload) 
5357        . service ( download) 
5458        . service ( list_signatures) 
59+         . service ( verify_signatures) 
5560        . service ( label:: set) 
5661        . service ( label:: update) ; 
5762} 
@@ -275,15 +280,91 @@ pub async fn download(
275280#[ get( "/v2/advisory/{key}/signature" ) ]  
276281pub  async  fn  list_signatures ( 
277282    db :  web:: Data < Database > , 
283+     service :  web:: Data < SignatureService > , 
278284    key :  web:: Path < String > , 
279285    web:: Query ( paginated) :  web:: Query < Paginated > , 
280286    _:  Require < ReadAdvisory > , 
281287)  -> Result < impl  Responder ,  Error >  { 
282288    let  id = Id :: from_str ( & key) . map_err ( Error :: IdKey ) ?; 
283289
284-     let  result = SignatureService 
285-         . list_signatures ( DocumentType :: Sbom ,  id,  paginated,  db. get_ref ( ) ) 
290+     let  result = service 
291+         . list_signatures ( DocumentType :: Advisory ,  id,  paginated,  db. get_ref ( ) ) 
286292        . await ?; 
287293
288294    Ok ( HttpResponse :: Ok ( ) . json ( result) ) 
289295} 
296+ 
297+ /// Get signatures of an SBOM 
298+ #[ utoipa:: path(  
299+     tag = "advisory" ,  
300+     operation_id = "verifyAdvisorySignatures" ,  
301+     params(  
302+         ( "key"  = Id ,  Path ) ,  
303+     ) ,  
304+     responses(  
305+         ( status = 200 ,  description = "Signatures of an advisory" ,  body = PaginatedResults <VerificationResult >) ,  
306+         ( status = 404 ,  description = "The document could not be found" ) ,  
307+     )  
308+ ) ] 
309+ #[ get( "/v2/advisory/{key}/verify" ) ]  
310+ #[ allow( clippy:: too_many_arguments) ]  
311+ pub  async  fn  verify_signatures ( 
312+     db :  web:: Data < Database > , 
313+     signature_service :  web:: Data < SignatureService > , 
314+     trust_anchor_service :  web:: Data < TrustAnchorService > , 
315+     ingestor :  web:: Data < IngestorService > , 
316+     advisory :  web:: Data < AdvisoryService > , 
317+     key :  web:: Path < String > , 
318+     web:: Query ( paginated) :  web:: Query < Paginated > , 
319+     _:  Require < ReadAdvisory > , 
320+ )  -> Result < impl  Responder ,  Error >  { 
321+     let  id = Id :: from_str ( & key) . map_err ( Error :: IdKey ) ?; 
322+ 
323+     // fetch signatures of the document 
324+     let  result = signature_service
325+         . list_signatures ( DocumentType :: Advisory ,  id. clone ( ) ,  paginated,  db. get_ref ( ) ) 
326+         . await ?; 
327+ 
328+     if  result. items . is_empty ( )  { 
329+         // early exit, so we don't need to fetch content or trust anchors. 
330+         return  Ok ( HttpResponse :: Ok ( ) . json ( PaginatedResults :: < VerificationResult > :: default ( ) ) ) ; 
331+     } 
332+ 
333+     // look up document by id 
334+     let  Some ( advisory)  = advisory
335+         . fetch_advisory ( id. clone ( ) ,  db. as_ref ( ) ) 
336+         . await ?
337+         . and_then ( |advisory| advisory. source_document ) 
338+     else  { 
339+         return  Ok ( HttpResponse :: NotFound ( ) . finish ( ) ) ; 
340+     } ; 
341+ 
342+     let  stream = ingestor
343+         . get_ref ( ) 
344+         . storage ( ) 
345+         . clone ( ) 
346+         . retrieve ( advisory. try_into ( ) ?) 
347+         . await 
348+         . map_err ( Error :: Storage ) ?; 
349+ 
350+     let  Some ( stream)  = stream else  { 
351+         return  Ok ( HttpResponse :: NotFound ( ) . finish ( ) ) ; 
352+     } ; 
353+ 
354+     let  content = tempfile:: tempfile ( ) ?; 
355+     tokio:: io:: copy ( 
356+         & mut  StreamReader :: new ( 
357+             stream. map_err ( |err| std:: io:: Error :: new ( std:: io:: ErrorKind :: Other ,  err. to_string ( ) ) ) , 
358+         ) , 
359+         & mut  tokio:: fs:: File :: from_std ( content. try_clone ( ) . map_err ( |err| Error :: Any ( err. into ( ) ) ) ?) , 
360+     ) 
361+     . await 
362+     . map_err ( |err| Error :: Any ( err. into ( ) ) ) ?; 
363+ 
364+     let  result = PaginatedResults  { 
365+         items :  trust_anchor_service. verify ( result. items ,  content) . await ?, 
366+         total :  result. total , 
367+     } ; 
368+ 
369+     Ok ( HttpResponse :: Ok ( ) . json ( result) ) 
370+ } 
0 commit comments