kanidm_cli/system_config/
badlist.rs
1use crate::common::OpType;
2use crate::{handle_client_error, PwBadlistOpt};
3
4use std::fs::File;
6use std::io::Read;
7use tokio::task;
8use zxcvbn::Score;
9
10const CHUNK_SIZE: usize = 1000;
11
12impl PwBadlistOpt {
13 pub fn debug(&self) -> bool {
14 match self {
15 PwBadlistOpt::Show(copt) => copt.debug,
16 PwBadlistOpt::Upload { copt, .. } => copt.debug,
17 PwBadlistOpt::Remove { copt, .. } => copt.debug,
18 }
19 }
20
21 pub async fn exec(&self) {
22 match self {
23 PwBadlistOpt::Show(copt) => {
24 let client = copt.to_client(OpType::Read).await;
25 match client.system_password_badlist_get().await {
26 Ok(list) => {
27 for i in list {
28 println!("{}", i);
29 }
30 eprintln!("--");
31 eprintln!("Success");
32 }
33 Err(e) => crate::handle_client_error(e, copt.output_mode),
34 }
35 }
36 PwBadlistOpt::Upload {
37 copt,
38 paths,
39 dryrun,
40 } => {
41 info!("pre-processing - this may take a while ...");
42
43 let mut pwset: Vec<String> = Vec::new();
44
45 for f in paths.iter() {
46 let mut file = match File::open(f) {
47 Ok(v) => v,
48 Err(e) => {
49 debug!(?e);
50 info!("Skipping file -> {:?}", f);
51 continue;
52 }
53 };
54 let mut contents = String::new();
55 if let Err(e) = file.read_to_string(&mut contents) {
56 error!("{:?} -> {:?}", f, e);
57 continue;
58 }
59 let mut inner_pw: Vec<_> =
60 contents.as_str().lines().map(str::to_string).collect();
61 pwset.append(&mut inner_pw);
62 }
63
64 debug!("Deduplicating pre-set ...");
65 pwset.sort_unstable();
66 pwset.dedup();
67
68 info!("Have {} unique passwords to process", pwset.len());
69
70 let task_handles: Vec<_> = pwset
72 .chunks(CHUNK_SIZE)
73 .map(|chunk| chunk.to_vec())
74 .map(|chunk| {
75 task::spawn_blocking(move || {
76 let x = chunk
77 .iter()
78 .filter(|v| {
79 if v.len() < 10 {
80 return false;
81 }
82 zxcvbn::zxcvbn(v.as_str(), &[]).score() >= Score::Four
83 })
84 .map(|s| s.to_string())
85 .collect::<Vec<_>>();
86 eprint!(".");
87 x
88 })
89 })
90 .collect();
91
92 let mut filt_pwset = Vec::with_capacity(pwset.len());
93
94 for task_handle in task_handles {
95 let Ok(mut results) = task_handle.await else {
96 error!("Failed to join a worker thread, unable to proceed");
97 return;
98 };
99 filt_pwset.append(&mut results);
100 }
101
102 filt_pwset.sort_unstable();
103
104 info!(
105 "{} passwords passed zxcvbn, uploading ...",
106 filt_pwset.len()
107 );
108
109 if *dryrun {
110 for pw in filt_pwset {
111 println!("{}", pw);
112 }
113 } else {
114 let client = copt.to_client(OpType::Write).await;
115 match client.system_password_badlist_append(filt_pwset).await {
116 Ok(_) => println!("Success"),
117 Err(e) => handle_client_error(e, copt.output_mode),
118 }
119 }
120 } PwBadlistOpt::Remove { copt, paths } => {
122 let client = copt.to_client(OpType::Write).await;
123
124 let mut pwset: Vec<String> = Vec::new();
125
126 for f in paths.iter() {
127 let mut file = match File::open(f) {
128 Ok(v) => v,
129 Err(e) => {
130 debug!(?e);
131 info!("Skipping file -> {:?}", f);
132 continue;
133 }
134 };
135 let mut contents = String::new();
136 if let Err(e) = file.read_to_string(&mut contents) {
137 error!("{:?} -> {:?}", f, e);
138 continue;
139 }
140 let mut inner_pw: Vec<_> =
141 contents.as_str().lines().map(str::to_string).collect();
142 pwset.append(&mut inner_pw);
143 }
144
145 debug!("Deduplicating pre-set ...");
146 pwset.sort_unstable();
147 pwset.dedup();
148
149 if pwset.is_empty() {
150 eprintln!("No entries to remove?");
151 return;
152 }
153
154 match client.system_password_badlist_remove(pwset).await {
155 Ok(_) => println!("Success"),
156 Err(e) => handle_client_error(e, copt.output_mode),
157 }
158 } }
160 }
161}