use std::{error, fmt, fs, io};
use std::str::FromStr;
use std::sync::Arc;
use rpki::uri;
use crate::{api, constants};
use crate::api::ta::{
ApiTrustAnchorSignedRequest, TrustAnchorSignedResponse,
TrustAnchorSignerInfo,
};
use crate::cli::options::args::JsonFile;
use crate::cli::report::{Report, ReportFormat};
use crate::cli::ta::signer::{
SignerClientError, SignerInitInfo, SignerReissueInfo, TrustAnchorSignerManager
};
use crate::tasigner::{Config, ConfigError, TrustAnchorProxySignerExchanges};
#[derive(clap::Args)]
pub struct Command {
#[arg(
long, short,
value_name = "path",
default_value = constants::KRILL_DEFAULT_TA_CONFIG_FILE,
)]
config: ConfigFile,
#[arg(
short, long,
env = "KRILL_CLI_FORMAT",
default_value = "text",
)]
pub format: ReportFormat,
#[command(subcommand)]
pub command: Subcommand,
}
impl Command {
pub fn run(self) -> Report {
self.command.run(&self.config.0)
}
}
#[derive(clap::Subcommand)]
pub enum Subcommand {
Init(Init),
Reissue(Reissue),
Show(Show),
Process(Process),
Last(Last),
Exchanges(Exchanges),
}
impl Subcommand {
pub fn run(self, manager: &TrustAnchorSignerManager) -> Report {
match self {
Self::Init(cmd) => cmd.run(manager).into(),
Self::Reissue(cmd) => cmd.run(manager).into(),
Self::Show(cmd) => cmd.run(manager).into(),
Self::Process(cmd) => cmd.run(manager).into(),
Self::Last(cmd) => cmd.run(manager).into(),
Self::Exchanges(cmd) => cmd.run(manager).into(),
}
}
}
#[derive(clap::Args)]
pub struct Init {
#[arg(long, short = 'i', value_name = "path")]
proxy_id: JsonFile<api::ca::IdCertInfo, IdiMsg>,
#[arg(long, short = 'r', value_name = "path")]
proxy_repository_contact: JsonFile<api::admin::RepositoryContact, RcMsg>,
#[arg(long, value_name = "rsync URI")]
tal_rsync: uri::Rsync,
#[arg(long, value_name = "HTTPS URI")]
tal_https: Vec<uri::Https>,
#[arg(long, value_name = "path")]
private_key_pem: Option<PrivateKeyFile>,
#[arg(long, value_name = "number", default_value = "1")]
initial_manifest_number: u64,
}
impl Init {
pub fn run(
self, manager: &TrustAnchorSignerManager
) -> Result<api::status::Success, SignerClientError> {
manager.init(
SignerInitInfo {
proxy_id: self.proxy_id.content,
repo_info: self.proxy_repository_contact.content.into(),
tal_https: self.tal_https,
tal_rsync: self.tal_rsync,
private_key_pem: self.private_key_pem.map(|x| x.0),
ta_mft_nr_override: Some(self.initial_manifest_number),
}
)
}
}
#[derive(Clone, Copy, Debug, Default)]
struct IdiMsg;
impl fmt::Display for IdiMsg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("proxy ID")
}
}
#[derive(Clone, Copy, Debug, Default)]
struct RcMsg;
impl fmt::Display for RcMsg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("repository contact")
}
}
#[derive(clap::Args)]
pub struct Reissue {
#[arg(long, short = 'i', value_name = "path")]
proxy_id: JsonFile<api::ca::IdCertInfo, IdiMsg>,
#[arg(long, short = 'r', value_name = "path")]
proxy_repository_contact: JsonFile<api::admin::RepositoryContact, RcMsg>,
#[arg(long, value_name = "rsync URI")]
tal_rsync: uri::Rsync,
#[arg(long, value_name = "HTTPS URI")]
tal_https: Vec<uri::Https>,
}
impl Reissue {
pub fn run(
self, manager: &TrustAnchorSignerManager
) -> Result<api::status::Success, SignerClientError> {
manager.reissue(
SignerReissueInfo {
proxy_id: self.proxy_id.content,
repo_info: self.proxy_repository_contact.content.into(),
tal_https: self.tal_https,
tal_rsync: self.tal_rsync,
}
)
}
}
#[derive(clap::Args)]
pub struct Show;
impl Show {
pub fn run(
self, manager: &TrustAnchorSignerManager
) -> Result<TrustAnchorSignerInfo, SignerClientError> {
manager.show()
}
}
#[derive(clap::Args)]
pub struct Process {
#[arg(long, short, value_name = "path")]
request: JsonFile<ApiTrustAnchorSignedRequest, TasrMsg>,
#[arg(long, value_name = "number")]
ta_mft_number_override: Option<u64>,
}
impl Process {
pub fn run(
self, manager: &TrustAnchorSignerManager
) -> Result<TrustAnchorSignedResponse, SignerClientError> {
manager.process(
self.request.content.into(), self.ta_mft_number_override
)
}
}
#[derive(Clone, Copy, Debug, Default)]
struct TasrMsg;
impl fmt::Display for TasrMsg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("proxy request")
}
}
#[derive(clap::Args)]
pub struct Last;
impl Last {
pub fn run(
self, manager: &TrustAnchorSignerManager
) -> Result<TrustAnchorSignedResponse, SignerClientError> {
manager.show_last_response()
}
}
#[derive(clap::Args)]
pub struct Exchanges;
impl Exchanges {
pub fn run(
self, manager: &TrustAnchorSignerManager
) -> Result<TrustAnchorProxySignerExchanges, SignerClientError> {
manager.show_exchanges()
}
}
#[derive(Clone)]
pub struct ConfigFile(Arc<TrustAnchorSignerManager>);
impl FromStr for ConfigFile {
type Err = ConfigFileError;
fn from_str(path: &str) -> Result<Self, Self::Err> {
Config::parse(path).map_err(|err| {
ConfigFileError::Parse(path.into(), err)
}).and_then(|config| {
TrustAnchorSignerManager::create(config).map_err(
ConfigFileError::Create
)
}).map(|manager| Self(manager.into()))
}
}
#[derive(Clone)]
struct PrivateKeyFile(String);
impl FromStr for PrivateKeyFile {
type Err = PrivateKeyFileError;
fn from_str(path: &str) -> Result<Self, Self::Err> {
fs::read_to_string(path).map(Self).map_err(|err| {
PrivateKeyFileError { path: path.into(), err }
})
}
}
#[derive(Debug)]
#[allow(clippy::large_enum_variant)]
pub enum ConfigFileError {
Parse(String, ConfigError),
Create(SignerClientError),
}
impl fmt::Display for ConfigFileError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Parse(path, err) => {
write!(
f, "Failed to read config file '{path}': {err}"
)
}
Self::Create(err) => err.fmt(f)
}
}
}
impl error::Error for ConfigFileError { }
#[derive(Debug)]
pub struct PrivateKeyFileError {
path: String,
err: io::Error,
}
impl fmt::Display for PrivateKeyFileError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f, "Failed to read private key file '{}': {}'",
self.path, self.err
)
}
}
impl error::Error for PrivateKeyFileError { }