start ACL api
This commit is contained in:
parent
a83eab3c4d
commit
ed3e60ae69
|
@ -14,6 +14,7 @@ use crate::api2::types::*;
|
||||||
|
|
||||||
pub mod user;
|
pub mod user;
|
||||||
pub mod domain;
|
pub mod domain;
|
||||||
|
pub mod acl;
|
||||||
|
|
||||||
fn authenticate_user(username: &str, password: &str) -> Result<(), Error> {
|
fn authenticate_user(username: &str, password: &str) -> Result<(), Error> {
|
||||||
|
|
||||||
|
@ -130,6 +131,7 @@ fn change_password(
|
||||||
|
|
||||||
#[sortable]
|
#[sortable]
|
||||||
const SUBDIRS: SubdirMap = &sorted!([
|
const SUBDIRS: SubdirMap = &sorted!([
|
||||||
|
("acl", &acl::ROUTER),
|
||||||
(
|
(
|
||||||
"password", &Router::new()
|
"password", &Router::new()
|
||||||
.put(&API_METHOD_CHANGE_PASSWORD)
|
.put(&API_METHOD_CHANGE_PASSWORD)
|
||||||
|
|
|
@ -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 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 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 SYSTEMD_DATETIME_FORMAT: ApiStringFormat =
|
||||||
|
@ -90,6 +92,9 @@ pub const PROXMOX_USER_ID_FORMAT: ApiStringFormat =
|
||||||
pub const PASSWORD_FORMAT: ApiStringFormat =
|
pub const PASSWORD_FORMAT: ApiStringFormat =
|
||||||
ApiStringFormat::Pattern(&PASSWORD_REGEX);
|
ApiStringFormat::Pattern(&PASSWORD_REGEX);
|
||||||
|
|
||||||
|
pub const ACL_PATH_FORMAT: ApiStringFormat =
|
||||||
|
ApiStringFormat::Pattern(&ACL_PATH_REGEX);
|
||||||
|
|
||||||
|
|
||||||
pub const PASSWORD_SCHEMA: Schema = StringSchema::new("Password.")
|
pub const PASSWORD_SCHEMA: Schema = StringSchema::new("Password.")
|
||||||
.format(&PASSWORD_FORMAT)
|
.format(&PASSWORD_FORMAT)
|
||||||
|
|
|
@ -171,6 +171,60 @@ fn user_commands() -> CommandLineInterface {
|
||||||
cmd_def.into()
|
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 {
|
fn datastore_commands() -> CommandLineInterface {
|
||||||
|
|
||||||
let cmd_def = CliCommandMap::new()
|
let cmd_def = CliCommandMap::new()
|
||||||
|
@ -539,6 +593,7 @@ async fn pull_datastore(
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
||||||
let cmd_def = CliCommandMap::new()
|
let cmd_def = CliCommandMap::new()
|
||||||
|
.insert("acl", acl_commands())
|
||||||
.insert("datastore", datastore_commands())
|
.insert("datastore", datastore_commands())
|
||||||
.insert("user", user_commands())
|
.insert("user", user_commands())
|
||||||
.insert("remote", remote_commands())
|
.insert("remote", remote_commands())
|
||||||
|
|
|
@ -64,13 +64,13 @@ fn split_acl_path(path: &str) -> Vec<&str> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AclTree {
|
pub struct AclTree {
|
||||||
root: AclTreeNode,
|
pub root: AclTreeNode,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AclTreeNode {
|
pub struct AclTreeNode {
|
||||||
users: HashMap<String, HashMap<String, bool>>,
|
pub users: HashMap<String, HashMap<String, bool>>,
|
||||||
groups: HashMap<String, HashMap<String, bool>>,
|
pub groups: HashMap<String, HashMap<String, bool>>,
|
||||||
children: BTreeMap<String, AclTreeNode>,
|
pub children: BTreeMap<String, AclTreeNode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AclTreeNode {
|
impl AclTreeNode {
|
||||||
|
|
Loading…
Reference in New Issue