]>
Commit | Line | Data |
---|---|---|
1a75668d LW |
1 | use proxmox_http_error::HttpError; |
2 | ||
3 | use crate::api::{http_bail, http_err}; | |
21c5c9a0 LW |
4 | use crate::endpoints::sendmail::{ |
5 | DeleteableSendmailProperty, SendmailConfig, SendmailConfigUpdater, SENDMAIL_TYPENAME, | |
6 | }; | |
7 | use crate::Config; | |
8 | ||
9 | /// Get a list of all sendmail endpoints. | |
10 | /// | |
11 | /// The caller is responsible for any needed permission checks. | |
1a75668d LW |
12 | /// Returns a list of all sendmail endpoints or a `HttpError` if the config is |
13 | /// erroneous (`500 Internal server error`). | |
14 | pub fn get_endpoints(config: &Config) -> Result<Vec<SendmailConfig>, HttpError> { | |
21c5c9a0 LW |
15 | config |
16 | .config | |
17 | .convert_to_typed_array(SENDMAIL_TYPENAME) | |
1a75668d | 18 | .map_err(|e| http_err!(NOT_FOUND, "Could not fetch endpoints: {e}")) |
21c5c9a0 LW |
19 | } |
20 | ||
21 | /// Get sendmail endpoint with given `name`. | |
22 | /// | |
23 | /// The caller is responsible for any needed permission checks. | |
1a75668d LW |
24 | /// Returns the endpoint or a `HttpError` if the endpoint was not found (`404 Not found`). |
25 | pub fn get_endpoint(config: &Config, name: &str) -> Result<SendmailConfig, HttpError> { | |
21c5c9a0 LW |
26 | config |
27 | .config | |
28 | .lookup(SENDMAIL_TYPENAME, name) | |
1a75668d | 29 | .map_err(|_| http_err!(NOT_FOUND, "endpoint '{name}' not found")) |
21c5c9a0 LW |
30 | } |
31 | ||
32 | /// Add a new sendmail endpoint. | |
33 | /// | |
34 | /// The caller is responsible for any needed permission checks. | |
35 | /// The caller also responsible for locking the configuration files. | |
1a75668d LW |
36 | /// Returns a `HttpError` if: |
37 | /// - an entity with the same name already exists (`400 Bad request`) | |
1a75668d LW |
38 | /// - the configuration could not be saved (`500 Internal server error`) |
39 | /// - mailto *and* mailto_user are both set to `None` | |
40 | pub fn add_endpoint(config: &mut Config, endpoint: &SendmailConfig) -> Result<(), HttpError> { | |
2756c11c | 41 | super::ensure_unique(config, &endpoint.name)?; |
21c5c9a0 | 42 | |
c5f91aa1 | 43 | if endpoint.mailto.is_none() && endpoint.mailto_user.is_none() { |
1a75668d LW |
44 | http_bail!( |
45 | BAD_REQUEST, | |
46 | "must at least provide one recipient, either in mailto or in mailto-user" | |
47 | ); | |
c5f91aa1 LW |
48 | } |
49 | ||
21c5c9a0 LW |
50 | config |
51 | .config | |
52 | .set_data(&endpoint.name, SENDMAIL_TYPENAME, endpoint) | |
53 | .map_err(|e| { | |
1a75668d LW |
54 | http_err!( |
55 | INTERNAL_SERVER_ERROR, | |
56 | "could not save endpoint '{}': {e}", | |
57 | endpoint.name | |
21c5c9a0 | 58 | ) |
1a75668d | 59 | }) |
21c5c9a0 LW |
60 | } |
61 | ||
62 | /// Update existing sendmail endpoint | |
63 | /// | |
64 | /// The caller is responsible for any needed permission checks. | |
65 | /// The caller also responsible for locking the configuration files. | |
1a75668d | 66 | /// Returns a `HttpError` if: |
1a75668d LW |
67 | /// - the configuration could not be saved (`500 Internal server error`) |
68 | /// - mailto *and* mailto_user are both set to `None` | |
21c5c9a0 LW |
69 | pub fn update_endpoint( |
70 | config: &mut Config, | |
71 | name: &str, | |
72 | updater: &SendmailConfigUpdater, | |
73 | delete: Option<&[DeleteableSendmailProperty]>, | |
74 | digest: Option<&[u8]>, | |
1a75668d | 75 | ) -> Result<(), HttpError> { |
21c5c9a0 LW |
76 | super::verify_digest(config, digest)?; |
77 | ||
78 | let mut endpoint = get_endpoint(config, name)?; | |
79 | ||
80 | if let Some(delete) = delete { | |
81 | for deleteable_property in delete { | |
82 | match deleteable_property { | |
83 | DeleteableSendmailProperty::FromAddress => endpoint.from_address = None, | |
84 | DeleteableSendmailProperty::Author => endpoint.author = None, | |
85 | DeleteableSendmailProperty::Comment => endpoint.comment = None, | |
c5f91aa1 LW |
86 | DeleteableSendmailProperty::Mailto => endpoint.mailto = None, |
87 | DeleteableSendmailProperty::MailtoUser => endpoint.mailto_user = None, | |
306f4005 | 88 | DeleteableSendmailProperty::Disable => endpoint.disable = None, |
21c5c9a0 LW |
89 | } |
90 | } | |
91 | } | |
92 | ||
93 | if let Some(mailto) = &updater.mailto { | |
c5f91aa1 LW |
94 | endpoint.mailto = Some(mailto.iter().map(String::from).collect()); |
95 | } | |
96 | ||
97 | if let Some(mailto_user) = &updater.mailto_user { | |
98 | endpoint.mailto_user = Some(mailto_user.iter().map(String::from).collect()); | |
21c5c9a0 LW |
99 | } |
100 | ||
101 | if let Some(from_address) = &updater.from_address { | |
102 | endpoint.from_address = Some(from_address.into()); | |
103 | } | |
104 | ||
105 | if let Some(author) = &updater.author { | |
106 | endpoint.author = Some(author.into()); | |
107 | } | |
108 | ||
109 | if let Some(comment) = &updater.comment { | |
110 | endpoint.comment = Some(comment.into()); | |
111 | } | |
112 | ||
306f4005 LW |
113 | if let Some(disable) = &updater.disable { |
114 | endpoint.disable = Some(*disable); | |
115 | } | |
116 | ||
c5f91aa1 | 117 | if endpoint.mailto.is_none() && endpoint.mailto_user.is_none() { |
1a75668d LW |
118 | http_bail!( |
119 | BAD_REQUEST, | |
120 | "must at least provide one recipient, either in mailto or in mailto-user" | |
121 | ); | |
c5f91aa1 LW |
122 | } |
123 | ||
21c5c9a0 LW |
124 | config |
125 | .config | |
126 | .set_data(name, SENDMAIL_TYPENAME, &endpoint) | |
127 | .map_err(|e| { | |
1a75668d LW |
128 | http_err!( |
129 | INTERNAL_SERVER_ERROR, | |
130 | "could not save endpoint '{}': {e}", | |
131 | endpoint.name | |
21c5c9a0 | 132 | ) |
1a75668d | 133 | }) |
21c5c9a0 LW |
134 | } |
135 | ||
136 | /// Delete existing sendmail endpoint | |
137 | /// | |
138 | /// The caller is responsible for any needed permission checks. | |
139 | /// The caller also responsible for locking the configuration files. | |
1a75668d LW |
140 | /// Returns a `HttpError` if: |
141 | /// - an entity with the same name already exists (`400 Bad request`) | |
142 | /// - a referenced filter does not exist (`400 Bad request`) | |
143 | /// - the configuration could not be saved (`500 Internal server error`) | |
144 | pub fn delete_endpoint(config: &mut Config, name: &str) -> Result<(), HttpError> { | |
21c5c9a0 LW |
145 | // Check if the endpoint exists |
146 | let _ = get_endpoint(config, name)?; | |
50fa98e2 | 147 | super::ensure_safe_to_delete(config, name)?; |
21c5c9a0 LW |
148 | |
149 | config.config.sections.remove(name); | |
150 | ||
151 | Ok(()) | |
152 | } | |
153 | ||
9bea76c6 | 154 | #[cfg(all(feature = "pve-context", test))] |
21c5c9a0 LW |
155 | pub mod tests { |
156 | use super::*; | |
157 | use crate::api::test_helpers::*; | |
158 | ||
1a75668d LW |
159 | pub fn add_sendmail_endpoint_for_test( |
160 | config: &mut Config, | |
161 | name: &str, | |
162 | ) -> Result<(), HttpError> { | |
21c5c9a0 LW |
163 | add_endpoint( |
164 | config, | |
165 | &SendmailConfig { | |
166 | name: name.into(), | |
c5f91aa1 LW |
167 | mailto: Some(vec!["user1@example.com".into()]), |
168 | mailto_user: None, | |
21c5c9a0 LW |
169 | from_address: Some("from@example.com".into()), |
170 | author: Some("root".into()), | |
171 | comment: Some("Comment".into()), | |
ee0ab52b | 172 | filter: None, |
306f4005 | 173 | ..Default::default() |
21c5c9a0 LW |
174 | }, |
175 | )?; | |
176 | ||
177 | assert!(get_endpoint(config, name).is_ok()); | |
178 | Ok(()) | |
179 | } | |
180 | ||
181 | #[test] | |
1a75668d | 182 | fn test_sendmail_create() -> Result<(), HttpError> { |
21c5c9a0 LW |
183 | let mut config = empty_config(); |
184 | ||
21c5c9a0 LW |
185 | add_sendmail_endpoint_for_test(&mut config, "sendmail-endpoint")?; |
186 | ||
187 | // Endpoints must have a unique name | |
188 | assert!(add_sendmail_endpoint_for_test(&mut config, "sendmail-endpoint").is_err()); | |
21c5c9a0 LW |
189 | Ok(()) |
190 | } | |
191 | ||
192 | #[test] | |
1a75668d | 193 | fn test_update_not_existing_returns_error() -> Result<(), HttpError> { |
21c5c9a0 LW |
194 | let mut config = empty_config(); |
195 | ||
196 | assert!(update_endpoint(&mut config, "test", &Default::default(), None, None,).is_err()); | |
197 | ||
198 | Ok(()) | |
199 | } | |
200 | ||
201 | #[test] | |
1a75668d | 202 | fn test_update_invalid_digest_returns_error() -> Result<(), HttpError> { |
21c5c9a0 LW |
203 | let mut config = empty_config(); |
204 | add_sendmail_endpoint_for_test(&mut config, "sendmail-endpoint")?; | |
205 | ||
206 | assert!(update_endpoint( | |
207 | &mut config, | |
208 | "sendmail-endpoint", | |
209 | &SendmailConfigUpdater { | |
210 | mailto: Some(vec!["user2@example.com".into(), "user3@example.com".into()]), | |
c5f91aa1 | 211 | mailto_user: None, |
21c5c9a0 LW |
212 | from_address: Some("root@example.com".into()), |
213 | author: Some("newauthor".into()), | |
214 | comment: Some("new comment".into()), | |
306f4005 | 215 | ..Default::default() |
21c5c9a0 LW |
216 | }, |
217 | None, | |
218 | Some(&[0; 32]), | |
219 | ) | |
220 | .is_err()); | |
221 | ||
222 | Ok(()) | |
223 | } | |
224 | ||
225 | #[test] | |
1a75668d | 226 | fn test_sendmail_update() -> Result<(), HttpError> { |
21c5c9a0 LW |
227 | let mut config = empty_config(); |
228 | add_sendmail_endpoint_for_test(&mut config, "sendmail-endpoint")?; | |
229 | ||
230 | let digest = config.digest; | |
231 | ||
232 | update_endpoint( | |
233 | &mut config, | |
234 | "sendmail-endpoint", | |
235 | &SendmailConfigUpdater { | |
236 | mailto: Some(vec!["user2@example.com".into(), "user3@example.com".into()]), | |
c5f91aa1 | 237 | mailto_user: Some(vec!["root@pam".into()]), |
21c5c9a0 LW |
238 | from_address: Some("root@example.com".into()), |
239 | author: Some("newauthor".into()), | |
240 | comment: Some("new comment".into()), | |
306f4005 | 241 | ..Default::default() |
21c5c9a0 LW |
242 | }, |
243 | None, | |
244 | Some(&digest), | |
245 | )?; | |
246 | ||
247 | let endpoint = get_endpoint(&config, "sendmail-endpoint")?; | |
248 | ||
249 | assert_eq!( | |
250 | endpoint.mailto, | |
c5f91aa1 | 251 | Some(vec![ |
21c5c9a0 LW |
252 | "user2@example.com".to_string(), |
253 | "user3@example.com".to_string() | |
c5f91aa1 | 254 | ]) |
21c5c9a0 | 255 | ); |
c5f91aa1 | 256 | assert_eq!(endpoint.mailto_user, Some(vec!["root@pam".to_string(),])); |
21c5c9a0 LW |
257 | assert_eq!(endpoint.from_address, Some("root@example.com".to_string())); |
258 | assert_eq!(endpoint.author, Some("newauthor".to_string())); | |
259 | assert_eq!(endpoint.comment, Some("new comment".to_string())); | |
260 | ||
261 | // Test property deletion | |
262 | update_endpoint( | |
263 | &mut config, | |
264 | "sendmail-endpoint", | |
265 | &Default::default(), | |
266 | Some(&[ | |
267 | DeleteableSendmailProperty::FromAddress, | |
268 | DeleteableSendmailProperty::Author, | |
269 | ]), | |
270 | None, | |
271 | )?; | |
272 | ||
273 | let endpoint = get_endpoint(&config, "sendmail-endpoint")?; | |
274 | ||
275 | assert_eq!(endpoint.from_address, None); | |
276 | assert_eq!(endpoint.author, None); | |
277 | ||
278 | Ok(()) | |
279 | } | |
280 | ||
281 | #[test] | |
1a75668d | 282 | fn test_sendmail_delete() -> Result<(), HttpError> { |
21c5c9a0 LW |
283 | let mut config = empty_config(); |
284 | add_sendmail_endpoint_for_test(&mut config, "sendmail-endpoint")?; | |
285 | ||
286 | delete_endpoint(&mut config, "sendmail-endpoint")?; | |
287 | assert!(delete_endpoint(&mut config, "sendmail-endpoint").is_err()); | |
21c5c9a0 LW |
288 | |
289 | Ok(()) | |
290 | } | |
291 | } |