use crate::error::Error;
use crate::kani::KanidmOrcaClient;
use crate::model::ActorRole;
use crate::profile::Profile;
use crate::state::{Credential, Flag, Group, GroupName, Person, PreflightState, State};
use hashbrown::HashMap;
use rand::distributions::{Alphanumeric, DistString, Uniform};
use rand::seq::{index, SliceRandom};
use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha8Rng;
use std::collections::BTreeSet;
const PEOPLE_PREFIX: &str = "person";
fn random_name(prefix: &str, rng: &mut ChaCha8Rng) -> String {
let suffix = Alphanumeric.sample_string(rng, 8).to_lowercase();
format!("{}_{}", prefix, suffix)
}
fn random_password(rng: &mut ChaCha8Rng) -> String {
Alphanumeric.sample_string(rng, 24)
}
pub async fn populate(_client: &KanidmOrcaClient, profile: Profile) -> Result<State, Error> {
let mut seeded_rng = ChaCha8Rng::seed_from_u64(profile.seed());
let female_given_names = std::include_str!("../names-dataset/dataset/Female_given_names.txt");
let male_given_names = std::include_str!("../names-dataset/dataset/Male_given_names.txt");
let given_names = female_given_names
.split('\n')
.chain(male_given_names.split('\n'))
.collect::<Vec<_>>();
let surnames = std::include_str!("../names-dataset/dataset/Surnames.txt");
let surnames = surnames.split('\n').collect::<Vec<_>>();
debug!(
"name pool: given: {} - family: {}",
given_names.len(),
surnames.len()
);
let thread_count = profile.thread_count();
let preflight_flags = vec![
Flag::DisableAllPersonsMFAPolicy,
Flag::ExtendPrivilegedAuthExpiry,
];
let mut groups = vec![
Group {
name: GroupName::RolePeopleSelfSetPassword,
role: ActorRole::PeopleSelfSetPassword,
..Default::default()
},
Group {
name: GroupName::RolePeoplePiiReader,
role: ActorRole::PeoplePiiReader,
..Default::default()
},
Group {
name: GroupName::RolePeopleSelfMailWrite,
role: ActorRole::PeopleSelfMailWrite,
..Default::default()
},
Group {
name: GroupName::RolePeopleSelfReadProfile,
role: ActorRole::PeopleSelfReadProfile,
..Default::default()
},
Group {
name: GroupName::RolePeopleSelfReadMemberOf,
role: ActorRole::PeopleSelfReadMemberOf,
..Default::default()
},
Group {
name: GroupName::RolePeopleGroupAdmin,
role: ActorRole::PeopleGroupAdmin,
..Default::default()
},
];
let mut persons = Vec::with_capacity(profile.person_count() as usize);
let mut person_usernames = BTreeSet::new();
let model = *profile.model();
for _ in 0..profile.person_count() {
let given_name = given_names
.choose(&mut seeded_rng)
.expect("name set corrupted");
let surname = surnames
.choose(&mut seeded_rng)
.expect("name set corrupted");
let display_name = format!("{} {}", given_name, surname);
let username = display_name
.chars()
.filter(|c| c.is_ascii_alphanumeric())
.collect::<String>()
.to_lowercase();
let mut username = if username.is_empty() {
random_name(PEOPLE_PREFIX, &mut seeded_rng)
} else {
username
};
while person_usernames.contains(&username) {
username = random_name(PEOPLE_PREFIX, &mut seeded_rng);
}
let password = random_password(&mut seeded_rng);
let roles = BTreeSet::new();
let p = Person {
preflight_state: PreflightState::Present,
username: username.clone(),
display_name,
roles,
credential: Credential::Password { plain: password },
model,
};
debug!(?p);
person_usernames.insert(username.clone());
persons.push(p);
}
let member_count_by_group: HashMap<GroupName, u64> = profile
.get_properties_by_group()
.iter()
.filter_map(|(name, properties)| {
let group_name = GroupName::try_from(name).ok()?;
properties.member_count.map(|count| (group_name, count))
})
.collect();
for group in groups.iter_mut() {
let persons_to_choose = match member_count_by_group.get(&group.name) {
Some(person_count) => *person_count as usize,
None => {
let baseline = persons.len() / 3;
let inverse = persons.len() - baseline;
let extra = Uniform::new(0, inverse);
baseline + seeded_rng.sample(extra)
}
};
assert!(persons_to_choose <= persons.len());
debug!(?persons_to_choose);
let person_index = index::sample(&mut seeded_rng, persons.len(), persons_to_choose);
let mut person_index = person_index.into_vec();
person_index.sort_unstable();
for p_idx in person_index {
let person = persons.get_mut(p_idx).unwrap();
group.members.insert(person.username.clone());
person.roles.insert(group.role.clone());
}
}
drop(member_count_by_group); let state = State {
profile,
groups,
preflight_flags,
persons,
thread_count,
};
Ok(state)
}