xref: /DragonOS/user/apps/user-manage/src/check/check.rs (revision 03746da3d9f3ea616cecdb6e581414002075f866)
1*03746da3SJomo use super::info::{GAddInfo, GDelInfo, GModInfo, PasswdInfo, UAddInfo, UDelInfo, UModInfo};
2*03746da3SJomo use crate::{
3*03746da3SJomo     error::error::{ErrorHandler, ExitStatus},
4*03746da3SJomo     parser::cmd::{CmdOption, GroupCommand, PasswdCommand, UserCommand},
5*03746da3SJomo };
6*03746da3SJomo use std::{
7*03746da3SJomo     collections::{HashMap, HashSet},
8*03746da3SJomo     fs,
9*03746da3SJomo     io::Write,
10*03746da3SJomo };
11*03746da3SJomo 
12*03746da3SJomo /// useradd命令检查器
13*03746da3SJomo #[derive(Debug)]
14*03746da3SJomo pub struct UAddCheck;
15*03746da3SJomo 
16*03746da3SJomo impl UAddCheck {
17*03746da3SJomo     /// **校验解析后的useradd命令**
18*03746da3SJomo     ///
19*03746da3SJomo     /// ## 参数
20*03746da3SJomo     /// - `cmd`: 解析后的useradd命令
21*03746da3SJomo     ///
22*03746da3SJomo     /// ## 返回
23*03746da3SJomo     /// - `UAddInfo`: 校验后的信息
check(cmd: UserCommand) -> UAddInfo24*03746da3SJomo     pub fn check(cmd: UserCommand) -> UAddInfo {
25*03746da3SJomo         let mut info = UAddInfo::default();
26*03746da3SJomo         info.username = cmd.username;
27*03746da3SJomo 
28*03746da3SJomo         // 填充信息
29*03746da3SJomo         for (option, arg) in cmd.options.iter() {
30*03746da3SJomo             match option {
31*03746da3SJomo                 CmdOption::Shell => {
32*03746da3SJomo                     info.shell = arg.clone();
33*03746da3SJomo                 }
34*03746da3SJomo                 CmdOption::Comment => {
35*03746da3SJomo                     info.comment = arg.clone();
36*03746da3SJomo                 }
37*03746da3SJomo                 CmdOption::Uid => {
38*03746da3SJomo                     info.uid = arg.clone();
39*03746da3SJomo                 }
40*03746da3SJomo                 CmdOption::Group => {
41*03746da3SJomo                     info.group = arg.clone();
42*03746da3SJomo                 }
43*03746da3SJomo                 CmdOption::Gid => {
44*03746da3SJomo                     info.gid = arg.clone();
45*03746da3SJomo                 }
46*03746da3SJomo                 CmdOption::Dir => {
47*03746da3SJomo                     info.home_dir = arg.clone();
48*03746da3SJomo                 }
49*03746da3SJomo                 _ => {
50*03746da3SJomo                     let op: &str = option.clone().into();
51*03746da3SJomo                     ErrorHandler::error_handle(
52*03746da3SJomo                         format!("Unimplemented option: {}", op),
53*03746da3SJomo                         ExitStatus::InvalidCmdSyntax,
54*03746da3SJomo                     );
55*03746da3SJomo                 }
56*03746da3SJomo             }
57*03746da3SJomo         }
58*03746da3SJomo 
59*03746da3SJomo         // 完善用户信息
60*03746da3SJomo         if info.username.is_empty() {
61*03746da3SJomo             ErrorHandler::error_handle("Invalid username".to_string(), ExitStatus::InvalidArg);
62*03746da3SJomo         }
63*03746da3SJomo 
64*03746da3SJomo         if info.uid.is_empty() {
65*03746da3SJomo             ErrorHandler::error_handle("Uid is required".to_string(), ExitStatus::InvalidCmdSyntax);
66*03746da3SJomo         }
67*03746da3SJomo 
68*03746da3SJomo         if info.comment.is_empty() {
69*03746da3SJomo             info.comment = info.username.clone() + ",,,";
70*03746da3SJomo         }
71*03746da3SJomo         if info.home_dir.is_empty() {
72*03746da3SJomo             let home_dir = format!("/home/{}", info.username.clone());
73*03746da3SJomo             info.home_dir = home_dir;
74*03746da3SJomo         }
75*03746da3SJomo         if info.shell.is_empty() {
76*03746da3SJomo             info.shell = "/bin/NovaShell".to_string();
77*03746da3SJomo         }
78*03746da3SJomo 
79*03746da3SJomo         // 校验终端是否有效
80*03746da3SJomo         check_shell(&info.shell);
81*03746da3SJomo 
82*03746da3SJomo         // 校验是否有重复用户名和用户id
83*03746da3SJomo         scan_passwd(
84*03746da3SJomo             PasswdField {
85*03746da3SJomo                 username: Some(info.username.clone()),
86*03746da3SJomo                 uid: Some(info.uid.clone()),
87*03746da3SJomo             },
88*03746da3SJomo             false,
89*03746da3SJomo         );
90*03746da3SJomo 
91*03746da3SJomo         // 判断group和gid是否有效
92*03746da3SJomo         Self::check_group_gid(&mut info);
93*03746da3SJomo 
94*03746da3SJomo         info
95*03746da3SJomo     }
96*03746da3SJomo 
97*03746da3SJomo     /// 检查组名、组id是否有效,如果组名不存在,则创建新的用户组
check_group_gid(info: &mut UAddInfo)98*03746da3SJomo     fn check_group_gid(info: &mut UAddInfo) {
99*03746da3SJomo         if info.group.is_empty() && info.gid.is_empty() {
100*03746da3SJomo             ErrorHandler::error_handle(
101*03746da3SJomo                 "user must belong to a group".to_string(),
102*03746da3SJomo                 ExitStatus::InvalidCmdSyntax,
103*03746da3SJomo             );
104*03746da3SJomo         }
105*03746da3SJomo 
106*03746da3SJomo         let r = fs::read_to_string("/etc/group");
107*03746da3SJomo         let mut max_gid: u32 = 0;
108*03746da3SJomo         match r {
109*03746da3SJomo             Ok(content) => {
110*03746da3SJomo                 for line in content.lines() {
111*03746da3SJomo                     let data: Vec<&str> = line.split(":").collect();
112*03746da3SJomo                     let (groupname, gid) = (data[0].to_string(), data[2].to_string());
113*03746da3SJomo                     if !info.group.is_empty() && info.group == groupname {
114*03746da3SJomo                         if !info.gid.is_empty() && info.gid != gid {
115*03746da3SJomo                             ErrorHandler::error_handle(
116*03746da3SJomo                                 format!("The gid of the group [{}] isn't {}", info.group, info.gid),
117*03746da3SJomo                                 ExitStatus::InvalidArg,
118*03746da3SJomo                             )
119*03746da3SJomo                         } else if info.gid.is_empty() || info.gid == gid {
120*03746da3SJomo                             info.gid = gid;
121*03746da3SJomo                             return;
122*03746da3SJomo                         }
123*03746da3SJomo                     }
124*03746da3SJomo 
125*03746da3SJomo                     if !info.gid.is_empty() && info.gid == gid {
126*03746da3SJomo                         if !info.group.is_empty() && info.group != groupname {
127*03746da3SJomo                             ErrorHandler::error_handle(
128*03746da3SJomo                                 format!("The gid of the group [{}] isn't {}", info.group, info.gid),
129*03746da3SJomo                                 ExitStatus::InvalidArg,
130*03746da3SJomo                             )
131*03746da3SJomo                         } else if info.group.is_empty() || info.group == groupname {
132*03746da3SJomo                             info.group = groupname;
133*03746da3SJomo                             return;
134*03746da3SJomo                         }
135*03746da3SJomo                     }
136*03746da3SJomo 
137*03746da3SJomo                     max_gid = max_gid.max(u32::from_str_radix(data[2], 10).unwrap());
138*03746da3SJomo                 }
139*03746da3SJomo             }
140*03746da3SJomo             Err(_) => {
141*03746da3SJomo                 ErrorHandler::error_handle(
142*03746da3SJomo                     "Can't read file: /etc/group".to_string(),
143*03746da3SJomo                     ExitStatus::GroupFile,
144*03746da3SJomo                 );
145*03746da3SJomo             }
146*03746da3SJomo         }
147*03746da3SJomo 
148*03746da3SJomo         // 没有对应的用户组,默认创建新的用户组
149*03746da3SJomo         let mut groupname = info.username.clone();
150*03746da3SJomo         let mut gid = (max_gid + 1).to_string();
151*03746da3SJomo         if !info.group.is_empty() {
152*03746da3SJomo             groupname = info.group.clone();
153*03746da3SJomo         } else {
154*03746da3SJomo             info.group = groupname.clone();
155*03746da3SJomo         }
156*03746da3SJomo 
157*03746da3SJomo         if !info.gid.is_empty() {
158*03746da3SJomo             gid = info.gid.clone();
159*03746da3SJomo         } else {
160*03746da3SJomo             info.gid = gid.clone();
161*03746da3SJomo         }
162*03746da3SJomo         let mut success = true;
163*03746da3SJomo         let r = std::process::Command::new("/bin/groupadd")
164*03746da3SJomo             .arg("-g")
165*03746da3SJomo             .arg(gid.clone())
166*03746da3SJomo             .arg(groupname)
167*03746da3SJomo             .status();
168*03746da3SJomo         if let Ok(exit_status) = r {
169*03746da3SJomo             if exit_status.code() != Some(0) {
170*03746da3SJomo                 success = false;
171*03746da3SJomo             }
172*03746da3SJomo         } else {
173*03746da3SJomo             success = false;
174*03746da3SJomo         }
175*03746da3SJomo 
176*03746da3SJomo         if !success {
177*03746da3SJomo             ErrorHandler::error_handle("groupadd failed".to_string(), ExitStatus::GroupaddFail);
178*03746da3SJomo         }
179*03746da3SJomo     }
180*03746da3SJomo }
181*03746da3SJomo 
182*03746da3SJomo /// userdel命令检查器
183*03746da3SJomo #[derive(Debug)]
184*03746da3SJomo pub struct UDelCheck;
185*03746da3SJomo 
186*03746da3SJomo impl UDelCheck {
187*03746da3SJomo     /// **校验userdel命令**
188*03746da3SJomo     ///
189*03746da3SJomo     /// ## 参数
190*03746da3SJomo     /// - `cmd`: userdel命令
191*03746da3SJomo     ///
192*03746da3SJomo     /// ## 返回
193*03746da3SJomo     /// - `UDelInfo`: 校验后的用户信息
check(cmd: UserCommand) -> UDelInfo194*03746da3SJomo     pub fn check(cmd: UserCommand) -> UDelInfo {
195*03746da3SJomo         let mut info = UDelInfo::default();
196*03746da3SJomo         info.username = cmd.username;
197*03746da3SJomo 
198*03746da3SJomo         // 检查用户是否存在
199*03746da3SJomo         scan_passwd(
200*03746da3SJomo             PasswdField {
201*03746da3SJomo                 username: Some(info.username.clone()),
202*03746da3SJomo                 uid: None,
203*03746da3SJomo             },
204*03746da3SJomo             true,
205*03746da3SJomo         );
206*03746da3SJomo 
207*03746da3SJomo         if let Some(_) = cmd.options.get(&CmdOption::Remove) {
208*03746da3SJomo             info.home = Some(Self::home(&info.username));
209*03746da3SJomo         }
210*03746da3SJomo 
211*03746da3SJomo         info
212*03746da3SJomo     }
213*03746da3SJomo 
214*03746da3SJomo     /// 获取用户家目录
home(username: &String) -> String215*03746da3SJomo     fn home(username: &String) -> String {
216*03746da3SJomo         let mut home = String::new();
217*03746da3SJomo         match std::fs::read_to_string("/etc/passwd") {
218*03746da3SJomo             Ok(data) => {
219*03746da3SJomo                 for line in data.lines() {
220*03746da3SJomo                     let data = line.split(':').collect::<Vec<&str>>();
221*03746da3SJomo                     if data[0] == username {
222*03746da3SJomo                         home = data[5].to_string();
223*03746da3SJomo                         break;
224*03746da3SJomo                     }
225*03746da3SJomo                 }
226*03746da3SJomo             }
227*03746da3SJomo             Err(_) => {
228*03746da3SJomo                 ErrorHandler::error_handle(
229*03746da3SJomo                     "Can't read file: /etc/passwd".to_string(),
230*03746da3SJomo                     ExitStatus::PasswdFile,
231*03746da3SJomo                 );
232*03746da3SJomo             }
233*03746da3SJomo         }
234*03746da3SJomo         home
235*03746da3SJomo     }
236*03746da3SJomo }
237*03746da3SJomo 
238*03746da3SJomo /// usermod命令检查器
239*03746da3SJomo #[derive(Debug)]
240*03746da3SJomo pub struct UModCheck;
241*03746da3SJomo 
242*03746da3SJomo impl UModCheck {
243*03746da3SJomo     /// **校验usermod命令**
244*03746da3SJomo     ///
245*03746da3SJomo     /// ## 参数
246*03746da3SJomo     /// - `cmd`: usermod命令
247*03746da3SJomo     ///
248*03746da3SJomo     /// ## 返回
249*03746da3SJomo     /// - `UModInfo`: 校验后的用户信息
check(cmd: UserCommand) -> UModInfo250*03746da3SJomo     pub fn check(cmd: UserCommand) -> UModInfo {
251*03746da3SJomo         let mut info = Self::parse_options(&cmd.options);
252*03746da3SJomo         info.username = cmd.username;
253*03746da3SJomo 
254*03746da3SJomo         // 校验shell是否有效
255*03746da3SJomo         if let Some(shell) = &info.new_shell {
256*03746da3SJomo             check_shell(shell);
257*03746da3SJomo         }
258*03746da3SJomo 
259*03746da3SJomo         // 校验new_home是否有效
260*03746da3SJomo         if let Some(new_home) = &info.new_home {
261*03746da3SJomo             Self::check_home(new_home);
262*03746da3SJomo         }
263*03746da3SJomo 
264*03746da3SJomo         // 校验用户是否存在
265*03746da3SJomo         scan_passwd(
266*03746da3SJomo             PasswdField {
267*03746da3SJomo                 username: Some(info.username.clone()),
268*03746da3SJomo                 uid: None,
269*03746da3SJomo             },
270*03746da3SJomo             true,
271*03746da3SJomo         );
272*03746da3SJomo 
273*03746da3SJomo         // 校验new_name、new_uid是否有效
274*03746da3SJomo         scan_passwd(
275*03746da3SJomo             PasswdField {
276*03746da3SJomo                 username: info.new_name.clone(),
277*03746da3SJomo                 uid: info.new_uid.clone(),
278*03746da3SJomo             },
279*03746da3SJomo             false,
280*03746da3SJomo         );
281*03746da3SJomo 
282*03746da3SJomo         // 校验groups、new_gid是否有效
283*03746da3SJomo         scan_group(
284*03746da3SJomo             GroupField {
285*03746da3SJomo                 groups: info.groups.clone(),
286*03746da3SJomo                 gid: info.new_gid.clone(),
287*03746da3SJomo             },
288*03746da3SJomo             true,
289*03746da3SJomo         );
290*03746da3SJomo 
291*03746da3SJomo         info
292*03746da3SJomo     }
293*03746da3SJomo 
294*03746da3SJomo     /// **校验home目录是否有效**
295*03746da3SJomo     ///
296*03746da3SJomo     /// ## 参数
297*03746da3SJomo     /// - `home`: home目录路径
check_home(home: &String)298*03746da3SJomo     fn check_home(home: &String) {
299*03746da3SJomo         if fs::File::open(home).is_ok() {
300*03746da3SJomo             ErrorHandler::error_handle(format!("{} already exists", home), ExitStatus::InvalidArg);
301*03746da3SJomo         }
302*03746da3SJomo     }
303*03746da3SJomo 
304*03746da3SJomo     /// **解析options**
305*03746da3SJomo     ///
306*03746da3SJomo     /// ## 参数
307*03746da3SJomo     /// - `options`: 命令选项
308*03746da3SJomo     ///
309*03746da3SJomo     /// ## 返回
310*03746da3SJomo     /// - `UModInfo`: 用户信息
parse_options(options: &HashMap<CmdOption, String>) -> UModInfo311*03746da3SJomo     fn parse_options(options: &HashMap<CmdOption, String>) -> UModInfo {
312*03746da3SJomo         let mut info = UModInfo::default();
313*03746da3SJomo         for (option, arg) in options {
314*03746da3SJomo             match option {
315*03746da3SJomo                 CmdOption::Append => {
316*03746da3SJomo                     info.groups = Some(arg.split(",").map(|s| s.to_string()).collect());
317*03746da3SJomo                 }
318*03746da3SJomo                 CmdOption::Comment => {
319*03746da3SJomo                     info.new_comment = Some(arg.clone());
320*03746da3SJomo                 }
321*03746da3SJomo                 CmdOption::Dir => {
322*03746da3SJomo                     info.new_home = Some(arg.clone());
323*03746da3SJomo                 }
324*03746da3SJomo                 CmdOption::Gid => {
325*03746da3SJomo                     info.new_gid = Some(arg.clone());
326*03746da3SJomo                 }
327*03746da3SJomo                 CmdOption::Login => {
328*03746da3SJomo                     info.new_name = Some(arg.clone());
329*03746da3SJomo                 }
330*03746da3SJomo                 CmdOption::Shell => {
331*03746da3SJomo                     info.new_shell = Some(arg.clone());
332*03746da3SJomo                 }
333*03746da3SJomo                 CmdOption::Uid => {
334*03746da3SJomo                     info.new_uid = Some(arg.clone());
335*03746da3SJomo                 }
336*03746da3SJomo                 _ => ErrorHandler::error_handle(
337*03746da3SJomo                     "Invalid option".to_string(),
338*03746da3SJomo                     ExitStatus::InvalidCmdSyntax,
339*03746da3SJomo                 ),
340*03746da3SJomo             }
341*03746da3SJomo         }
342*03746da3SJomo         info
343*03746da3SJomo     }
344*03746da3SJomo }
345*03746da3SJomo 
346*03746da3SJomo /// passwd命令检查器
347*03746da3SJomo #[derive(Debug)]
348*03746da3SJomo pub struct PasswdCheck;
349*03746da3SJomo 
350*03746da3SJomo impl PasswdCheck {
351*03746da3SJomo     /// **校验passwd命令**
352*03746da3SJomo     ///
353*03746da3SJomo     /// ## 参数
354*03746da3SJomo     /// - `cmd`: passwd命令
355*03746da3SJomo     ///
356*03746da3SJomo     /// ## 返回
357*03746da3SJomo     /// - `PasswdInfo`: 校验后的信息
check(cmd: PasswdCommand) -> PasswdInfo358*03746da3SJomo     pub fn check(cmd: PasswdCommand) -> PasswdInfo {
359*03746da3SJomo         let uid = unsafe { libc::geteuid().to_string() };
360*03746da3SJomo         let cur_username = Self::cur_username(uid.clone());
361*03746da3SJomo         let mut to_change_username = String::new();
362*03746da3SJomo 
363*03746da3SJomo         if let Some(username) = cmd.username {
364*03746da3SJomo             to_change_username = username.clone();
365*03746da3SJomo 
366*03746da3SJomo             // 不是root用户不能修改别人的密码
367*03746da3SJomo             if uid != "0" && cur_username != username {
368*03746da3SJomo                 ErrorHandler::error_handle(
369*03746da3SJomo                     "You can't change password for other users".to_string(),
370*03746da3SJomo                     ExitStatus::PermissionDenied,
371*03746da3SJomo                 );
372*03746da3SJomo             }
373*03746da3SJomo 
374*03746da3SJomo             // 检验待修改用户是否存在
375*03746da3SJomo             scan_passwd(
376*03746da3SJomo                 PasswdField {
377*03746da3SJomo                     username: Some(username.clone()),
378*03746da3SJomo                     uid: None,
379*03746da3SJomo                 },
380*03746da3SJomo                 true,
381*03746da3SJomo             );
382*03746da3SJomo         }
383*03746da3SJomo 
384*03746da3SJomo         let mut new_password = String::new();
385*03746da3SJomo         match uid.as_str() {
386*03746da3SJomo             "0" => {
387*03746da3SJomo                 if to_change_username.is_empty() {
388*03746da3SJomo                     to_change_username = cur_username;
389*03746da3SJomo                 }
390*03746da3SJomo                 print!("New password: ");
391*03746da3SJomo                 std::io::stdout().flush().unwrap();
392*03746da3SJomo                 std::io::stdin().read_line(&mut new_password).unwrap();
393*03746da3SJomo                 new_password = new_password.trim().to_string();
394*03746da3SJomo                 let mut check_password = String::new();
395*03746da3SJomo                 print!("\nRe-enter new password: ");
396*03746da3SJomo                 std::io::stdout().flush().unwrap();
397*03746da3SJomo                 std::io::stdin().read_line(&mut check_password).unwrap();
398*03746da3SJomo                 check_password = check_password.trim().to_string();
399*03746da3SJomo                 if new_password != check_password {
400*03746da3SJomo                     ErrorHandler::error_handle(
401*03746da3SJomo                         "\nThe two passwords that you entered do not match.".to_string(),
402*03746da3SJomo                         ExitStatus::InvalidArg,
403*03746da3SJomo                     )
404*03746da3SJomo                 }
405*03746da3SJomo             }
406*03746da3SJomo             _ => {
407*03746da3SJomo                 to_change_username = cur_username.clone();
408*03746da3SJomo                 print!("Old password: ");
409*03746da3SJomo                 std::io::stdout().flush().unwrap();
410*03746da3SJomo                 let mut old_password = String::new();
411*03746da3SJomo                 std::io::stdin().read_line(&mut old_password).unwrap();
412*03746da3SJomo                 old_password = old_password.trim().to_string();
413*03746da3SJomo                 Self::check_password(cur_username, old_password);
414*03746da3SJomo                 print!("\nNew password: ");
415*03746da3SJomo                 std::io::stdout().flush().unwrap();
416*03746da3SJomo                 std::io::stdin().read_line(&mut new_password).unwrap();
417*03746da3SJomo                 new_password = new_password.trim().to_string();
418*03746da3SJomo                 print!("\nRe-enter new password: ");
419*03746da3SJomo                 std::io::stdout().flush().unwrap();
420*03746da3SJomo                 let mut check_password = String::new();
421*03746da3SJomo                 std::io::stdin().read_line(&mut check_password).unwrap();
422*03746da3SJomo                 check_password = check_password.trim().to_string();
423*03746da3SJomo                 if new_password != check_password {
424*03746da3SJomo                     println!("{}", new_password);
425*03746da3SJomo                     ErrorHandler::error_handle(
426*03746da3SJomo                         "\nThe two passwords that you entered do not match.".to_string(),
427*03746da3SJomo                         ExitStatus::InvalidArg,
428*03746da3SJomo                     )
429*03746da3SJomo                 }
430*03746da3SJomo             }
431*03746da3SJomo         };
432*03746da3SJomo 
433*03746da3SJomo         PasswdInfo {
434*03746da3SJomo             username: to_change_username,
435*03746da3SJomo             new_password,
436*03746da3SJomo         }
437*03746da3SJomo     }
438*03746da3SJomo 
439*03746da3SJomo     /// **获取uid对应的用户名**
440*03746da3SJomo     ///
441*03746da3SJomo     /// ## 参数
442*03746da3SJomo     /// - `uid`: 用户id
443*03746da3SJomo     ///
444*03746da3SJomo     /// ## 返回
445*03746da3SJomo     /// 用户名
cur_username(uid: String) -> String446*03746da3SJomo     fn cur_username(uid: String) -> String {
447*03746da3SJomo         let r = fs::read_to_string("/etc/passwd");
448*03746da3SJomo         let mut cur_username = String::new();
449*03746da3SJomo 
450*03746da3SJomo         match r {
451*03746da3SJomo             Ok(content) => {
452*03746da3SJomo                 for line in content.lines() {
453*03746da3SJomo                     let field = line.split(":").collect::<Vec<&str>>();
454*03746da3SJomo                     if uid == field[2] {
455*03746da3SJomo                         cur_username = field[0].to_string();
456*03746da3SJomo                     }
457*03746da3SJomo                 }
458*03746da3SJomo             }
459*03746da3SJomo             Err(_) => {
460*03746da3SJomo                 ErrorHandler::error_handle(
461*03746da3SJomo                     "Can't read /etc/passwd".to_string(),
462*03746da3SJomo                     ExitStatus::PasswdFile,
463*03746da3SJomo                 );
464*03746da3SJomo             }
465*03746da3SJomo         }
466*03746da3SJomo 
467*03746da3SJomo         cur_username
468*03746da3SJomo     }
469*03746da3SJomo 
470*03746da3SJomo     /// **校验密码**
471*03746da3SJomo     ///
472*03746da3SJomo     /// ## 参数
473*03746da3SJomo     /// - `username`: 用户名
474*03746da3SJomo     /// - `password`: 密码
check_password(username: String, password: String)475*03746da3SJomo     fn check_password(username: String, password: String) {
476*03746da3SJomo         let r = fs::read_to_string("/etc/shadow");
477*03746da3SJomo         match r {
478*03746da3SJomo             Ok(content) => {
479*03746da3SJomo                 for line in content.lines() {
480*03746da3SJomo                     let field = line.split(":").collect::<Vec<&str>>();
481*03746da3SJomo                     if username == field[0] {
482*03746da3SJomo                         if password != field[1] {
483*03746da3SJomo                             ErrorHandler::error_handle(
484*03746da3SJomo                                 "Password error".to_string(),
485*03746da3SJomo                                 ExitStatus::InvalidArg,
486*03746da3SJomo                             );
487*03746da3SJomo                         } else {
488*03746da3SJomo                             return;
489*03746da3SJomo                         }
490*03746da3SJomo                     }
491*03746da3SJomo                 }
492*03746da3SJomo             }
493*03746da3SJomo             Err(_) => {
494*03746da3SJomo                 ErrorHandler::error_handle(
495*03746da3SJomo                     "Can't read /etc/shadow".to_string(),
496*03746da3SJomo                     ExitStatus::ShadowFile,
497*03746da3SJomo                 );
498*03746da3SJomo             }
499*03746da3SJomo         }
500*03746da3SJomo     }
501*03746da3SJomo }
502*03746da3SJomo 
503*03746da3SJomo /// groupadd命令检查器
504*03746da3SJomo #[derive(Debug)]
505*03746da3SJomo pub struct GAddCheck;
506*03746da3SJomo 
507*03746da3SJomo impl GAddCheck {
508*03746da3SJomo     /// **校验groupadd命令**
509*03746da3SJomo     ///
510*03746da3SJomo     /// ## 参数
511*03746da3SJomo     /// - `cmd`: groupadd命令
512*03746da3SJomo     ///
513*03746da3SJomo     /// ## 返回
514*03746da3SJomo     /// - `GAddInfo`: 校验后的组信息
check(cmd: GroupCommand) -> GAddInfo515*03746da3SJomo     pub fn check(cmd: GroupCommand) -> GAddInfo {
516*03746da3SJomo         let mut info = GAddInfo {
517*03746da3SJomo             groupname: cmd.groupname.clone(),
518*03746da3SJomo             gid: String::new(),
519*03746da3SJomo             passwd: None,
520*03746da3SJomo         };
521*03746da3SJomo 
522*03746da3SJomo         if info.groupname.is_empty() {
523*03746da3SJomo             ErrorHandler::error_handle("groupname is required".to_string(), ExitStatus::InvalidArg);
524*03746da3SJomo         }
525*03746da3SJomo 
526*03746da3SJomo         if let Some(gid) = cmd.options.get(&CmdOption::Gid) {
527*03746da3SJomo             info.gid = gid.clone();
528*03746da3SJomo         } else {
529*03746da3SJomo             ErrorHandler::error_handle("gid is required".to_string(), ExitStatus::InvalidArg);
530*03746da3SJomo         }
531*03746da3SJomo 
532*03746da3SJomo         if let Some(passwd) = cmd.options.get(&CmdOption::Passwd) {
533*03746da3SJomo             info.passwd = Some(passwd.clone());
534*03746da3SJomo         }
535*03746da3SJomo 
536*03746da3SJomo         // 检查组名或组id是否已存在
537*03746da3SJomo         scan_group(
538*03746da3SJomo             GroupField {
539*03746da3SJomo                 groups: Some(vec![info.groupname.clone()]),
540*03746da3SJomo                 gid: Some(info.gid.clone()),
541*03746da3SJomo             },
542*03746da3SJomo             false,
543*03746da3SJomo         );
544*03746da3SJomo 
545*03746da3SJomo         info
546*03746da3SJomo     }
547*03746da3SJomo }
548*03746da3SJomo 
549*03746da3SJomo /// groupdel命令检查器
550*03746da3SJomo #[derive(Debug)]
551*03746da3SJomo pub struct GDelCheck;
552*03746da3SJomo 
553*03746da3SJomo impl GDelCheck {
554*03746da3SJomo     /// **校验groupdel命令**
555*03746da3SJomo     ///
556*03746da3SJomo     /// ## 参数
557*03746da3SJomo     /// - `cmd`: groupdel命令
558*03746da3SJomo     ///
559*03746da3SJomo     /// ## 返回
560*03746da3SJomo     /// - `GDelInfo`: 校验后的组信息
check(cmd: GroupCommand) -> GDelInfo561*03746da3SJomo     pub fn check(cmd: GroupCommand) -> GDelInfo {
562*03746da3SJomo         if let Some(gid) = check_groupname(cmd.groupname.clone()) {
563*03746da3SJomo             // 检查group是不是某个用户的主组,如果是的话则不能删除
564*03746da3SJomo             Self::is_main_group(gid);
565*03746da3SJomo         } else {
566*03746da3SJomo             // 用户组不存在
567*03746da3SJomo             ErrorHandler::error_handle(
568*03746da3SJomo                 format!("group:[{}] doesn't exist", cmd.groupname),
569*03746da3SJomo                 ExitStatus::GroupNotExist,
570*03746da3SJomo             );
571*03746da3SJomo         }
572*03746da3SJomo         GDelInfo {
573*03746da3SJomo             groupname: cmd.groupname,
574*03746da3SJomo         }
575*03746da3SJomo     }
576*03746da3SJomo 
577*03746da3SJomo     /// **检查该组是否为某个用户的主用户组**
578*03746da3SJomo     ///
579*03746da3SJomo     /// ## 参数
580*03746da3SJomo     /// - `gid`: 组id
581*03746da3SJomo     ///
582*03746da3SJomo     /// ## 返回
583*03746da3SJomo     /// Some(gid): 组id
584*03746da3SJomo     /// None
is_main_group(gid: String)585*03746da3SJomo     fn is_main_group(gid: String) {
586*03746da3SJomo         // 读取/etc/passwd文件
587*03746da3SJomo         let r = fs::read_to_string("/etc/passwd");
588*03746da3SJomo         match r {
589*03746da3SJomo             Ok(content) => {
590*03746da3SJomo                 for line in content.lines() {
591*03746da3SJomo                     let field = line.split(":").collect::<Vec<&str>>();
592*03746da3SJomo                     if field[3] == gid {
593*03746da3SJomo                         ErrorHandler::error_handle(
594*03746da3SJomo                             format!(
595*03746da3SJomo                                 "groupdel failed: group is main group of user:[{}]",
596*03746da3SJomo                                 field[0]
597*03746da3SJomo                             ),
598*03746da3SJomo                             ExitStatus::InvalidArg,
599*03746da3SJomo                         )
600*03746da3SJomo                     }
601*03746da3SJomo                 }
602*03746da3SJomo             }
603*03746da3SJomo             Err(_) => {
604*03746da3SJomo                 ErrorHandler::error_handle(
605*03746da3SJomo                     "Can't read file: /etc/passwd".to_string(),
606*03746da3SJomo                     ExitStatus::PasswdFile,
607*03746da3SJomo                 );
608*03746da3SJomo             }
609*03746da3SJomo         }
610*03746da3SJomo     }
611*03746da3SJomo }
612*03746da3SJomo 
613*03746da3SJomo /// groupmod命令检查器
614*03746da3SJomo #[derive(Debug)]
615*03746da3SJomo pub struct GModCheck;
616*03746da3SJomo 
617*03746da3SJomo impl GModCheck {
618*03746da3SJomo     /// **校验groupmod命令**
619*03746da3SJomo     ///
620*03746da3SJomo     /// ## 参数
621*03746da3SJomo     /// - `cmd`: groupmod命令
622*03746da3SJomo     ///
623*03746da3SJomo     /// ## 返回
624*03746da3SJomo     /// - `GModInfo`: 校验后的组信息
check(cmd: GroupCommand) -> GModInfo625*03746da3SJomo     pub fn check(cmd: GroupCommand) -> GModInfo {
626*03746da3SJomo         let mut info = GModInfo::default();
627*03746da3SJomo         info.groupname = cmd.groupname;
628*03746da3SJomo 
629*03746da3SJomo         if let Some(new_groupname) = cmd.options.get(&CmdOption::NewGroupName) {
630*03746da3SJomo             info.new_groupname = Some(new_groupname.clone());
631*03746da3SJomo         }
632*03746da3SJomo 
633*03746da3SJomo         if let Some(new_gid) = cmd.options.get(&CmdOption::Gid) {
634*03746da3SJomo             info.new_gid = Some(new_gid.clone());
635*03746da3SJomo         }
636*03746da3SJomo 
637*03746da3SJomo         Self::check_group_file(&mut info);
638*03746da3SJomo 
639*03746da3SJomo         info
640*03746da3SJomo     }
641*03746da3SJomo 
642*03746da3SJomo     /// 查看groupname是否存在,同时检测new_gid、new_groupname是否重复
check_group_file(info: &mut GModInfo)643*03746da3SJomo     fn check_group_file(info: &mut GModInfo) {
644*03746da3SJomo         let mut is_group_exist = false;
645*03746da3SJomo         let r = fs::read_to_string("/etc/group");
646*03746da3SJomo         match r {
647*03746da3SJomo             Ok(content) => {
648*03746da3SJomo                 for line in content.lines() {
649*03746da3SJomo                     let field = line.split(':').collect::<Vec<&str>>();
650*03746da3SJomo                     if field[0] == info.groupname {
651*03746da3SJomo                         is_group_exist = true;
652*03746da3SJomo                         info.gid = field[2].to_string();
653*03746da3SJomo                     }
654*03746da3SJomo 
655*03746da3SJomo                     if let Some(new_gid) = &info.new_gid {
656*03746da3SJomo                         if new_gid == field[2] {
657*03746da3SJomo                             ErrorHandler::error_handle(
658*03746da3SJomo                                 format!("gid:[{}] is already used", new_gid),
659*03746da3SJomo                                 ExitStatus::InvalidArg,
660*03746da3SJomo                             );
661*03746da3SJomo                         }
662*03746da3SJomo                     }
663*03746da3SJomo 
664*03746da3SJomo                     if let Some(new_groupname) = &info.new_groupname {
665*03746da3SJomo                         if new_groupname == field[0] {
666*03746da3SJomo                             ErrorHandler::error_handle(
667*03746da3SJomo                                 format!("groupname:[{}] is already used", new_groupname),
668*03746da3SJomo                                 ExitStatus::InvalidArg,
669*03746da3SJomo                             );
670*03746da3SJomo                         }
671*03746da3SJomo                     }
672*03746da3SJomo                 }
673*03746da3SJomo             }
674*03746da3SJomo             Err(_) => ErrorHandler::error_handle(
675*03746da3SJomo                 "Can't read file: /etc/group".to_string(),
676*03746da3SJomo                 ExitStatus::GroupFile,
677*03746da3SJomo             ),
678*03746da3SJomo         }
679*03746da3SJomo 
680*03746da3SJomo         if !is_group_exist {
681*03746da3SJomo             ErrorHandler::error_handle(
682*03746da3SJomo                 format!("groupname:[{}] doesn't exist", info.groupname),
683*03746da3SJomo                 ExitStatus::GroupNotExist,
684*03746da3SJomo             );
685*03746da3SJomo         }
686*03746da3SJomo     }
687*03746da3SJomo }
688*03746da3SJomo 
689*03746da3SJomo /// passwd文件待校验字段
690*03746da3SJomo pub struct PasswdField {
691*03746da3SJomo     username: Option<String>,
692*03746da3SJomo     uid: Option<String>,
693*03746da3SJomo }
694*03746da3SJomo 
695*03746da3SJomo /// group文件待校验字段
696*03746da3SJomo pub struct GroupField {
697*03746da3SJomo     groups: Option<Vec<String>>,
698*03746da3SJomo     gid: Option<String>,
699*03746da3SJomo }
700*03746da3SJomo 
701*03746da3SJomo /// **校验uid**
702*03746da3SJomo ///
703*03746da3SJomo /// ## 参数
704*03746da3SJomo /// - `passwd_field`: passwd文件字段
705*03746da3SJomo /// - `should_exist`: 是否应该存在
scan_passwd(passwd_field: PasswdField, should_exist: bool)706*03746da3SJomo fn scan_passwd(passwd_field: PasswdField, should_exist: bool) {
707*03746da3SJomo     let mut username_check = false;
708*03746da3SJomo     let mut uid_check = false;
709*03746da3SJomo     match fs::read_to_string("/etc/passwd") {
710*03746da3SJomo         Ok(content) => {
711*03746da3SJomo             for line in content.lines() {
712*03746da3SJomo                 let field = line.split(':').collect::<Vec<&str>>();
713*03746da3SJomo                 if let Some(uid) = &passwd_field.uid {
714*03746da3SJomo                     // uid必须是有效的数字
715*03746da3SJomo                     let r = uid.parse::<u32>();
716*03746da3SJomo                     if r.is_err() {
717*03746da3SJomo                         ErrorHandler::error_handle(
718*03746da3SJomo                             format!("Uid {} is invalid", uid),
719*03746da3SJomo                             ExitStatus::InvalidArg,
720*03746da3SJomo                         );
721*03746da3SJomo                     }
722*03746da3SJomo                     if field[2] == uid {
723*03746da3SJomo                         uid_check = true;
724*03746da3SJomo                         // username如果不用校验或者被校验过了,才可以return
725*03746da3SJomo                         if should_exist && (passwd_field.username.is_none() || username_check) {
726*03746da3SJomo                             return;
727*03746da3SJomo                         } else {
728*03746da3SJomo                             ErrorHandler::error_handle(
729*03746da3SJomo                                 format!("UID {} already exists", uid),
730*03746da3SJomo                                 ExitStatus::UidInUse,
731*03746da3SJomo                             );
732*03746da3SJomo                         }
733*03746da3SJomo                     }
734*03746da3SJomo                 }
735*03746da3SJomo 
736*03746da3SJomo                 if let Some(username) = &passwd_field.username {
737*03746da3SJomo                     if field[0] == username {
738*03746da3SJomo                         username_check = true;
739*03746da3SJomo                         // uid如果不用校验或者被校验过了,才可以return
740*03746da3SJomo                         if should_exist && (passwd_field.uid.is_none() || uid_check) {
741*03746da3SJomo                             return;
742*03746da3SJomo                         } else {
743*03746da3SJomo                             ErrorHandler::error_handle(
744*03746da3SJomo                                 format!("Username {} already exists", username),
745*03746da3SJomo                                 ExitStatus::UsernameInUse,
746*03746da3SJomo                             );
747*03746da3SJomo                         }
748*03746da3SJomo                     }
749*03746da3SJomo                 }
750*03746da3SJomo             }
751*03746da3SJomo 
752*03746da3SJomo             if should_exist {
753*03746da3SJomo                 if let Some(uid) = &passwd_field.uid {
754*03746da3SJomo                     if !uid_check {
755*03746da3SJomo                         ErrorHandler::error_handle(
756*03746da3SJomo                             format!("UID {} doesn't exist", uid),
757*03746da3SJomo                             ExitStatus::InvalidArg,
758*03746da3SJomo                         );
759*03746da3SJomo                     }
760*03746da3SJomo                 }
761*03746da3SJomo                 if let Some(username) = &passwd_field.username {
762*03746da3SJomo                     if !username_check {
763*03746da3SJomo                         ErrorHandler::error_handle(
764*03746da3SJomo                             format!("User {} doesn't exist", username),
765*03746da3SJomo                             ExitStatus::InvalidArg,
766*03746da3SJomo                         );
767*03746da3SJomo                     }
768*03746da3SJomo                 }
769*03746da3SJomo             }
770*03746da3SJomo         }
771*03746da3SJomo         Err(_) => ErrorHandler::error_handle(
772*03746da3SJomo             "Can't read file: /etc/passwd".to_string(),
773*03746da3SJomo             ExitStatus::PasswdFile,
774*03746da3SJomo         ),
775*03746da3SJomo     }
776*03746da3SJomo }
777*03746da3SJomo 
778*03746da3SJomo /// **校验gid**
779*03746da3SJomo ///
780*03746da3SJomo /// ## 参数
781*03746da3SJomo /// - `group_field`: group文件字段
782*03746da3SJomo /// - `should_exist`: 是否应该存在
scan_group(group_field: GroupField, should_exist: bool)783*03746da3SJomo fn scan_group(group_field: GroupField, should_exist: bool) {
784*03746da3SJomo     let mut gid_check = false;
785*03746da3SJomo     let mut set1 = HashSet::new();
786*03746da3SJomo     let mut set2 = HashSet::new();
787*03746da3SJomo     if let Some(groups) = group_field.groups.clone() {
788*03746da3SJomo         set2.extend(groups.into_iter());
789*03746da3SJomo     }
790*03746da3SJomo     match fs::read_to_string("/etc/group") {
791*03746da3SJomo         Ok(content) => {
792*03746da3SJomo             for line in content.lines() {
793*03746da3SJomo                 let field = line.split(':').collect::<Vec<&str>>();
794*03746da3SJomo                 if let Some(gid) = &group_field.gid {
795*03746da3SJomo                     // gid必须是有效的数字
796*03746da3SJomo                     let r = gid.parse::<u32>();
797*03746da3SJomo                     if r.is_err() {
798*03746da3SJomo                         ErrorHandler::error_handle(
799*03746da3SJomo                             format!("Gid {} is invalid", gid),
800*03746da3SJomo                             ExitStatus::InvalidArg,
801*03746da3SJomo                         );
802*03746da3SJomo                     }
803*03746da3SJomo                     if field[2] == gid {
804*03746da3SJomo                         gid_check = true;
805*03746da3SJomo                         if should_exist && group_field.groups.is_none() {
806*03746da3SJomo                             return;
807*03746da3SJomo                         } else {
808*03746da3SJomo                             ErrorHandler::error_handle(
809*03746da3SJomo                                 format!("GID {} already exists", gid),
810*03746da3SJomo                                 ExitStatus::InvalidArg,
811*03746da3SJomo                             );
812*03746da3SJomo                         }
813*03746da3SJomo                     }
814*03746da3SJomo                 }
815*03746da3SJomo 
816*03746da3SJomo                 // 统计所有组
817*03746da3SJomo                 set1.insert(field[0].to_string());
818*03746da3SJomo             }
819*03746da3SJomo 
820*03746da3SJomo             if should_exist {
821*03746da3SJomo                 if let Some(gid) = &group_field.gid {
822*03746da3SJomo                     if !gid_check {
823*03746da3SJomo                         ErrorHandler::error_handle(
824*03746da3SJomo                             format!("GID {} doesn't exist", gid),
825*03746da3SJomo                             ExitStatus::InvalidArg,
826*03746da3SJomo                         );
827*03746da3SJomo                     }
828*03746da3SJomo                 }
829*03746da3SJomo                 if group_field.groups.is_some() {
830*03746da3SJomo                     let mut non_exist_group = Vec::new();
831*03746da3SJomo                     for group in set2.iter() {
832*03746da3SJomo                         if !set1.contains(group) {
833*03746da3SJomo                             non_exist_group.push(group.clone());
834*03746da3SJomo                         }
835*03746da3SJomo                     }
836*03746da3SJomo 
837*03746da3SJomo                     if non_exist_group.len() > 0 {
838*03746da3SJomo                         ErrorHandler::error_handle(
839*03746da3SJomo                             format!("group: {} doesn't exist", non_exist_group.join(",")),
840*03746da3SJomo                             ExitStatus::GroupNotExist,
841*03746da3SJomo                         );
842*03746da3SJomo                     }
843*03746da3SJomo                 }
844*03746da3SJomo             }
845*03746da3SJomo         }
846*03746da3SJomo 
847*03746da3SJomo         Err(_) => ErrorHandler::error_handle(
848*03746da3SJomo             "Can't read file: /etc/group".to_string(),
849*03746da3SJomo             ExitStatus::GroupFile,
850*03746da3SJomo         ),
851*03746da3SJomo     }
852*03746da3SJomo }
853*03746da3SJomo 
854*03746da3SJomo /// **校验shell是否有效**
855*03746da3SJomo ///
856*03746da3SJomo /// ## 参数
857*03746da3SJomo /// - `shell`: shell路径
check_shell(shell: &String)858*03746da3SJomo fn check_shell(shell: &String) {
859*03746da3SJomo     if let Ok(file) = fs::File::open(shell.clone()) {
860*03746da3SJomo         if !file.metadata().unwrap().is_file() {
861*03746da3SJomo             ErrorHandler::error_handle(format!("{} is not a file", shell), ExitStatus::InvalidArg);
862*03746da3SJomo         }
863*03746da3SJomo     } else {
864*03746da3SJomo         ErrorHandler::error_handle(format!("{} doesn't exist", shell), ExitStatus::InvalidArg);
865*03746da3SJomo     }
866*03746da3SJomo }
867*03746da3SJomo 
868*03746da3SJomo /// **校验组名,判断该用户组是否存在,以及成员是否为空**
869*03746da3SJomo ///
870*03746da3SJomo /// ## 参数
871*03746da3SJomo /// - `groupname`: 组名
872*03746da3SJomo ///
873*03746da3SJomo /// ## 返回
874*03746da3SJomo /// Some(gid): 组id
875*03746da3SJomo /// None
check_groupname(groupname: String) -> Option<String>876*03746da3SJomo fn check_groupname(groupname: String) -> Option<String> {
877*03746da3SJomo     let r = fs::read_to_string("/etc/group");
878*03746da3SJomo     match r {
879*03746da3SJomo         Ok(content) => {
880*03746da3SJomo             for line in content.lines() {
881*03746da3SJomo                 let field = line.split(":").collect::<Vec<&str>>();
882*03746da3SJomo                 let users = field[3].split(",").collect::<Vec<&str>>();
883*03746da3SJomo                 let filter_users = users
884*03746da3SJomo                     .iter()
885*03746da3SJomo                     .filter(|&x| !x.is_empty())
886*03746da3SJomo                     .collect::<Vec<&&str>>();
887*03746da3SJomo                 if field[0] == groupname {
888*03746da3SJomo                     if filter_users.is_empty() {
889*03746da3SJomo                         return Some(field[2].to_string());
890*03746da3SJomo                     } else {
891*03746da3SJomo                         ErrorHandler::error_handle(
892*03746da3SJomo                             format!("group:[{}] is not empty, unable to delete", groupname),
893*03746da3SJomo                             ExitStatus::InvalidArg,
894*03746da3SJomo                         )
895*03746da3SJomo                     }
896*03746da3SJomo                 }
897*03746da3SJomo             }
898*03746da3SJomo         }
899*03746da3SJomo         Err(_) => {
900*03746da3SJomo             ErrorHandler::error_handle(
901*03746da3SJomo                 "Can't read file: /etc/group".to_string(),
902*03746da3SJomo                 ExitStatus::GroupFile,
903*03746da3SJomo             );
904*03746da3SJomo         }
905*03746da3SJomo     }
906*03746da3SJomo 
907*03746da3SJomo     None
908*03746da3SJomo }
909