voa-core 0.4.1

File Hierarchy for the Verification of OS Artifacts (VOA)
Documentation
//! Searches and lists verifiers on stdout
//!
//! ```
//! Usage: voa-list [OPTIONS] --os-id <OS_ID> --role <ROLE>
//!
//! Options:
//!       --os-id <OS_ID>
//!           Os id
//!       --os-version-id <OS_VERSION_ID>
//!           Os version id
//!       --os-variant-id <OS_VARIANT_ID>
//!           Os variant id
//!       --os-image-id <OS_IMAGE_ID>
//!           Os image id
//!       --os-image-version <OS_IMAGE_VERSION>
//!           Os image version
//!       --role <ROLE>
//!           Role
//!       --mode <MODE>
//!           Mode [default: artifact-verifier] [possible values: artifact-verifier, trust-anchor]
//!       --custom-context <CUSTOM_CONTEXT>
//!           Set the "Context" identifier to another value than "default"
//!   -h, --help
//!           Print help
//!   -V, --version
//!           Print version
//! ```
//!
//! - Os: `--os-id` is a mandatory parameter (all other fragments of "Os" are optional).
//! - Purpose: a `--role` must be given, `--mode` may be given to deviate from "ArtifactVerifier"
//! - Context: Is set to "default", unless `--custom-context` is passed.
//! - Technology: Is hardwired to "openpgp" in this example.
//!
//! # Example
//!
//! ```sh
//! cargo run --example voa-list -- --os-id arch --role packages
//! ```
//!
//! Output:
//!
//! ```json
//! Found verifiers: {
//!     "/etc/voa/arch/packages/default/openpgp/foo.pgp": [
//!         Verifier {
//!             voa_location: VoaLocation {
//!                 load_path: LoadPath {
//!                     path: "/etc/voa/",
//!                     ephemeral: false,
//!                     writable: true,
//!                 },
//!                 os: Os {
//!                     id: "arch",
//!                     version_id: None,
//!                     variant_id: None,
//!                     image_id: None,
//!                     image_version: None,
//!                 },
//!                 purpose: Purpose {
//!                     role: Packages,
//!                     mode: ArtifactVerifier,
//!                 },
//!                 context: Default,
//!                 technology: OpenPGP,
//!             },
//!             canonicalized: "/etc/voa/arch/packages/default/openpgp/foo.pgp",
//!         },
//!     ],
//! }
//! ```

use std::{path::PathBuf, str::FromStr};

use clap::{Parser, ValueEnum};
use log::debug;
use serde::Serialize;
use simplelog::{ColorChoice, Config, LevelFilter, TermLogger, TerminalMode};
use voa_core::{
    Voa,
    identifiers::{Context, CustomContext, IdentifierString, Mode, Os, Purpose, Role, Technology},
};

#[derive(Debug, Parser)]
#[command(about = "List VOA entries", version)]
struct Cli {
    /// Os id
    #[arg(long)]
    pub os_id: IdentifierString,

    /// Os version id
    #[arg(long)]
    pub os_version_id: Option<IdentifierString>,

    /// Os variant id
    #[arg(long)]
    pub os_variant_id: Option<IdentifierString>,

    /// Os image id
    #[arg(long)]
    pub os_image_id: Option<IdentifierString>,

    /// Os image version
    #[arg(long)]
    pub os_image_version: Option<IdentifierString>,

    /// Role
    #[arg(long)]
    pub role: String,

    /// Mode
    #[arg(long)]
    #[clap(default_value = "artifact-verifier")]
    pub mode: ModeArg,

    /// Set the "Context" identifier to another value than "default"
    #[arg(long)]
    pub custom_context: Option<IdentifierString>,
}

#[derive(Clone, Debug, ValueEnum)]
enum ModeArg {
    ArtifactVerifier,
    TrustAnchor,
}

impl From<ModeArg> for Mode {
    fn from(mode_arg: ModeArg) -> Self {
        match mode_arg {
            ModeArg::ArtifactVerifier => Mode::ArtifactVerifier,
            ModeArg::TrustAnchor => Mode::TrustAnchor,
        }
    }
}

#[derive(Debug, Serialize)]
struct Verifier {
    load_path: PathBuf,
    verifier: PathBuf,
}

fn init_logger() {
    if TermLogger::init(
        LevelFilter::Debug,
        Config::default(),
        TerminalMode::Stderr,
        ColorChoice::Auto,
    )
    .is_err()
    {
        debug!("Not initializing another logger, as one is initialized already.");
    }
}

fn main() -> anyhow::Result<()> {
    init_logger();

    let cli = Cli::parse();

    let os = Os::new(
        cli.os_id,
        cli.os_version_id,
        cli.os_variant_id,
        cli.os_image_id,
        cli.os_image_version,
    );

    let purpose = Purpose::new(Role::from_str(&cli.role)?, cli.mode.into());

    let context = if let Some(custom_context) = &cli.custom_context {
        CustomContext::new(custom_context.clone()).into()
    } else {
        Context::Default
    };
    let technology = Technology::Openpgp;

    let voa = Voa::new();
    let verifiers = voa.lookup(os, purpose, context, technology);

    let out: Vec<Verifier> = verifiers
        .values()
        .flat_map(|vec| vec.iter())
        .map(|v| Verifier {
            load_path: v.voa_location().load_path().path().into(),
            verifier: v.canonicalized().into(),
        })
        .collect();

    let json = serde_json::to_string(&out)?;

    println!("{json}");

    Ok(())
}