]> git.proxmox.com Git - proxmox-backup.git/commitdiff
move user configuration to pbs_config workspace
authorDietmar Maurer <dietmar@proxmox.com>
Fri, 10 Sep 2021 04:53:53 +0000 (06:53 +0200)
committerDietmar Maurer <dietmar@proxmox.com>
Fri, 10 Sep 2021 05:09:04 +0000 (07:09 +0200)
Also moved memcom.rs and cached_user_info.rs

45 files changed:
pbs-config/Cargo.toml
pbs-config/src/cached_user_info.rs [new file with mode: 0644]
pbs-config/src/lib.rs
pbs-config/src/memcom.rs [new file with mode: 0644]
pbs-config/src/user.rs [new file with mode: 0644]
src/api2/access/acl.rs
src/api2/access/mod.rs
src/api2/access/openid.rs
src/api2/access/tfa.rs
src/api2/access/user.rs
src/api2/admin/datastore.rs
src/api2/admin/sync.rs
src/api2/admin/verify.rs
src/api2/backup/mod.rs
src/api2/config/changer.rs
src/api2/config/datastore.rs
src/api2/config/drive.rs
src/api2/config/media_pool.rs
src/api2/config/remote.rs
src/api2/config/sync.rs
src/api2/config/tape_backup_job.rs
src/api2/config/verify.rs
src/api2/node/subscription.rs
src/api2/node/tasks.rs
src/api2/pull.rs
src/api2/reader/mod.rs
src/api2/status.rs
src/api2/tape/backup.rs
src/api2/tape/changer.rs
src/api2/tape/drive.rs
src/api2/tape/media.rs
src/api2/tape/restore.rs
src/bin/docgen.rs
src/bin/proxmox_backup_manager/acl.rs
src/bin/proxmox_backup_manager/user.rs
src/bin/proxmox_restore_daemon/auth.rs
src/config/cached_user_info.rs [deleted file]
src/config/mod.rs
src/config/user.rs [deleted file]
src/server/auth.rs
src/server/email_notifications.rs
src/server/prune_job.rs
src/server/rest.rs
src/tools/memcom.rs [deleted file]
src/tools/mod.rs

index ed71a88c69d00098a914baffe609a6e6d2ead9c3..7f4258bd1cd4cb9d844d008bf5509ecb4487bc9e 100644 (file)
@@ -14,7 +14,7 @@ serde_json = "1.0"
 openssl = "0.10"
 nix = "0.19.1"
 regex = "1.2"
-
+once_cell = "1.3.1"
 
 proxmox = { version = "0.13.0", default-features = false, features = [ "cli" ] }
 
diff --git a/pbs-config/src/cached_user_info.rs b/pbs-config/src/cached_user_info.rs
new file mode 100644 (file)
index 0000000..960864f
--- /dev/null
@@ -0,0 +1,185 @@
+//! Cached user info for fast ACL permission checks
+
+use std::sync::{RwLock, Arc};
+
+use anyhow::{Error, bail};
+
+use proxmox::api::section_config::SectionConfigData;
+use lazy_static::lazy_static;
+use proxmox::api::UserInformation;
+use proxmox::tools::time::epoch_i64;
+
+use pbs_api_types::{Authid, Userid, User, ApiToken, ROLE_ADMIN};
+
+use crate::acl::{AclTree, ROLE_NAMES};
+use crate::memcom::Memcom;
+
+/// Cache User/Group/Token/Acl configuration data for fast permission tests
+pub struct CachedUserInfo {
+    user_cfg: Arc<SectionConfigData>,
+    acl_tree: Arc<AclTree>,
+}
+
+struct ConfigCache {
+    data: Option<Arc<CachedUserInfo>>,
+    last_update: i64,
+    last_user_cache_generation: usize,
+}
+
+lazy_static! {
+    static ref CACHED_CONFIG: RwLock<ConfigCache> = RwLock::new(
+        ConfigCache { data: None, last_update: 0, last_user_cache_generation: 0 }
+    );
+}
+
+impl CachedUserInfo {
+
+    /// Returns a cached instance (up to 5 seconds old).
+    pub fn new() -> Result<Arc<Self>, Error> {
+        let now = epoch_i64();
+
+        let memcom = Memcom::new()?;
+        let user_cache_generation = memcom.user_cache_generation();
+
+        { // limit scope
+            let cache = CACHED_CONFIG.read().unwrap();
+            if (user_cache_generation == cache.last_user_cache_generation) &&
+                ((now - cache.last_update) < 5)
+            {
+                if let Some(ref config) = cache.data {
+                    return Ok(config.clone());
+                }
+            }
+        }
+
+        let config = Arc::new(CachedUserInfo {
+            user_cfg: crate::user::cached_config()?,
+            acl_tree: crate::acl::cached_config()?,
+        });
+
+        let mut cache = CACHED_CONFIG.write().unwrap();
+        cache.last_update = now;
+        cache.last_user_cache_generation = user_cache_generation;
+        cache.data = Some(config.clone());
+
+        Ok(config)
+    }
+
+    /// Only exposed for testing
+    #[doc(hidden)]
+    pub fn test_new(user_cfg: SectionConfigData, acl_tree: AclTree) -> Self {
+        Self {
+            user_cfg: Arc::new(user_cfg),
+            acl_tree: Arc::new(acl_tree),
+        }
+    }
+
+    /// Test if a user_id is enabled and not expired
+    pub fn is_active_user_id(&self, userid: &Userid) -> bool {
+        if let Ok(info) = self.user_cfg.lookup::<User>("user", userid.as_str()) {
+            info.is_active()
+        } else {
+            false
+        }
+    }
+
+    /// Test if a authentication id is enabled and not expired
+    pub fn is_active_auth_id(&self, auth_id: &Authid) -> bool {
+        let userid = auth_id.user();
+
+        if !self.is_active_user_id(userid) {
+            return false;
+        }
+
+        if auth_id.is_token() {
+            if let Ok(info) = self.user_cfg.lookup::<ApiToken>("token", &auth_id.to_string()) {
+                return info.is_active();
+            } else {
+                return false;
+            }
+        }
+
+        true
+    }
+
+    pub fn check_privs(
+        &self,
+        auth_id: &Authid,
+        path: &[&str],
+        required_privs: u64,
+        partial: bool,
+    ) -> Result<(), Error> {
+        let privs = self.lookup_privs(&auth_id, path);
+        let allowed = if partial {
+            (privs & required_privs) != 0
+        } else {
+            (privs & required_privs) == required_privs
+        };
+        if !allowed {
+            // printing the path doesn't leaks any information as long as we
+            // always check privilege before resource existence
+            bail!("no permissions on '/{}'", path.join("/"));
+        }
+        Ok(())
+    }
+
+    pub fn is_superuser(&self, auth_id: &Authid) -> bool {
+        !auth_id.is_token() && auth_id.user() == "root@pam"
+    }
+
+    pub fn is_group_member(&self, _userid: &Userid, _group: &str) -> bool {
+        false
+    }
+
+    pub fn lookup_privs(&self, auth_id: &Authid, path: &[&str]) -> u64 {
+        let (privs, _) = self.lookup_privs_details(auth_id, path);
+        privs
+    }
+
+    pub fn lookup_privs_details(&self, auth_id: &Authid, path: &[&str]) -> (u64, u64) {
+        if self.is_superuser(auth_id) {
+            return (ROLE_ADMIN, ROLE_ADMIN);
+        }
+
+        let roles = self.acl_tree.roles(auth_id, path);
+        let mut privs: u64 = 0;
+        let mut propagated_privs: u64 = 0;
+        for (role, propagate) in roles {
+            if let Some((role_privs, _)) = ROLE_NAMES.get(role.as_str()) {
+                if propagate {
+                    propagated_privs |= role_privs;
+                }
+                privs |= role_privs;
+            }
+        }
+
+        if auth_id.is_token() {
+            // limit privs to that of owning user
+            let user_auth_id = Authid::from(auth_id.user().clone());
+            privs &= self.lookup_privs(&user_auth_id, path);
+            let (owner_privs, owner_propagated_privs) = self.lookup_privs_details(&user_auth_id, path);
+            privs &= owner_privs;
+            propagated_privs &= owner_propagated_privs;
+        }
+
+        (privs, propagated_privs)
+    }
+
+}
+
+impl UserInformation for CachedUserInfo {
+    fn is_superuser(&self, userid: &str) -> bool {
+        userid == "root@pam"
+    }
+
+    fn is_group_member(&self, _userid: &str, _group: &str) -> bool {
+        false
+    }
+
+    fn lookup_privs(&self, auth_id: &str, path: &[&str]) -> u64 {
+        match auth_id.parse::<Authid>() {
+            Ok(auth_id) => Self::lookup_privs(self, &auth_id, path),
+            Err(_) => 0,
+        }
+    }
+}
index a5a50aff1b44e3d1bf8a7558fe97eeceb96d20d0..3a3d51a63f706f66ab7954bca8bb12b4cfb4ea88 100644 (file)
@@ -1,4 +1,6 @@
 pub mod acl;
