1use crate::error::Error;
2use crate::kani::KanidmOrcaClient;
3use crate::model::ActorRole;
4use crate::profile::Profile;
5use crate::state::{Credential, Flag, Group, GroupName, Person, PreflightState, State};
6use hashbrown::HashMap;
7use rand::distr::{Alphanumeric, SampleString, Uniform};
8use rand::seq::{index, IndexedRandom};
9
10use rand::{Rng, SeedableRng};
11use rand_chacha::ChaCha8Rng;
12
13use std::collections::BTreeSet;
14
15const PEOPLE_PREFIX: &str = "person";
16
17fn random_name(prefix: &str, rng: &mut ChaCha8Rng) -> String {
24 let suffix = Alphanumeric.sample_string(rng, 8).to_lowercase();
25 format!("{}_{}", prefix, suffix)
26}
27
28fn random_password(rng: &mut ChaCha8Rng) -> String {
29 Alphanumeric.sample_string(rng, 24)
30}
31
32pub async fn populate(_client: &KanidmOrcaClient, profile: Profile) -> Result<State, Error> {
33 let mut seeded_rng = ChaCha8Rng::seed_from_u64(profile.seed());
36
37 let female_given_names = std::include_str!("../names-dataset/dataset/Female_given_names.txt");
38 let male_given_names = std::include_str!("../names-dataset/dataset/Male_given_names.txt");
39
40 let given_names = female_given_names
41 .split('\n')
42 .chain(male_given_names.split('\n'))
43 .collect::<Vec<_>>();
44
45 let surnames = std::include_str!("../names-dataset/dataset/Surnames.txt");
46
47 let surnames = surnames.split('\n').collect::<Vec<_>>();
48
49 debug!(
50 "name pool: given: {} - family: {}",
51 given_names.len(),
52 surnames.len()
53 );
54
55 let thread_count = profile.thread_count();
56
57 let preflight_flags = vec![
59 Flag::DisableAllPersonsMFAPolicy,
60 Flag::ExtendPrivilegedAuthExpiry,
61 ];
62
63 let mut groups = vec![
68 Group {
69 name: GroupName::RolePeopleSelfSetPassword,
70 role: ActorRole::PeopleSelfSetPassword,
71 ..Default::default()
72 },
73 Group {
74 name: GroupName::RolePeoplePiiReader,
75 role: ActorRole::PeoplePiiReader,
76 ..Default::default()
77 },
78 Group {
79 name: GroupName::RolePeopleSelfMailWrite,
80 role: ActorRole::PeopleSelfMailWrite,
81 ..Default::default()
82 },
83 Group {
84 name: GroupName::RolePeopleSelfReadProfile,
85 role: ActorRole::PeopleSelfReadProfile,
86 ..Default::default()
87 },
88 Group {
89 name: GroupName::RolePeopleSelfReadMemberOf,
90 role: ActorRole::PeopleSelfReadMemberOf,
91 ..Default::default()
92 },
93 Group {
94 name: GroupName::RolePeopleGroupAdmin,
95 role: ActorRole::PeopleGroupAdmin,
96 ..Default::default()
97 },
98 ];
99
100 let mut persons = Vec::with_capacity(profile.person_count() as usize);
103 let mut person_usernames = BTreeSet::new();
104
105 let model = *profile.model();
106
107 for _ in 0..profile.person_count() {
108 let given_name = given_names
109 .choose(&mut seeded_rng)
110 .expect("name set corrupted");
111 let surname = surnames
112 .choose(&mut seeded_rng)
113 .expect("name set corrupted");
114
115 let display_name = format!("{} {}", given_name, surname);
116
117 let username = display_name
118 .chars()
119 .filter(|c| c.is_ascii_alphanumeric())
120 .collect::<String>()
121 .to_lowercase();
122
123 let mut username = if username.is_empty() {
124 random_name(PEOPLE_PREFIX, &mut seeded_rng)
125 } else {
126 username
127 };
128
129 while person_usernames.contains(&username) {
130 username = random_name(PEOPLE_PREFIX, &mut seeded_rng);
131 }
132
133 let password = random_password(&mut seeded_rng);
134
135 let roles = BTreeSet::new();
136
137 let p = Person {
139 preflight_state: PreflightState::Present,
140 username: username.clone(),
141 display_name,
142 roles,
143 credential: Credential::Password { plain: password },
144 model,
145 };
146
147 debug!(?p);
148
149 person_usernames.insert(username.clone());
150 persons.push(p);
151 }
152
153 let member_count_by_group: HashMap<GroupName, u64> = profile
160 .get_properties_by_group()
161 .iter()
162 .filter_map(|(name, properties)| {
163 let group_name = GroupName::try_from(name).ok()?;
164 properties.member_count.map(|count| (group_name, count))
165 })
166 .collect();
167
168 for group in groups.iter_mut() {
169 let persons_to_choose = match member_count_by_group.get(&group.name) {
170 Some(person_count) => *person_count as usize,
171 None => {
172 let baseline = persons.len() / 3;
173 let inverse = persons.len() - baseline;
174 let extra =
176 Uniform::new(0, inverse).map_err(|err| Error::RandomNumber(err.to_string()))?;
177 baseline + seeded_rng.sample(extra)
178 }
179 };
180
181 assert!(persons_to_choose <= persons.len());
182
183 debug!(?persons_to_choose);
184
185 let person_index = index::sample(&mut seeded_rng, persons.len(), persons_to_choose);
186
187 let mut person_index = person_index.into_vec();
189 person_index.sort_unstable();
190
191 for p_idx in person_index {
192 let person = persons.get_mut(p_idx).unwrap();
193
194 group.members.insert(person.username.clone());
196
197 person.roles.insert(group.role.clone());
200 }
201 }
202
203 drop(member_count_by_group); let state = State {
216 profile,
217 groups,
219 preflight_flags,
220 persons,
221 thread_count,
222 };
223
224 Ok(state)
225}