api: filter snapshot counts

unprivileged users should only see the counts related to their part of
the datastore.

while we're at it, switch to a list groups, filter groups, count
snapshots approach (like list_snapshots) to speedup calls to this
endpoint when many unprivileged users share a datastore.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
This commit is contained in:
Fabian Grünbichler 2020-11-12 11:30:32 +01:00 committed by Wolfgang Bumiller
parent 98afc7b152
commit fdfcb74d67
2 changed files with 47 additions and 46 deletions

View File

@ -472,51 +472,40 @@ pub fn list_snapshots (
}) })
} }
fn get_snapshots_count(store: &DataStore) -> Result<Counts, Error> { fn get_snapshots_count(store: &DataStore, filter_owner: Option<&Authid>) -> Result<Counts, Error> {
let base_path = store.base_path(); let base_path = store.base_path();
let backup_list = BackupInfo::list_backups(&base_path)?; let groups = BackupInfo::list_backup_groups(&base_path)?;
let mut groups = HashSet::new();
let mut result = Counts { groups.iter()
ct: None, .filter(|group| {
host: None, let owner = match store.get_owner(&group) {
vm: None, Ok(owner) => owner,
other: None, Err(err) => {
}; eprintln!("Failed to get owner of group '{}' - {}", group, err);
return false;
},
};
for info in backup_list { match filter_owner {
let group = info.backup_dir.group(); Some(filter) => check_backup_owner(&owner, filter).is_ok(),
None => true,
}
})
.try_fold(Counts::default(), |mut counts, group| {
let snapshot_count = group.list_backups(&base_path)?.len() as u64;
let id = group.backup_id(); let type_count = match group.backup_type() {
let backup_type = group.backup_type(); "ct" => counts.ct.get_or_insert(Default::default()),
"vm" => counts.vm.get_or_insert(Default::default()),
"host" => counts.host.get_or_insert(Default::default()),
_ => counts.other.get_or_insert(Default::default()),
};
let mut new_id = false; type_count.groups += 1;
type_count.snapshots += snapshot_count;
if groups.insert(format!("{}-{}", &backup_type, &id)) { Ok(counts)
new_id = true; })
}
let mut counts = match backup_type {
"ct" => result.ct.take().unwrap_or(Default::default()),
"host" => result.host.take().unwrap_or(Default::default()),
"vm" => result.vm.take().unwrap_or(Default::default()),
_ => result.other.take().unwrap_or(Default::default()),
};
counts.snapshots += 1;
if new_id {
counts.groups +=1;
}
match backup_type {
"ct" => result.ct = Some(counts),
"host" => result.host = Some(counts),
"vm" => result.vm = Some(counts),
_ => result.other = Some(counts),
}
}
Ok(result)
} }
#[api( #[api(
@ -546,15 +535,27 @@ pub fn status(
store: String, store: String,
verbose: bool, verbose: bool,
_info: &ApiMethod, _info: &ApiMethod,
_rpcenv: &mut dyn RpcEnvironment, rpcenv: &mut dyn RpcEnvironment,
) -> Result<DataStoreStatus, Error> { ) -> Result<DataStoreStatus, Error> {
let datastore = DataStore::lookup_datastore(&store)?; let datastore = DataStore::lookup_datastore(&store)?;
let storage = crate::tools::disks::disk_usage(&datastore.base_path())?; let storage = crate::tools::disks::disk_usage(&datastore.base_path())?;
let (counts, gc_status) = match verbose { let (counts, gc_status) = if verbose {
true => { let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
(Some(get_snapshots_count(&datastore)?), Some(datastore.last_gc_status())) let user_info = CachedUserInfo::new()?;
},
false => (None, None), let store_privs = user_info.lookup_privs(&auth_id, &["datastore", &store]);
let filter_owner = if store_privs & PRIV_DATASTORE_AUDIT != 0 {
None
} else {
Some(&auth_id)
};
let counts = Some(get_snapshots_count(&datastore, filter_owner)?);
let gc_status = Some(datastore.last_gc_status());
(counts, gc_status)
} else {
(None, None)
}; };
Ok(DataStoreStatus { Ok(DataStoreStatus {

View File

@ -692,7 +692,7 @@ pub struct TypeCounts {
}, },
}, },
)] )]
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize, Default)]
/// Counts of groups/snapshots per BackupType. /// Counts of groups/snapshots per BackupType.
pub struct Counts { pub struct Counts {
/// The counts for CT backups /// The counts for CT backups