+mod cached_user_info;
+pub use cached_user_info::CachedUserInfo;
 pub mod domains;
 pub mod drive;
 pub mod key_config;
@@ -9,8 +11,11 @@ pub mod sync;
 pub mod tape_encryption_keys;
 pub mod tape_job;
 pub mod token_shadow;
+pub mod user;
 pub mod verify;
 
+pub(crate) mod memcom;
+
 use anyhow::{format_err, Error};
 
 pub use pbs_buildcfg::{BACKUP_USER_NAME, BACKUP_GROUP_NAME};
diff --git a/pbs-config/src/memcom.rs b/pbs-config/src/memcom.rs
new file mode 100644 (file)
index 0000000..96b577b
--- /dev/null
@@ -0,0 +1,81 @@
+//! Memory based communication channel between proxy & daemon for things such as cache
+//! invalidation.
+
+use std::os::unix::io::AsRawFd;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Arc;
+
+use anyhow::Error;
+use nix::fcntl::OFlag;
+use nix::sys::mman::{MapFlags, ProtFlags};
+use nix::sys::stat::Mode;
+use once_cell::sync::OnceCell;
+
+use proxmox::tools::fs::CreateOptions;
+use proxmox::tools::mmap::Mmap;
+
+/// In-memory communication channel.
+pub struct Memcom {
+    mmap: Mmap<u8>,
+}
+
+#[repr(C)]
+struct Head {
+    // User (user.cfg) cache generation/version.
+    user_cache_generation: AtomicUsize,
+}
+
+static INSTANCE: OnceCell<Arc<Memcom>> = OnceCell::new();
+
+const MEMCOM_FILE_PATH: &str = pbs_buildcfg::rundir!("/proxmox-backup-memcom");
+const EMPTY_PAGE: [u8; 4096] = [0u8; 4096];
+
+impl Memcom {
+    /// Open the memory based communication channel singleton.
+    pub fn new() -> Result<Arc<Self>, Error> {
+        INSTANCE.get_or_try_init(Self::open).map(Arc::clone)
+    }
+
+    // Actual work of `new`:
+    fn open() -> Result<Arc<Self>, Error> {
+        let user = crate::backup_user()?;
+        let options = CreateOptions::new()
+            .perm(Mode::from_bits_truncate(0o660))
+            .owner(user.uid)
+            .group(user.gid);
+
+        let file = proxmox::tools::fs::atomic_open_or_create_file(
+            MEMCOM_FILE_PATH,
+            OFlag::O_RDWR | OFlag::O_CLOEXEC,
+            &EMPTY_PAGE, options)?;
+
+        let mmap = unsafe {
+            Mmap::<u8>::map_fd(
+                file.as_raw_fd(),
+                0,
+                4096,
+                ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
+                MapFlags::MAP_SHARED | MapFlags::MAP_NORESERVE | MapFlags::MAP_POPULATE,
+            )?
+        };
+
+        Ok(Arc::new(Self { mmap }))
+    }
+
+    // Shortcut to get the mapped `Head` as a `Head`.
+    fn head(&self) -> &Head {
+        unsafe { &*(self.mmap.as_ptr() as *const u8 as *const Head) }
+    }
+
+    /// Returns the user cache generation number.
+    pub fn user_cache_generation(&self) -> usize {
+        self.head().user_cache_generation.load(Ordering::Acquire)
+    }
+
+    /// Increase the user cache generation number.
+    pub fn increase_user_cache_generation(&self) {
+        self.head()
+            .user_cache_generation
+            .fetch_add(1, Ordering::AcqRel);
+    }
+}
diff --git a/pbs-config/src/user.rs b/pbs-config/src/user.rs
new file mode 100644 (file)
index 0000000..1328773
--- /dev/null
@@ -0,0 +1,200 @@
+use std::collections::HashMap;
+use std::sync::{Arc, RwLock};
+
+use anyhow::{bail, Error};
+use lazy_static::lazy_static;
+
+use proxmox::api::{
+    schema::*,
+    section_config::{
+        SectionConfig,
+        SectionConfigData,
+        SectionConfigPlugin,
+    }
+};
+
+use pbs_api_types::{
+    Authid, Userid, ApiToken, User,
+};
+
+use crate::memcom::Memcom;
+
+use crate::{open_backup_lockfile, replace_backup_config, BackupLockGuard};
+
+lazy_static! {
+    pub static ref CONFIG: SectionConfig = init();
+}
+
+fn init() -> SectionConfig {
+    let mut config = SectionConfig::new(&Authid::API_SCHEMA);
+
+    let user_schema = match User::API_SCHEMA {
+        Schema::Object(ref user_schema) => user_schema,
+        _ => unreachable!(),
+    };
+    let user_plugin = SectionConfigPlugin::new("user".to_string(), Some("userid".to_string()), user_schema);
+    config.register_plugin(user_plugin);
+
+    let token_schema = match ApiToken::API_SCHEMA {
+        Schema::Object(ref token_schema) => token_schema,
+        _ => unreachable!(),
+    };
+    let token_plugin = SectionConfigPlugin::new("token".to_string(), Some("tokenid".to_string()), token_schema);
+    config.register_plugin(token_plugin);
+
+    config
+}
+
+pub const USER_CFG_FILENAME: &str = "/etc/proxmox-backup/user.cfg";
+pub const USER_CFG_LOCKFILE: &str = "/etc/proxmox-backup/.user.lck";
+
+/// Get exclusive lock
+pub fn lock_config() -> Result<BackupLockGuard, Error> {
+    open_backup_lockfile(USER_CFG_LOCKFILE, None, true)
+}
+
+pub fn config() -> Result<(SectionConfigData, [u8;32]), Error> {
+
+    let content = proxmox::tools::fs::file_read_optional_string(USER_CFG_FILENAME)?
+        .unwrap_or_else(|| "".to_string());
+
+    let digest = openssl::sha::sha256(content.as_bytes());
+    let mut data = CONFIG.parse(USER_CFG_FILENAME, &content)?;
+
+    if data.sections.get("root@pam").is_none() {
+        let user: User = User {
+            userid: Userid::root_userid().clone(),
+            comment: Some("Superuser".to_string()),
+            enable: None,
+            expire: None,
+            firstname: None,
+            lastname: None,
+            email: None,
+        };
+        data.set_data("root@pam", "user", &user).unwrap();
+    }
+
+    Ok((data, digest))
+}
+
+pub fn cached_config() -> Result<Arc<SectionConfigData>, Error> {
+
+    struct ConfigCache {
+        data: Option<Arc<SectionConfigData>>,
+        last_mtime: i64,
+        last_mtime_nsec: i64,
+    }
+
+    lazy_static! {
+        static ref CACHED_CONFIG: RwLock<ConfigCache> = RwLock::new(
+            ConfigCache { data: None, last_mtime: 0, last_mtime_nsec: 0 });
+    }
+
+    let stat = match nix::sys::stat::stat(USER_CFG_FILENAME) {
+        Ok(stat) => Some(stat),
+        Err(nix::Error::Sys(nix::errno::Errno::ENOENT)) => None,
+        Err(err) => bail!("unable to stat '{}' - {}", USER_CFG_FILENAME, err),
+    };
+
+    { // limit scope
+        let cache = CACHED_CONFIG.read().unwrap();
+        if let Some(ref config) = cache.data {
+            if let Some(stat) = stat {
+                if stat.st_mtime == cache.last_mtime && stat.st_mtime_nsec == cache.last_mtime_nsec {
+                    return Ok(config.clone());
+                }
+            } else if cache.last_mtime == 0 && cache.last_mtime_nsec == 0 {
+                return Ok(config.clone());
+            }
+        }
+    }
+
+    let (config, _digest) = config()?;
+    let config = Arc::new(config);
+
+    let mut cache = CACHED_CONFIG.write().unwrap();
+    if let Some(stat) = stat {
+        cache.last_mtime = stat.st_mtime;
+        cache.last_mtime_nsec = stat.st_mtime_nsec;
+    }
+    cache.data = Some(config.clone());
+
+    Ok(config)
+}
+
+pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
+    let raw = CONFIG.write(USER_CFG_FILENAME, &config)?;
+    replace_backup_config(USER_CFG_FILENAME, raw.as_bytes())?;
+
+    // increase user cache generation
+    // We use this in CachedUserInfo
+    let memcom = Memcom::new()?;
+    memcom.increase_user_cache_generation();
+
+    Ok(())
+}
+
+/// Only exposed for testing
+#[doc(hidden)]
+pub fn test_cfg_from_str(raw: &str) -> Result<(SectionConfigData, [u8;32]), Error> {
+    let cfg = init();
+    let parsed = cfg.parse("test_user_cfg", raw)?;
+
+    Ok((parsed, [0;32]))
+}
+
+// shell completion helper
+pub fn complete_userid(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
+    match config() {
+        Ok((data, _digest)) => {
+            data.sections.iter()
+                .filter_map(|(id, (section_type, _))| {
+                    if section_type == "user" {
+                        Some(id.to_string())
+                    } else {
+                        None
+                    }
+                }).collect()
+        },
+        Err(_) => return vec![],
+    }
+}
+
+// shell completion helper
+pub fn complete_authid(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
+    match config() {
+        Ok((data, _digest)) => data.sections.iter().map(|(id, _)| id.to_string()).collect(),
+        Err(_) => vec![],
+    }
+}
+
+// shell completion helper
+pub fn complete_token_name(_arg: &str, param: &HashMap<String, String>) -> Vec<String> {
+    let data = match config() {
+        Ok((data, _digest)) => data,
+        Err(_) => return Vec::new(),
+    };
+
+    match param.get("userid") {
+        Some(userid) => {
+            let user = data.lookup::<User>("user", userid);
+            let tokens = data.convert_to_typed_array("token");
+            match (user, tokens) {
+                (Ok(_), Ok(tokens)) => {
+                    tokens
+                        .into_iter()
+                        .filter_map(|token: ApiToken| {
+                            let tokenid = token.tokenid;
+                            if tokenid.is_token() && tokenid.user() == userid {
+                                Some(tokenid.tokenname().unwrap().as_str().to_string())
+                            } else {
+                                None
+                            }
+                        }).collect()
+                },
+                _ => vec![],
+            }
+        },
+        None => vec![],
+    }
+}
index 4dec36601a26c7476581d81c9dbd887a7a9fee54..764bd46fdc0406a8e0576f6560b1461a886445ac 100644 (file)
@@ -12,7 +12,7 @@ use pbs_api_types::{
 
 use pbs_config::acl::AclTreeNode;
 
-use crate::config::cached_user_info::CachedUserInfo;
+use pbs_config::CachedUserInfo;
 
 fn extract_acl_node_data(
     node: &AclTreeNode,
@@ -221,7 +221,7 @@ pub fn update_acl(
         bail!("parameter 'group' - groups are currently not supported.");
     } else if let Some(ref auth_id) = auth_id {
         if !delete { // Note: we allow to delete non-existent users
-            let user_cfg = crate::config::user::cached_config()?;
+            let user_cfg = pbs_config::user::cached_config()?;
             if user_cfg.sections.get(&auth_id.to_string()).is_none() {
                 bail!(format!("no such {}.",
                               if auth_id.is_token() { "API token" } else { "user" }));
index f504d7637933095fa9fc0814d57c994a08fea3a0..58ac8ca414ef9276f218b78d06876d79d2dbbe9e 100644 (file)
@@ -22,7 +22,7 @@ use pbs_config::acl::AclTreeNode;
 use crate::auth_helpers::*;
 use crate::server::ticket::ApiTicket;
 
-use crate::config::cached_user_info::CachedUserInfo;
+use pbs_config::CachedUserInfo;
 use crate::config::tfa::TfaChallenge;
 
 pub mod acl;
index 99636e9569d919060ad7ece47d5651f016720c2a..38fab409320dfa8c4087ec45323ce5991d8bbfe0 100644 (file)
@@ -19,7 +19,7 @@ use pbs_tools::ticket::Ticket;
 use pbs_config::domains::{OpenIdUserAttribute, OpenIdRealmConfig};
 
 use crate::server::ticket::ApiTicket;
-use crate::config::cached_user_info::CachedUserInfo;
+use pbs_config::CachedUserInfo;
 
 use pbs_config::open_backup_lockfile;
 
@@ -116,7 +116,7 @@ pub fn openid_login(
 
     if !user_info.is_active_user_id(&user_id) {
         if config.autocreate.unwrap_or(false) {
-            use crate::config::user;
+            use pbs_config::user;
             let _lock = open_backup_lockfile(user::USER_CFG_LOCKFILE, None, true)?;
             let user = User {
                 userid: user_id.clone(),
index 8b0166c6375ed36d0e6d5448329a424f393f61d3..0426fcd40ca6b66568ec1fedd196777bfa7fd163 100644 (file)
@@ -9,7 +9,7 @@ use proxmox::{http_bail, http_err};
 
 use pbs_api_types::{Authid, Userid, User, PASSWORD_SCHEMA, PRIV_PERMISSIONS_MODIFY, PRIV_SYS_AUDIT};
 
-use crate::config::cached_user_info::CachedUserInfo;
+use pbs_config::CachedUserInfo;
 use crate::config::tfa::{TfaInfo, TfaUserData};
 
 /// Perform first-factor (password) authentication only. Ignore password for the root user.
@@ -34,7 +34,7 @@ fn tfa_update_auth(
 
     // After authentication, verify that the to-be-modified user actually exists:
     if must_exist && authid.user() != userid {
-        let (config, _digest) = crate::config::user::config()?;
+        let (config, _digest) = pbs_config::user::config()?;
 
         if config
             .lookup::<User>("user", userid.as_str())
index 6a2fe83c83613deaa4936dd55aadceccf8fffeb2..75071cf1844a101f8e1f2242f3a02842654d511a 100644 (file)
@@ -16,7 +16,7 @@ use pbs_api_types::{
 };
 use pbs_config::token_shadow;
 
-use crate::config::cached_user_info::CachedUserInfo;
+use pbs_config::CachedUserInfo;
 
 fn new_user_with_tokens(user: User) -> UserWithTokens {
     UserWithTokens {
@@ -59,7 +59,7 @@ pub fn list_users(
     mut rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<Vec<UserWithTokens>, Error> {
 
-    let (config, digest) = crate::config::user::config()?;
+    let (config, digest) = pbs_config::user::config()?;
 
     let auth_id: Authid = rpcenv
         .get_auth_id()
@@ -138,9 +138,9 @@ pub fn create_user(
     rpcenv: &mut dyn RpcEnvironment
 ) -> Result<(), Error> {
 
-    let _lock = crate::config::user::lock_config()?;
+    let _lock = pbs_config::user::lock_config()?;
 
-    let (mut section_config, _digest) = crate::config::user::config()?;
+    let (mut section_config, _digest) = pbs_config::user::config()?;
 
     if section_config.sections.get(config.userid.as_str()).is_some() {
         bail!("user '{}' already exists.", config.userid);
@@ -153,7 +153,7 @@ pub fn create_user(
     // Fails if realm does not exist!
     let authenticator = crate::auth::lookup_authenticator(realm)?;
 
-    crate::config::user::save_config(&section_config)?;
+    pbs_config::user::save_config(&section_config)?;
 
     if let Some(password) = password {
         let user_info = CachedUserInfo::new()?;
@@ -185,7 +185,7 @@ pub fn create_user(
 )]
 /// Read user configuration data.
 pub fn read_user(userid: Userid, mut rpcenv: &mut dyn RpcEnvironment) -> Result<User, Error> {
-    let (config, digest) = crate::config::user::config()?;
+    let (config, digest) = pbs_config::user::config()?;
     let user = config.lookup("user", userid.as_str())?;
     rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
     Ok(user)
@@ -253,9 +253,9 @@ pub fn update_user(
     rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<(), Error> {
 
-    let _lock = crate::config::user::lock_config()?;
+    let _lock = pbs_config::user::lock_config()?;
 
-    let (mut config, expected_digest) = crate::config::user::config()?;
+    let (mut config, expected_digest) = pbs_config::user::config()?;
 
     if let Some(ref digest) = digest {
         let digest = proxmox::tools::hex_to_digest(digest)?;
@@ -317,7 +317,7 @@ pub fn update_user(
 
     config.set_data(userid.as_str(), "user", &data)?;
 
-    crate::config::user::save_config(&config)?;
+    pbs_config::user::save_config(&config)?;
 
     Ok(())
 }
@@ -345,10 +345,10 @@ pub fn update_user(
 /// Remove a user from the configuration file.
 pub fn delete_user(userid: Userid, digest: Option<String>) -> Result<(), Error> {
 
-    let _lock = crate::config::user::lock_config()?;
+    let _lock = pbs_config::user::lock_config()?;
     let _tfa_lock = crate::config::tfa::write_lock()?;
  
-    let (mut config, expected_digest) = crate::config::user::config()?;
+    let (mut config, expected_digest) = pbs_config::user::config()?;
 
     if let Some(ref digest) = digest {
         let digest = proxmox::tools::hex_to_digest(digest)?;
@@ -360,7 +360,7 @@ pub fn delete_user(userid: Userid, digest: Option<String>) -> Result<(), Error>
         None => bail!("user '{}' does not exist.", userid),
     }
 
-    crate::config::user::save_config(&config)?;
+    pbs_config::user::save_config(&config)?;
 
     let authenticator = crate::auth::lookup_authenticator(userid.realm())?;
     match authenticator.remove_password(userid.name()) {
@@ -416,7 +416,7 @@ pub fn read_token(
     mut rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<ApiToken, Error> {
 
-    let (config, digest) = crate::config::user::config()?;
+    let (config, digest) = pbs_config::user::config()?;
 
     let tokenid = Authid::from((userid, Some(tokenname)));
 
@@ -482,9 +482,9 @@ pub fn generate_token(
     digest: Option<String>,
 ) -> Result<Value, Error> {
 
-    let _lock = crate::config::user::lock_config()?;
+    let _lock = pbs_config::user::lock_config()?;
 
-    let (mut config, expected_digest) = crate::config::user::config()?;
+    let (mut config, expected_digest) = pbs_config::user::config()?;
 
     if let Some(ref digest) = digest {
         let digest = proxmox::tools::hex_to_digest(digest)?;
@@ -510,7 +510,7 @@ pub fn generate_token(
 
     config.set_data(&tokenid_string, "token", &token)?;
 
-    crate::config::user::save_config(&config)?;
+    pbs_config::user::save_config(&config)?;
 
     Ok(json!({
         "tokenid": tokenid_string,
@@ -563,9 +563,9 @@ pub fn update_token(
     digest: Option<String>,
 ) -> Result<(), Error> {
 
-    let _lock = crate::config::user::lock_config()?;
+    let _lock = pbs_config::user::lock_config()?;
 
-    let (mut config, expected_digest) = crate::config::user::config()?;
+    let (mut config, expected_digest) = pbs_config::user::config()?;
 
     if let Some(ref digest) = digest {
         let digest = proxmox::tools::hex_to_digest(digest)?;
@@ -596,7 +596,7 @@ pub fn update_token(
 
     config.set_data(&tokenid_string, "token", &data)?;
 
-    crate::config::user::save_config(&config)?;
+    pbs_config::user::save_config(&config)?;
 
     Ok(())
 }
@@ -631,9 +631,9 @@ pub fn delete_token(
     digest: Option<String>,
 ) -> Result<(), Error> {
 
-    let _lock = crate::config::user::lock_config()?;
+    let _lock = pbs_config::user::lock_config()?;
 
-    let (mut config, expected_digest) = crate::config::user::config()?;
+    let (mut config, expected_digest) = pbs_config::user::config()?;
 
     if let Some(ref digest) = digest {
         let digest = proxmox::tools::hex_to_digest(digest)?;
@@ -650,7 +650,7 @@ pub fn delete_token(
 
     token_shadow::delete_secret(&tokenid)?;
 
-    crate::config::user::save_config(&config)?;
+    pbs_config::user::save_config(&config)?;
 
     Ok(())
 }
@@ -682,7 +682,7 @@ pub fn list_tokens(
     mut rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<Vec<ApiToken>, Error> {
 
-    let (config, digest) = crate::config::user::config()?;
+    let (config, digest) = pbs_config::user::config()?;
 
     let list:Vec<ApiToken> = config.convert_to_typed_array("token")?;
 
index 0863d00085d23833b6c34549a180a60d2ec66b50..5b935737402018979689808aebe8ad57bf9eefe8 100644 (file)
@@ -58,7 +58,7 @@ use crate::backup::{
     DataStore, LocalChunkReader,
 };
 use crate::config::datastore;
-use crate::config::cached_user_info::CachedUserInfo;
+use pbs_config::CachedUserInfo;
 
 use crate::server::{jobstate::Job, WorkerTask};
 
index 83d5c05dae7b0bc429abe342baee6d25a257aafc..07f268b5242b02bac2c33faaff48667d2934419b 100644 (file)
@@ -9,6 +9,7 @@ use proxmox::{list_subdirs_api_method, sortable};
 
 use pbs_api_types::{DATASTORE_SCHEMA, JOB_ID_SCHEMA, Authid, SyncJobConfig, SyncJobStatus};
 use pbs_config::sync;
+use pbs_config::CachedUserInfo;
 
 use crate::{
     api2::{
@@ -18,9 +19,6 @@ use crate::{
             check_sync_job_read_access,
         },
     },
-    config::{
-        cached_user_info::CachedUserInfo,
-    },
     server::{
         jobstate::{
             Job,
index cc30f25884cc2503fa26400d4461ccba5aa8082a..72050c042629fdd61189e5b78ce588271b0809da 100644 (file)
@@ -12,6 +12,7 @@ use pbs_api_types::{
     PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_VERIFY,
 };
 use pbs_config::verify;
+use pbs_config::CachedUserInfo;
 
 use crate::{
     api2::types::{
@@ -25,7 +26,6 @@ use crate::{
             compute_schedule_status,
         },
     },
-    config::cached_user_info::CachedUserInfo,
 };
 
 #[api(
index 76e0e28ab507460444ab63a96c9b642bef897c99..cb9a859f503f88bf49acd3bdbd6df42a42931b88 100644 (file)
@@ -26,7 +26,7 @@ use pbs_datastore::manifest::{archive_type, ArchiveType};
 
 use crate::server::{WorkerTask, H2Service};
 use crate::backup::DataStore;
-use crate::config::cached_user_info::CachedUserInfo;
+use pbs_config::CachedUserInfo;
 
 mod environment;
 use environment::*;
index 720fc6bd01a75cd948c6f6c0e0f8343a6946a18e..5ef974a0f180dddcd910c2920d171a069f1bb6a4 100644 (file)
@@ -15,9 +15,9 @@ use pbs_api_types::{
     PROXMOX_CONFIG_DIGEST_SCHEMA, CHANGER_NAME_SCHEMA, SLOT_ARRAY_SCHEMA,
     PRIV_TAPE_AUDIT, PRIV_TAPE_MODIFY,
 };
+use pbs_config::CachedUserInfo;
 
 use crate::{
-    config::cached_user_info::CachedUserInfo,
     tape::{
         linux_tape_changer_list,
         check_drive_path,
index 9b1271151cdba657e28c848b1a0d03177b2e2163..512b59d36ecea65772732eef4a7b1446bc37f525 100644 (file)
@@ -24,7 +24,7 @@ use crate::api2::admin::{
     sync::list_sync_jobs,
     verify::list_verification_jobs,
 };
-use crate::config::cached_user_info::CachedUserInfo;
+use pbs_config::CachedUserInfo;
 use crate::config::datastore::{self, DataStoreConfig, DataStoreConfigUpdater};
 use crate::server::{jobstate, WorkerTask};
 
index 74ce3cbb6eb2498ab7cd62c57bed1e09a6ddcef6..703bf00a08c98ade5cd19dcb64589443070978ea 100644 (file)
@@ -8,9 +8,9 @@ use pbs_api_types::{
     Authid, LtoTapeDrive, LtoTapeDriveUpdater, ScsiTapeChanger,
     PROXMOX_CONFIG_DIGEST_SCHEMA, DRIVE_NAME_SCHEMA, PRIV_TAPE_AUDIT, PRIV_TAPE_MODIFY,
 };
+use pbs_config::CachedUserInfo;
 
 use crate::{
-    config::cached_user_info::CachedUserInfo,
     tape::{
         lto_tape_device_list,
         check_drive_path,
index 56f8649876c0a37a0846ae2cd3b795c51c27a5b4..6782e56c5e53cf819fba6c469f0d3f8cc8af4dd9 100644 (file)
@@ -15,7 +15,7 @@ use pbs_api_types::{
     PRIV_TAPE_AUDIT, PRIV_TAPE_MODIFY,
 };
 
-use crate::config::cached_user_info::CachedUserInfo;
+use pbs_config::CachedUserInfo;
 
 #[api(
     protected: true,
index 4daca23cb4bc2a29ebfad8711a6161bce722a6cc..acf7cfcf9d9b1c8d83d0ebc6a0f92b1efd638901 100644 (file)
@@ -13,7 +13,7 @@ use pbs_api_types::{
 };
 use pbs_config::sync;
 
-use crate::config::cached_user_info::CachedUserInfo;
+use pbs_config::CachedUserInfo;
 
 #[api(
     input: {
index bb2152fa950134e1086370ea525cff65bfb531c4..3c2bfd782cb7a6d846256110eb86abd8a34a3135 100644 (file)
@@ -11,7 +11,7 @@ use pbs_api_types::{
 };
 use pbs_config::sync;
 
-use crate::config::cached_user_info::CachedUserInfo;
+use pbs_config::CachedUserInfo;
 
 pub fn check_sync_job_read_access(
     user_info: &CachedUserInfo,
@@ -356,7 +356,7 @@ pub const ROUTER: Router = Router::new()
 
 #[test]
 fn sync_job_access_test() -> Result<(), Error> {
-    let (user_cfg, _) = crate::config::user::test_cfg_from_str(r###"
+    let (user_cfg, _) = pbs_config::user::test_cfg_from_str(r###"
 user: noperm@pbs
 
 user: read@pbs
index b0c41bf6e80da359c70593fa9332d20333ef5670..9213580419b522e6ca3660b985d74cf1b55cb282 100644 (file)
@@ -10,7 +10,7 @@ use pbs_api_types::{
     PRIV_TAPE_AUDIT, PRIV_TAPE_MODIFY,
 };
 
-use crate::config::cached_user_info::CachedUserInfo;
+use pbs_config::CachedUserInfo;
 
 #[api(
     input: {
index 7c8946d47f92cee6228e6501c510b7a72ab75416..7958646a66b09df4394dfd4c28bd008d1c7559c9 100644 (file)
@@ -10,7 +10,7 @@ use pbs_api_types::{
 };
 use pbs_config::verify;
 
-use crate::config::cached_user_info::CachedUserInfo;
+use pbs_config::CachedUserInfo;
 
 #[api(
     input: {
index 58e80e5322b4a3ec1c8ca942743ef274162d32ad..721ef40acdb14bd031eaea28a2538ad79bcb3a44 100644 (file)
@@ -10,7 +10,7 @@ use pbs_api_types::{
 
 use crate::tools;
 use crate::tools::subscription::{self, SubscriptionStatus, SubscriptionInfo};
-use crate::config::cached_user_info::CachedUserInfo;
+use pbs_config::CachedUserInfo;
 
 #[api(
     input: {
index 29a0ee14b5df1e94080eb4530d490322f3a54508..e11a89be990573b8ca212670687bbe992c7016ce 100644 (file)
@@ -18,7 +18,7 @@ use pbs_api_types::{
 use crate::api2::types::TaskStateType;
 use crate::api2::pull::check_pull_privs;
 use crate::server::{self, UPID, UPIDExt, TaskState, TaskListInfoIterator};
-use crate::config::cached_user_info::CachedUserInfo;
+use pbs_config::CachedUserInfo;
 
 // matches respective job execution privileges
 fn check_job_privs(auth_id: &Authid, user_info: &CachedUserInfo, upid: &UPID) -> Result<(), Error> {
index a24f7983e0a04c585510298968af48e98afb605f..d7b155a1bfac40d5a160715e5a3942bc8ffdebe1 100644 (file)
@@ -16,7 +16,7 @@ use pbs_api_types::{
 
 use crate::server::{WorkerTask, jobstate::Job, pull::pull_store};
 use crate::backup::DataStore;
-use crate::config::cached_user_info::CachedUserInfo;
+use pbs_config::CachedUserInfo;
 
 pub fn check_pull_privs(
     auth_id: &Authid,
index 692eee8f75a07e4dbd540cc1229a38bf1974a659..a655b6d7924f73873079838327fdfeed35dd877d 100644 (file)
@@ -37,6 +37,7 @@ use pbs_datastore::PROXMOX_BACKUP_READER_PROTOCOL_ID_V1;
 use pbs_datastore::backup_info::BackupDir;
 use pbs_datastore::index::IndexFile;
 use pbs_datastore::manifest::{archive_type, ArchiveType};
+use pbs_config::CachedUserInfo;
 
 use crate::{
     api2::helpers,
@@ -45,7 +46,6 @@ use crate::{
         WorkerTask,
         H2Service,
     },
-    config::cached_user_info::CachedUserInfo,
 };
 
 mod environment;
index 7250c616ea16824940e74a3faec79b43ee794943..5fa3050f6ff5f116f7aec51e5e0a643376493daf 100644 (file)
@@ -22,7 +22,7 @@ use pbs_api_types::{
 use crate::backup::DataStore;
 use crate::config::datastore;
 use crate::tools::statistics::{linear_regression};
-use crate::config::cached_user_info::CachedUserInfo;
+use pbs_config::CachedUserInfo;
 
 #[api(
     returns: {
index 2369d69ccde60ac0bef793c52acba932fe6c967d..39e5feea0aa4b4cfd9e11df4fe3870c1551c8175 100644 (file)
@@ -23,9 +23,9 @@ use pbs_api_types::{
 use pbs_datastore::{task_log, task_warn, StoreProgress};
 use pbs_datastore::backup_info::{BackupDir, BackupInfo};
 use pbs_datastore::task::TaskState;
+use pbs_config::CachedUserInfo;
 
 use crate::{
-    config::cached_user_info::CachedUserInfo,
     server::{
         lookup_user_email,
         TapeBackupJobSummary,
index ee2c70d71a89ff837d605deabc7c1f137388e95d..362507884601724e33b0c71513142ff5840f4f49 100644 (file)
@@ -11,9 +11,9 @@ use pbs_api_types::{
     Authid, ChangerListEntry, LtoTapeDrive, MtxEntryKind, MtxStatusEntry, ScsiTapeChanger,
     CHANGER_NAME_SCHEMA, PRIV_TAPE_AUDIT, PRIV_TAPE_READ,
 };
+use pbs_config::CachedUserInfo;
 
 use crate::{
-    config::cached_user_info::CachedUserInfo,
     tape::{
         TAPE_STATUS_DIR,
         Inventory,
index 263210ee1d7844d106c730d1ceada14598c5ef79..9c8bacc3fc0fab070243232fecddf137bf9953ea 100644 (file)
@@ -30,9 +30,9 @@ use pbs_api_types::{
  
 use pbs_datastore::task_log;
 use pbs_api_types::{PRIV_TAPE_AUDIT, PRIV_TAPE_READ, PRIV_TAPE_WRITE};
+use pbs_config::CachedUserInfo;
 
 use crate::{
-    config::cached_user_info::CachedUserInfo,
     api2::tape::restore::{
         fast_catalog_restore,
         restore_media,
index d669d720e60770d7f68b1abe7a0a3ca3063ae4f8..72948f524e2d5a8b3187ab9cdd936f4b9b88fb0b 100644 (file)
@@ -16,9 +16,9 @@ use pbs_api_types::{
     MediaStatus, MediaContentEntry, MediaContentListFilter,
     PRIV_TAPE_AUDIT,
 };
+use pbs_config::CachedUserInfo;
 
 use crate::{
-    config::cached_user_info::CachedUserInfo,
     tape::{
         TAPE_STATUS_DIR,
         Inventory,
index 1147c3d4c130164efab7031589a82011c73165d8..24e8765fbf40303c87d7a9c2e60ac7b3a0156f32 100644 (file)
@@ -41,10 +41,10 @@ use pbs_datastore::fixed_index::FixedIndexReader;
 use pbs_datastore::index::IndexFile;
 use pbs_datastore::manifest::{archive_type, ArchiveType, BackupManifest, MANIFEST_BLOB_NAME};
 use pbs_datastore::task::TaskState;
+use pbs_config::CachedUserInfo;
 
 use crate::{
     tools::ParallelHandler,
-    config::cached_user_info::CachedUserInfo,
     backup::DataStore,
     server::{
         lookup_user_email,
index b54e418531288f303c867989e1d068533e2eb50b..40c05cbfe59f84fe5a257e4daaaefd2561926b93 100644 (file)
@@ -53,7 +53,7 @@ fn main() -> Result<(), Error> {
             "datastore.cfg" => dump_section_config(&config::datastore::CONFIG),
             "tape.cfg" => dump_section_config(&pbs_config::drive::CONFIG),
             "tape-job.cfg" => dump_section_config(&pbs_config::tape_job::CONFIG),
-            "user.cfg" => dump_section_config(&config::user::CONFIG),
+            "user.cfg" => dump_section_config(&pbs_config::user::CONFIG),
             "remote.cfg" => dump_section_config(&pbs_config::remote::CONFIG),
             "sync.cfg" => dump_section_config(&pbs_config::sync::CONFIG),
             "verification.cfg" => dump_section_config(&pbs_config::verify::CONFIG),
index b23943ca4507b38ba87cfc323bc821b26b171c10..8733c84423b6c4dc94c1ef7949b2f67e51528b77 100644 (file)
@@ -60,7 +60,7 @@ pub fn acl_commands() -> CommandLineInterface {
             "update",
             CliCommand::new(&api2::access::acl::API_METHOD_UPDATE_ACL)
                 .arg_param(&["path", "role"])
-                .completion_cb("auth-id", config::user::complete_authid)
+                .completion_cb("auth-id", pbs_config::user::complete_authid)
                 .completion_cb("path", config::datastore::complete_acl_path)
 
         );
index b793521464a553726ee19fa17ec27aa3e0f827c5..2ee15a819926ad42a009a56bc06c6327b5345af0 100644 (file)
@@ -181,38 +181,38 @@ pub fn user_commands() -> CommandLineInterface {
             "update",
             CliCommand::new(&api2::access::user::API_METHOD_UPDATE_USER)
                 .arg_param(&["userid"])
-                .completion_cb("userid", config::user::complete_userid)
+                .completion_cb("userid", pbs_config::user::complete_userid)
         )
         .insert(
             "remove",
             CliCommand::new(&api2::access::user::API_METHOD_DELETE_USER)
                 .arg_param(&["userid"])
-                .completion_cb("userid", config::user::complete_userid)
+                .completion_cb("userid", pbs_config::user::complete_userid)
         )
         .insert(
             "list-tokens",
             CliCommand::new(&&API_METHOD_LIST_TOKENS)
                 .arg_param(&["userid"])
-                .completion_cb("userid", config::user::complete_userid)
+                .completion_cb("userid", pbs_config::user::complete_userid)
         )
         .insert(
             "generate-token",
             CliCommand::new(&api2::access::user::API_METHOD_GENERATE_TOKEN)
                 .arg_param(&["userid", "tokenname"])
-                .completion_cb("userid", config::user::complete_userid)
+                .completion_cb("userid", pbs_config::user::complete_userid)
         )
         .insert(
             "delete-token",
             CliCommand::new(&api2::access::user::API_METHOD_DELETE_TOKEN)
                 .arg_param(&["userid", "tokenname"])
-                .completion_cb("userid", config::user::complete_userid)
-                .completion_cb("tokenname", config::user::complete_token_name)
+                .completion_cb("userid", pbs_config::user::complete_userid)
+                .completion_cb("tokenname", pbs_config::user::complete_token_name)
         )
         .insert(
             "permissions",
             CliCommand::new(&&API_METHOD_LIST_PERMISSIONS)
                 .arg_param(&["auth-id"])
-                .completion_cb("auth-id", config::user::complete_authid)
+                .completion_cb("auth-id", pbs_config::user::complete_authid)
                 .completion_cb("path", config::datastore::complete_acl_path)
         );
 
index 32a8fd9845e1bc628f6fd0def6efe81a9f71193f..30309bb88d2ecae2d359793d86ee13eadc3fba4c 100644 (file)
@@ -6,7 +6,7 @@ use anyhow::{bail, format_err, Error};
 
 use pbs_api_types::Authid;
 
-use proxmox_backup::config::cached_user_info::CachedUserInfo;
+use pbs_config::CachedUserInfo;
 use proxmox_backup::server::auth::{ApiAuth, AuthError};
 
 const TICKET_FILE: &str = "/ticket";
diff --git a/src/config/cached_user_info.rs b/src/config/cached_user_info.rs
deleted file mode 100644 (file)
index 4763157..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-//! Cached user info for fast ACL permission checks
-
-use std::sync::{RwLock, Arc};
-
-use anyhow::{Error, bail};
-
-use proxmox::api::section_config::SectionConfigData;
-use lazy_static::lazy_static;
-use proxmox::api::UserInformation;
-use proxmox::tools::time::epoch_i64;
-
-use pbs_api_types::{Authid, Userid, User, ApiToken, ROLE_ADMIN};
-use pbs_config::acl::{AclTree, ROLE_NAMES};
-
-use crate::tools::Memcom;
-
-/// Cache User/Group/Token/Acl configuration data for fast permission tests
-pub struct CachedUserInfo {
-    user_cfg: Arc<SectionConfigData>,
-    acl_tree: Arc<AclTree>,
-}
-
-struct ConfigCache {
-    data: Option<Arc<CachedUserInfo>>,
-    last_update: i64,
-    last_user_cache_generation: usize,
-}
-
-lazy_static! {
-    static ref CACHED_CONFIG: RwLock<ConfigCache> = RwLock::new(
-        ConfigCache { data: None, last_update: 0, last_user_cache_generation: 0 }
-    );
-}
-
-impl CachedUserInfo {
-
-    /// Returns a cached instance (up to 5 seconds old).
-    pub fn new() -> Result<Arc<Self>, Error> {
-        let now = epoch_i64();
-
-        let memcom = Memcom::new()?;
-        let user_cache_generation = memcom.user_cache_generation();
-
-        { // limit scope
-            let cache = CACHED_CONFIG.read().unwrap();
-            if (user_cache_generation == cache.last_user_cache_generation) &&
-                ((now - cache.last_update) < 5)
-            {
-                if let Some(ref config) = cache.data {
-                    return Ok(config.clone());
-                }
-            }
-        }
-
-        let config = Arc::new(CachedUserInfo {
-            user_cfg: super::user::cached_config()?,
-            acl_tree: pbs_config::acl::cached_config()?,
-        });
-
-        let mut cache = CACHED_CONFIG.write().unwrap();
-        cache.last_update = now;
-        cache.last_user_cache_generation = user_cache_generation;
-        cache.data = Some(config.clone());
-
-        Ok(config)
-    }
-
-    #[cfg(test)]
-    pub(crate) fn test_new(user_cfg: SectionConfigData, acl_tree: AclTree) -> Self {
-        Self {
-            user_cfg: Arc::new(user_cfg),
-            acl_tree: Arc::new(acl_tree),
-        }
-    }
-
-    /// Test if a user_id is enabled and not expired
-    pub fn is_active_user_id(&self, userid: &Userid) -> bool {
-        if let Ok(info) = self.user_cfg.lookup::<User>("user", userid.as_str()) {
-            info.is_active()
-        } else {
-            false
-        }
-    }
-
-    /// Test if a authentication id is enabled and not expired
-    pub fn is_active_auth_id(&self, auth_id: &Authid) -> bool {
-        let userid = auth_id.user();
-
-        if !self.is_active_user_id(userid) {
-            return false;
-        }
-
-        if auth_id.is_token() {
-            if let Ok(info) = self.user_cfg.lookup::<ApiToken>("token", &auth_id.to_string()) {
-                return info.is_active();
-            } else {
-                return false;
-            }
-        }
-
-        true
-    }
-
-    pub fn check_privs(
-        &self,
-        auth_id: &Authid,
-        path: &[&str],
-        required_privs: u64,
-        partial: bool,
-    ) -> Result<(), Error> {
-        let privs = self.lookup_privs(&auth_id, path);
-        let allowed = if partial {
-            (privs & required_privs) != 0
-        } else {
-            (privs & required_privs) == required_privs
-        };
-        if !allowed {
-            // printing the path doesn't leaks any information as long as we
-            // always check privilege before resource existence
-            bail!("no permissions on '/{}'", path.join("/"));
-        }
-        Ok(())
-    }
-
-    pub fn is_superuser(&self, auth_id: &Authid) -> bool {
-        !auth_id.is_token() && auth_id.user() == "root@pam"
-    }
-
-    pub fn is_group_member(&self, _userid: &Userid, _group: &str) -> bool {
-        false
-    }
-
-    pub fn lookup_privs(&self, auth_id: &Authid, path: &[&str]) -> u64 {
-        let (privs, _) = self.lookup_privs_details(auth_id, path);
-        privs
-    }
-
-    pub fn lookup_privs_details(&self, auth_id: &Authid, path: &[&str]) -> (u64, u64) {
-        if self.is_superuser(auth_id) {
-            return (ROLE_ADMIN, ROLE_ADMIN);
-        }
-
-        let roles = self.acl_tree.roles(auth_id, path);
-        let mut privs: u64 = 0;
-        let mut propagated_privs: u64 = 0;
-        for (role, propagate) in roles {
-            if let Some((role_privs, _)) = ROLE_NAMES.get(role.as_str()) {
-                if propagate {
-                    propagated_privs |= role_privs;
-                }
-                privs |= role_privs;
-            }
-        }
-
-        if auth_id.is_token() {
-            // limit privs to that of owning user
-            let user_auth_id = Authid::from(auth_id.user().clone());
-            privs &= self.lookup_privs(&user_auth_id, path);
-            let (owner_privs, owner_propagated_privs) = self.lookup_privs_details(&user_auth_id, path);
-            privs &= owner_privs;
-            propagated_privs &= owner_propagated_privs;
-        }
-
-        (privs, propagated_privs)
-    }
-
-}
-
-impl UserInformation for CachedUserInfo {
-    fn is_superuser(&self, userid: &str) -> bool {
-        userid == "root@pam"
-    }
-
-    fn is_group_member(&self, _userid: &str, _group: &str) -> bool {
-        false
-    }
-
-    fn lookup_privs(&self, auth_id: &str, path: &[&str]) -> u64 {
-        match auth_id.parse::<Authid>() {
-            Ok(auth_id) => Self::lookup_privs(self, &auth_id, path),
-            Err(_) => 0,
-        }
-    }
-}
index cfb114cc18c081353cf416b2d2cd5ad7af151991..ffc3b57b55726be30a959462b5647d2e343c38a7 100644 (file)
@@ -15,11 +15,9 @@ use proxmox::try_block;
 use pbs_buildcfg::{self, configdir};
 
 pub mod acme;
-pub mod cached_user_info;
 pub mod datastore;
 pub mod node;
 pub mod tfa;
-pub mod user;
 
 /// Check configuration directory permissions
 ///
diff --git a/src/config/user.rs b/src/config/user.rs
deleted file mode 100644 (file)
index 71ba5a4..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-use std::collections::HashMap;
-use std::sync::{Arc, RwLock};
-
-use anyhow::{bail, Error};
-use lazy_static::lazy_static;
-
-use proxmox::api::{
-    schema::*,
-    section_config::{
-        SectionConfig,
-        SectionConfigData,
-        SectionConfigPlugin,
-    }
-};
-
-use pbs_api_types::{
-    Authid, Userid, ApiToken, User,
-};
-use pbs_config::{open_backup_lockfile, replace_backup_config, BackupLockGuard};
-
-use crate::tools::Memcom;
-
-lazy_static! {
-    pub static ref CONFIG: SectionConfig = init();
-}
-
-fn init() -> SectionConfig {
-    let mut config = SectionConfig::new(&Authid::API_SCHEMA);
-
-    let user_schema = match User::API_SCHEMA {
-        Schema::Object(ref user_schema) => user_schema,
-        _ => unreachable!(),
-    };
-    let user_plugin = SectionConfigPlugin::new("user".to_string(), Some("userid".to_string()), user_schema);
-    config.register_plugin(user_plugin);
-
-    let token_schema = match ApiToken::API_SCHEMA {
-        Schema::Object(ref token_schema) => token_schema,
-        _ => unreachable!(),
-    };
-    let token_plugin = SectionConfigPlugin::new("token".to_string(), Some("tokenid".to_string()), token_schema);
-    config.register_plugin(token_plugin);
-
-    config
-}
-
-pub const USER_CFG_FILENAME: &str = "/etc/proxmox-backup/user.cfg";
-pub const USER_CFG_LOCKFILE: &str = "/etc/proxmox-backup/.user.lck";
-
-/// Get exclusive lock
-pub fn lock_config() -> Result<BackupLockGuard, Error> {
-    open_backup_lockfile(USER_CFG_LOCKFILE, None, true)
-}
-
-pub fn config() -> Result<(SectionConfigData, [u8;32]), Error> {
-
-    let content = proxmox::tools::fs::file_read_optional_string(USER_CFG_FILENAME)?
-        .unwrap_or_else(|| "".to_string());
-
-    let digest = openssl::sha::sha256(content.as_bytes());
-    let mut data = CONFIG.parse(USER_CFG_FILENAME, &content)?;
-
-    if data.sections.get("root@pam").is_none() {
-        let user: User = User {
-            userid: Userid::root_userid().clone(),
-            comment: Some("Superuser".to_string()),
-            enable: None,
-            expire: None,
-            firstname: None,
-            lastname: None,
-            email: None,
-        };
-        data.set_data("root@pam", "user", &user).unwrap();
-    }
-
-    Ok((data, digest))
-}
-
-pub fn cached_config() -> Result<Arc<SectionConfigData>, Error> {
-
-    struct ConfigCache {
-        data: Option<Arc<SectionConfigData>>,
-        last_mtime: i64,
-        last_mtime_nsec: i64,
-    }
-
-    lazy_static! {
-        static ref CACHED_CONFIG: RwLock<ConfigCache> = RwLock::new(
-            ConfigCache { data: None, last_mtime: 0, last_mtime_nsec: 0 });
-    }
-
-    let stat = match nix::sys::stat::stat(USER_CFG_FILENAME) {
-        Ok(stat) => Some(stat),
-        Err(nix::Error::Sys(nix::errno::Errno::ENOENT)) => None,
-        Err(err) => bail!("unable to stat '{}' - {}", USER_CFG_FILENAME, err),
-    };
-
-    { // limit scope
-        let cache = CACHED_CONFIG.read().unwrap();
-        if let Some(ref config) = cache.data {
-            if let Some(stat) = stat {
-                if stat.st_mtime == cache.last_mtime && stat.st_mtime_nsec == cache.last_mtime_nsec {
-                    return Ok(config.clone());
-                }
-            } else if cache.last_mtime == 0 && cache.last_mtime_nsec == 0 {
-                return Ok(config.clone());
-            }
-        }
-    }
-
-    let (config, _digest) = config()?;
-    let config = Arc::new(config);
-
-    let mut cache = CACHED_CONFIG.write().unwrap();
-    if let Some(stat) = stat {
-        cache.last_mtime = stat.st_mtime;
-        cache.last_mtime_nsec = stat.st_mtime_nsec;
-    }
-    cache.data = Some(config.clone());
-
-    Ok(config)
-}
-
-pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
-    let raw = CONFIG.write(USER_CFG_FILENAME, &config)?;
-    replace_backup_config(USER_CFG_FILENAME, raw.as_bytes())?;
-
-    // increase user cache generation
-    // We use this in CachedUserInfo
-    let memcom = Memcom::new()?;
-    memcom.increase_user_cache_generation();
-
-    Ok(())
-}
-
-#[cfg(test)]
-pub(crate) fn test_cfg_from_str(raw: &str) -> Result<(SectionConfigData, [u8;32]), Error> {
-    let cfg = init();
-    let parsed = cfg.parse("test_user_cfg", raw)?;
-
-    Ok((parsed, [0;32]))
-}
-
-// shell completion helper
-pub fn complete_userid(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
-    match config() {
-        Ok((data, _digest)) => {
-            data.sections.iter()
-                .filter_map(|(id, (section_type, _))| {
-                    if section_type == "user" {
-                        Some(id.to_string())
-                    } else {
-                        None
-                    }
-                }).collect()
-        },
-        Err(_) => return vec![],
-    }
-}
-
-// shell completion helper
-pub fn complete_authid(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
-    match config() {
-        Ok((data, _digest)) => data.sections.iter().map(|(id, _)| id.to_string()).collect(),
-        Err(_) => vec![],
-    }
-}
-
-// shell completion helper
-pub fn complete_token_name(_arg: &str, param: &HashMap<String, String>) -> Vec<String> {
-    let data = match config() {
-        Ok((data, _digest)) => data,
-        Err(_) => return Vec::new(),
-    };
-
-    match param.get("userid") {
-        Some(userid) => {
-            let user = data.lookup::<User>("user", userid);
-            let tokens = data.convert_to_typed_array("token");
-            match (user, tokens) {
-                (Ok(_), Ok(tokens)) => {
-                    tokens
-                        .into_iter()
-                        .filter_map(|token: ApiToken| {
-                            let tokenid = token.tokenid;
-                            if tokenid.is_token() && tokenid.user() == userid {
-                                Some(tokenid.tokenname().unwrap().as_str().to_string())
-                            } else {
-                                None
-                            }
-                        }).collect()
-                },
-                _ => vec![],
-            }
-        },
-        None => vec![],
-    }
-}
index b5b0e0bc0267690d12af5e60755356e0e275aae2..c555d517ed04a864243838ba5d8e376305cdb1a1 100644 (file)
@@ -8,7 +8,7 @@ use pbs_config::token_shadow;
 
 use crate::api2::types::{Authid, Userid};
 use crate::auth_helpers::*;
-use crate::config::cached_user_info::CachedUserInfo;
+use pbs_config::CachedUserInfo;
 use crate::tools;
 
 use hyper::header;
index f26d2cea92c7d1d77eb02e87c7b6245152ef1ef4..c81027be2e3714f345e3a3f71072e21422fe5c71 100644 (file)
@@ -548,7 +548,7 @@ pub fn send_updates_available(
 /// Lookup users email address
 pub fn lookup_user_email(userid: &Userid) -> Option<String> {
 
-    if let Ok(user_config) = crate::config::user::cached_config() {
+    if let Ok(user_config) = pbs_config::user::cached_config() {
         if let Ok(user) = user_config.lookup::<User>("user", userid.as_str()) {
             return user.email;
         }
index 1c0d4cc04ebd0e310c7f93514dfbaebec713c0f0..5475d18fca2d3f2a30e0121cf6f65d1ff39681b1 100644 (file)
@@ -6,9 +6,9 @@ use pbs_datastore::{task_log, task_warn};
 use pbs_datastore::backup_info::BackupInfo;
 use pbs_datastore::prune::{compute_prune_info, PruneOptions};
 use pbs_api_types::{Authid, PRIV_DATASTORE_MODIFY};
+use pbs_config::CachedUserInfo;
 
 use crate::{
-    config::cached_user_info::CachedUserInfo,
     backup::DataStore,
     server::jobstate::Job,
     server::WorkerTask,
index 62b63a5d430b30c0ee2ce4d0e2d37e93a3f893ff..48b0f8f2b8b95af925d86141b703c812675f6fac 100644 (file)
@@ -40,7 +40,7 @@ use super::ApiConfig;
 
 use crate::api2::types::{Authid, Userid};
 use crate::auth_helpers::*;
-use crate::config::cached_user_info::CachedUserInfo;
+use pbs_config::CachedUserInfo;
 use crate::tools;
 use crate::tools::compression::CompressionMethod;
 use crate::tools::FileLogger;
diff --git a/src/tools/memcom.rs b/src/tools/memcom.rs
deleted file mode 100644 (file)
index 11c7190..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-//! Memory based communication channel between proxy & daemon for things such as cache
-//! invalidation.
-
-use std::os::unix::io::AsRawFd;
-use std::sync::atomic::{AtomicUsize, Ordering};
-use std::sync::Arc;
-
-use anyhow::Error;
-use nix::fcntl::OFlag;
-use nix::sys::mman::{MapFlags, ProtFlags};
-use nix::sys::stat::Mode;
-use once_cell::sync::OnceCell;
-
-use proxmox::tools::fs::CreateOptions;
-use proxmox::tools::mmap::Mmap;
-
-/// In-memory communication channel.
-pub struct Memcom {
-    mmap: Mmap<u8>,
-}
-
-#[repr(C)]
-struct Head {
-    // User (user.cfg) cache generation/version.
-    user_cache_generation: AtomicUsize,
-}
-
-static INSTANCE: OnceCell<Arc<Memcom>> = OnceCell::new();
-
-const MEMCOM_FILE_PATH: &str = pbs_buildcfg::rundir!("/proxmox-backup-memcom");
-const EMPTY_PAGE: [u8; 4096] = [0u8; 4096];
-
-impl Memcom {
-    /// Open the memory based communication channel singleton.
-    pub fn new() -> Result<Arc<Self>, Error> {
-        INSTANCE.get_or_try_init(Self::open).map(Arc::clone)
-    }
-
-    // Actual work of `new`:
-    fn open() -> Result<Arc<Self>, Error> {
-        let user = pbs_config::backup_user()?;
-        let options = CreateOptions::new()
-            .perm(Mode::from_bits_truncate(0o660))
-            .owner(user.uid)
-            .group(user.gid);
-
-        let file = proxmox::tools::fs::atomic_open_or_create_file(
-            MEMCOM_FILE_PATH,
-            OFlag::O_RDWR | OFlag::O_CLOEXEC,
-            &EMPTY_PAGE, options)?;
-
-        let mmap = unsafe {
-            Mmap::<u8>::map_fd(
-                file.as_raw_fd(),
-                0,
-                4096,
-                ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
-                MapFlags::MAP_SHARED | MapFlags::MAP_NORESERVE | MapFlags::MAP_POPULATE,
-            )?
-        };
-
-        Ok(Arc::new(Self { mmap }))
-    }
-
-    // Shortcut to get the mapped `Head` as a `Head`.
-    fn head(&self) -> &Head {
-        unsafe { &*(self.mmap.as_ptr() as *const u8 as *const Head) }
-    }
-
-    /// Returns the user cache generation number.
-    pub fn user_cache_generation(&self) -> usize {
-        self.head().user_cache_generation.load(Ordering::Acquire)
-    }
-
-    /// Increase the user cache generation number.
-    pub fn increase_user_cache_generation(&self) {
-        self.head()
-            .user_cache_generation
-            .fetch_add(1, Ordering::AcqRel);
-    }
-}
index 27265db676405564057f9f0393842b649de31e76..c2bcd215b5bf0fca0ca9c0bacbb91751df3464da 100644 (file)
@@ -28,9 +28,6 @@ pub mod config;
 pub mod daemon;
 pub mod disks;
 
-mod memcom;
-pub use memcom::Memcom;
-
 pub mod serde_filter;
 pub mod statistics;
 pub mod subscription;