1#[derive(Debug, Clone, PartialEq, Eq)]
2pub struct ProcessIdentity {
3 pub uid: u32,
4 pub gid: u32,
5 pub euid: u32,
6 pub egid: u32,
7 pub supplementary_gids: Vec<u32>,
8}
9
10impl Default for ProcessIdentity {
11 fn default() -> Self {
12 Self {
13 uid: 1000,
14 gid: 1000,
15 euid: 1000,
16 egid: 1000,
17 supplementary_gids: vec![1000],
18 }
19 }
20}
21
22#[derive(Debug, Clone, Default, PartialEq, Eq)]
23pub struct UserConfig {
24 pub uid: Option<u32>,
25 pub gid: Option<u32>,
26 pub euid: Option<u32>,
27 pub egid: Option<u32>,
28 pub username: Option<String>,
29 pub homedir: Option<String>,
30 pub shell: Option<String>,
31 pub gecos: Option<String>,
32 pub group_name: Option<String>,
33 pub supplementary_gids: Vec<u32>,
34}
35
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct UserManager {
38 pub uid: u32,
39 pub gid: u32,
40 pub euid: u32,
41 pub egid: u32,
42 pub username: String,
43 pub homedir: String,
44 pub shell: String,
45 pub gecos: String,
46 pub group_name: String,
47 pub supplementary_gids: Vec<u32>,
48}
49
50impl Default for UserManager {
51 fn default() -> Self {
52 Self::from_config(UserConfig::default())
53 }
54}
55
56impl UserManager {
57 pub fn new() -> Self {
58 Self::default()
59 }
60
61 pub fn from_config(config: UserConfig) -> Self {
62 let uid = config.uid.unwrap_or(1000);
63 let gid = config.gid.unwrap_or(1000);
64 let username = config.username.unwrap_or_else(|| String::from("user"));
65 let supplementary_gids = normalize_supplementary_gids(gid, config.supplementary_gids);
66
67 Self {
68 uid,
69 gid,
70 euid: config.euid.unwrap_or(uid),
71 egid: config.egid.unwrap_or(gid),
72 username: username.clone(),
73 homedir: config.homedir.unwrap_or_else(|| String::from("/home/user")),
74 shell: config.shell.unwrap_or_else(|| String::from("/bin/sh")),
75 gecos: config.gecos.unwrap_or_default(),
76 group_name: config.group_name.unwrap_or(username),
77 supplementary_gids,
78 }
79 }
80
81 pub fn identity(&self) -> ProcessIdentity {
82 ProcessIdentity {
83 uid: self.uid,
84 gid: self.gid,
85 euid: self.euid,
86 egid: self.egid,
87 supplementary_gids: self.supplementary_gids.clone(),
88 }
89 }
90
91 pub fn getgroups(&self) -> Vec<u32> {
92 self.supplementary_gids.clone()
93 }
94
95 pub fn getpwuid(&self, uid: u32) -> Option<String> {
96 if uid == self.uid {
97 return Some(format!(
98 "{}:x:{}:{}:{}:{}:{}",
99 self.username, self.uid, self.gid, self.gecos, self.homedir, self.shell
100 ));
101 }
102
103 None
104 }
105
106 pub fn getgrgid(&self, gid: u32) -> Option<String> {
107 if gid == self.gid {
108 return Some(format!(
109 "{}:x:{}:{}",
110 self.group_name, self.gid, self.username
111 ));
112 }
113
114 if self.supplementary_gids.contains(&gid) {
115 let group_name = format!("group{gid}");
116 return Some(format!("{group_name}:x:{gid}:{}", self.username));
117 }
118
119 None
120 }
121}
122
123fn normalize_supplementary_gids(primary_gid: u32, supplementary_gids: Vec<u32>) -> Vec<u32> {
124 let mut normalized = Vec::with_capacity(supplementary_gids.len() + 1);
125 normalized.push(primary_gid);
126 for gid in supplementary_gids {
127 if !normalized.contains(&gid) {
128 normalized.push(gid);
129 }
130 }
131 normalized
132}