start ACL api
This commit is contained in:
		| @ -14,6 +14,7 @@ use crate::api2::types::*; | ||||
|  | ||||
| pub mod user; | ||||
| pub mod domain; | ||||
| pub mod acl; | ||||
|  | ||||
| fn authenticate_user(username: &str, password: &str) -> Result<(), Error> { | ||||
|  | ||||
| @ -130,6 +131,7 @@ fn change_password( | ||||
|  | ||||
| #[sortable] | ||||
| const SUBDIRS: SubdirMap = &sorted!([ | ||||
|     ("acl", &acl::ROUTER), | ||||
|     ( | ||||
|         "password", &Router::new() | ||||
|             .put(&API_METHOD_CHANGE_PASSWORD) | ||||
|  | ||||
							
								
								
									
										121
									
								
								src/api2/access/acl.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								src/api2/access/acl.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,121 @@ | ||||
| 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); | ||||
| @ -55,6 +55,8 @@ const_regex!{ | ||||
|     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 = | ||||
| @ -90,6 +92,9 @@ pub const PROXMOX_USER_ID_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) | ||||
|  | ||||
| @ -171,6 +171,60 @@ fn user_commands() -> CommandLineInterface { | ||||
|     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() | ||||
| @ -539,6 +593,7 @@ async fn pull_datastore( | ||||
| fn main() { | ||||
|  | ||||
|     let cmd_def = CliCommandMap::new() | ||||
|         .insert("acl", acl_commands()) | ||||
|         .insert("datastore", datastore_commands()) | ||||
|         .insert("user", user_commands()) | ||||
|         .insert("remote", remote_commands()) | ||||
|  | ||||
| @ -64,13 +64,13 @@ fn split_acl_path(path: &str) -> Vec<&str> { | ||||
| } | ||||
|  | ||||
| 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 { | ||||
|  | ||||
		Reference in New Issue
	
	Block a user