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