csaf_walker/
retrieve.rs

1//! Retrieval
2
3use crate::{
4    discover::{AsDiscovered, DiscoveredAdvisory, DiscoveredContext, DiscoveredVisitor},
5    source::Source,
6};
7use bytes::Bytes;
8use sha2::{Sha256, Sha512};
9use std::{
10    fmt::Debug,
11    future::Future,
12    ops::{Deref, DerefMut},
13};
14use url::Url;
15use walker_common::{
16    retrieve::{RetrievalError, RetrievalMetadata, RetrievedDigest, RetrievedDocument},
17    utils::{openpgp::PublicKey, url::Urlify},
18    validate::source::{KeySource, KeySourceError},
19};
20
21/// A retrieved (but unverified) advisory
22#[derive(Clone, Debug)]
23pub struct RetrievedAdvisory {
24    /// The discovered advisory
25    pub discovered: DiscoveredAdvisory,
26
27    /// The advisory data
28    pub data: Bytes,
29    /// Signature data
30    pub signature: Option<String>,
31
32    /// SHA-256 digest
33    pub sha256: Option<RetrievedDigest<Sha256>>,
34    /// SHA-512 digest
35    pub sha512: Option<RetrievedDigest<Sha512>>,
36
37    /// Metadata from the retrieval process
38    pub metadata: RetrievalMetadata,
39}
40
41impl Urlify for RetrievedAdvisory {
42    fn url(&self) -> &Url {
43        &self.url
44    }
45
46    fn relative_base_and_url(&self) -> Option<(&Url, String)> {
47        self.discovered.relative_base_and_url()
48    }
49}
50
51/// Get a document as [`RetrievedAdvisory`]
52pub trait AsRetrieved: Debug {
53    fn as_retrieved(&self) -> &RetrievedAdvisory;
54}
55
56impl AsDiscovered for RetrievedAdvisory {
57    fn as_discovered(&self) -> &DiscoveredAdvisory {
58        &self.discovered
59    }
60}
61
62impl AsRetrieved for RetrievedAdvisory {
63    fn as_retrieved(&self) -> &RetrievedAdvisory {
64        self
65    }
66}
67
68impl Deref for RetrievedAdvisory {
69    type Target = DiscoveredAdvisory;
70
71    fn deref(&self) -> &Self::Target {
72        &self.discovered
73    }
74}
75
76impl DerefMut for RetrievedAdvisory {
77    fn deref_mut(&mut self) -> &mut Self::Target {
78        &mut self.discovered
79    }
80}
81
82impl RetrievedDocument for RetrievedAdvisory {
83    type Discovered = DiscoveredAdvisory;
84}
85
86pub struct RetrievalContext<'c> {
87    pub discovered: &'c DiscoveredContext<'c>,
88    pub keys: &'c Vec<PublicKey>,
89}
90
91impl<'c> Deref for RetrievalContext<'c> {
92    type Target = DiscoveredContext<'c>;
93
94    fn deref(&self) -> &Self::Target {
95        self.discovered
96    }
97}
98
99pub trait RetrievedVisitor<S: Source> {
100    type Error: std::fmt::Display + Debug;
101    type Context;
102
103    fn visit_context(
104        &self,
105        context: &RetrievalContext,
106    ) -> impl Future<Output = Result<Self::Context, Self::Error>>;
107
108    fn visit_advisory(
109        &self,
110        context: &Self::Context,
111        result: Result<RetrievedAdvisory, RetrievalError<DiscoveredAdvisory, S>>,
112    ) -> impl Future<Output = Result<(), Self::Error>>;
113}
114
115impl<F, E, Fut, S> RetrievedVisitor<S> for F
116where
117    F: Fn(Result<RetrievedAdvisory, RetrievalError<DiscoveredAdvisory, S>>) -> Fut,
118    Fut: Future<Output = Result<(), E>>,
119    E: std::fmt::Display + Debug,
120    S: Source,
121{
122    type Error = E;
123    type Context = ();
124
125    async fn visit_context(
126        &self,
127        _context: &RetrievalContext<'_>,
128    ) -> Result<Self::Context, Self::Error> {
129        Ok(())
130    }
131
132    async fn visit_advisory(
133        &self,
134        _ctx: &Self::Context,
135        outcome: Result<RetrievedAdvisory, RetrievalError<DiscoveredAdvisory, S>>,
136    ) -> Result<(), Self::Error> {
137        self(outcome).await
138    }
139}
140
141pub struct RetrievingVisitor<V: RetrievedVisitor<S>, S: Source + KeySource> {
142    visitor: V,
143    source: S,
144}
145
146impl<V, S> RetrievingVisitor<V, S>
147where
148    V: RetrievedVisitor<S>,
149    S: Source + KeySource,
150{
151    pub fn new(source: S, visitor: V) -> Self {
152        Self { visitor, source }
153    }
154}
155
156#[derive(Debug, thiserror::Error)]
157pub enum Error<VE, SE, KSE>
158where
159    VE: std::fmt::Display + Debug,
160    SE: std::fmt::Display + Debug,
161    KSE: std::fmt::Display + Debug,
162{
163    #[error("Source error: {0}")]
164    Source(SE),
165    #[error("Key source error: {0}")]
166    KeySource(KeySourceError<KSE>),
167    #[error(transparent)]
168    Visitor(VE),
169}
170
171impl<V, S> DiscoveredVisitor for RetrievingVisitor<V, S>
172where
173    V: RetrievedVisitor<S>,
174    S: Source + KeySource,
175{
176    type Error =
177        Error<V::Error, <S as walker_common::source::Source>::Error, <S as KeySource>::Error>;
178    type Context = V::Context;
179
180    async fn visit_context(
181        &self,
182        context: &DiscoveredContext<'_>,
183    ) -> Result<Self::Context, Self::Error> {
184        let mut keys = Vec::with_capacity(context.metadata.public_openpgp_keys.len());
185
186        for key in &context.metadata.public_openpgp_keys {
187            keys.push(
188                self.source
189                    .load_public_key(key.into())
190                    .await
191                    .map_err(Error::KeySource)?,
192            );
193        }
194
195        log::info!(
196            "Loaded {} public key{}",
197            keys.len(),
198            if keys.len() != 1 {
199                "s"
200            } else {
201                Default::default()
202            }
203        );
204        if log::log_enabled!(log::Level::Debug) {
205            for key in keys.iter().flat_map(|k| &k.certs) {
206                log::debug!("   {}", key.key_handle());
207                for id in key.userids() {
208                    log::debug!("     {}", id.userid());
209                }
210            }
211        }
212
213        self.visitor
214            .visit_context(&RetrievalContext {
215                discovered: context,
216                keys: &keys,
217            })
218            .await
219            .map_err(Error::Visitor)
220    }
221
222    async fn visit_advisory(
223        &self,
224        context: &Self::Context,
225        discovered: DiscoveredAdvisory,
226    ) -> Result<(), Self::Error> {
227        let advisory = self
228            .source
229            .load_advisory(discovered.clone())
230            .await
231            .map_err(|err| RetrievalError::Source { err, discovered });
232
233        self.visitor
234            .visit_advisory(context, advisory)
235            .await
236            .map_err(Error::Visitor)?;
237
238        Ok(())
239    }
240}