]> git.proxmox.com Git - proxmox-backup.git/blob - src/api2/config/verify.rs
dee4c66983c28473df4826316bb6be28acb2d183
[proxmox-backup.git] / src / api2 / config / verify.rs
1 use anyhow::{bail, Error};
2 use serde_json::Value;
3 use ::serde::{Deserialize, Serialize};
4
5 use proxmox::api::{api, Permission, Router, RpcEnvironment};
6 use proxmox::tools::fs::open_file_locked;
7
8 use crate::api2::types::*;
9
10 use crate::config::acl::{
11 PRIV_DATASTORE_AUDIT,
12 PRIV_DATASTORE_VERIFY,
13 };
14
15 use crate::config::cached_user_info::CachedUserInfo;
16
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 },
28 access: {
29 permission: &Permission::Anybody,
30 description: "Requires Datastore.Audit or Datastore.Verify on datastore.",
31 },
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> {
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;
42
43 let (config, digest) = verify::config()?;
44
45 let list = config.convert_to_typed_array("verification")?;
46
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
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 }
87 },
88 access: {
89 permission: &Permission::Anybody,
90 description: "Requires Datastore.Verify on job's datastore.",
91 },
92 )]
93 /// Create a new verification job.
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()?;
100
101 let verification_job: verify::VerificationJobConfig = serde_json::from_value(param)?;
102
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
107 let (mut config, _digest) = verify::config()?;
108
109 if config.sections.get(&verification_job.id).is_some() {
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
117 crate::server::jobstate::create_state_file("verificationjob", &verification_job.id)?;
118
119 Ok(())
120 }
121
122 #[api(
123 input: {
124 properties: {
125 id: {
126 schema: JOB_ID_SCHEMA,
127 },
128 },
129 },
130 returns: { type: verify::VerificationJobConfig },
131 access: {
132 permission: &Permission::Anybody,
133 description: "Requires Datastore.Audit or Datastore.Verify on job's datastore.",
134 },
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> {
141 let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
142 let user_info = CachedUserInfo::new()?;
143
144 let (config, digest) = verify::config()?;
145
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
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 },
212 access: {
213 permission: &Permission::Anybody,
214 description: "Requires Datastore.Verify on job's datastore.",
215 },
216 )]
217 /// Update verification job config.
218 #[allow(clippy::too_many_arguments)]
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>,
228 rpcenv: &mut dyn RpcEnvironment,
229 ) -> Result<(), Error> {
230 let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
231 let user_info = CachedUserInfo::new()?;
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
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 {
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
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
274
275 if ignore_verified.is_some() { data.ignore_verified = ignore_verified; }
276 if outdated_after.is_some() { data.outdated_after = outdated_after; }
277 let schedule_changed = data.schedule != schedule;
278 if schedule.is_some() { data.schedule = schedule; }
279
280 config.set_data(&id, "verification", &data)?;
281
282 verify::save_config(&config)?;
283
284 if schedule_changed {
285 crate::server::jobstate::try_update_state_file("verificationjob", &id)?;
286 }
287
288 Ok(())
289 }
290
291 #[api(
292 protected: true,
293 input: {
294 properties: {
295 id: {
296 schema: JOB_ID_SCHEMA,
297 },
298 digest: {
299 optional: true,
300 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
301 },
302 },
303 },
304 access: {
305 permission: &Permission::Anybody,
306 description: "Requires Datastore.Verify on job's datastore.",
307 },
308 )]
309 /// Remove a verification job configuration
310 pub fn delete_verification_job(
311 id: String,
312 digest: Option<String>,
313 rpcenv: &mut dyn RpcEnvironment,
314 ) -> Result<(), Error> {
315 let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
316 let user_info = CachedUserInfo::new()?;
317
318 let _lock = open_file_locked(verify::VERIFICATION_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
319
320 let (mut config, expected_digest) = verify::config()?;
321
322 let job: verify::VerificationJobConfig = config.lookup("verification", &id)?;
323 user_info.check_privs(&auth_id, &["datastore", &job.store], PRIV_DATASTORE_VERIFY, true)?;
324
325 if let Some(ref digest) = digest {
326 let digest = proxmox::tools::hex_to_digest(digest)?;
327 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
328 }
329
330 match config.sections.get(&id) {
331 Some(_) => { config.sections.remove(&id); },
332 None => bail!("job '{}' does not exist.", id),
333 }
334
335 verify::save_config(&config)?;
336
337 crate::server::jobstate::remove_state_file("verificationjob", &id)?;
338
339 Ok(())
340 }
341
342 const ITEM_ROUTER: Router = Router::new()
343 .get(&API_METHOD_READ_VERIFICATION_JOB)
344 .put(&API_METHOD_UPDATE_VERIFICATION_JOB)
345 .delete(&API_METHOD_DELETE_VERIFICATION_JOB);
346
347 pub const ROUTER: Router = Router::new()
348 .get(&API_METHOD_LIST_VERIFICATION_JOBS)
349 .post(&API_METHOD_CREATE_VERIFICATION_JOB)
350 .match_all("id", &ITEM_ROUTER);