2020-04-17 12:11:25 +00:00
|
|
|
use anyhow::{bail, Error};
|
2020-04-13 09:09:44 +00:00
|
|
|
use ::serde::{Deserialize, Serialize};
|
|
|
|
|
2020-04-17 08:03:09 +00:00
|
|
|
use proxmox::api::{api, Router, RpcEnvironment, Permission};
|
2020-04-13 09:09:44 +00:00
|
|
|
|
|
|
|
use crate::api2::types::*;
|
|
|
|
use crate::config::acl;
|
2020-04-30 07:30:00 +00:00
|
|
|
use crate::config::acl::{Role, PRIV_SYS_AUDIT, PRIV_PERMISSIONS_MODIFY};
|
2020-04-13 09:09:44 +00:00
|
|
|
|
|
|
|
#[api(
|
|
|
|
properties: {
|
|
|
|
propagate: {
|
|
|
|
schema: ACL_PROPAGATE_SCHEMA,
|
|
|
|
},
|
|
|
|
path: {
|
|
|
|
schema: ACL_PATH_SCHEMA,
|
|
|
|
},
|
|
|
|
ugid_type: {
|
|
|
|
schema: ACL_UGID_TYPE_SCHEMA,
|
|
|
|
},
|
|
|
|
ugid: {
|
|
|
|
type: String,
|
|
|
|
description: "User or Group ID.",
|
|
|
|
},
|
|
|
|
roleid: {
|
2020-04-29 11:01:24 +00:00
|
|
|
type: Role,
|
2020-04-13 09:09:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
)]
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
|
|
/// ACL list entry.
|
|
|
|
pub struct AclListItem {
|
|
|
|
path: String,
|
|
|
|
ugid: String,
|
|
|
|
ugid_type: String,
|
|
|
|
propagate: bool,
|
|
|
|
roleid: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn extract_acl_node_data(
|
|
|
|
node: &acl::AclTreeNode,
|
|
|
|
path: &str,
|
|
|
|
list: &mut Vec<AclListItem>,
|
2020-05-20 10:15:34 +00:00
|
|
|
exact: bool,
|
2020-04-13 09:09:44 +00:00
|
|
|
) {
|
|
|
|
for (user, roles) in &node.users {
|
|
|
|
for (role, propagate) in roles {
|
|
|
|
list.push(AclListItem {
|
|
|
|
path: if path.is_empty() { String::from("/") } else { path.to_string() },
|
|
|
|
propagate: *propagate,
|
|
|
|
ugid_type: String::from("user"),
|
|
|
|
ugid: user.to_string(),
|
|
|
|
roleid: role.to_string(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (group, roles) in &node.groups {
|
|
|
|
for (role, propagate) in roles {
|
|
|
|
list.push(AclListItem {
|
|
|
|
path: if path.is_empty() { String::from("/") } else { path.to_string() },
|
|
|
|
propagate: *propagate,
|
|
|
|
ugid_type: String::from("group"),
|
|
|
|
ugid: group.to_string(),
|
|
|
|
roleid: role.to_string(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2020-05-20 10:15:34 +00:00
|
|
|
if exact {
|
|
|
|
return;
|
|
|
|
}
|
2020-04-13 09:09:44 +00:00
|
|
|
for (comp, child) in &node.children {
|
|
|
|
let new_path = format!("{}/{}", path, comp);
|
2020-05-20 10:15:34 +00:00
|
|
|
extract_acl_node_data(child, &new_path, list, exact);
|
2020-04-13 09:09:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[api(
|
2020-05-20 10:15:35 +00:00
|
|
|
input: {
|
|
|
|
properties: {
|
|
|
|
path: {
|
|
|
|
schema: ACL_PATH_SCHEMA,
|
|
|
|
optional: true,
|
|
|
|
},
|
|
|
|
exact: {
|
|
|
|
description: "If set, returns only ACL for the exact path.",
|
|
|
|
type: bool,
|
|
|
|
optional: true,
|
|
|
|
default: false,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2020-04-13 09:09:44 +00:00
|
|
|
returns: {
|
|
|
|
description: "ACL entry list.",
|
|
|
|
type: Array,
|
|
|
|
items: {
|
|
|
|
type: AclListItem,
|
|
|
|
}
|
2020-04-17 08:03:09 +00:00
|
|
|
},
|
|
|
|
access: {
|
2020-04-30 07:30:00 +00:00
|
|
|
permission: &Permission::Privilege(&["access", "acl"], PRIV_SYS_AUDIT, false),
|
2020-04-17 08:03:09 +00:00
|
|
|
},
|
2020-04-13 09:09:44 +00:00
|
|
|
)]
|
|
|
|
/// Read Access Control List (ACLs).
|
|
|
|
pub fn read_acl(
|
2020-05-20 10:15:35 +00:00
|
|
|
path: Option<String>,
|
|
|
|
exact: bool,
|
|
|
|
mut rpcenv: &mut dyn RpcEnvironment,
|
2020-04-13 09:09:44 +00:00
|
|
|
) -> Result<Vec<AclListItem>, Error> {
|
|
|
|
|
|
|
|
//let auth_user = rpcenv.get_user().unwrap();
|
|
|
|
|
2020-05-20 10:15:35 +00:00
|
|
|
let (mut tree, digest) = acl::config()?;
|
2020-04-13 09:09:44 +00:00
|
|
|
|
|
|
|
let mut list: Vec<AclListItem> = Vec::new();
|
2020-05-20 10:15:35 +00:00
|
|
|
if let Some(path) = &path {
|
|
|
|
if let Some(node) = &tree.find_node(path) {
|
|
|
|
extract_acl_node_data(&node, path, &mut list, exact);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
extract_acl_node_data(&tree.root, "", &mut list, exact);
|
|
|
|
}
|
|
|
|
|
|
|
|
rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
|
2020-04-13 09:09:44 +00:00
|
|
|
|
|
|
|
Ok(list)
|
|
|
|
}
|
|
|
|
|
2020-04-14 06:40:53 +00:00
|
|
|
#[api(
|
2020-05-20 10:15:36 +00:00
|
|
|
protected: true,
|
2020-04-14 06:40:53 +00:00
|
|
|
input: {
|
|
|
|
properties: {
|
|
|
|
path: {
|
|
|
|
schema: ACL_PATH_SCHEMA,
|
|
|
|
},
|
|
|
|
role: {
|
2020-04-29 11:01:24 +00:00
|
|
|
type: Role,
|
2020-04-14 06:40:53 +00:00
|
|
|
},
|
|
|
|
propagate: {
|
|
|
|
optional: true,
|
|
|
|
schema: ACL_PROPAGATE_SCHEMA,
|
|
|
|
},
|
|
|
|
userid: {
|
|
|
|
optional: true,
|
|
|
|
schema: PROXMOX_USER_ID_SCHEMA,
|
|
|
|
},
|
|
|
|
group: {
|
|
|
|
optional: true,
|
|
|
|
schema: PROXMOX_GROUP_ID_SCHEMA,
|
|
|
|
},
|
2020-04-14 11:46:27 +00:00
|
|
|
delete: {
|
2020-04-14 06:40:53 +00:00
|
|
|
optional: true,
|
|
|
|
description: "Remove permissions (instead of adding it).",
|
|
|
|
type: bool,
|
|
|
|
},
|
|
|
|
digest: {
|
|
|
|
optional: true,
|
|
|
|
schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2020-04-17 08:03:09 +00:00
|
|
|
access: {
|
2020-04-30 07:30:00 +00:00
|
|
|
permission: &Permission::Privilege(&["access", "acl"], PRIV_PERMISSIONS_MODIFY, false),
|
2020-04-17 08:03:09 +00:00
|
|
|
},
|
2020-04-14 06:40:53 +00:00
|
|
|
)]
|
|
|
|
/// Update Access Control List (ACLs).
|
|
|
|
pub fn update_acl(
|
|
|
|
path: String,
|
|
|
|
role: String,
|
|
|
|
propagate: Option<bool>,
|
|
|
|
userid: Option<String>,
|
|
|
|
group: Option<String>,
|
|
|
|
delete: Option<bool>,
|
|
|
|
digest: Option<String>,
|
|
|
|
_rpcenv: &mut dyn RpcEnvironment,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
|
|
|
|
let _lock = crate::tools::open_file_locked(acl::ACL_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
|
|
|
|
|
|
|
|
let (mut tree, expected_digest) = acl::config()?;
|
|
|
|
|
|
|
|
if let Some(ref digest) = digest {
|
|
|
|
let digest = proxmox::tools::hex_to_digest(digest)?;
|
|
|
|
crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
let propagate = propagate.unwrap_or(true);
|
|
|
|
|
|
|
|
let delete = delete.unwrap_or(false);
|
|
|
|
|
2020-04-14 15:23:48 +00:00
|
|
|
if let Some(ref _group) = group {
|
2020-04-14 11:46:27 +00:00
|
|
|
bail!("parameter 'group' - groups are currently not supported.");
|
|
|
|
} else if let Some(ref userid) = userid {
|
|
|
|
if !delete { // Note: we allow to delete non-existent users
|
2020-04-15 09:35:57 +00:00
|
|
|
let user_cfg = crate::config::user::cached_config()?;
|
2020-04-14 11:46:27 +00:00
|
|
|
if user_cfg.sections.get(userid).is_none() {
|
|
|
|
bail!("no such user.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bail!("missing 'userid' or 'group' parameter.");
|
|
|
|
}
|
|
|
|
|
2020-04-14 15:23:48 +00:00
|
|
|
if !delete { // Note: we allow to delete entries with invalid path
|
2020-04-30 07:30:00 +00:00
|
|
|
acl::check_acl_path(&path)?;
|
2020-04-14 15:23:48 +00:00
|
|
|
}
|
|
|
|
|
2020-04-14 06:40:53 +00:00
|
|
|
if let Some(userid) = userid {
|
|
|
|
if delete {
|
|
|
|
tree.delete_user_role(&path, &userid, &role);
|
|
|
|
} else {
|
|
|
|
tree.insert_user_role(&path, &userid, &role, propagate);
|
|
|
|
}
|
|
|
|
} else if let Some(group) = group {
|
|
|
|
if delete {
|
|
|
|
tree.delete_group_role(&path, &group, &role);
|
|
|
|
} else {
|
|
|
|
tree.insert_group_role(&path, &group, &role, propagate);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
acl::save_config(&tree)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-04-13 09:09:44 +00:00
|
|
|
pub const ROUTER: Router = Router::new()
|
2020-04-14 06:40:53 +00:00
|
|
|
.get(&API_METHOD_READ_ACL)
|
|
|
|
.put(&API_METHOD_UPDATE_ACL);
|