]>
Commit | Line | Data |
---|---|---|
9b2bad7a HL |
1 | use anyhow::{bail, Error}; |
2 | use serde_json::Value; | |
3 | use ::serde::{Deserialize, Serialize}; | |
4 | ||
b7ce2e57 | 5 | use proxmox::api::{api, Permission, Router, RpcEnvironment}; |
9b2bad7a HL |
6 | use proxmox::tools::fs::open_file_locked; |
7 | ||
8 | use crate::api2::types::*; | |
b7ce2e57 FG |
9 | |
10 | use crate::config::acl::{ | |
11 | PRIV_DATASTORE_AUDIT, | |
b7ce2e57 FG |
12 | PRIV_DATASTORE_VERIFY, |
13 | }; | |
14 | ||
35c80d69 FG |
15 | use crate::config::cached_user_info::CachedUserInfo; |
16 | ||
9b2bad7a HL |
17 | use crate::config::verify::{self, VerificationJobConfig}; |
18 | ||
19 | #[api( | |
20 | input: { | |
21 | properties: {}, | |
22 | }, | |
23 | returns: { | |
24 | description: "List configured jobs.", | |
25 | type: Array, | |
26 | items: { type: verify::VerificationJobConfig }, | |
27 | }, | |
b7ce2e57 | 28 | access: { |
35c80d69 FG |
29 | permission: &Permission::Anybody, |
30 | description: "Requires Datastore.Audit or Datastore.Verify on datastore.", | |
b7ce2e57 | 31 | }, |
9b2bad7a HL |
32 | )] |
33 | /// List all verification jobs | |
34 | pub fn list_verification_jobs( | |
35 | _param: Value, | |
36 | mut rpcenv: &mut dyn RpcEnvironment, | |
37 | ) -> Result<Vec<VerificationJobConfig>, Error> { | |
35c80d69 FG |
38 | let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; |
39 | let user_info = CachedUserInfo::new()?; | |
40 | ||
41 | let required_privs = PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_VERIFY; | |
9b2bad7a HL |
42 | |
43 | let (config, digest) = verify::config()?; | |
44 | ||
45 | let list = config.convert_to_typed_array("verification")?; | |
46 | ||
35c80d69 FG |
47 | let list = list.into_iter() |
48 | .filter(|job: &VerificationJobConfig| { | |
49 | let privs = user_info.lookup_privs(&auth_id, &["datastore", &job.store]); | |
50 | ||
51 | privs & required_privs != 00 | |
52 | }).collect(); | |
53 | ||
9b2bad7a HL |
54 | rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into(); |
55 | ||
56 | Ok(list) | |
57 | } | |
58 | ||
59 | ||
60 | #[api( | |
61 | protected: true, | |
62 | input: { | |
63 | properties: { | |
64 | id: { | |
65 | schema: JOB_ID_SCHEMA, | |
66 | }, | |
67 | store: { | |
68 | schema: DATASTORE_SCHEMA, | |
69 | }, | |
70 | "ignore-verified": { | |
71 | optional: true, | |
72 | schema: IGNORE_VERIFIED_BACKUPS_SCHEMA, | |
73 | }, | |
74 | "outdated-after": { | |
75 | optional: true, | |
76 | schema: VERIFICATION_OUTDATED_AFTER_SCHEMA, | |
77 | }, | |
78 | comment: { | |
79 | optional: true, | |
80 | schema: SINGLE_LINE_COMMENT_SCHEMA, | |
81 | }, | |
82 | schedule: { | |
83 | optional: true, | |
84 | schema: VERIFICATION_SCHEDULE_SCHEMA, | |
85 | }, | |
86 | } | |
b7ce2e57 FG |
87 | }, |
88 | access: { | |
35c80d69 FG |
89 | permission: &Permission::Anybody, |
90 | description: "Requires Datastore.Verify on job's datastore.", | |
b7ce2e57 | 91 | }, |
9b2bad7a HL |
92 | )] |
93 | /// Create a new verification job. | |
35c80d69 FG |
94 | pub fn create_verification_job( |
95 | param: Value, | |
96 | rpcenv: &mut dyn RpcEnvironment | |
97 | ) -> Result<(), Error> { | |
98 | let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; | |
99 | let user_info = CachedUserInfo::new()?; | |
9b2bad7a | 100 | |
44288184 | 101 | let verification_job: verify::VerificationJobConfig = serde_json::from_value(param)?; |
9b2bad7a | 102 | |
35c80d69 FG |
103 | user_info.check_privs(&auth_id, &["datastore", &verification_job.store], PRIV_DATASTORE_VERIFY, false)?; |
104 | ||
105 | let _lock = open_file_locked(verify::VERIFICATION_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?; | |
106 | ||
9b2bad7a HL |
107 | let (mut config, _digest) = verify::config()?; |
108 | ||
3984a5fd | 109 | if config.sections.get(&verification_job.id).is_some() { |
9b2bad7a HL |
110 | bail!("job '{}' already exists.", verification_job.id); |
111 | } | |
112 | ||
113 | config.set_data(&verification_job.id, "verification", &verification_job)?; | |
114 | ||
115 | verify::save_config(&config)?; | |
116 | ||
1298618a | 117 | crate::server::jobstate::create_state_file("verificationjob", &verification_job.id)?; |
9b2bad7a HL |
118 | |
119 | Ok(()) | |
120 | } | |
121 | ||
122 | #[api( | |
123 | input: { | |
124 | properties: { | |
125 | id: { | |
126 | schema: JOB_ID_SCHEMA, | |
127 | }, | |
128 | }, | |
129 | }, | |
9b93c620 | 130 | returns: { type: verify::VerificationJobConfig }, |
b7ce2e57 | 131 | access: { |
35c80d69 FG |
132 | permission: &Permission::Anybody, |
133 | description: "Requires Datastore.Audit or Datastore.Verify on job's datastore.", | |
b7ce2e57 | 134 | }, |
9b2bad7a HL |
135 | )] |
136 | /// Read a verification job configuration. | |
137 | pub fn read_verification_job( | |
138 | id: String, | |
139 | mut rpcenv: &mut dyn RpcEnvironment, | |
140 | ) -> Result<VerificationJobConfig, Error> { | |
35c80d69 FG |
141 | let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; |
142 | let user_info = CachedUserInfo::new()?; | |
143 | ||
9b2bad7a HL |
144 | let (config, digest) = verify::config()?; |
145 | ||
35c80d69 FG |
146 | let verification_job: verify::VerificationJobConfig = config.lookup("verification", &id)?; |
147 | ||
148 | let required_privs = PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_VERIFY; | |
149 | user_info.check_privs(&auth_id, &["datastore", &verification_job.store], required_privs, true)?; | |
150 | ||
9b2bad7a HL |
151 | rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into(); |
152 | ||
153 | Ok(verification_job) | |
154 | } | |
155 | ||
156 | #[api()] | |
157 | #[derive(Serialize, Deserialize)] | |
158 | #[serde(rename_all="kebab-case")] | |
159 | /// Deletable property name | |
160 | pub enum DeletableProperty { | |
161 | /// Delete the ignore verified property. | |
162 | IgnoreVerified, | |
163 | /// Delete the comment property. | |
164 | Comment, | |
165 | /// Delete the job schedule. | |
166 | Schedule, | |
167 | /// Delete outdated after property. | |
168 | OutdatedAfter | |
169 | } | |
170 | ||
171 | #[api( | |
172 | protected: true, | |
173 | input: { | |
174 | properties: { | |
175 | id: { | |
176 | schema: JOB_ID_SCHEMA, | |
177 | }, | |
178 | store: { | |
179 | optional: true, | |
180 | schema: DATASTORE_SCHEMA, | |
181 | }, | |
182 | "ignore-verified": { | |
183 | optional: true, | |
184 | schema: IGNORE_VERIFIED_BACKUPS_SCHEMA, | |
185 | }, | |
186 | "outdated-after": { | |
187 | optional: true, | |
188 | schema: VERIFICATION_OUTDATED_AFTER_SCHEMA, | |
189 | }, | |
190 | comment: { | |
191 | optional: true, | |
192 | schema: SINGLE_LINE_COMMENT_SCHEMA, | |
193 | }, | |
194 | schedule: { | |
195 | optional: true, | |
196 | schema: VERIFICATION_SCHEDULE_SCHEMA, | |
197 | }, | |
198 | delete: { | |
199 | description: "List of properties to delete.", | |
200 | type: Array, | |
201 | optional: true, | |
202 | items: { | |
203 | type: DeletableProperty, | |
204 | } | |
205 | }, | |
206 | digest: { | |
207 | optional: true, | |
208 | schema: PROXMOX_CONFIG_DIGEST_SCHEMA, | |
209 | }, | |
210 | }, | |
211 | }, | |
b7ce2e57 | 212 | access: { |
35c80d69 FG |
213 | permission: &Permission::Anybody, |
214 | description: "Requires Datastore.Verify on job's datastore.", | |
b7ce2e57 | 215 | }, |
9b2bad7a HL |
216 | )] |
217 | /// Update verification job config. | |
367c0ff7 | 218 | #[allow(clippy::too_many_arguments)] |
9b2bad7a HL |
219 | pub fn update_verification_job( |
220 | id: String, | |
221 | store: Option<String>, | |
222 | ignore_verified: Option<bool>, | |
223 | outdated_after: Option<i64>, | |
224 | comment: Option<String>, | |
225 | schedule: Option<String>, | |
226 | delete: Option<Vec<DeletableProperty>>, | |
227 | digest: Option<String>, | |
35c80d69 | 228 | rpcenv: &mut dyn RpcEnvironment, |
9b2bad7a | 229 | ) -> Result<(), Error> { |
35c80d69 FG |
230 | let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; |
231 | let user_info = CachedUserInfo::new()?; | |
9b2bad7a HL |
232 | |
233 | let _lock = open_file_locked(verify::VERIFICATION_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?; | |
234 | ||
235 | // pass/compare digest | |
236 | let (mut config, expected_digest) = verify::config()?; | |
237 | ||
238 | if let Some(ref digest) = digest { | |
239 | let digest = proxmox::tools::hex_to_digest(digest)?; | |
240 | crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; | |
241 | } | |
242 | ||
243 | let mut data: verify::VerificationJobConfig = config.lookup("verification", &id)?; | |
244 | ||
35c80d69 FG |
245 | // check existing store |
246 | user_info.check_privs(&auth_id, &["datastore", &data.store], PRIV_DATASTORE_VERIFY, true)?; | |
247 | ||
248 | if let Some(delete) = delete { | |
9b2bad7a HL |
249 | for delete_prop in delete { |
250 | match delete_prop { | |
251 | DeletableProperty::IgnoreVerified => { data.ignore_verified = None; }, | |
252 | DeletableProperty::OutdatedAfter => { data.outdated_after = None; }, | |
253 | DeletableProperty::Comment => { data.comment = None; }, | |
254 | DeletableProperty::Schedule => { data.schedule = None; }, | |
255 | } | |
256 | } | |
257 | } | |
258 | ||
259 | if let Some(comment) = comment { | |
260 | let comment = comment.trim().to_string(); | |
261 | if comment.is_empty() { | |
262 | data.comment = None; | |
263 | } else { | |
264 | data.comment = Some(comment); | |
265 | } | |
266 | } | |
267 | ||
35c80d69 FG |
268 | if let Some(store) = store { |
269 | // check new store | |
270 | user_info.check_privs(&auth_id, &["datastore", &store], PRIV_DATASTORE_VERIFY, true)?; | |
271 | data.store = store; | |
272 | } | |
273 | ||
9b2bad7a HL |
274 | |
275 | if ignore_verified.is_some() { data.ignore_verified = ignore_verified; } | |
276 | if outdated_after.is_some() { data.outdated_after = outdated_after; } | |
277 | if schedule.is_some() { data.schedule = schedule; } | |
278 | ||
279 | config.set_data(&id, "verification", &data)?; | |
280 | ||
281 | verify::save_config(&config)?; | |
282 | ||
283 | Ok(()) | |
284 | } | |
285 | ||
286 | #[api( | |
287 | protected: true, | |
288 | input: { | |
289 | properties: { | |
290 | id: { | |
291 | schema: JOB_ID_SCHEMA, | |
292 | }, | |
293 | digest: { | |
294 | optional: true, | |
295 | schema: PROXMOX_CONFIG_DIGEST_SCHEMA, | |
296 | }, | |
297 | }, | |
298 | }, | |
b7ce2e57 | 299 | access: { |
35c80d69 FG |
300 | permission: &Permission::Anybody, |
301 | description: "Requires Datastore.Verify on job's datastore.", | |
b7ce2e57 | 302 | }, |
9b2bad7a HL |
303 | )] |
304 | /// Remove a verification job configuration | |
35c80d69 FG |
305 | pub fn delete_verification_job( |
306 | id: String, | |
307 | digest: Option<String>, | |
308 | rpcenv: &mut dyn RpcEnvironment, | |
309 | ) -> Result<(), Error> { | |
310 | let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; | |
311 | let user_info = CachedUserInfo::new()?; | |
9b2bad7a HL |
312 | |
313 | let _lock = open_file_locked(verify::VERIFICATION_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?; | |
314 | ||
315 | let (mut config, expected_digest) = verify::config()?; | |
316 | ||
35c80d69 FG |
317 | let job: verify::VerificationJobConfig = config.lookup("verification", &id)?; |
318 | user_info.check_privs(&auth_id, &["datastore", &job.store], PRIV_DATASTORE_VERIFY, true)?; | |
319 | ||
9b2bad7a HL |
320 | if let Some(ref digest) = digest { |
321 | let digest = proxmox::tools::hex_to_digest(digest)?; | |
322 | crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; | |
323 | } | |
324 | ||
325 | match config.sections.get(&id) { | |
326 | Some(_) => { config.sections.remove(&id); }, | |
327 | None => bail!("job '{}' does not exist.", id), | |
328 | } | |
329 | ||
330 | verify::save_config(&config)?; | |
331 | ||
1298618a | 332 | crate::server::jobstate::remove_state_file("verificationjob", &id)?; |
9b2bad7a HL |
333 | |
334 | Ok(()) | |
335 | } | |
336 | ||
337 | const ITEM_ROUTER: Router = Router::new() | |
338 | .get(&API_METHOD_READ_VERIFICATION_JOB) | |
339 | .put(&API_METHOD_UPDATE_VERIFICATION_JOB) | |
340 | .delete(&API_METHOD_DELETE_VERIFICATION_JOB); | |
341 | ||
342 | pub const ROUTER: Router = Router::new() | |
343 | .get(&API_METHOD_LIST_VERIFICATION_JOBS) | |
344 | .post(&API_METHOD_CREATE_VERIFICATION_JOB) | |
1298618a | 345 | .match_all("id", &ITEM_ROUTER); |