api: status: include empty entry for stores with ns-only privs

I.e., for those that only got permissions on a sub namespace and
those that onlöy got BACKUP_READ, as both they could just list and
count themselves too after all, so not exactly secret info.

The UI needs some adaptions to cope with gc-stats and usage being
optional, will be done in a next commit.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
Thomas Lamprecht 2022-05-26 13:36:12 +02:00
parent de77a20d3d
commit 84de101272
2 changed files with 52 additions and 15 deletions

View File

@ -623,8 +623,9 @@ fn get_snapshots_count(store: &Arc<DataStore>, 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<DataStoreStatus, Error> {
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 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 {
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,
}
})
}

View File

@ -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;
}