diff --git a/src/api2/admin/datastore.rs b/src/api2/admin/datastore.rs index d2341b4e..8ab222f7 100644 --- a/src/api2/admin/datastore.rs +++ b/src/api2/admin/datastore.rs @@ -32,14 +32,14 @@ use pxar::accessor::aio::Accessor; use pxar::EntryKind; use pbs_api_types::{ - print_ns_and_snapshot, privs_to_priv_names, Authid, BackupContent, BackupNamespace, BackupType, - Counts, CryptMode, DataStoreListItem, DataStoreStatus, DatastoreWithNamespace, - GarbageCollectionStatus, GroupListItem, Operation, PruneOptions, RRDMode, RRDTimeFrame, - SnapshotListItem, SnapshotVerifyState, BACKUP_ARCHIVE_NAME_SCHEMA, BACKUP_ID_SCHEMA, - BACKUP_NAMESPACE_SCHEMA, BACKUP_TIME_SCHEMA, BACKUP_TYPE_SCHEMA, DATASTORE_SCHEMA, - IGNORE_VERIFIED_BACKUPS_SCHEMA, MAX_NAMESPACE_DEPTH, NS_MAX_DEPTH_SCHEMA, PRIV_DATASTORE_AUDIT, - PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_PRUNE, PRIV_DATASTORE_READ, - PRIV_DATASTORE_VERIFY, UPID_SCHEMA, VERIFICATION_OUTDATED_AFTER_SCHEMA, + print_ns_and_snapshot, Authid, BackupContent, BackupNamespace, BackupType, Counts, CryptMode, + DataStoreListItem, DataStoreStatus, DatastoreWithNamespace, GarbageCollectionStatus, + GroupListItem, Operation, PruneOptions, RRDMode, RRDTimeFrame, SnapshotListItem, + SnapshotVerifyState, BACKUP_ARCHIVE_NAME_SCHEMA, BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA, + BACKUP_TIME_SCHEMA, BACKUP_TYPE_SCHEMA, DATASTORE_SCHEMA, IGNORE_VERIFIED_BACKUPS_SCHEMA, + MAX_NAMESPACE_DEPTH, NS_MAX_DEPTH_SCHEMA, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, + PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_PRUNE, PRIV_DATASTORE_READ, PRIV_DATASTORE_VERIFY, + UPID_SCHEMA, VERIFICATION_OUTDATED_AFTER_SCHEMA, }; use pbs_client::pxar::{create_tar, create_zip}; use pbs_config::CachedUserInfo; @@ -63,7 +63,7 @@ use proxmox_rest_server::{formatter, WorkerTask}; use crate::api2::backup::optional_ns_param; use crate::api2::node::rrd::create_value_from_rrd; use crate::backup::{ - verify_all_backups, verify_backup_dir, verify_backup_group, verify_filter, + check_ns_privs_full, verify_all_backups, verify_backup_dir, verify_backup_group, verify_filter, ListAccessibleBackupGroups, }; @@ -81,63 +81,29 @@ fn get_group_note_path( note_path } -// TODO: move somewhere we can reuse it from (namespace has its own copy atm.) -fn get_ns_privs(store_with_ns: &DatastoreWithNamespace, auth_id: &Authid) -> Result { - let user_info = CachedUserInfo::new()?; - - Ok(user_info.lookup_privs(auth_id, &store_with_ns.acl_path())) -} - -// asserts that either either `full_access_privs` or `partial_access_privs` are fulfilled, -// returning value indicates whether further checks like group ownerships are required -fn check_ns_privs( - store: &str, - ns: &BackupNamespace, - auth_id: &Authid, - full_access_privs: u64, - partial_access_privs: u64, -) -> Result { - let store_with_ns = DatastoreWithNamespace { - store: store.to_string(), - ns: ns.clone(), - }; - let privs = get_ns_privs(&store_with_ns, auth_id)?; - - if full_access_privs != 0 && (privs & full_access_privs) != 0 { - return Ok(false); - } - if partial_access_privs != 0 && (privs & partial_access_privs) != 0 { - return Ok(true); - } - - let priv_names = privs_to_priv_names(full_access_privs | partial_access_privs).join("|"); - let path = format!("/{}", store_with_ns.acl_path().join("/")); - - proxmox_router::http_bail!( - FORBIDDEN, - "permission check failed - missing {priv_names} on {path}" - ); -} - // helper to unify common sequence of checks: // 1. check privs on NS (full or limited access) // 2. load datastore // 3. if needed (only limited access), check owner of group fn check_privs_and_load_store( - store: &str, - ns: &BackupNamespace, + store_with_ns: &DatastoreWithNamespace, auth_id: &Authid, full_access_privs: u64, partial_access_privs: u64, operation: Option, backup_group: &pbs_api_types::BackupGroup, ) -> Result, Error> { - let limited = check_ns_privs(store, ns, auth_id, full_access_privs, partial_access_privs)?; + let limited = check_ns_privs_full( + store_with_ns, + auth_id, + full_access_privs, + partial_access_privs, + )?; - let datastore = DataStore::lookup_datastore(&store, operation)?; + let datastore = DataStore::lookup_datastore(&store_with_ns.store, operation)?; if limited { - let owner = datastore.get_owner(&ns, backup_group)?; + let owner = datastore.get_owner(&store_with_ns.ns, backup_group)?; check_backup_owner(&owner, &auth_id)?; } @@ -222,19 +188,19 @@ pub fn list_groups( let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let ns = ns.unwrap_or_default(); - let list_all = !check_ns_privs( - &store, - &ns, + let store_with_ns = DatastoreWithNamespace { + store: store.clone(), + ns: ns.clone(), + }; + + let list_all = !check_ns_privs_full( + &store_with_ns, &auth_id, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, )?; let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?; - let store_with_ns = DatastoreWithNamespace { - store: store.to_owned(), - ns: ns.clone(), - }; datastore .iter_backup_groups(ns.clone())? // FIXME: Namespaces and recursion parameters! @@ -327,8 +293,10 @@ pub fn delete_group( let ns = ns.unwrap_or_default(); let datastore = check_privs_and_load_store( - &store, - &ns, + &DatastoreWithNamespace { + store: store.clone(), + ns: ns.clone(), + }, &auth_id, PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_PRUNE, @@ -375,10 +343,13 @@ pub fn list_snapshot_files( let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let ns = ns.unwrap_or_default(); + let store_with_ns = DatastoreWithNamespace { + store: store.clone(), + ns: ns.clone(), + }; let datastore = check_privs_and_load_store( - &store, - &ns, + &store_with_ns, &auth_id, PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_READ, PRIV_DATASTORE_BACKUP, @@ -426,9 +397,13 @@ pub fn delete_snapshot( let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let ns = ns.unwrap_or_default(); + let store_with_ns = DatastoreWithNamespace { + store: store.clone(), + ns: ns.clone(), + }; + let datastore = check_privs_and_load_store( - &store, - &ns, + &store_with_ns, &auth_id, PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_PRUNE, @@ -482,20 +457,19 @@ pub fn list_snapshots( let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let ns = ns.unwrap_or_default(); + let store_with_ns = DatastoreWithNamespace { + store: store.clone(), + ns: ns.clone(), + }; - let list_all = !check_ns_privs( - &store, - &ns, + let list_all = !check_ns_privs_full( + &store_with_ns, &auth_id, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, )?; let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?; - let store_with_ns = DatastoreWithNamespace { - store: store.to_owned(), - ns: ns.clone(), - }; // FIXME: filter also owner before collecting, for doing that nicely the owner should move into // backup group and provide an error free (Err -> None) accessor @@ -774,9 +748,13 @@ pub fn verify( ) -> Result { let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let ns = ns.unwrap_or_default(); - let owner_check_required = check_ns_privs( - &store, - &ns, + let store_with_ns = DatastoreWithNamespace { + store: store.clone(), + ns: ns.clone(), + }; + + let owner_check_required = check_ns_privs_full( + &store_with_ns, &auth_id, PRIV_DATASTORE_VERIFY, PRIV_DATASTORE_BACKUP, @@ -947,19 +925,19 @@ pub fn prune( ) -> Result { let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let ns = ns.unwrap_or_default(); + let store_with_ns = DatastoreWithNamespace { + store: store.clone(), + ns: ns.clone(), + }; + let datastore = check_privs_and_load_store( - &store, - &ns, + &store_with_ns, &auth_id, PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_PRUNE, Some(Operation::Write), &group, )?; - let store_with_ns = DatastoreWithNamespace { - store: store.to_owned(), - ns: ns.clone(), - }; let worker_id = format!("{}:{}:{}", store, ns, group); let group = datastore.backup_group(ns, group); @@ -1307,8 +1285,7 @@ pub fn download_file( }; let backup_dir: pbs_api_types::BackupDir = Deserialize::deserialize(¶m)?; let datastore = check_privs_and_load_store( - &store, - &backup_ns, + &store_with_ns, &auth_id, PRIV_DATASTORE_READ, PRIV_DATASTORE_BACKUP, @@ -1392,8 +1369,7 @@ pub fn download_file_decoded( }; let backup_dir_api: pbs_api_types::BackupDir = Deserialize::deserialize(¶m)?; let datastore = check_privs_and_load_store( - &store, - &backup_ns, + &store_with_ns, &auth_id, PRIV_DATASTORE_READ, PRIV_DATASTORE_BACKUP, @@ -1523,8 +1499,7 @@ pub fn upload_backup_log( let backup_dir_api: pbs_api_types::BackupDir = Deserialize::deserialize(¶m)?; let datastore = check_privs_and_load_store( - &store, - &backup_ns, + &store_with_ns, &auth_id, 0, PRIV_DATASTORE_BACKUP, @@ -1597,9 +1572,13 @@ pub fn catalog( ) -> Result, Error> { let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let ns = ns.unwrap_or_default(); + let store_with_ns = DatastoreWithNamespace { + store: store.clone(), + ns: ns.clone(), + }; + let datastore = check_privs_and_load_store( - &store, - &ns, + &store_with_ns, &auth_id, PRIV_DATASTORE_READ, PRIV_DATASTORE_BACKUP, @@ -1676,10 +1655,13 @@ pub fn pxar_file_download( let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let store = required_string_param(¶m, "store")?; let ns = optional_ns_param(¶m)?; + let store_with_ns = DatastoreWithNamespace { + store: store.to_owned(), + ns: ns.clone(), + }; let backup_dir: pbs_api_types::BackupDir = Deserialize::deserialize(¶m)?; let datastore = check_privs_and_load_store( - &store, - &ns, + &store_with_ns, &auth_id, PRIV_DATASTORE_READ, PRIV_DATASTORE_BACKUP, @@ -1883,9 +1865,13 @@ pub fn get_group_notes( ) -> Result { let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let ns = ns.unwrap_or_default(); + let store_with_ns = DatastoreWithNamespace { + store: store.clone(), + ns: ns.clone(), + }; + let datastore = check_privs_and_load_store( - &store, - &ns, + &store_with_ns, &auth_id, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, @@ -1930,9 +1916,12 @@ pub fn set_group_notes( ) -> Result<(), Error> { let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let ns = ns.unwrap_or_default(); + let store_with_ns = DatastoreWithNamespace { + store: store.clone(), + ns: ns.clone(), + }; let datastore = check_privs_and_load_store( - &store, - &ns, + &store_with_ns, &auth_id, PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_BACKUP, @@ -1975,9 +1964,13 @@ pub fn get_notes( ) -> Result { let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let ns = ns.unwrap_or_default(); + let store_with_ns = DatastoreWithNamespace { + store: store.clone(), + ns: ns.clone(), + }; + let datastore = check_privs_and_load_store( - &store, - &ns, + &store_with_ns, &auth_id, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, @@ -2027,9 +2020,13 @@ pub fn set_notes( ) -> Result<(), Error> { let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let ns = ns.unwrap_or_default(); + let store_with_ns = DatastoreWithNamespace { + store: store.clone(), + ns: ns.clone(), + }; + let datastore = check_privs_and_load_store( - &store, - &ns, + &store_with_ns, &auth_id, PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_BACKUP, @@ -2077,9 +2074,12 @@ pub fn get_protection( ) -> Result { let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let ns = ns.unwrap_or_default(); + let store_with_ns = DatastoreWithNamespace { + store: store.clone(), + ns: ns.clone(), + }; let datastore = check_privs_and_load_store( - &store, - &ns, + &store_with_ns, &auth_id, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, @@ -2125,9 +2125,12 @@ pub fn set_protection( ) -> Result<(), Error> { let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let ns = ns.unwrap_or_default(); + let store_with_ns = DatastoreWithNamespace { + store: store.clone(), + ns: ns.clone(), + }; let datastore = check_privs_and_load_store( - &store, - &ns, + &store_with_ns, &auth_id, PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_BACKUP, @@ -2173,9 +2176,12 @@ pub fn set_backup_owner( ) -> Result<(), Error> { let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let ns = ns.unwrap_or_default(); - let owner_check_required = check_ns_privs( - &store, - &ns, + let store_with_ns = DatastoreWithNamespace { + store: store.clone(), + ns: ns.clone(), + }; + let owner_check_required = check_ns_privs_full( + &store_with_ns, &auth_id, PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_BACKUP, diff --git a/src/api2/admin/namespace.rs b/src/api2/admin/namespace.rs index 8541f248..f7f68eff 100644 --- a/src/api2/admin/namespace.rs +++ b/src/api2/admin/namespace.rs @@ -2,7 +2,7 @@ use anyhow::{bail, Error}; use serde_json::Value; use pbs_config::CachedUserInfo; -use proxmox_router::{http_err, ApiMethod, Permission, Router, RpcEnvironment}; +use proxmox_router::{ApiMethod, Permission, Router, RpcEnvironment}; use proxmox_schema::*; use pbs_api_types::{ @@ -13,18 +13,7 @@ use pbs_api_types::{ use pbs_datastore::DataStore; -// TODO: move somewhere we can reuse it from (datastore has its own copy atm.) -fn check_ns_privs( - store_with_ns: &DatastoreWithNamespace, - auth_id: &Authid, - privs: u64, -) -> Result<(), Error> { - let user_info = CachedUserInfo::new()?; - - user_info - .check_privs(auth_id, &store_with_ns.acl_path(), privs, true) - .map_err(|err| http_err!(FORBIDDEN, "{err}")) -} +use crate::backup::{check_ns_modification_privs, check_ns_privs}; #[api( input: { @@ -62,12 +51,15 @@ pub fn create_namespace( let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let parent = parent.unwrap_or_default(); - let store_with_parent = DatastoreWithNamespace { + let mut ns = parent.clone(); + ns.push(name.clone())?; + + let store_with_ns = DatastoreWithNamespace { store: store.clone(), - ns: parent.clone(), + ns, }; - check_ns_privs(&store_with_parent, &auth_id, PRIV_DATASTORE_MODIFY)?; + check_ns_modification_privs(&store_with_ns, &auth_id)?; let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?; @@ -165,17 +157,12 @@ pub fn delete_namespace( _info: &ApiMethod, rpcenv: &mut dyn RpcEnvironment, ) -> Result { - // we could allow it as easy purge-whole datastore, but lets be more restrictive for now - if ns.is_root() { - bail!("cannot delete root namespace!"); - }; let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; - let parent = ns.parent(); // must have MODIFY permission on parent to allow deletion - let store_with_parent = DatastoreWithNamespace { + let store_with_ns = DatastoreWithNamespace { store: store.clone(), - ns: parent.clone(), + ns: ns.clone(), }; - check_ns_privs(&store_with_parent, &auth_id, PRIV_DATASTORE_MODIFY)?; + check_ns_modification_privs(&store_with_ns, &auth_id)?; let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?; diff --git a/src/api2/tape/restore.rs b/src/api2/tape/restore.rs index d84e1357..926ab2e5 100644 --- a/src/api2/tape/restore.rs +++ b/src/api2/tape/restore.rs @@ -34,6 +34,7 @@ use pbs_tape::{ }; use proxmox_rest_server::WorkerTask; +use crate::backup::check_ns_modification_privs; use crate::{ server::lookup_user_email, tape::{ @@ -237,25 +238,16 @@ fn check_and_create_namespaces( // try create recursively if it does not exist if !store.namespace_exists(ns) { - let mut tmp_ns: BackupNamespace = Default::default(); + store_with_ns.ns = Default::default(); for comp in ns.components() { - tmp_ns.push(comp.to_string())?; - if !store.namespace_exists(&tmp_ns) { - // check parent modification privs - user_info - .check_privs( - auth_id, - &store_with_ns.acl_path(), - PRIV_DATASTORE_MODIFY, - false, - ) - .map_err(|_err| format_err!("no permission to create namespace '{tmp_ns}'"))?; + store_with_ns.ns.push(comp.to_string())?; + if !store.namespace_exists(&store_with_ns.ns) { + check_ns_modification_privs(&store_with_ns, auth_id).map_err(|_err| { + format_err!("no permission to create namespace '{}'", store_with_ns.ns) + })?; - store.create_namespace(&tmp_ns.parent(), comp.to_string())?; - - // update parent for next component - store_with_ns.ns = tmp_ns.clone(); + store.create_namespace(&store_with_ns.ns.parent(), comp.to_string())?; } } } diff --git a/src/backup/hierarchy.rs b/src/backup/hierarchy.rs index b5f9b937..344c6401 100644 --- a/src/backup/hierarchy.rs +++ b/src/backup/hierarchy.rs @@ -1,14 +1,72 @@ use std::sync::Arc; -use anyhow::Error; +use anyhow::{bail, Error}; use pbs_api_types::{ - Authid, BackupNamespace, DatastoreWithNamespace, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, - PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_READ, + privs_to_priv_names, Authid, BackupNamespace, DatastoreWithNamespace, PRIV_DATASTORE_AUDIT, + PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_READ, }; use pbs_config::CachedUserInfo; use pbs_datastore::{backup_info::BackupGroup, DataStore, ListGroups, ListNamespacesRecursive}; +/// Asserts that `privs` are fulfilled on datastore + (optional) namespace. +pub fn check_ns_privs( + store_with_ns: &DatastoreWithNamespace, + auth_id: &Authid, + privs: u64, +) -> Result<(), Error> { + check_ns_privs_full(store_with_ns, auth_id, privs, 0).map(|_| ()) +} + +/// Asserts that `privs` for creating/destroying namespace in datastore are fulfilled. +pub fn check_ns_modification_privs( + store_with_ns: &DatastoreWithNamespace, + auth_id: &Authid, +) -> Result<(), Error> { + // we could allow it as easy purge-whole datastore, but lets be more restrictive for now + if store_with_ns.ns.is_root() { + // TODO + bail!("Cannot create/delete root namespace!"); + } + + let parent = DatastoreWithNamespace { + store: store_with_ns.store.clone(), + ns: store_with_ns.ns.parent(), + }; + + check_ns_privs(&parent, auth_id, PRIV_DATASTORE_MODIFY) +} + +/// Asserts that either either `full_access_privs` or `partial_access_privs` are fulfilled on +/// datastore + (optional) namespace. +/// +/// Return value indicates whether further checks like group ownerships are required because +/// `full_access_privs` are missing. +pub fn check_ns_privs_full( + store_with_ns: &DatastoreWithNamespace, + auth_id: &Authid, + full_access_privs: u64, + partial_access_privs: u64, +) -> Result { + let user_info = CachedUserInfo::new()?; + let privs = user_info.lookup_privs(auth_id, &store_with_ns.acl_path()); + + if full_access_privs != 0 && (privs & full_access_privs) != 0 { + return Ok(false); + } + if partial_access_privs != 0 && (privs & partial_access_privs) != 0 { + return Ok(true); + } + + let priv_names = privs_to_priv_names(full_access_privs | partial_access_privs).join("|"); + let path = format!("/{}", store_with_ns.acl_path().join("/")); + + proxmox_router::http_bail!( + FORBIDDEN, + "permission check failed - missing {priv_names} on {path}" + ); +} + /// A priviledge aware iterator for all backup groups in all Namespaces below an anchor namespace, /// most often that will be the `BackupNamespace::root()` one. /// diff --git a/src/server/pull.rs b/src/server/pull.rs index 1162481f..3f28af39 100644 --- a/src/server/pull.rs +++ b/src/server/pull.rs @@ -18,7 +18,7 @@ use proxmox_sys::task_log; use pbs_api_types::{ Authid, BackupNamespace, DatastoreWithNamespace, GroupFilter, GroupListItem, NamespaceListItem, Operation, RateLimitConfig, Remote, SnapshotListItem, MAX_NAMESPACE_DEPTH, - PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_MODIFY, + PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, }; use pbs_client::{ @@ -35,6 +35,7 @@ use pbs_datastore::{check_backup_owner, DataStore, StoreProgress}; use pbs_tools::sha::sha256; use proxmox_rest_server::WorkerTask; +use crate::backup::{check_ns_modification_privs, check_ns_privs}; use crate::tools::parallel_handler::ParallelHandler; /// Parameters for a pull operation. @@ -791,18 +792,6 @@ async fn query_namespaces( Ok(list.iter().map(|item| item.ns.clone()).collect()) } -fn check_ns_privs( - store_with_ns: &DatastoreWithNamespace, - owner: &Authid, - privs: u64, -) -> Result<(), Error> { - let user_info = CachedUserInfo::new()?; - - // TODO re-sync with API, maybe find common place? - - user_info.check_privs(owner, &store_with_ns.acl_path(), privs, true) -} - fn check_and_create_ns( params: &PullParameters, store_with_ns: &DatastoreWithNamespace, @@ -811,32 +800,26 @@ fn check_and_create_ns( let mut created = false; if !ns.is_root() && !params.store.namespace_path(&ns).exists() { - let mut parent = ns.clone(); - let name = parent.pop(); - - let parent = params.store_with_ns(parent); - - check_ns_privs(&parent, ¶ms.owner, PRIV_DATASTORE_MODIFY) + check_ns_modification_privs(&store_with_ns, ¶ms.owner) .map_err(|err| format_err!("Creating {ns} not allowed - {err}"))?; - if let Some(name) = name { - if let Err(err) = params.store.create_namespace(&parent.ns, name) { - bail!( - "sync into {} failed - namespace creation failed: {}", - &store_with_ns, - err - ); + let name = match ns.components().last() { + Some(name) => name.to_owned(), + None => { + bail!("Failed to determine last component of namespace."); } - created = true; - } else { + }; + + if let Err(err) = params.store.create_namespace(&ns.parent(), name) { bail!( - "sync into {} failed - namespace creation failed - couldn't determine parent namespace", + "sync into {} failed - namespace creation failed: {}", &store_with_ns, + err ); } + created = true; } - // TODO re-sync with API, maybe find common place? check_ns_privs(&store_with_ns, ¶ms.owner, PRIV_DATASTORE_BACKUP) .map_err(|err| format_err!("sync into {store_with_ns} not allowed - {err}"))?; @@ -844,10 +827,10 @@ fn check_and_create_ns( } fn check_and_remove_ns(params: &PullParameters, local_ns: &BackupNamespace) -> Result { - let parent = local_ns.clone().parent(); - let store_with_parent = params.store_with_ns(parent); - check_ns_privs(&store_with_parent, ¶ms.owner, PRIV_DATASTORE_MODIFY) + let store_with_ns = params.store_with_ns(local_ns.clone()); + check_ns_modification_privs(&store_with_ns, ¶ms.owner) .map_err(|err| format_err!("Removing {local_ns} not allowed - {err}"))?; + params.store.remove_namespace_recursive(local_ns, true) }