comtrya_lib/atoms/file/
chown.rs1use crate::atoms::Outcome;
2
3use super::super::Atom;
4use super::FileAtom;
5use std::path::PathBuf;
6
7#[cfg(unix)]
8use tracing::error;
9
10#[derive(Debug)]
11pub struct Chown {
12 pub path: PathBuf,
13 pub owner: String,
14 pub group: String,
15}
16
17impl FileAtom for Chown {
18 fn get_path(&self) -> &PathBuf {
19 &self.path
20 }
21}
22
23impl std::fmt::Display for Chown {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 write!(
26 f,
27 "The owner and group on {} need to be set to {}:{}",
28 self.path.display(),
29 self.owner,
30 self.group,
31 )
32 }
33}
34
35#[cfg(unix)]
36use std::os::unix::prelude::MetadataExt;
37
38#[cfg(unix)]
39use file_owner::PathExt;
40
41#[cfg(unix)]
42impl Atom for Chown {
43 fn plan(&self) -> anyhow::Result<Outcome> {
44 if !self.path.exists() {
47 return Ok(Outcome {
48 side_effects: vec![],
49 should_run: true,
50 });
51 }
52
53 let metadata = match std::fs::metadata(&self.path) {
54 Ok(m) => m,
55 Err(err) => {
56 error!(
57 "Couldn't get metadata for {}, rejecting atom: {}",
58 &self.path.display(),
59 err.to_string()
60 );
61
62 return Ok(Outcome {
63 side_effects: vec![],
64 should_run: false,
65 });
66 }
67 };
68
69 if let (Some(current_owner), Some(current_group)) = (
70 uzers::get_user_by_uid(metadata.uid()),
71 uzers::get_group_by_gid(metadata.gid()),
72 ) {
73 let requested_owner = match uzers::get_user_by_name(self.owner.as_str()) {
74 Some(owner) => owner,
75 None => {
76 error!(
77 "Skipping chown as requested owner, {}, does not exist",
78 self.owner,
79 );
80 return Ok(Outcome {
81 side_effects: vec![],
82 should_run: false,
83 });
84 }
85 };
86
87 let requested_group = match uzers::get_group_by_name(self.group.as_str()) {
88 Some(group) => group,
89 None => {
90 error!(
91 "Skipping chown as requested group, {}, does not exist",
92 self.group,
93 );
94
95 return Ok(Outcome {
96 side_effects: vec![],
97 should_run: false,
98 });
99 }
100 };
101
102 if current_owner.uid() != requested_owner.uid() {
103 return Ok(Outcome {
104 side_effects: vec![],
105 should_run: true,
106 });
107 }
108
109 if current_group.gid() != requested_group.gid() {
110 return Ok(Outcome {
111 side_effects: vec![],
112 should_run: true,
113 });
114 }
115 }
116
117 error!("Something happened here");
118
119 Ok(Outcome {
120 side_effects: vec![],
121 should_run: false,
122 })
123 }
124
125 fn execute(&mut self) -> anyhow::Result<()> {
126 if !self.owner.is_empty() {
127 self.path.set_owner(self.owner.as_str())?;
128 }
129
130 if !self.group.is_empty() {
131 self.path.set_group(self.group.as_str())?;
132 }
133
134 Ok(())
135 }
136}
137
138#[cfg(not(unix))]
139impl Atom for Chown {
140 fn plan(&self) -> anyhow::Result<Outcome> {
141 Ok(Outcome {
143 side_effects: vec![],
144 should_run: false,
145 })
146 }
147
148 fn execute(&mut self) -> anyhow::Result<()> {
149 Ok(())
150 }
151}
152
153#[cfg(test)]
154#[cfg(unix)]
155mod tests {
156 use super::*;
157 use pretty_assertions::assert_eq;
158
159 #[test]
160 fn it_can() {
161 let user = uzers::get_current_username()
165 .unwrap_or_else(|| std::ffi::OsString::from("root"))
166 .into_string()
167 .unwrap();
168
169 let group = uzers::get_current_groupname()
170 .unwrap_or_else(|| std::ffi::OsString::from("root"))
171 .into_string()
172 .unwrap();
173
174 let temp_file = match tempfile::NamedTempFile::new() {
175 std::result::Result::Ok(file) => file,
176 std::result::Result::Err(_) => {
177 assert_eq!(false, true);
178 return;
179 }
180 };
181
182 let file_chown = Chown {
183 path: temp_file.path().to_path_buf(),
184 owner: user.clone(),
185 group: group.clone(),
186 };
187
188 assert_eq!(false, file_chown.plan().unwrap().should_run);
189
190 let file_chown = Chown {
191 path: temp_file.path().to_path_buf(),
192 owner: user,
193 group: String::from("daemon"),
194 };
195
196 assert_eq!(true, file_chown.plan().unwrap().should_run);
197
198 let file_chown = Chown {
199 path: temp_file.path().to_path_buf(),
200 owner: String::from("root"),
201 group,
202 };
203
204 assert_eq!(true, file_chown.plan().unwrap().should_run);
205
206 let file_chown = Chown {
207 path: temp_file.path().to_path_buf(),
208 owner: String::from("root"),
209 group: String::from("daemon"),
210 };
211
212 assert_eq!(true, file_chown.plan().unwrap().should_run);
213 }
214}