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