diff --git a/src/backup/hierarchy.rs b/src/backup/hierarchy.rs new file mode 100644 index 00000000..953ea4ce --- /dev/null +++ b/src/backup/hierarchy.rs @@ -0,0 +1,100 @@ +use std::sync::Arc; + +use anyhow::Error; + +use pbs_api_types::{ + Authid, BackupNamespace, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_MODIFY, +}; +use pbs_config::CachedUserInfo; +use pbs_datastore::{backup_info::BackupGroup, DataStore, ListGroups, ListNamespacesRecursive}; + +/// A priviledge aware iterator for all backup groups in all Namespaces below an anchor namespace, +/// most often that will be the `BackupNamespace::root()` one. +/// +/// Is basically just a filter-iter for pbs_datastore::ListNamespacesRecursive including access and +/// optional owner checks. +pub struct ListAccessibleBackupGroups { + store: Arc, + auth_id: Option, + user_info: Arc, + state: Option, + ns_iter: ListNamespacesRecursive, +} + +impl ListAccessibleBackupGroups { + // TODO: builder pattern + + pub fn new( + store: Arc, + ns: BackupNamespace, + max_depth: usize, + auth_id: Option, + ) -> Result { + let ns_iter = ListNamespacesRecursive::new_max_depth(Arc::clone(&store), ns, max_depth)?; + Ok(ListAccessibleBackupGroups { + auth_id, + ns_iter, + state: None, + store: store, + user_info: CachedUserInfo::new()?, + }) + } +} + +impl Iterator for ListAccessibleBackupGroups { + type Item = Result; + + fn next(&mut self) -> Option { + const PRIVS_OK: u64 = PRIV_DATASTORE_MODIFY | PRIV_DATASTORE_BACKUP | PRIV_DATASTORE_AUDIT; + loop { + if let Some(ref mut state) = self.state { + match state.next() { + Some(Ok(group)) => { + if let Some(auth_id) = &self.auth_id { + match self.store.owns_backup( + &group.backup_ns(), + group.group(), + &auth_id, + ) { + Ok(is_owner) if is_owner => return Some(Ok(group)), + Ok(_) => continue, + Err(err) => return Some(Err(err)), + } + } else { + return Some(Ok(group)); + } + } + Some(Err(err)) => return Some(Err(err)), + None => { + self.state = None; // level exhausted, need to check next NS + } + } + } else { + match self.ns_iter.next() { + Some(Ok(ns)) => { + if let Some(auth_id) = &self.auth_id { + let info = &self.user_info; + let privs = if ns.is_root() { + info.lookup_privs(&auth_id, &["datastore", self.store.name()]) + } else { + info.lookup_privs( + &auth_id, + &["datastore", self.store.name(), &ns.to_string()], + ) + }; + if privs & PRIVS_OK == 0 { + continue; + } + } + self.state = match ListGroups::new(Arc::clone(&self.store), ns) { + Ok(iter) => Some(iter), + Err(err) => return Some(Err(err)), + }; + } + Some(Err(err)) => return Some(Err(err)), + None => return None, // exhausted with all NS -> done + } + } + } + } +} diff --git a/src/backup/mod.rs b/src/backup/mod.rs index ca060c9f..8c84b8ce 100644 --- a/src/backup/mod.rs +++ b/src/backup/mod.rs @@ -5,3 +5,6 @@ pub const CATALOG_NAME: &str = "catalog.pcat1.didx"; mod verify; pub use verify::*; + +mod hierarchy; +pub use hierarchy::*;