luct-extension 0.2.0

Core types and parsers for certificate transparency
Documentation
use crate::{USER_AGENT, store::browser_local_store};
use luct_otlsp::OtlspClientConfig;
use luct_scanner::ScannerConfig;
use serde::{Deserialize, Serialize};
use url::Url;
use web_time::Duration;

/// Loads the config from the local store
///
/// If no settings exist, it will create some
pub fn load_config() -> Result<ExtensionConfig, String> {
    let store = browser_local_store()?;

    let settings = match store
        .get_item("settings")
        .map_err(|err| err.as_string().unwrap())?
    {
        Some(settings) => serde_json::from_str::<ExtensionConfig>(&settings),
        None => {
            tracing::info!("Could not find a config. Initalizing with default");
            let settings = serde_json::from_str::<ExtensionConfig>("{}").unwrap();
            Ok(settings)
        }
    }
    .map_err(|err| err.to_string())?;

    store
        .set_item("settings", &serde_json::to_string(&settings).unwrap())
        .map_err(|err| err.as_string().unwrap())?;

    Ok(settings)
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExtensionConfig {
    #[serde(default = "default_false")]
    validate_cert_chain: bool,

    #[serde(default = "default_true")]
    use_otlsp: bool,

    #[serde(default = "default_otlsp_url")]
    otlsp_url: String,

    #[serde(default = "default_otlsp_connection_timeout")]
    otlsp_connection_timeout: u64,

    #[serde(default = "default_sth_freshness_threshold")]
    sth_freshness_threshold: u64,

    #[serde(default = "default_sth_update_threshold")]
    sth_update_threshold: u64,

    #[serde(default = "default_report_lru_cache")]
    report_lru_cache: usize,

    #[serde(default = "default_false")]
    debug_output: bool,
}

impl ExtensionConfig {
    pub fn report_lru_cache(&self) -> usize {
        self.report_lru_cache
    }
}

fn default_otlsp_url() -> String {
    "https://node.luct.dev/otlsp".to_string()
}

fn default_otlsp_connection_timeout() -> u64 {
    30
}

fn default_sth_freshness_threshold() -> u64 {
    24 * 60 * 60
}

fn default_sth_update_threshold() -> u64 {
    8 * 60 * 60
}

fn default_report_lru_cache() -> usize {
    1000
}

fn default_true() -> bool {
    true
}

fn default_false() -> bool {
    false
}

impl TryFrom<&ExtensionConfig> for ScannerConfig {
    type Error = String;

    fn try_from(config: &ExtensionConfig) -> Result<Self, Self::Error> {
        let config = ScannerConfig::builder()
            .validate_cert_chain(config.validate_cert_chain)
            .sth_freshness_threshold(Duration::from_secs(config.sth_freshness_threshold))
            .sth_update_threshold(Duration::from_secs(config.sth_update_threshold))
            .build()
            .map_err(|err| err.to_string())?;

        Ok(config)
    }
}

impl TryFrom<&ExtensionConfig> for OtlspClientConfig {
    type Error = String;

    fn try_from(config: &ExtensionConfig) -> Result<Self, Self::Error> {
        let config = OtlspClientConfig::builder()
            .agent(USER_AGENT.to_string())
            .proxy_url(if config.use_otlsp {
                Some(Url::parse(&config.otlsp_url).map_err(|err| err.to_string())?)
            } else {
                None
            })
            .connection_timeout(Duration::from_secs(config.otlsp_connection_timeout))
            .build()
            .map_err(|err| err.to_string())?;

        Ok(config)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use wasm_bindgen_test::wasm_bindgen_test;

    wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);

    #[test]
    fn default_config() {
        serde_json::from_str::<ExtensionConfig>("{}").unwrap();
    }

    #[wasm_bindgen_test]
    fn initalize_config() {
        load_config().unwrap();
    }
}