use std::{collections::HashMap, path::PathBuf};
use clap::Parser;
use cosmian_kms_client::{
KmsClient,
cosmian_kmip::kmip_2_1::{
kmip_operations::{GetAttributes, GetAttributesResponse},
kmip_types::{AttributeReference, Tag, UniqueIdentifier},
},
reexport::cosmian_kms_client_utils::attributes_utils::{CLinkType, parse_selected_attributes},
write_bytes_to_file,
};
use cosmian_logger::{debug, trace};
use serde_json::Value;
use crate::{
actions::kms::{console, labels::ATTRIBUTE_ID, shared::get_key_uid},
error::result::KmsCliResult,
};
#[derive(Parser, Debug, Default)]
#[clap(verbatim_doc_comment)]
pub struct GetAttributesAction {
#[clap(long = ATTRIBUTE_ID, short = 'i', group = "id-tags")]
pub id: Option<String>,
#[clap(long = "tag", short = 't', value_name = "TAG", group = "id-tags")]
pub tags: Option<Vec<String>>,
#[clap(
long = "attribute",
short = 'a',
value_name = "ATTRIBUTE",
verbatim_doc_comment
)]
pub attribute_tags: Vec<Tag>,
#[clap(
long = "link-type",
short = 'l',
value_name = "LINK_TYPE",
verbatim_doc_comment
)]
pub attribute_link_types: Vec<CLinkType>,
#[clap(long = "output-file", short = 'o', verbatim_doc_comment)]
pub output_file: Option<PathBuf>,
}
impl GetAttributesAction {
pub async fn run(&self, kms_rest_client: KmsClient) -> KmsCliResult<HashMap<String, Value>> {
trace!("{self:?}");
let id = get_key_uid(self.id.as_ref(), self.tags.as_ref(), ATTRIBUTE_ID)?;
let (unique_identifier, results) = get_attributes(
&kms_rest_client,
&id,
&self.attribute_tags,
&self.attribute_link_types,
)
.await?;
if let Some(output_file) = &self.output_file {
let json = serde_json::to_string_pretty(&results)?;
debug!("GetAttributes response for {unique_identifier}: {}", json);
write_bytes_to_file(json.as_bytes(), output_file)?;
let stdout = format!(
"The attributes for {unique_identifier} were exported to {}",
output_file.display()
);
console::Stdout::new(&stdout).write()?;
} else {
let mut stdout = console::Stdout::new(&format!("Attributes for {unique_identifier}"));
stdout.set_unique_identifier(&unique_identifier);
stdout.set_attributes(results.clone());
stdout.write()?;
}
Ok(results)
}
}
pub(crate) async fn get_attributes(
kms_rest_client: &KmsClient,
id_or_tags: &str,
attribute_tags: &[Tag],
attribute_link_types: &[CLinkType],
) -> KmsCliResult<(UniqueIdentifier, HashMap<String, Value>)> {
let mut references: Vec<AttributeReference> = Vec::with_capacity(attribute_tags.len());
for tag in attribute_tags {
references.push(AttributeReference::Standard(*tag));
}
let GetAttributesResponse {
unique_identifier,
attributes,
} = kms_rest_client
.get_attributes(GetAttributes {
unique_identifier: Some(UniqueIdentifier::TextString(id_or_tags.to_owned())),
attribute_reference: if references.is_empty() {
None
} else {
Some(references)
},
})
.await?;
debug!("GetAttributes response for {unique_identifier}: {attributes}",);
let results = parse_selected_attributes(
kms_rest_client.config.vendor_id.as_str(),
&attributes,
attribute_tags,
attribute_link_types,
)?;
Ok((unique_identifier, results))
}