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