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