use std::{collections::HashMap, sync::Arc, time::Instant};
use crate::{
error::Error,
hsm::{self, group::types::Member},
node::utils::validate_xnames_format_and_membership_agaisnt_single_hsm,
};
use tokio::sync::Semaphore;
use crate::hsm::group::{http_client, types::Group};
use super::http_client::{delete_member, post_member};
pub async fn add_member(
auth_token: &str,
base_url: &str,
root_cert: &[u8],
group_label: &str,
new_member: &str,
) -> Result<Vec<String>, Error> {
let group_vec = crate::hsm::group::http_client::get(
base_url,
auth_token,
root_cert,
Some(&group_label.to_string()),
None,
)
.await?;
if let Some(group) = group_vec.first().cloned().as_mut() {
let new_member = new_member.to_string();
let member = crate::hsm::group::types::Member {
id: Some(new_member.clone()),
};
let _ = crate::hsm::group::http_client::post_member(
auth_token,
base_url,
root_cert,
group_label,
member,
)
.await?;
group.get_members().push(new_member);
Ok(group.get_members())
} else {
Err(Error::Message(format!(
"No HSM group '{}' found",
group_label
)))
}
}
pub async fn get_member_vec_from_hsm_name_vec_2(
auth_token: &str,
base_url: &str,
root_cert: &[u8],
hsm_name_vec: Vec<String>,
) -> Result<Vec<String>, Error> {
log::info!("Get xnames for HSM groups: {:?}", hsm_name_vec);
let start = Instant::now();
let mut hsm_group_member_vec: Vec<String> = Vec::new();
let pipe_size = 10;
let mut tasks = tokio::task::JoinSet::new();
let sem = Arc::new(Semaphore::new(pipe_size)); for hsm_name in hsm_name_vec {
let auth_token_string = auth_token.to_string();
let base_url_string = base_url.to_string();
let root_cert_vec = root_cert.to_vec();
let permit = Arc::clone(&sem).acquire_owned().await;
tasks.spawn(async move {
let _permit = permit;
let hsm_vec: Result<Vec<Group>, Error> = http_client::get(
&base_url_string,
&auth_token_string,
&root_cert_vec,
Some(&hsm_name),
None,
)
.await;
hsm_vec
});
}
while let Some(message) = tasks.join_next().await {
match message {
Ok(Ok(hsm_group_vec)) => {
let mut hsm_grop_members = hsm_group_vec
.first()
.unwrap()
.members
.as_ref()
.unwrap()
.ids
.clone()
.unwrap();
hsm_group_member_vec.append(&mut hsm_grop_members);
}
Ok(Err(error)) => log::warn!("{error}"),
Err(error) => {
return Err(Error::Message(error.to_string()));
}
}
}
let duration = start.elapsed();
log::info!("Time elapsed to get HSM members is: {:?}", duration);
Ok(hsm_group_member_vec)
}
pub async fn get_hsm_map_and_filter_by_hsm_name_vec(
auth_token: &str,
base_url: &str,
root_cert: &[u8],
hsm_name_vec: Vec<&str>,
) -> Result<HashMap<String, Vec<String>>, Error> {
let hsm_group_vec = http_client::get_all(base_url, auth_token, root_cert)
.await
.map_err(|e| Error::Message(e.to_string()))?;
Ok(filter_and_convert_to_map(hsm_name_vec, hsm_group_vec))
}
pub fn filter_and_convert_to_map(
hsm_name_vec: Vec<&str>,
hsm_group_vec: Vec<Group>,
) -> HashMap<String, Vec<String>> {
let mut hsm_group_map: HashMap<String, Vec<String>> = HashMap::new();
for hsm_group in hsm_group_vec {
if hsm_name_vec.contains(&hsm_group.label.as_str()) {
hsm_group_map.entry(hsm_group.label).or_insert(
hsm_group
.members
.and_then(|members| Some(members.ids.unwrap_or_default()))
.unwrap(),
);
}
}
hsm_group_map
}
pub async fn update_hsm_group_members(
auth_token: &str,
base_url: &str,
root_cert: &[u8],
group_label: &str,
group_members_to_delete: &Vec<String>,
group_members_to_add: &Vec<String>,
) -> Result<(), Error> {
let group =
hsm::group::http_client::get_one(base_url, auth_token, root_cert, group_label).await?;
let mut group_members = group.members.unwrap().ids.unwrap();
group_members.retain(|xname| group_members_to_delete.contains(xname));
for xname in group_members_to_add {
group_members.push(xname.to_string());
}
Ok(())
}
pub async fn migrate_hsm_members(
shasta_token: &str,
shasta_base_url: &str,
shasta_root_cert: &[u8],
target_hsm_group_name: &str,
parent_hsm_group_name: &str,
new_target_hsm_members: Vec<&str>,
nodryrun: bool,
) -> Result<(Vec<String>, Vec<String>), Error> {
if !validate_xnames_format_and_membership_agaisnt_single_hsm(
shasta_token,
shasta_base_url,
shasta_root_cert,
new_target_hsm_members.as_slice(),
Some(&parent_hsm_group_name.to_string()),
)
.await
{
let error_msg = format!("Nodes '{}' not valid", new_target_hsm_members.join(", "));
return Err(Error::Message(error_msg));
}
let mut target_hsm_group_member_vec: Vec<String> = get_member_vec_from_hsm_name_vec_2(
shasta_token,
shasta_base_url,
shasta_root_cert,
vec![target_hsm_group_name.to_string()],
)
.await?;
target_hsm_group_member_vec
.extend(new_target_hsm_members.iter().map(|xname| xname.to_string()));
target_hsm_group_member_vec.sort();
target_hsm_group_member_vec.dedup();
let mut parent_hsm_group_member_vec: Vec<String> = get_member_vec_from_hsm_name_vec_2(
shasta_token,
shasta_base_url,
shasta_root_cert,
vec![parent_hsm_group_name.to_string()],
)
.await?;
parent_hsm_group_member_vec
.retain(|parent_member| !target_hsm_group_member_vec.contains(parent_member));
parent_hsm_group_member_vec.sort();
parent_hsm_group_member_vec.dedup();
if !nodryrun {
let target_hsm_group = serde_json::json!({
"label": target_hsm_group_name,
"decription": "",
"members": target_hsm_group_member_vec,
"tags": []
});
println!(
"Target HSM group:\n{}",
serde_json::to_string_pretty(&target_hsm_group).unwrap()
);
let parent_hsm_group = serde_json::json!({
"label": parent_hsm_group_name,
"decription": "",
"members": parent_hsm_group_member_vec,
"tags": []
});
println!(
"Parent HSM group:\n{}",
serde_json::to_string_pretty(&parent_hsm_group).unwrap()
);
println!("dry-run enabled, changes not persisted.");
} else {
for xname in new_target_hsm_members {
let member = Member {
id: Some(xname.to_string()),
};
let _ = post_member(
shasta_token,
shasta_base_url,
shasta_root_cert,
target_hsm_group_name,
member,
)
.await;
let _ = delete_member(
shasta_token,
shasta_base_url,
shasta_root_cert,
parent_hsm_group_name,
xname,
)
.await;
}
}
Ok((target_hsm_group_member_vec, parent_hsm_group_member_vec))
}