From: Shannon Sterz <s.sterz@proxmox.com>
To: pdm-devel@lists.proxmox.com
Subject: [pdm-devel] [PATCH datacenter-manager v2 1/2] server/api-types: move AccessControlConfig to shared api types
Date: Wed, 22 Oct 2025 15:11:23 +0200 [thread overview]
Message-ID: <20251022131126.358790-8-s.sterz@proxmox.com> (raw)
In-Reply-To: <20251022131126.358790-1-s.sterz@proxmox.com>
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 <s.sterz@proxmox.com>
---
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<HashMap<&str, u64>> =
+ LazyLock::new(|| PRIVILEGES.iter().copied().collect());
+
+ &PRIVS
+ }
+
+ #[rustfmt::skip]
+ fn roles(&self) -> &HashMap<&str, (u64, &str)> {
+ static ROLES: LazyLock<HashMap<&str, (u64, &str)>> = 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<HashMap<&str, u64>> = OnceLock::new();
-static ROLES: OnceLock<HashMap<&str, (u64, &str)>> = 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
next prev parent reply other threads:[~2025-10-22 13:11 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-22 13:11 [pdm-devel] [PATCH datacenter-manager/proxmox/yew-comp v2 00/10] add support for checking acl permissions in (yew) front-ends Shannon Sterz
2025-10-22 13:11 ` [pdm-devel] [PATCH proxmox v2 1/4] access-control: add acl feature to only expose types and the AclTree Shannon Sterz
2025-10-23 9:24 ` Dominik Csapak
2025-10-23 11:32 ` Shannon Sterz
2025-10-22 13:11 ` [pdm-devel] [PATCH proxmox v2 2/4] access-control: move functions querying privileges to " Shannon Sterz
2025-10-22 13:11 ` [pdm-devel] [PATCH proxmox v2 3/4] access-control: derive Debug and PartialEq on AclTree and AclTreeNode Shannon Sterz
2025-10-22 13:11 ` [pdm-devel] [PATCH proxmox v2 4/4] access-control: allow reading all acls of the current authid Shannon Sterz
2025-10-23 9:31 ` Dominik Csapak
2025-10-23 11:32 ` Shannon Sterz
2025-10-22 13:11 ` [pdm-devel] [PATCH yew-comp v2 1/2] acl_context: add AclContext and AclContextProvider Shannon Sterz
2025-10-23 10:00 ` Dominik Csapak
2025-10-23 11:33 ` Shannon Sterz
2025-10-23 11:39 ` Dominik Csapak
2025-10-22 13:11 ` [pdm-devel] [PATCH yew-comp v2 2/2] http_helpers: reload LocalAclTree when logging in or refreshing a ticket Shannon Sterz
2025-10-22 13:11 ` Shannon Sterz [this message]
2025-10-22 13:11 ` [pdm-devel] [PATCH datacenter-manager v2 2/2] ui: add an AclContext via the AclContextProvider to the main app ui Shannon Sterz
2025-10-22 13:11 ` [pdm-devel] [PATCH yew-comp v2 1/1] notes view: allow hiding the toolbar if editing isn't supported Shannon Sterz
2025-10-23 9:36 ` Dominik Csapak
2025-10-23 11:33 ` Shannon Sterz
2025-10-22 13:11 ` [pdm-devel] [PATCH datacenter-manager v2 1/1] ui: main menu: use the AclContext to hide the Notes if appropriate Shannon Sterz
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251022131126.358790-8-s.sterz@proxmox.com \
--to=s.sterz@proxmox.com \
--cc=pdm-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox