kanidmd_lib/
utils.rs
1use crate::prelude::*;
4use hashbrown::HashSet;
5use rand::distr::{Distribution, Uniform};
6use rand::{rng, Rng};
7use std::ops::Range;
8
9#[derive(Debug)]
10pub struct DistinctAlpha;
11
12pub type Sid = [u8; 4];
13
14pub fn uuid_to_gid_u32(u: Uuid) -> u32 {
15 let b_ref = u.as_bytes();
16 let mut x: [u8; 4] = [0; 4];
17 x.clone_from_slice(&b_ref[12..16]);
18 u32::from_be_bytes(x)
19}
20
21fn uuid_from_u64_u32(a: u64, b: u32, sid: Sid) -> Uuid {
22 let mut v: Vec<u8> = Vec::with_capacity(16);
23 v.extend_from_slice(&a.to_be_bytes());
24 v.extend_from_slice(&b.to_be_bytes());
25 v.extend_from_slice(&sid);
26
27 #[allow(clippy::expect_used)]
28 uuid::Builder::from_slice(v.as_slice())
29 .expect("invalid slice for uuid builder")
30 .into_uuid()
31}
32
33pub fn uuid_from_duration(d: Duration, sid: Sid) -> Uuid {
34 uuid_from_u64_u32(d.as_secs(), d.subsec_nanos(), sid)
35}
36
37pub(crate) fn password_from_random_len(len: u32) -> String {
38 rng()
39 .sample_iter(&DistinctAlpha)
40 .take(len as usize)
41 .collect::<String>()
42}
43
44pub fn password_from_random() -> String {
45 password_from_random_len(48)
46}
47
48pub fn backup_code_from_random() -> HashSet<String> {
49 (0..8).map(|_| readable_password_from_random()).collect()
50}
51
52pub fn readable_password_from_random() -> String {
53 let mut trng = rng();
56 format!(
57 "{}-{}-{}-{}",
58 (&mut trng)
59 .sample_iter(&DistinctAlpha)
60 .take(5)
61 .collect::<String>(),
62 (&mut trng)
63 .sample_iter(&DistinctAlpha)
64 .take(5)
65 .collect::<String>(),
66 (&mut trng)
67 .sample_iter(&DistinctAlpha)
68 .take(5)
69 .collect::<String>(),
70 (&mut trng)
71 .sample_iter(&DistinctAlpha)
72 .take(5)
73 .collect::<String>(),
74 )
75}
76
77impl Distribution<char> for DistinctAlpha {
78 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> char {
79 const RANGE: u32 = 55;
80 const GEN_ASCII_STR_CHARSET: &[u8] = b"ABCDEFGHJKLMNPQRSTUVWXYZ\
81 abcdefghjkpqrstuvwxyz\
82 0123456789";
83 #[allow(clippy::expect_used)]
85 let range = Uniform::new(0, RANGE).expect("Failed to get a uniform range");
86
87 let n = range.sample(rng);
88 GEN_ASCII_STR_CHARSET[n as usize] as char
89 }
90}
91
92pub(crate) struct GraphemeClusterIter<'a> {
93 value: &'a str,
94 char_bounds: Vec<usize>,
95 window: usize,
96 range: Range<usize>,
97}
98
99impl<'a> GraphemeClusterIter<'a> {
100 pub fn new(value: &'a str, window: usize) -> Self {
101 let char_bounds = if value.len() < window {
102 Vec::with_capacity(0)
103 } else {
104 let mut char_bounds = Vec::with_capacity(value.len());
105 for idx in 0..value.len() {
106 if value.is_char_boundary(idx) {
107 char_bounds.push(idx);
108 }
109 }
110 char_bounds.push(value.len());
111 char_bounds
112 };
113
114 let window_max = char_bounds.len().saturating_sub(window);
115 let range = 0..window_max;
116
117 GraphemeClusterIter {
118 value,
119 char_bounds,
120 window,
121 range,
122 }
123 }
124}
125
126impl<'a> Iterator for GraphemeClusterIter<'a> {
127 type Item = &'a str;
128
129 fn next(&mut self) -> Option<&'a str> {
130 self.range.next().map(|idx| {
131 let min = self.char_bounds[idx];
132 let max = self.char_bounds[idx + self.window];
133 &self.value[min..max]
134 })
135 }
136
137 fn size_hint(&self) -> (usize, Option<usize>) {
138 let clusters = self.char_bounds.len().saturating_sub(1);
139 (clusters, Some(clusters))
140 }
141}
142
143pub(crate) fn trigraph_iter(value: &str) -> impl Iterator<Item = &str> {
144 GraphemeClusterIter::new(value, 3)
145 .chain(GraphemeClusterIter::new(value, 2))
146 .chain(GraphemeClusterIter::new(value, 1))
147}
148
149#[cfg(test)]
150mod tests {
151 use crate::prelude::*;
152 use std::time::Duration;
153
154 use crate::utils::{uuid_from_duration, uuid_to_gid_u32, GraphemeClusterIter};
155
156 #[test]
157 fn test_utils_uuid_from_duration() {
158 let u1 = uuid_from_duration(Duration::from_secs(1), [0xff; 4]);
159 assert_eq!(
160 "00000000-0000-0001-0000-0000ffffffff",
161 u1.as_hyphenated().to_string()
162 );
163
164 let u2 = uuid_from_duration(Duration::from_secs(1000), [0xff; 4]);
165 assert_eq!(
166 "00000000-0000-03e8-0000-0000ffffffff",
167 u2.as_hyphenated().to_string()
168 );
169 }
170
171 #[test]
172 fn test_utils_uuid_to_gid_u32() {
173 let u1 = uuid!("00000000-0000-0001-0000-000000000000");
174 let r1 = uuid_to_gid_u32(u1);
175 assert_eq!(r1, 0);
176
177 let u2 = uuid!("00000000-0000-0001-0000-0000ffffffff");
178 let r2 = uuid_to_gid_u32(u2);
179 assert_eq!(r2, 0xffffffff);
180
181 let u3 = uuid!("00000000-0000-0001-0000-ffff12345678");
182 let r3 = uuid_to_gid_u32(u3);
183 assert_eq!(r3, 0x12345678);
184 }
185
186 #[test]
187 fn test_utils_grapheme_cluster_iter() {
188 let d = "❤️🧡💛💚💙💜";
189
190 let gc_expect = vec!["❤", "\u{fe0f}", "🧡", "💛", "💚", "💙", "💜"];
191 let gc: Vec<_> = GraphemeClusterIter::new(d, 1).collect();
192 assert_eq!(gc, gc_expect);
193
194 let gc_expect = vec!["❤\u{fe0f}", "\u{fe0f}🧡", "🧡💛", "💛💚", "💚💙", "💙💜"];
195 let gc: Vec<_> = GraphemeClusterIter::new(d, 2).collect();
196 assert_eq!(gc, gc_expect);
197
198 let gc_expect = vec!["❤\u{fe0f}🧡", "\u{fe0f}🧡💛", "🧡💛💚", "💛💚💙", "💚💙💜"];
199 let gc: Vec<_> = GraphemeClusterIter::new(d, 3).collect();
200 assert_eq!(gc, gc_expect);
201
202 let d = "🤷🏿♂️";
203
204 let gc_expect = vec!["🤷", "🏿", "\u{200d}", "♂", "\u{fe0f}"];
205 let gc: Vec<_> = GraphemeClusterIter::new(d, 1).collect();
206 assert_eq!(gc, gc_expect);
207
208 let gc_expect = vec!["🤷🏿", "🏿\u{200d}", "\u{200d}♂", "♂\u{fe0f}"];
209 let gc: Vec<_> = GraphemeClusterIter::new(d, 2).collect();
210 assert_eq!(gc, gc_expect);
211
212 let gc_expect = vec!["🤷🏿\u{200d}", "🏿\u{200d}♂", "\u{200d}♂\u{fe0f}"];
213 let gc: Vec<_> = GraphemeClusterIter::new(d, 3).collect();
214 assert_eq!(gc, gc_expect);
215 }
216}