move and unify namespace priv helpers

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
This commit is contained in:
Fabian Grünbichler 2022-05-25 10:07:54 +02:00 committed by Thomas Lamprecht
parent 77bd14f68a
commit ea2e91e52f
5 changed files with 208 additions and 182 deletions

View File

@ -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<u64, Error> {
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<bool, Error> {
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<Operation>,
backup_group: &pbs_api_types::BackupGroup,
) -> Result<Arc<DataStore>, 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<Value, 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_VERIFY,
PRIV_DATASTORE_BACKUP,
@ -947,19 +925,19 @@ pub fn prune(
) -> Result<Value, 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_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(&param)?;
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(&param)?;
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(&param)?;
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<Vec<ArchiveEntry>, 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(&param, "store")?;
let ns = optional_ns_param(&param)?;
let store_with_ns = DatastoreWithNamespace {
store: store.to_owned(),
ns: ns.clone(),
};
let backup_dir: pbs_api_types::BackupDir = Deserialize::deserialize(&param)?;
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<String, 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_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<String, 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_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<bool, 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_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,

View File

@ -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<Value, Error> {
// 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))?;

View File

@ -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())?;
}
}
}

View File

@ -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<bool, Error> {
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.
///

View File

@ -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, &params.owner, PRIV_DATASTORE_MODIFY)
check_ns_modification_privs(&store_with_ns, &params.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, &params.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<bool, Error> {
let parent = local_ns.clone().parent();
let store_with_parent = params.store_with_ns(parent);
check_ns_privs(&store_with_parent, &params.owner, PRIV_DATASTORE_MODIFY)
let store_with_ns = params.store_with_ns(local_ns.clone());
check_ns_modification_privs(&store_with_ns, &params.owner)
.map_err(|err| format_err!("Removing {local_ns} not allowed - {err}"))?;
params.store.remove_namespace_recursive(local_ns, true)
}