]>
Commit | Line | Data |
---|---|---|
20b29089 LW |
1 | use proxmox_http_error::HttpError; |
2 | ||
3 | use crate::api::{http_bail, http_err}; | |
4 | use crate::endpoints::smtp::{ | |
5 | DeleteableSmtpProperty, SmtpConfig, SmtpConfigUpdater, SmtpPrivateConfig, | |
6 | SmtpPrivateConfigUpdater, SMTP_TYPENAME, | |
7 | }; | |
8 | use crate::Config; | |
9 | ||
10 | /// Get a list of all smtp endpoints. | |
11 | /// | |
12 | /// The caller is responsible for any needed permission checks. | |
13 | /// Returns a list of all smtp endpoints or a `HttpError` if the config is | |
14 | /// erroneous (`500 Internal server error`). | |
15 | pub fn get_endpoints(config: &Config) -> Result<Vec<SmtpConfig>, HttpError> { | |
16 | config | |
17 | .config | |
18 | .convert_to_typed_array(SMTP_TYPENAME) | |
19 | .map_err(|e| http_err!(NOT_FOUND, "Could not fetch endpoints: {e}")) | |
20 | } | |
21 | ||
22 | /// Get smtp endpoint with given `name`. | |
23 | /// | |
24 | /// The caller is responsible for any needed permission checks. | |
25 | /// Returns the endpoint or a `HttpError` if the endpoint was not found (`404 Not found`). | |
26 | pub fn get_endpoint(config: &Config, name: &str) -> Result<SmtpConfig, HttpError> { | |
27 | config | |
28 | .config | |
29 | .lookup(SMTP_TYPENAME, name) | |
30 | .map_err(|_| http_err!(NOT_FOUND, "endpoint '{name}' not found")) | |
31 | } | |
32 | ||
33 | /// Add a new smtp endpoint. | |
34 | /// | |
35 | /// The caller is responsible for any needed permission checks. | |
36 | /// The caller also responsible for locking the configuration files. | |
37 | /// Returns a `HttpError` if: | |
38 | /// - an entity with the same name already exists (`400 Bad request`) | |
39 | /// - the configuration could not be saved (`500 Internal server error`) | |
40 | /// - mailto *and* mailto_user are both set to `None` | |
41 | pub fn add_endpoint( | |
42 | config: &mut Config, | |
43 | endpoint_config: &SmtpConfig, | |
44 | private_endpoint_config: &SmtpPrivateConfig, | |
45 | ) -> Result<(), HttpError> { | |
46 | if endpoint_config.name != private_endpoint_config.name { | |
47 | // Programming error by the user of the crate, thus we panic | |
48 | panic!("name for endpoint config and private config must be identical"); | |
49 | } | |
50 | ||
51 | super::ensure_unique(config, &endpoint_config.name)?; | |
52 | ||
53 | if endpoint_config.mailto.is_none() && endpoint_config.mailto_user.is_none() { | |
54 | http_bail!( | |
55 | BAD_REQUEST, | |
56 | "must at least provide one recipient, either in mailto or in mailto-user" | |
57 | ); | |
58 | } | |
59 | ||
60 | super::set_private_config_entry( | |
61 | config, | |
62 | private_endpoint_config, | |
63 | SMTP_TYPENAME, | |
64 | &endpoint_config.name, | |
65 | )?; | |
66 | ||
67 | config | |
68 | .config | |
69 | .set_data(&endpoint_config.name, SMTP_TYPENAME, endpoint_config) | |
70 | .map_err(|e| { | |
71 | http_err!( | |
72 | INTERNAL_SERVER_ERROR, | |
73 | "could not save endpoint '{}': {e}", | |
74 | endpoint_config.name | |
75 | ) | |
76 | }) | |
77 | } | |
78 | ||
79 | /// Update existing smtp endpoint | |
80 | /// | |
81 | /// The caller is responsible for any needed permission checks. | |
82 | /// The caller also responsible for locking the configuration files. | |
83 | /// Returns a `HttpError` if: | |
84 | /// - the configuration could not be saved (`500 Internal server error`) | |
85 | /// - mailto *and* mailto_user are both set to `None` | |
86 | pub fn update_endpoint( | |
87 | config: &mut Config, | |
88 | name: &str, | |
89 | updater: &SmtpConfigUpdater, | |
90 | private_endpoint_config_updater: &SmtpPrivateConfigUpdater, | |
91 | delete: Option<&[DeleteableSmtpProperty]>, | |
92 | digest: Option<&[u8]>, | |
93 | ) -> Result<(), HttpError> { | |
94 | super::verify_digest(config, digest)?; | |
95 | ||
96 | let mut endpoint = get_endpoint(config, name)?; | |
97 | ||
98 | if let Some(delete) = delete { | |
99 | for deleteable_property in delete { | |
100 | match deleteable_property { | |
101 | DeleteableSmtpProperty::Author => endpoint.author = None, | |
102 | DeleteableSmtpProperty::Comment => endpoint.comment = None, | |
306f4005 | 103 | DeleteableSmtpProperty::Disable => endpoint.disable = None, |
20b29089 LW |
104 | DeleteableSmtpProperty::Mailto => endpoint.mailto = None, |
105 | DeleteableSmtpProperty::MailtoUser => endpoint.mailto_user = None, | |
106 | DeleteableSmtpProperty::Password => super::set_private_config_entry( | |
107 | config, | |
108 | &SmtpPrivateConfig { | |
109 | name: name.to_string(), | |
110 | password: None, | |
111 | }, | |
112 | SMTP_TYPENAME, | |
113 | name, | |
114 | )?, | |
115 | DeleteableSmtpProperty::Port => endpoint.port = None, | |
116 | DeleteableSmtpProperty::Username => endpoint.username = None, | |
117 | } | |
118 | } | |
119 | } | |
120 | ||
121 | if let Some(mailto) = &updater.mailto { | |
122 | endpoint.mailto = Some(mailto.iter().map(String::from).collect()); | |
123 | } | |
124 | if let Some(mailto_user) = &updater.mailto_user { | |
125 | endpoint.mailto_user = Some(mailto_user.iter().map(String::from).collect()); | |
126 | } | |
127 | if let Some(from_address) = &updater.from_address { | |
128 | endpoint.from_address = from_address.into(); | |
129 | } | |
130 | if let Some(server) = &updater.server { | |
131 | endpoint.server = server.into(); | |
132 | } | |
133 | if let Some(port) = &updater.port { | |
134 | endpoint.port = Some(*port); | |
135 | } | |
136 | if let Some(username) = &updater.username { | |
137 | endpoint.username = Some(username.into()); | |
138 | } | |
139 | if let Some(mode) = &updater.mode { | |
140 | endpoint.mode = Some(*mode); | |
141 | } | |
142 | if let Some(password) = &private_endpoint_config_updater.password { | |
143 | super::set_private_config_entry( | |
144 | config, | |
145 | &SmtpPrivateConfig { | |
146 | name: name.into(), | |
147 | password: Some(password.into()), | |
148 | }, | |
149 | SMTP_TYPENAME, | |
150 | name, | |
151 | )?; | |
152 | } | |
153 | ||
154 | if let Some(author) = &updater.author { | |
155 | endpoint.author = Some(author.into()); | |
156 | } | |
157 | ||
158 | if let Some(comment) = &updater.comment { | |
159 | endpoint.comment = Some(comment.into()); | |
160 | } | |
161 | ||
306f4005 LW |
162 | if let Some(disable) = &updater.disable { |
163 | endpoint.disable = Some(*disable); | |
164 | } | |
165 | ||
20b29089 LW |
166 | if endpoint.mailto.is_none() && endpoint.mailto_user.is_none() { |
167 | http_bail!( | |
168 | BAD_REQUEST, | |
169 | "must at least provide one recipient, either in mailto or in mailto-user" | |
170 | ); | |
171 | } | |
172 | ||
173 | config | |
174 | .config | |
175 | .set_data(name, SMTP_TYPENAME, &endpoint) | |
176 | .map_err(|e| { | |
177 | http_err!( | |
178 | INTERNAL_SERVER_ERROR, | |
179 | "could not save endpoint '{}': {e}", | |
180 | endpoint.name | |
181 | ) | |
182 | }) | |
183 | } | |
184 | ||
185 | /// Delete existing smtp endpoint | |
186 | /// | |
187 | /// The caller is responsible for any needed permission checks. | |
188 | /// The caller also responsible for locking the configuration files. | |
189 | /// Returns a `HttpError` if: | |
190 | /// - an entity with the same name already exists (`400 Bad request`) | |
191 | /// - the configuration could not be saved (`500 Internal server error`) | |
192 | pub fn delete_endpoint(config: &mut Config, name: &str) -> Result<(), HttpError> { | |
193 | // Check if the endpoint exists | |
194 | let _ = get_endpoint(config, name)?; | |
195 | super::ensure_unused(config, name)?; | |
196 | ||
197 | super::remove_private_config_entry(config, name)?; | |
198 | config.config.sections.remove(name); | |
199 | ||
200 | Ok(()) | |
201 | } | |
202 | ||
203 | #[cfg(test)] | |
204 | pub mod tests { | |
205 | use super::*; | |
206 | use crate::api::test_helpers::*; | |
207 | use crate::endpoints::smtp::SmtpMode; | |
208 | ||
209 | pub fn add_smtp_endpoint_for_test(config: &mut Config, name: &str) -> Result<(), HttpError> { | |
210 | add_endpoint( | |
211 | config, | |
212 | &SmtpConfig { | |
213 | name: name.into(), | |
214 | mailto: Some(vec!["user1@example.com".into()]), | |
215 | mailto_user: None, | |
216 | from_address: "from@example.com".into(), | |
217 | author: Some("root".into()), | |
218 | comment: Some("Comment".into()), | |
219 | mode: Some(SmtpMode::StartTls), | |
220 | server: "localhost".into(), | |
221 | port: Some(555), | |
222 | username: Some("username".into()), | |
306f4005 | 223 | ..Default::default() |
20b29089 LW |
224 | }, |
225 | &SmtpPrivateConfig { | |
226 | name: name.into(), | |
227 | password: Some("password".into()), | |
228 | }, | |
229 | )?; | |
230 | ||
231 | assert!(get_endpoint(config, name).is_ok()); | |
232 | Ok(()) | |
233 | } | |
234 | ||
235 | #[test] | |
236 | fn test_smtp_create() -> Result<(), HttpError> { | |
237 | let mut config = empty_config(); | |
238 | ||
239 | assert_eq!(get_endpoints(&config)?.len(), 0); | |
240 | add_smtp_endpoint_for_test(&mut config, "smtp-endpoint")?; | |
241 | ||
242 | // Endpoints must have a unique name | |
243 | assert!(add_smtp_endpoint_for_test(&mut config, "smtp-endpoint").is_err()); | |
244 | assert_eq!(get_endpoints(&config)?.len(), 1); | |
245 | Ok(()) | |
246 | } | |
247 | ||
248 | #[test] | |
249 | fn test_update_not_existing_returns_error() -> Result<(), HttpError> { | |
250 | let mut config = empty_config(); | |
251 | ||
252 | assert!(update_endpoint( | |
253 | &mut config, | |
254 | "test", | |
255 | &Default::default(), | |
256 | &Default::default(), | |
257 | None, | |
258 | None, | |
259 | ) | |
260 | .is_err()); | |
261 | ||
262 | Ok(()) | |
263 | } | |
264 | ||
265 | #[test] | |
266 | fn test_update_invalid_digest_returns_error() -> Result<(), HttpError> { | |
267 | let mut config = empty_config(); | |
268 | add_smtp_endpoint_for_test(&mut config, "sendmail-endpoint")?; | |
269 | ||
270 | assert!(update_endpoint( | |
271 | &mut config, | |
272 | "sendmail-endpoint", | |
273 | &Default::default(), | |
274 | &Default::default(), | |
275 | None, | |
276 | Some(&[0; 32]), | |
277 | ) | |
278 | .is_err()); | |
279 | ||
280 | Ok(()) | |
281 | } | |
282 | ||
283 | #[test] | |
284 | fn test_update() -> Result<(), HttpError> { | |
285 | let mut config = empty_config(); | |
286 | add_smtp_endpoint_for_test(&mut config, "smtp-endpoint")?; | |
287 | ||
288 | let digest = config.digest; | |
289 | ||
290 | update_endpoint( | |
291 | &mut config, | |
292 | "smtp-endpoint", | |
293 | &SmtpConfigUpdater { | |
294 | mailto: Some(vec!["user2@example.com".into(), "user3@example.com".into()]), | |
295 | mailto_user: Some(vec!["root@pam".into()]), | |
296 | from_address: Some("root@example.com".into()), | |
297 | author: Some("newauthor".into()), | |
298 | comment: Some("new comment".into()), | |
299 | mode: Some(SmtpMode::Insecure), | |
300 | server: Some("pali".into()), | |
301 | port: Some(444), | |
302 | username: Some("newusername".into()), | |
303 | ..Default::default() | |
304 | }, | |
305 | &Default::default(), | |
306 | None, | |
307 | Some(&digest), | |
308 | )?; | |
309 | ||
310 | let endpoint = get_endpoint(&config, "smtp-endpoint")?; | |
311 | ||
312 | assert_eq!( | |
313 | endpoint.mailto, | |
314 | Some(vec![ | |
315 | "user2@example.com".to_string(), | |
316 | "user3@example.com".to_string() | |
317 | ]) | |
318 | ); | |
319 | assert_eq!(endpoint.mailto_user, Some(vec!["root@pam".to_string(),])); | |
320 | assert_eq!(endpoint.from_address, "root@example.com".to_string()); | |
321 | assert_eq!(endpoint.author, Some("newauthor".to_string())); | |
322 | assert_eq!(endpoint.comment, Some("new comment".to_string())); | |
323 | ||
324 | // Test property deletion | |
325 | update_endpoint( | |
326 | &mut config, | |
327 | "smtp-endpoint", | |
328 | &Default::default(), | |
329 | &Default::default(), | |
330 | Some(&[ | |
331 | DeleteableSmtpProperty::Author, | |
332 | DeleteableSmtpProperty::MailtoUser, | |
333 | DeleteableSmtpProperty::Port, | |
334 | DeleteableSmtpProperty::Username, | |
335 | DeleteableSmtpProperty::Comment, | |
336 | ]), | |
337 | None, | |
338 | )?; | |
339 | ||
340 | let endpoint = get_endpoint(&config, "smtp-endpoint")?; | |
341 | ||
342 | assert_eq!(endpoint.author, None); | |
343 | assert_eq!(endpoint.comment, None); | |
344 | assert_eq!(endpoint.port, None); | |
345 | assert_eq!(endpoint.username, None); | |
346 | assert_eq!(endpoint.mailto_user, None); | |
347 | ||
348 | Ok(()) | |
349 | } | |
350 | ||
351 | #[test] | |
352 | fn test_delete() -> Result<(), HttpError> { | |
353 | let mut config = empty_config(); | |
354 | add_smtp_endpoint_for_test(&mut config, "smtp-endpoint")?; | |
355 | ||
356 | delete_endpoint(&mut config, "smtp-endpoint")?; | |
357 | assert!(delete_endpoint(&mut config, "smtp-endpoint").is_err()); | |
358 | assert_eq!(get_endpoints(&config)?.len(), 0); | |
359 | ||
360 | Ok(()) | |
361 | } | |
362 | } |