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