--- /dev/null
+use failure::*;
+use serde_json::Value;
+use ::serde::{Deserialize, Serialize};
+
+use proxmox::api::{api, ApiMethod, Router, RpcEnvironment};
+use proxmox::api::schema::{Schema, StringSchema, BooleanSchema, ApiStringFormat};
+
+use crate::api2::types::*;
+use crate::config::acl;
+
+pub const ACL_PROPAGATE_SCHEMA: Schema = BooleanSchema::new(
+ "Allow to propagate (inherit) permissions.")
+ .default(true)
+ .schema();
+
+pub const ACL_PATH_SCHEMA: Schema = StringSchema::new(
+ "Access control path.")
+ .format(&ACL_PATH_FORMAT)
+ .min_length(1)
+ .max_length(128)
+ .schema();
+
+pub const ACL_UGID_TYPE_SCHEMA: Schema = StringSchema::new(
+ "Type of 'ugid' property.")
+ .format(&ApiStringFormat::Enum(&["user", "group"]))
+ .schema();
+
+pub const ACL_ROLE_SCHEMA: Schema = StringSchema::new(
+ "Role.")
+ .format(&ApiStringFormat::Enum(&["Admin", "User", "Audit", "NoAccess"]))
+ .schema();
+
+#[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: {
+ schema: ACL_ROLE_SCHEMA,
+ }
+ }
+)]
+#[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>,
+) {
+ 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(),
+ });
+ }
+ }
+ for (comp, child) in &node.children {
+ let new_path = format!("{}/{}", path, comp);
+ extract_acl_node_data(child, &new_path, list);
+ }
+}
+
+#[api(
+ returns: {
+ description: "ACL entry list.",
+ type: Array,
+ items: {
+ type: AclListItem,
+ }
+ }
+)]
+/// Read Access Control List (ACLs).
+pub fn read_acl(
+ _rpcenv: &mut dyn RpcEnvironment,
+) -> Result<Vec<AclListItem>, Error> {
+
+ //let auth_user = rpcenv.get_user().unwrap();
+
+ let (tree, digest) = acl::config()?;
+
+ let mut list: Vec<AclListItem> = Vec::new();
+ extract_acl_node_data(&tree.root, "", &mut list);
+
+ Ok(list)
+}
+
+pub const ROUTER: Router = Router::new()
+ .get(&API_METHOD_READ_ACL);
pub PROXMOX_USER_ID_REGEX = concat!(r"^", USER_NAME_REGEX_STR!(), r"@", PROXMOX_SAFE_ID_REGEX_STR!(), r"$");
pub CERT_FINGERPRINT_SHA256_REGEX = r"^(?:[0-9a-fA-F][0-9a-fA-F])(?::[0-9a-fA-F][0-9a-fA-F]){31}$";
+
+ pub ACL_PATH_REGEX = concat!(r"^(?:\/|", r"(?:\/", PROXMOX_SAFE_ID_REGEX_STR!(), ")+", r")$");
}
pub const SYSTEMD_DATETIME_FORMAT: ApiStringFormat =
pub const PASSWORD_FORMAT: ApiStringFormat =
ApiStringFormat::Pattern(&PASSWORD_REGEX);
+pub const ACL_PATH_FORMAT: ApiStringFormat =
+ ApiStringFormat::Pattern(&ACL_PATH_REGEX);
+
pub const PASSWORD_SCHEMA: Schema = StringSchema::new("Password.")
.format(&PASSWORD_FORMAT)
cmd_def.into()
}
+#[api(
+ input: {
+ properties: {
+ "output-format": {
+ schema: OUTPUT_FORMAT,
+ optional: true,
+ },
+ }
+ }
+)]
+/// Access Control list.
+fn list_acls(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
+
+ let output_format = get_output_format(¶m);
+
+ let info = &api2::access::acl::API_METHOD_READ_ACL;
+ let mut data = match info.handler {
+ ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
+ _ => unreachable!(),
+ };
+
+ fn render_ugid(value: &Value, record: &Value) -> Result<String, Error> {
+ if value.is_null() { return Ok(String::new()); }
+ let ugid = value.as_str().unwrap();
+ let ugid_type = record["ugid_type"].as_str().unwrap();
+
+ if ugid_type == "user" {
+ Ok(ugid.to_string())
+ } else if ugid_type == "group" {
+ Ok(format!("@{}", ugid))
+ } else {
+ bail!("render_ugid: got unknown ugid_type");
+ }
+ }
+
+ let options = default_table_format_options()
+ .column(ColumnConfig::new("ugid").renderer(render_ugid))
+ .column(ColumnConfig::new("path"))
+ .column(ColumnConfig::new("propagate"))
+ .column(ColumnConfig::new("roleid"));
+
+ format_and_print_result_full(&mut data, info.returns, &output_format, &options);
+
+ Ok(Value::Null)
+}
+
+fn acl_commands() -> CommandLineInterface {
+
+ let cmd_def = CliCommandMap::new()
+ .insert("list", CliCommand::new(&&API_METHOD_LIST_ACLS));
+
+ cmd_def.into()
+}
+
fn datastore_commands() -> CommandLineInterface {
let cmd_def = CliCommandMap::new()
fn main() {
let cmd_def = CliCommandMap::new()
+ .insert("acl", acl_commands())
.insert("datastore", datastore_commands())
.insert("user", user_commands())
.insert("remote", remote_commands())
}
pub struct AclTree {
- root: AclTreeNode,
+ pub root: AclTreeNode,
}
-struct AclTreeNode {
- users: HashMap<String, HashMap<String, bool>>,
- groups: HashMap<String, HashMap<String, bool>>,
- children: BTreeMap<String, AclTreeNode>,
+pub struct AclTreeNode {
+ pub users: HashMap<String, HashMap<String, bool>>,
+ pub groups: HashMap<String, HashMap<String, bool>>,
+ pub children: BTreeMap<String, AclTreeNode>,
}
impl AclTreeNode {