]>
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, | |
a4d55947 LW |
43 | endpoint_config: SmtpConfig, |
44 | private_endpoint_config: SmtpPrivateConfig, | |
20b29089 LW |
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 | ||
d61e3fc7 | 53 | if endpoint_config.mailto.is_empty() && endpoint_config.mailto_user.is_empty() { |
20b29089 LW |
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 | |
a4d55947 | 69 | .set_data(&endpoint_config.name, SMTP_TYPENAME, &endpoint_config) |
20b29089 LW |
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, | |
a4d55947 LW |
89 | updater: SmtpConfigUpdater, |
90 | private_endpoint_config_updater: SmtpPrivateConfigUpdater, | |
20b29089 LW |
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, |
d61e3fc7 LW |
104 | DeleteableSmtpProperty::Mailto => endpoint.mailto.clear(), |
105 | DeleteableSmtpProperty::MailtoUser => endpoint.mailto_user.clear(), | |
20b29089 LW |
106 | DeleteableSmtpProperty::Password => super::set_private_config_entry( |
107 | config, | |
a4d55947 | 108 | SmtpPrivateConfig { |
20b29089 LW |
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 | ||
a4d55947 | 121 | if let Some(mailto) = updater.mailto { |
d61e3fc7 | 122 | endpoint.mailto = mailto; |
20b29089 | 123 | } |
a4d55947 | 124 | if let Some(mailto_user) = updater.mailto_user { |
d61e3fc7 | 125 | endpoint.mailto_user = mailto_user; |
20b29089 | 126 | } |
a4d55947 LW |
127 | if let Some(from_address) = updater.from_address { |
128 | endpoint.from_address = from_address; | |
20b29089 | 129 | } |
a4d55947 LW |
130 | if let Some(server) = updater.server { |
131 | endpoint.server = server; | |
20b29089 | 132 | } |
a4d55947 LW |
133 | if let Some(port) = updater.port { |
134 | endpoint.port = Some(port); | |
20b29089 | 135 | } |
a4d55947 LW |
136 | if let Some(username) = updater.username { |
137 | endpoint.username = Some(username); | |
20b29089 | 138 | } |
a4d55947 LW |
139 | if let Some(mode) = updater.mode { |
140 | endpoint.mode = Some(mode); | |
20b29089 | 141 | } |
a4d55947 | 142 | if let Some(password) = private_endpoint_config_updater.password { |
20b29089 LW |
143 | super::set_private_config_entry( |
144 | config, | |
a4d55947 | 145 | SmtpPrivateConfig { |
20b29089 | 146 | name: name.into(), |
a4d55947 | 147 | password: Some(password), |
20b29089 LW |
148 | }, |
149 | SMTP_TYPENAME, | |
150 | name, | |
151 | )?; | |
152 | } | |
153 | ||
a4d55947 LW |
154 | if let Some(author) = updater.author { |
155 | endpoint.author = Some(author); | |
20b29089 LW |
156 | } |
157 | ||
a4d55947 LW |
158 | if let Some(comment) = updater.comment { |
159 | endpoint.comment = Some(comment); | |
20b29089 LW |
160 | } |
161 | ||
a4d55947 LW |
162 | if let Some(disable) = updater.disable { |
163 | endpoint.disable = Some(disable); | |
306f4005 LW |
164 | } |
165 | ||
d61e3fc7 | 166 | if endpoint.mailto.is_empty() && endpoint.mailto_user.is_empty() { |
20b29089 LW |
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)?; | |
50fa98e2 | 195 | super::ensure_safe_to_delete(config, name)?; |
20b29089 LW |
196 | |
197 | super::remove_private_config_entry(config, name)?; | |
198 | config.config.sections.remove(name); | |
199 | ||
200 | Ok(()) | |
201 | } | |
202 | ||
9bea76c6 | 203 | #[cfg(all(feature = "pve-context", test))] |
20b29089 LW |
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, | |
a4d55947 | 212 | SmtpConfig { |
20b29089 | 213 | name: name.into(), |
d61e3fc7 LW |
214 | mailto: vec!["user1@example.com".into()], |
215 | mailto_user: vec![], | |
20b29089 LW |
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 | 224 | }, |
a4d55947 | 225 | SmtpPrivateConfig { |
20b29089 LW |
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", | |
a4d55947 LW |
255 | Default::default(), |
256 | Default::default(), | |
20b29089 LW |
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", | |
a4d55947 LW |
273 | Default::default(), |
274 | Default::default(), | |
20b29089 LW |
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", | |
a4d55947 | 293 | SmtpConfigUpdater { |
20b29089 LW |
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 | }, | |
a4d55947 | 305 | Default::default(), |
20b29089 LW |
306 | None, |
307 | Some(&digest), | |
308 | )?; | |
309 | ||
310 | let endpoint = get_endpoint(&config, "smtp-endpoint")?; | |
311 | ||
312 | assert_eq!( | |
313 | endpoint.mailto, | |
d61e3fc7 | 314 | vec![ |
20b29089 LW |
315 | "user2@example.com".to_string(), |
316 | "user3@example.com".to_string() | |
d61e3fc7 | 317 | ] |
20b29089 | 318 | ); |
d61e3fc7 | 319 | assert_eq!(endpoint.mailto_user, vec!["root@pam".to_string(),]); |
20b29089 LW |
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", | |
a4d55947 LW |
328 | Default::default(), |
329 | Default::default(), | |
20b29089 LW |
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); | |
d61e3fc7 | 346 | assert!(endpoint.mailto_user.is_empty()); |
20b29089 LW |
347 | |
348 | Ok(()) | |
349 | } | |
350 | ||
9bea76c6 LW |
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 | // } | |
20b29089 | 362 | } |