diff --git a/Cargo.toml b/Cargo.toml index 3fc63301..2df47e9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ pam = "0.7" pam-sys = "0.5" percent-encoding = "2.1" pin-utils = "0.1.0-alpha" -proxmox = { version = "0.1.24", features = [ "sortable-macro", "api-macro" ] } +proxmox = { version = "0.1.25", features = [ "sortable-macro", "api-macro" ] } #proxmox = { git = "ssh://gitolite3@proxdev.maurer-it.com/rust/proxmox", version = "0.1.2", features = [ "sortable-macro", "api-macro" ] } #proxmox = { path = "../proxmox/proxmox", features = [ "sortable-macro", "api-macro" ] } regex = "1.2" diff --git a/src/api2/access/acl.rs b/src/api2/access/acl.rs index d18b3976..92d4a31e 100644 --- a/src/api2/access/acl.rs +++ b/src/api2/access/acl.rs @@ -5,7 +5,7 @@ use proxmox::api::{api, Router, RpcEnvironment, Permission}; use crate::api2::types::*; use crate::config::acl; -use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY}; +use crate::config::acl::{Role, PRIV_SYS_AUDIT, PRIV_SYS_MODIFY}; #[api( properties: { @@ -23,7 +23,7 @@ use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY}; description: "User or Group ID.", }, roleid: { - schema: ACL_ROLE_SCHEMA, + type: Role, } } )] @@ -118,7 +118,7 @@ pub fn read_acl( schema: ACL_PATH_SCHEMA, }, role: { - schema: ACL_ROLE_SCHEMA, + type: Role, }, propagate: { optional: true, diff --git a/src/api2/access/role.rs b/src/api2/access/role.rs index bdf51fa3..3d284dea 100644 --- a/src/api2/access/role.rs +++ b/src/api2/access/role.rs @@ -6,7 +6,7 @@ use proxmox::api::{api, Permission}; use proxmox::api::router::Router; use crate::api2::types::*; -use crate::config::acl::ROLE_NAMES; +use crate::config::acl::{Role, ROLE_NAMES}; #[api( returns: { @@ -17,7 +17,7 @@ use crate::config::acl::ROLE_NAMES; description: "User name with description.", properties: { role: { - schema: ACL_ROLE_SCHEMA, + type: Role, }, comment: { schema: SINGLE_LINE_COMMENT_SCHEMA, diff --git a/src/api2/reader.rs b/src/api2/reader.rs index 88206bd9..cb000153 100644 --- a/src/api2/reader.rs +++ b/src/api2/reader.rs @@ -31,15 +31,9 @@ pub const API_METHOD_UPGRADE_BACKUP: ApiMethod = ApiMethod::new( concat!("Upgraded to backup protocol ('", PROXMOX_BACKUP_READER_PROTOCOL_ID_V1!(), "')."), &sorted!([ ("store", false, &DATASTORE_SCHEMA), - ("backup-type", false, &StringSchema::new("Backup type.") - .format(&ApiStringFormat::Enum(&["vm", "ct", "host"])) - .schema() - ), - ("backup-id", false, &StringSchema::new("Backup ID.").schema()), - ("backup-time", false, &IntegerSchema::new("Backup time (Unix epoch.)") - .minimum(1_547_797_308) - .schema() - ), + ("backup-type", false, &BACKUP_TYPE_SCHEMA), + ("backup-id", false, &BACKUP_ID_SCHEMA), + ("backup-time", false, &BACKUP_TIME_SCHEMA), ("debug", true, &BooleanSchema::new("Enable verbose debug logging.").schema()), ]), ) diff --git a/src/api2/types.rs b/src/api2/types.rs index 5a0b355d..f5c32d9d 100644 --- a/src/api2/types.rs +++ b/src/api2/types.rs @@ -248,24 +248,9 @@ pub const ACL_PROPAGATE_SCHEMA: Schema = BooleanSchema::new( 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", - "Audit", - "Datastore.Admin", - "Datastore.Reader", - "Datastore.Audit", - "Datastore.Backup", - "Datastore.PowerUser", - "Remote.Admin", - "Remote.Audit", - "Remote.SyncOperator", - "NoAccess", - ])) + EnumEntry::new("user", "User"), + EnumEntry::new("group", "Group")])) .schema(); pub const BACKUP_ARCHIVE_NAME_SCHEMA: Schema = @@ -275,7 +260,10 @@ pub const BACKUP_ARCHIVE_NAME_SCHEMA: Schema = pub const BACKUP_TYPE_SCHEMA: Schema = StringSchema::new("Backup type.") - .format(&ApiStringFormat::Enum(&["vm", "ct", "host"])) + .format(&ApiStringFormat::Enum(&[ + EnumEntry::new("vm", "Virtual Machine Backup"), + EnumEntry::new("ct", "Container Backup"), + EnumEntry::new("host", "Host Backup")])) .schema(); pub const BACKUP_ID_SCHEMA: Schema = diff --git a/src/bin/proxmox-backup-client.rs b/src/bin/proxmox-backup-client.rs index b54cd729..474a0ca5 100644 --- a/src/bin/proxmox-backup-client.rs +++ b/src/bin/proxmox-backup-client.rs @@ -1852,7 +1852,9 @@ fn key_mgmt_cli() -> CliCommandMap { const KDF_SCHEMA: Schema = StringSchema::new("Key derivation function. Choose 'none' to store the key unecrypted.") - .format(&ApiStringFormat::Enum(&["scrypt", "none"])) + .format(&ApiStringFormat::Enum(&[ + EnumEntry::new("scrypt", "SCrypt"), + EnumEntry::new("none", "Do not encrypt the key")])) .default("scrypt") .schema(); diff --git a/src/config/acl.rs b/src/config/acl.rs index 7a3b314a..c8734265 100644 --- a/src/config/acl.rs +++ b/src/config/acl.rs @@ -2,12 +2,17 @@ use std::io::Write; use std::collections::{HashMap, HashSet, BTreeMap, BTreeSet}; use std::path::{PathBuf, Path}; use std::sync::{Arc, RwLock}; +use std::str::FromStr; use anyhow::{bail, Error}; use lazy_static::lazy_static; +use ::serde::{Deserialize, Serialize}; +use serde::de::{value, IntoDeserializer}; + use proxmox::tools::{fs::replace_file, fs::CreateOptions}; +use proxmox::api::{api, schema::*}; // define Privilege bitfield @@ -83,56 +88,56 @@ PRIV_REMOTE_PRUNE; pub const ROLE_NAME_NO_ACCESS: &str ="NoAccess"; +#[api()] +#[repr(u64)] +#[derive(Serialize, Deserialize)] +/// Role +pub enum Role { + /// Administrator + Admin = ROLE_ADMIN, + /// Auditor + Audit = ROLE_AUDIT, + /// Disable Access + NoAccess = ROLE_NO_ACCESS, + /// Datastore Administrator + DatastoreAdmin = ROLE_DATASTORE_ADMIN, + /// Datastore Reader (inspect datastore content and do restores) + DatastoreReader = ROLE_DATASTORE_READER, + /// Datastore Backup (backup and restore owned backups) + DatastoreBackup = ROLE_DATASTORE_BACKUP, + /// Datastore PowerUser (backup, restore and prune owned backup) + DatastorePowerUser = ROLE_DATASTORE_POWERUSER, + /// Datastore Auditor + DatastoreAudit = ROLE_DATASTORE_AUDIT, + /// Remote Auditor + RemoteAudit = ROLE_REMOTE_AUDIT, + /// Remote Administrator + RemoteAdmin = ROLE_REMOTE_ADMIN, + /// Syncronisation Opertator + RemoteSyncOperator = ROLE_REMOTE_SYNC_OPERATOR, +} + +impl FromStr for Role { + type Err = value::Error; + + fn from_str(s: &str) -> Result { + Self::deserialize(s.into_deserializer()) + } +} + lazy_static! { pub static ref ROLE_NAMES: HashMap<&'static str, (u64, &'static str)> = { let mut map = HashMap::new(); - map.insert("Admin", ( - ROLE_ADMIN, - "Administrator", - )); - map.insert("Audit", ( - ROLE_AUDIT, - "Auditor", - )); - map.insert(ROLE_NAME_NO_ACCESS, ( - ROLE_NO_ACCESS, - "Disable access", - )); + let list = match Role::API_SCHEMA { + Schema::String(StringSchema { format: Some(ApiStringFormat::Enum(list)), .. }) => list, + _ => unreachable!(), + }; - map.insert("Datastore.Admin", ( - ROLE_DATASTORE_ADMIN, - "Datastore Administrator", - )); - map.insert("Datastore.Reader", ( - ROLE_DATASTORE_READER, - "Datastore Reader (inspect datastore content and do restores)", - )); - map.insert("Datastore.Backup", ( - ROLE_DATASTORE_BACKUP, - "Datastore Backup (backup and restore owned backups)", - )); - map.insert("Datastore.PowerUser", ( - ROLE_DATASTORE_POWERUSER, - "Datastore PowerUser (backup, restore and prune owned backup)", - )); - map.insert("Datastore.Audit", ( - ROLE_DATASTORE_AUDIT, - "Datastore Auditor", - )); - - map.insert("Remote.Audit", ( - ROLE_REMOTE_AUDIT, - "Remote Auditor", - )); - map.insert("Remote.Admin", ( - ROLE_REMOTE_ADMIN, - "Remote Administrator", - )); - map.insert("Remote.SyncOperator", ( - ROLE_REMOTE_SYNC_OPERATOR, - "Syncronisation Opertator", - )); + for entry in list.iter() { + let privs: u64 = Role::from_str(entry.value).unwrap() as u64; + map.insert(entry.value, (privs, entry.description)); + } map }; @@ -615,15 +620,15 @@ mod test { let tree = AclTree::from_raw(r###" acl:0:/store/store2:user1:Admin acl:0:/store/store2:user2:Admin -acl:0:/store/store2:user1:Datastore.Backup -acl:0:/store/store2:user2:Datastore.Backup +acl:0:/store/store2:user1:DatastoreBackup +acl:0:/store/store2:user2:DatastoreBackup "###)?; let mut raw: Vec = Vec::new(); tree.write_config(&mut raw)?; let raw = std::str::from_utf8(&raw)?; - assert_eq!(raw, "acl:0:/store/store2:user1,user2:Admin,Datastore.Backup\n"); + assert_eq!(raw, "acl:0:/store/store2:user1,user2:Admin,DatastoreBackup\n"); Ok(()) } @@ -633,18 +638,18 @@ acl:0:/store/store2:user2:Datastore.Backup let tree = AclTree::from_raw(r###" acl:1:/storage:user1@pbs:Admin -acl:1:/storage/store1:user1@pbs:Datastore.Backup -acl:1:/storage/store2:user2@pbs:Datastore.Backup +acl:1:/storage/store1:user1@pbs:DatastoreBackup +acl:1:/storage/store2:user2@pbs:DatastoreBackup "###)?; check_roles(&tree, "user1@pbs", "/", ""); check_roles(&tree, "user1@pbs", "/storage", "Admin"); - check_roles(&tree, "user1@pbs", "/storage/store1", "Datastore.Backup"); + check_roles(&tree, "user1@pbs", "/storage/store1", "DatastoreBackup"); check_roles(&tree, "user1@pbs", "/storage/store2", "Admin"); check_roles(&tree, "user2@pbs", "/", ""); check_roles(&tree, "user2@pbs", "/storage", ""); check_roles(&tree, "user2@pbs", "/storage/store1", ""); - check_roles(&tree, "user2@pbs", "/storage/store2", "Datastore.Backup"); + check_roles(&tree, "user2@pbs", "/storage/store2", "DatastoreBackup"); Ok(()) } @@ -655,22 +660,22 @@ acl:1:/storage/store2:user2@pbs:Datastore.Backup let tree = AclTree::from_raw(r###" acl:1:/:user1@pbs:Admin acl:1:/storage:user1@pbs:NoAccess -acl:1:/storage/store1:user1@pbs:Datastore.Backup +acl:1:/storage/store1:user1@pbs:DatastoreBackup "###)?; check_roles(&tree, "user1@pbs", "/", "Admin"); check_roles(&tree, "user1@pbs", "/storage", "NoAccess"); - check_roles(&tree, "user1@pbs", "/storage/store1", "Datastore.Backup"); + check_roles(&tree, "user1@pbs", "/storage/store1", "DatastoreBackup"); check_roles(&tree, "user1@pbs", "/storage/store2", "NoAccess"); check_roles(&tree, "user1@pbs", "/system", "Admin"); let tree = AclTree::from_raw(r###" acl:1:/:user1@pbs:Admin acl:0:/storage:user1@pbs:NoAccess -acl:1:/storage/store1:user1@pbs:Datastore.Backup +acl:1:/storage/store1:user1@pbs:DatastoreBackup "###)?; check_roles(&tree, "user1@pbs", "/", "Admin"); check_roles(&tree, "user1@pbs", "/storage", "NoAccess"); - check_roles(&tree, "user1@pbs", "/storage/store1", "Datastore.Backup"); + check_roles(&tree, "user1@pbs", "/storage/store1", "DatastoreBackup"); check_roles(&tree, "user1@pbs", "/storage/store2", "Admin"); check_roles(&tree, "user1@pbs", "/system", "Admin"); diff --git a/tests/verify-api.rs b/tests/verify-api.rs index a5887758..d919f154 100644 --- a/tests/verify-api.rs +++ b/tests/verify-api.rs @@ -142,27 +142,3 @@ fn verify_root_api() -> Result<(), Error> { Ok(()) } - -#[test] -fn verify_acl_role_schema() -> Result<(), Error> { - - let list = match api2::types::ACL_ROLE_SCHEMA { - Schema::String(StringSchema { format: Some(ApiStringFormat::Enum(list)), .. }) => list, - _ => unreachable!(), - }; - - let map = &proxmox_backup::config::acl::ROLE_NAMES; - for item in *list { - if !map.contains_key(item) { - bail!("found role '{}' without description/mapping", item); - } - } - - for role in map.keys() { - if !list.contains(role) { - bail!("role '{}' missing in ACL_ROLE_SCHEMA enum", role); - } - } - - Ok(()) -}