use crate::common::load_metadata_repo;
use crate::datetime::parse_datetime;
use crate::error::{self, Result};
use crate::source::parse_key_source;
use chrono::{DateTime, Utc};
use clap::Parser;
use snafu::ResultExt;
use std::collections::HashMap;
use std::num::NonZeroU64;
use std::path::PathBuf;
use tough::editor::targets::TargetsEditor;
use tough::key_source::KeySource;
use url::Url;
#[derive(Debug, Parser)]
pub(crate) struct AddKeyArgs {
#[clap(short = 'k', long = "key", required = true, parse(try_from_str = parse_key_source))]
keys: Vec<Box<dyn KeySource>>,
#[clap(long = "new-key", required = true, parse(try_from_str = parse_key_source))]
new_keys: Vec<Box<dyn KeySource>>,
#[clap(short = 'e', long = "expires", parse(try_from_str = parse_datetime))]
expires: DateTime<Utc>,
#[clap(short = 'v', long = "version")]
version: NonZeroU64,
#[clap(short = 'r', long = "root")]
root: PathBuf,
#[clap(short = 'm', long = "metadata-url")]
metadata_base_url: Url,
#[clap(short = 'o', long = "outdir")]
outdir: PathBuf,
#[clap(long = "delegated-role")]
delegated_role: Option<String>,
}
impl AddKeyArgs {
pub(crate) fn run(&self, role: &str) -> Result<()> {
let repository = load_metadata_repo(&self.root, self.metadata_base_url.clone())?;
self.add_key(
role,
TargetsEditor::from_repo(repository, role)
.context(error::EditorFromRepoSnafu { path: &self.root })?,
)
}
fn add_key(&self, role: &str, mut editor: TargetsEditor) -> Result<()> {
let mut key_pairs = HashMap::new();
for source in &self.new_keys {
let key_pair = source
.as_sign()
.context(error::KeyPairFromKeySourceSnafu)?
.tuf_key();
key_pairs.insert(
key_pair
.key_id()
.context(error::JsonSerializationSnafu {})?
.clone(),
key_pair,
);
}
let updated_role = editor
.add_key(key_pairs, self.delegated_role.as_deref())
.context(error::LoadMetadataSnafu)?
.version(self.version)
.expires(self.expires)
.sign(&self.keys)
.context(error::SignRepoSnafu)?;
let metadata_destination_out = &self.outdir.join("metadata");
updated_role
.write(metadata_destination_out, false)
.context(error::WriteRolesSnafu {
roles: [role.to_string()].to_vec(),
})?;
Ok(())
}
}