From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id C92421FF183 for ; Wed, 22 Oct 2025 15:11:11 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id C781F1895D; Wed, 22 Oct 2025 15:11:35 +0200 (CEST) From: Shannon Sterz To: pdm-devel@lists.proxmox.com Date: Wed, 22 Oct 2025 15:11:23 +0200 Message-ID: <20251022131126.358790-8-s.sterz@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20251022131126.358790-1-s.sterz@proxmox.com> References: <20251022131126.358790-1-s.sterz@proxmox.com> MIME-Version: 1.0 X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1761138682083 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.093 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment POISEN_SPAM_PILL 0.1 Meta: its spam POISEN_SPAM_PILL_1 0.1 random spam to be learned in bayes POISEN_SPAM_PILL_3 0.1 random spam to be learned in bayes SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pdm-devel] [PATCH datacenter-manager v2 1/2] server/api-types: move AccessControlConfig to shared api types X-BeenThere: pdm-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Datacenter Manager development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Proxmox Datacenter Manager development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pdm-devel-bounces@lists.proxmox.com Sender: "pdm-devel" this doesn't really contain any information that is secret. moving it to the shared api types allows re-using it in the ui to check what privileges a user needs to access certain features. Signed-off-by: Shannon Sterz --- lib/pdm-api-types/Cargo.toml | 1 + lib/pdm-api-types/src/acl.rs | 158 ++++++++++++++++++++++++++++++++++ server/src/acl.rs | 162 +---------------------------------- 3 files changed, 161 insertions(+), 160 deletions(-) diff --git a/lib/pdm-api-types/Cargo.toml b/lib/pdm-api-types/Cargo.toml index e66558b..4b0edde 100644 --- a/lib/pdm-api-types/Cargo.toml +++ b/lib/pdm-api-types/Cargo.toml @@ -14,6 +14,7 @@ serde.workspace = true serde_plain.workspace = true proxmox-acme-api.workspace = true +proxmox-access-control.workspace = true proxmox-auth-api = { workspace = true, features = ["api-types"] } proxmox-lang.workspace = true proxmox-config-digest.workspace = true diff --git a/lib/pdm-api-types/src/acl.rs b/lib/pdm-api-types/src/acl.rs index 9e69c2f..baba3da 100644 --- a/lib/pdm-api-types/src/acl.rs +++ b/lib/pdm-api-types/src/acl.rs @@ -1,6 +1,12 @@ +use std::collections::HashMap; use std::str::FromStr; +use std::sync::LazyLock; +use anyhow::{format_err, Context, Error}; use const_format::concatcp; +use proxmox_access_control::types::User; +use proxmox_auth_api::types::Authid; +use proxmox_section_config::SectionConfigData; use serde::de::{value, IntoDeserializer}; use serde::{Deserialize, Serialize}; @@ -179,3 +185,155 @@ pub struct AclListItem { pub propagate: bool, pub roleid: String, } + +pub struct AccessControlConfig; + +impl proxmox_access_control::init::AccessControlConfig for AccessControlConfig { + fn privileges(&self) -> &HashMap<&str, u64> { + static PRIVS: LazyLock> = + LazyLock::new(|| PRIVILEGES.iter().copied().collect()); + + &PRIVS + } + + #[rustfmt::skip] + fn roles(&self) -> &HashMap<&str, (u64, &str)> { + static ROLES: LazyLock> = LazyLock::new(|| { + [ + ("Administrator", (ROLE_ADMINISTRATOR, "Administrators can inspect and modify the system.")), + ("Auditor", (ROLE_AUDITOR, "An Auditor can inspect many aspects of the system, but not change them.")), + //("SystemAdministrator", pdm_api_types::ROLE_SYS_ADMINISTRATOR), + //("SystemAuditor", pdm_api_types::ROLE_SYS_AUDITOR), + //("ResourceAdministrator", pdm_api_types::ROLE_RESOURCE_ADMINISTRATOR), + //("ResourceAuditor", pdm_api_types::ROLE_RESOURCE_AUDITOR), + //("AccessAuditor", pdm_api_types::ROLE_ACCESS_AUDITOR), + ] + .into_iter() + .collect() + }); + + &ROLES + } + + fn is_superuser(&self, auth_id: &Authid) -> bool { + !auth_id.is_token() && auth_id.user() == "root@pam" + } + + fn role_admin(&self) -> Option<&str> { + Some("Administrator") + } + + fn init_user_config(&self, config: &mut SectionConfigData) -> Result<(), Error> { + if !config.sections.contains_key("root@pam") { + config + .set_data( + "root@pam", + "user", + User { + userid: "root@pam".parse().expect("invalid user id"), + comment: Some("Superuser".to_string()), + enable: None, + expire: None, + firstname: None, + lastname: None, + email: None, + }, + ) + .context("failed to insert default user into user config")? + } + + Ok(()) + } + + fn acl_audit_privileges(&self) -> u64 { + PRIV_ACCESS_AUDIT + } + + fn acl_modify_privileges(&self) -> u64 { + PRIV_ACCESS_MODIFY + } + + fn check_acl_path(&self, path: &str) -> Result<(), Error> { + let components = proxmox_access_control::acl::split_acl_path(path); + + let components_len = components.len(); + + if components_len == 0 { + return Ok(()); + } + match components[0] { + "access" => { + if components_len == 1 { + return Ok(()); + } + match components[1] { + "acl" | "users" | "realm" => { + if components_len == 2 { + return Ok(()); + } + } + _ => {} + } + } + "resource" => { + // `/resource` and `/resource/{remote}` + if components_len <= 2 { + return Ok(()); + } + // `/resource/{remote-id}/{resource-type=guest,storage}/{resource-id}` + match components[2] { + "guest" | "storage" => { + // /resource/{remote-id}/{resource-type} + // /resource/{remote-id}/{resource-type}/{resource-id} + if components_len <= 4 { + return Ok(()); + } + } + _ => {} + } + } + "system" => { + if components_len == 1 { + return Ok(()); + } + match components[1] { + "certificates" | "disks" | "log" | "notifications" | "status" | "tasks" + | "time" => { + if components_len == 2 { + return Ok(()); + } + } + "services" => { + // /system/services/{service} + if components_len <= 3 { + return Ok(()); + } + } + "network" => { + if components_len == 2 { + return Ok(()); + } + match components[2] { + "dns" => { + if components_len == 3 { + return Ok(()); + } + } + "interfaces" => { + // /system/network/interfaces/{iface} + if components_len <= 4 { + return Ok(()); + } + } + _ => {} + } + } + _ => {} + } + } + _ => {} + } + + Err(format_err!("invalid acl path '{}'.", path)) + } +} diff --git a/server/src/acl.rs b/server/src/acl.rs index 52a1f97..f421814 100644 --- a/server/src/acl.rs +++ b/server/src/acl.rs @@ -1,164 +1,6 @@ -use std::collections::HashMap; -use std::sync::OnceLock; - -use anyhow::{format_err, Context as _, Error}; - -use proxmox_access_control::types::User; -use proxmox_auth_api::types::Authid; -use proxmox_section_config::SectionConfigData; - -struct AccessControlConfig; - -static PRIVILEGES: OnceLock> = OnceLock::new(); -static ROLES: OnceLock> = OnceLock::new(); - -impl proxmox_access_control::init::AccessControlConfig for AccessControlConfig { - fn privileges(&self) -> &HashMap<&str, u64> { - PRIVILEGES.get_or_init(|| pdm_api_types::PRIVILEGES.iter().copied().collect()) - } - - #[rustfmt::skip] - fn roles(&self) -> &HashMap<&str, (u64, &str)> { - ROLES.get_or_init(|| { - [ - ("Administrator", (pdm_api_types::ROLE_ADMINISTRATOR, "Administrators can inspect and modify the system.")), - ("Auditor", (pdm_api_types::ROLE_AUDITOR, "An Auditor can inspect many aspects of the system, but not change them.")), - //("SystemAdministrator", pdm_api_types::ROLE_SYS_ADMINISTRATOR), - //("SystemAuditor", pdm_api_types::ROLE_SYS_AUDITOR), - //("ResourceAdministrator", pdm_api_types::ROLE_RESOURCE_ADMINISTRATOR), - //("ResourceAuditor", pdm_api_types::ROLE_RESOURCE_AUDITOR), - //("AccessAuditor", pdm_api_types::ROLE_ACCESS_AUDITOR), - ] - .into_iter() - .collect() - }) - } - - fn is_superuser(&self, auth_id: &Authid) -> bool { - !auth_id.is_token() && auth_id.user() == "root@pam" - } - - fn role_admin(&self) -> Option<&str> { - Some("Administrator") - } - - fn init_user_config(&self, config: &mut SectionConfigData) -> Result<(), Error> { - if !config.sections.contains_key("root@pam") { - config - .set_data( - "root@pam", - "user", - User { - userid: "root@pam".parse().expect("invalid user id"), - comment: Some("Superuser".to_string()), - enable: None, - expire: None, - firstname: None, - lastname: None, - email: None, - }, - ) - .context("failed to insert default user into user config")? - } - - Ok(()) - } - - fn acl_audit_privileges(&self) -> u64 { - pdm_api_types::PRIV_ACCESS_AUDIT - } - - fn acl_modify_privileges(&self) -> u64 { - pdm_api_types::PRIV_ACCESS_MODIFY - } - - fn check_acl_path(&self, path: &str) -> Result<(), Error> { - let components = proxmox_access_control::acl::split_acl_path(path); - - let components_len = components.len(); - - if components_len == 0 { - return Ok(()); - } - match components[0] { - "access" => { - if components_len == 1 { - return Ok(()); - } - match components[1] { - "acl" | "users" | "realm" => { - if components_len == 2 { - return Ok(()); - } - } - _ => {} - } - } - "resource" => { - // `/resource` and `/resource/{remote}` - if components_len <= 2 { - return Ok(()); - } - // `/resource/{remote-id}/{resource-type=guest,storage}/{resource-id}` - match components[2] { - "guest" | "storage" => { - // /resource/{remote-id}/{resource-type} - // /resource/{remote-id}/{resource-type}/{resource-id} - if components_len <= 4 { - return Ok(()); - } - } - _ => {} - } - } - "system" => { - if components_len == 1 { - return Ok(()); - } - match components[1] { - "certificates" | "disks" | "log" | "notifications" | "status" | "tasks" - | "time" => { - if components_len == 2 { - return Ok(()); - } - } - "services" => { - // /system/services/{service} - if components_len <= 3 { - return Ok(()); - } - } - "network" => { - if components_len == 2 { - return Ok(()); - } - match components[2] { - "dns" => { - if components_len == 3 { - return Ok(()); - } - } - "interfaces" => { - // /system/network/interfaces/{iface} - if components_len <= 4 { - return Ok(()); - } - } - _ => {} - } - } - _ => {} - } - } - _ => {} - } - - Err(format_err!("invalid acl path '{}'.", path)) - } -} - pub(crate) fn init() { - static ACCESS_CONTROL_CONFIG: AccessControlConfig = AccessControlConfig; + static ACCESS_CONTROL_CONFIG: pdm_api_types::AccessControlConfig = + pdm_api_types::AccessControlConfig; proxmox_access_control::init::init(&ACCESS_CONTROL_CONFIG, pdm_buildcfg::configdir!("/access")) .expect("failed to setup access control config"); -- 2.47.3 _______________________________________________ pdm-devel mailing list pdm-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel