api: tape: restore: improve permission checks
no redundant store+namespace mapping, and synchronize namespace creation check with that of manual creation and creation as part of sync. Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
This commit is contained in:
parent
0aa5815fb6
commit
99e1399729
|
@ -18,9 +18,10 @@ use proxmox_uuid::Uuid;
|
||||||
|
|
||||||
use pbs_api_types::{
|
use pbs_api_types::{
|
||||||
parse_ns_and_snapshot, print_ns_and_snapshot, Authid, BackupDir, BackupNamespace, CryptMode,
|
parse_ns_and_snapshot, print_ns_and_snapshot, Authid, BackupDir, BackupNamespace, CryptMode,
|
||||||
Operation, TapeRestoreNamespace, Userid, DATASTORE_MAP_ARRAY_SCHEMA, DATASTORE_MAP_LIST_SCHEMA,
|
DatastoreWithNamespace, Operation, TapeRestoreNamespace, Userid, DATASTORE_MAP_ARRAY_SCHEMA,
|
||||||
DRIVE_NAME_SCHEMA, MAX_NAMESPACE_DEPTH, PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_MODIFY,
|
DATASTORE_MAP_LIST_SCHEMA, DRIVE_NAME_SCHEMA, MAX_NAMESPACE_DEPTH, PRIV_DATASTORE_BACKUP,
|
||||||
PRIV_TAPE_READ, TAPE_RESTORE_NAMESPACE_SCHEMA, TAPE_RESTORE_SNAPSHOT_SCHEMA, UPID_SCHEMA,
|
PRIV_DATASTORE_MODIFY, PRIV_TAPE_READ, TAPE_RESTORE_NAMESPACE_SCHEMA,
|
||||||
|
TAPE_RESTORE_SNAPSHOT_SCHEMA, UPID_SCHEMA,
|
||||||
};
|
};
|
||||||
use pbs_config::CachedUserInfo;
|
use pbs_config::CachedUserInfo;
|
||||||
use pbs_datastore::dynamic_index::DynamicIndexReader;
|
use pbs_datastore::dynamic_index::DynamicIndexReader;
|
||||||
|
@ -198,22 +199,13 @@ impl DataStoreMap {
|
||||||
|
|
||||||
fn check_datastore_privs(
|
fn check_datastore_privs(
|
||||||
user_info: &CachedUserInfo,
|
user_info: &CachedUserInfo,
|
||||||
store: &str,
|
store_with_ns: &DatastoreWithNamespace,
|
||||||
ns: &BackupNamespace,
|
|
||||||
auth_id: &Authid,
|
auth_id: &Authid,
|
||||||
owner: Option<&Authid>,
|
owner: Option<&Authid>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let privs = if ns.is_root() {
|
let privs = user_info.lookup_privs(auth_id, &store_with_ns.acl_path());
|
||||||
user_info.lookup_privs(auth_id, &["datastore", store])
|
|
||||||
} else {
|
|
||||||
user_info.lookup_privs(auth_id, &["datastore", store, &ns.to_string()])
|
|
||||||
};
|
|
||||||
if (privs & PRIV_DATASTORE_BACKUP) == 0 {
|
if (privs & PRIV_DATASTORE_BACKUP) == 0 {
|
||||||
if ns.is_root() {
|
bail!("no permissions on /{}", store_with_ns.acl_path().join("/"));
|
||||||
bail!("no permissions on /datastore/{}", store);
|
|
||||||
} else {
|
|
||||||
bail!("no permissions on /datastore/{}/{}", store, &ns.to_string());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref owner) = owner {
|
if let Some(ref owner) = owner {
|
||||||
|
@ -237,29 +229,33 @@ fn check_and_create_namespaces(
|
||||||
owner: Option<&Authid>,
|
owner: Option<&Authid>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// check normal restore privs first
|
// check normal restore privs first
|
||||||
check_datastore_privs(user_info, store.name(), ns, auth_id, owner)?;
|
let mut store_with_ns = DatastoreWithNamespace {
|
||||||
|
store: store.name().to_string(),
|
||||||
|
ns: ns.clone(),
|
||||||
|
};
|
||||||
|
check_datastore_privs(user_info, &store_with_ns, auth_id, owner)?;
|
||||||
|
|
||||||
// try create recursively if it does not exist
|
// try create recursively if it does not exist
|
||||||
if !store.namespace_exists(ns) {
|
if !store.namespace_exists(ns) {
|
||||||
let mut tmp_ns: BackupNamespace = Default::default();
|
let mut tmp_ns: BackupNamespace = Default::default();
|
||||||
let has_datastore_priv = user_info.lookup_privs(auth_id, &["datastore", store.name()])
|
|
||||||
& PRIV_DATASTORE_MODIFY
|
|
||||||
!= 0;
|
|
||||||
|
|
||||||
for comp in ns.components() {
|
for comp in ns.components() {
|
||||||
tmp_ns.push(comp.to_string())?;
|
tmp_ns.push(comp.to_string())?;
|
||||||
if !store.namespace_exists(&tmp_ns) {
|
if !store.namespace_exists(&tmp_ns) {
|
||||||
if has_datastore_priv
|
// check parent modification privs
|
||||||
|| user_info.lookup_privs(
|
user_info
|
||||||
|
.check_privs(
|
||||||
auth_id,
|
auth_id,
|
||||||
&["datastore", store.name(), &tmp_ns.parent().to_string()],
|
&store_with_ns.acl_path(),
|
||||||
) & PRIV_DATASTORE_MODIFY
|
PRIV_DATASTORE_MODIFY,
|
||||||
!= 0
|
false,
|
||||||
{
|
)
|
||||||
|
.map_err(|_err| format_err!("no permission to create namespace '{tmp_ns}'"))?;
|
||||||
|
|
||||||
store.create_namespace(&tmp_ns.parent(), comp.to_string())?;
|
store.create_namespace(&tmp_ns.parent(), comp.to_string())?;
|
||||||
} else {
|
|
||||||
bail!("no permissions to create '{}'", tmp_ns);
|
// update parent for next component
|
||||||
}
|
store_with_ns.ns = tmp_ns.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -350,8 +346,10 @@ pub fn restore(
|
||||||
for (target, namespaces) in used_datastores.values() {
|
for (target, namespaces) in used_datastores.values() {
|
||||||
check_datastore_privs(
|
check_datastore_privs(
|
||||||
&user_info,
|
&user_info,
|
||||||
target.name(),
|
&DatastoreWithNamespace {
|
||||||
&Default::default(),
|
store: target.name().to_string(),
|
||||||
|
ns: Default::default(),
|
||||||
|
},
|
||||||
&auth_id,
|
&auth_id,
|
||||||
owner.as_ref(),
|
owner.as_ref(),
|
||||||
)?;
|
)?;
|
||||||
|
@ -595,13 +593,13 @@ fn check_snapshot_restorable(
|
||||||
let mut can_restore_some = false;
|
let mut can_restore_some = false;
|
||||||
for ns in namespaces {
|
for ns in namespaces {
|
||||||
// only simple check, ns creation comes later
|
// only simple check, ns creation comes later
|
||||||
if let Err(err) = check_datastore_privs(
|
let store_with_ns = DatastoreWithNamespace {
|
||||||
user_info,
|
store: datastore.name().to_string(),
|
||||||
datastore.name(),
|
ns: ns.clone(),
|
||||||
&ns,
|
};
|
||||||
auth_id,
|
if let Err(err) =
|
||||||
Some(restore_owner),
|
check_datastore_privs(user_info, &store_with_ns, auth_id, Some(restore_owner))
|
||||||
) {
|
{
|
||||||
task_warn!(worker, "cannot restore {store}:{snapshot} to {ns}: '{err}'");
|
task_warn!(worker, "cannot restore {store}:{snapshot} to {ns}: '{err}'");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue