]> git.proxmox.com Git - proxmox-backup.git/blame - src/api2/config/verify.rs
api: verify: support namespaces
[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
FG
47 .filter(|job: &VerificationJobConfig| {
48 let privs = user_info.lookup_privs(&auth_id, &["datastore", &job.store]);
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,
84 &["datastore", &config.store],
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,
135 &["datastore", &verification_job.store],
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,
9b2bad7a
HL
160}
161
162#[api(
163 protected: true,
164 input: {
165 properties: {
166 id: {
167 schema: JOB_ID_SCHEMA,
168 },
ffa403b5
DM
169 update: {
170 type: VerificationJobConfigUpdater,
171 flatten: true,
9b2bad7a
HL
172 },
173 delete: {
174 description: "List of properties to delete.",
175 type: Array,
176 optional: true,
177 items: {
178 type: DeletableProperty,
179 }
180 },
181 digest: {
182 optional: true,
183 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
184 },
185 },
186 },
b7ce2e57 187 access: {
35c80d69
FG
188 permission: &Permission::Anybody,
189 description: "Requires Datastore.Verify on job's datastore.",
b7ce2e57 190 },
9b2bad7a
HL
191)]
192/// Update verification job config.
367c0ff7 193#[allow(clippy::too_many_arguments)]
9b2bad7a
HL
194pub fn update_verification_job(
195 id: String,
ffa403b5 196 update: VerificationJobConfigUpdater,
9b2bad7a
HL
197 delete: Option<Vec<DeletableProperty>>,
198 digest: Option<String>,
35c80d69 199 rpcenv: &mut dyn RpcEnvironment,
9b2bad7a 200) -> Result<(), Error> {
35c80d69
FG
201 let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
202 let user_info = CachedUserInfo::new()?;
9b2bad7a 203
802189f7 204 let _lock = verify::lock_config()?;
9b2bad7a
HL
205
206 // pass/compare digest
207 let (mut config, expected_digest) = verify::config()?;
208
209 if let Some(ref digest) = digest {
25877d05 210 let digest = <[u8; 32]>::from_hex(digest)?;
9b2bad7a
HL
211 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
212 }
213
e3619d41 214 let mut data: VerificationJobConfig = config.lookup("verification", &id)?;
9b2bad7a 215
35c80d69 216 // check existing store
dc7a5b34
TL
217 user_info.check_privs(
218 &auth_id,
219 &["datastore", &data.store],
220 PRIV_DATASTORE_VERIFY,
221 true,
222 )?;
35c80d69
FG
223
224 if let Some(delete) = delete {
9b2bad7a
HL
225 for delete_prop in delete {
226 match delete_prop {
dc7a5b34
TL
227 DeletableProperty::IgnoreVerified => {
228 data.ignore_verified = None;
229 }
230 DeletableProperty::OutdatedAfter => {
231 data.outdated_after = None;
232 }
233 DeletableProperty::Comment => {
234 data.comment = None;
235 }
236 DeletableProperty::Schedule => {
237 data.schedule = None;
238 }
59229bd7
TL
239 DeletableProperty::Ns => {
240 data.ns = None;
241 }
9b2bad7a
HL
242 }
243 }
244 }
245
ffa403b5 246 if let Some(comment) = update.comment {
9b2bad7a
HL
247 let comment = comment.trim().to_string();
248 if comment.is_empty() {
249 data.comment = None;
250 } else {
251 data.comment = Some(comment);
252 }
253 }
254
ffa403b5 255 if let Some(store) = update.store {
35c80d69 256 // check new store
dc7a5b34
TL
257 user_info.check_privs(
258 &auth_id,
259 &["datastore", &store],
260 PRIV_DATASTORE_VERIFY,
261 true,
262 )?;
35c80d69
FG
263 data.store = store;
264 }
265
dc7a5b34
TL
266 if update.ignore_verified.is_some() {
267 data.ignore_verified = update.ignore_verified;
268 }
269 if update.outdated_after.is_some() {
270 data.outdated_after = update.outdated_after;
271 }
ffa403b5 272 let schedule_changed = data.schedule != update.schedule;
dc7a5b34
TL
273 if update.schedule.is_some() {
274 data.schedule = update.schedule;
275 }
59229bd7
TL
276 if let Some(ns) = update.ns {
277 if !ns.is_root() {
278 data.ns = Some(ns);
279 }
280 }
9b2bad7a
HL
281
282 config.set_data(&id, "verification", &data)?;
283
284 verify::save_config(&config)?;
285
951fe0cb 286 if schedule_changed {
37a634f5 287 crate::server::jobstate::update_job_last_run_time("verificationjob", &id)?;
951fe0cb
DC
288 }
289
9b2bad7a
HL
290 Ok(())
291}
292
293#[api(
294 protected: true,
295 input: {
296 properties: {
297 id: {
298 schema: JOB_ID_SCHEMA,
299 },
300 digest: {
301 optional: true,
302 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
303 },
304 },
305 },
b7ce2e57 306 access: {
35c80d69
FG
307 permission: &Permission::Anybody,
308 description: "Requires Datastore.Verify on job's datastore.",
b7ce2e57 309 },
9b2bad7a
HL
310)]
311/// Remove a verification job configuration
35c80d69
FG
312pub fn delete_verification_job(
313 id: String,
314 digest: Option<String>,
315 rpcenv: &mut dyn RpcEnvironment,
316) -> Result<(), Error> {
317 let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
318 let user_info = CachedUserInfo::new()?;
9b2bad7a 319
802189f7 320 let _lock = verify::lock_config()?;
9b2bad7a
HL
321
322 let (mut config, expected_digest) = verify::config()?;
323
e3619d41 324 let job: VerificationJobConfig = config.lookup("verification", &id)?;
dc7a5b34
TL
325 user_info.check_privs(
326 &auth_id,
327 &["datastore", &job.store],
328 PRIV_DATASTORE_VERIFY,
329 true,
330 )?;
35c80d69 331
9b2bad7a 332 if let Some(ref digest) = digest {
25877d05 333 let digest = <[u8; 32]>::from_hex(digest)?;
9b2bad7a
HL
334 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
335 }
336
337 match config.sections.get(&id) {
dc7a5b34
TL
338 Some(_) => {
339 config.sections.remove(&id);
340 }
dcd1518e 341 None => http_bail!(NOT_FOUND, "job '{}' does not exist.", id),
9b2bad7a
HL
342 }
343
344 verify::save_config(&config)?;
345
1298618a 346 crate::server::jobstate::remove_state_file("verificationjob", &id)?;
9b2bad7a
HL
347
348 Ok(())
349}
350
351const ITEM_ROUTER: Router = Router::new()
352 .get(&API_METHOD_READ_VERIFICATION_JOB)
353 .put(&API_METHOD_UPDATE_VERIFICATION_JOB)
354 .delete(&API_METHOD_DELETE_VERIFICATION_JOB);
355
356pub const ROUTER: Router = Router::new()
357 .get(&API_METHOD_LIST_VERIFICATION_JOBS)
358 .post(&API_METHOD_CREATE_VERIFICATION_JOB)
1298618a 359 .match_all("id", &ITEM_ROUTER);