diff --git a/src/api2/admin/datastore.rs b/src/api2/admin/datastore.rs index b69d83cb..db7ba7dd 100644 --- a/src/api2/admin/datastore.rs +++ b/src/api2/admin/datastore.rs @@ -623,8 +623,9 @@ fn get_snapshots_count(store: &Arc, owner: Option<&Authid>) -> Result type: DataStoreStatus, }, access: { - permission: &Permission::Privilege( - &["datastore", "{store}"], PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_BACKUP, true), + permission: &Permission::Anybody, + description: "Requires on /datastore/{store} either DATASTORE_AUDIT or DATASTORE_BACKUP for \ + the full statistics. Counts of accessible groups are always returned, if any", }, )] /// Get datastore status. @@ -634,13 +635,27 @@ pub fn status( _info: &ApiMethod, rpcenv: &mut dyn RpcEnvironment, ) -> Result { - let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?; - let storage = crate::tools::disks::disk_usage(&datastore.base_path())?; - let (counts, gc_status) = if verbose { - let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; - let user_info = CachedUserInfo::new()?; + let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; + let user_info = CachedUserInfo::new()?; + let store_privs = user_info.lookup_privs(&auth_id, &["datastore", &store]); - let store_privs = user_info.lookup_privs(&auth_id, &["datastore", &store]); + let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read)); + + let store_stats = if store_privs & (PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_BACKUP) != 0 { + true + } else if store_privs & PRIV_DATASTORE_READ != 0 { + false // allow at least counts, user can read groups anyway.. + } else if let Ok(ref datastore) = datastore { + if !can_access_any_namespace(Arc::clone(datastore), &auth_id, &user_info) { + return Err(http_err!(FORBIDDEN, "permission check failed")); + } + false + } else { + return Err(http_err!(FORBIDDEN, "permission check failed")); // avoid leaking existance info + }; + let datastore = datastore?; // only unwrap no to avoid leaking existance info + + let (counts, gc_status) = if verbose { let filter_owner = if store_privs & PRIV_DATASTORE_AUDIT != 0 { None } else { @@ -648,19 +663,34 @@ pub fn status( }; let counts = Some(get_snapshots_count(&datastore, filter_owner)?); - let gc_status = Some(datastore.last_gc_status()); + let gc_status = if store_stats { + Some(datastore.last_gc_status()) + } else { + None + }; (counts, gc_status) } else { (None, None) }; - Ok(DataStoreStatus { - total: storage.total, - used: storage.used, - avail: storage.avail, - gc_status, - counts, + Ok(if store_stats { + let storage = crate::tools::disks::disk_usage(&datastore.base_path())?; + DataStoreStatus { + total: storage.total, + used: storage.used, + avail: storage.avail, + gc_status, + counts, + } + } else { + DataStoreStatus { + total: 0, + used: 0, + avail: 0, + gc_status, + counts, + } }) } diff --git a/src/api2/status.rs b/src/api2/status.rs index 4d8c64e6..55c811a5 100644 --- a/src/api2/status.rs +++ b/src/api2/status.rs @@ -18,6 +18,8 @@ use pbs_datastore::DataStore; use crate::rrd_cache::extract_rrd_data; use crate::tools::statistics::linear_regression; +use crate::backup::can_access_any_namespace; + #[api( returns: { description: "Lists the Status of the Datastores.", @@ -47,6 +49,11 @@ pub fn datastore_status( let user_privs = user_info.lookup_privs(&auth_id, &["datastore", store]); let allowed = (user_privs & (PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_BACKUP)) != 0; if !allowed { + if let Ok(datastore) = DataStore::lookup_datastore(&store, Some(Operation::Lookup)) { + if can_access_any_namespace(datastore, &auth_id, &user_info) { + list.push(DataStoreStatusListItem::empty(store, None)); + } + } continue; }