]> git.proxmox.com Git - proxmox-backup.git/blame - src/config/cached_user_info.rs
fix #2847: api: datastore: change backup owner
[proxmox-backup.git] / src / config / cached_user_info.rs
CommitLineData
423e6561
DM
1//! Cached user info for fast ACL permission checks
2
6e695960 3use std::sync::{RwLock, Arc};
423e6561 4
a737179e 5use anyhow::{Error, bail};
423e6561
DM
6
7use proxmox::api::section_config::SectionConfigData;
6e695960 8use lazy_static::lazy_static;
423e6561
DM
9use proxmox::api::UserInformation;
10
1347b115 11use super::acl::{AclTree, ROLE_NAMES, ROLE_ADMIN};
423e6561 12use super::user::User;
e7cb4dc5 13use crate::api2::types::Userid;
423e6561
DM
14
15/// Cache User/Group/Acl configuration data for fast permission tests
16pub struct CachedUserInfo {
17 user_cfg: Arc<SectionConfigData>,
18 acl_tree: Arc<AclTree>,
19}
20
6e695960
DM
21fn now() -> i64 { unsafe { libc::time(std::ptr::null_mut()) } }
22
23struct ConfigCache {
24 data: Option<Arc<CachedUserInfo>>,
25 last_update: i64,
26}
27
28lazy_static! {
29 static ref CACHED_CONFIG: RwLock<ConfigCache> = RwLock::new(
30 ConfigCache { data: None, last_update: 0 }
31 );
32}
33
423e6561
DM
34impl CachedUserInfo {
35
6e695960
DM
36 /// Returns a cached instance (up to 5 seconds old).
37 pub fn new() -> Result<Arc<Self>, Error> {
38 let now = now();
39 { // limit scope
40 let cache = CACHED_CONFIG.read().unwrap();
41 if (now - cache.last_update) < 5 {
42 if let Some(ref config) = cache.data {
43 return Ok(config.clone());
44 }
45 }
46 }
47
48 let config = Arc::new(CachedUserInfo {
423e6561
DM
49 user_cfg: super::user::cached_config()?,
50 acl_tree: super::acl::cached_config()?,
6e695960
DM
51 });
52
53 let mut cache = CACHED_CONFIG.write().unwrap();
54 cache.last_update = now;
55 cache.data = Some(config.clone());
56
57 Ok(config)
423e6561
DM
58 }
59
60 /// Test if a user account is enabled and not expired
e7cb4dc5
WB
61 pub fn is_active_user(&self, userid: &Userid) -> bool {
62 if let Ok(info) = self.user_cfg.lookup::<User>("user", userid.as_str()) {
423e6561
DM
63 if !info.enable.unwrap_or(true) {
64 return false;
65 }
66 if let Some(expire) = info.expire {
67 if expire > 0 {
6e695960 68 if expire <= now() {
423e6561
DM
69 return false;
70 }
71 }
72 }
73 return true;
74 } else {
75 return false;
76 }
77 }
a737179e
DM
78
79 pub fn check_privs(
80 &self,
e7cb4dc5 81 userid: &Userid,
a737179e
DM
82 path: &[&str],
83 required_privs: u64,
84 partial: bool,
85 ) -> Result<(), Error> {
e7cb4dc5 86 let user_privs = self.lookup_privs(&userid, path);
a737179e
DM
87 let allowed = if partial {
88 (user_privs & required_privs) != 0
89 } else {
90 (user_privs & required_privs) == required_privs
91 };
92 if !allowed {
3cfc56f5
TL
93 // printing the path doesn't leaks any information as long as we
94 // always check privilege before resource existence
95 bail!("no permissions on '/{}'", path.join("/"));
a737179e
DM
96 }
97 Ok(())
98 }
423e6561 99
e7cb4dc5 100 pub fn is_superuser(&self, userid: &Userid) -> bool {
423e6561
DM
101 userid == "root@pam"
102 }
103
e7cb4dc5 104 pub fn is_group_member(&self, _userid: &Userid, _group: &str) -> bool {
423e6561
DM
105 false
106 }
107
e7cb4dc5 108 pub fn lookup_privs(&self, userid: &Userid, path: &[&str]) -> u64 {
1347b115 109
e7cb4dc5
WB
110 if self.is_superuser(userid) {
111 return ROLE_ADMIN;
112 }
1347b115 113
423e6561
DM
114 let roles = self.acl_tree.roles(userid, path);
115 let mut privs: u64 = 0;
116 for role in roles {
3fff55b2 117 if let Some((role_privs, _)) = ROLE_NAMES.get(role.as_str()) {
423e6561
DM
118 privs |= role_privs;
119 }
120 }
121 privs
122 }
123}
e7cb4dc5
WB
124
125impl UserInformation for CachedUserInfo {
126 fn is_superuser(&self, userid: &str) -> bool {
127 userid == "root@pam"
128 }
129
130 fn is_group_member(&self, _userid: &str, _group: &str) -> bool {
131 false
132 }
133
134 fn lookup_privs(&self, userid: &str, path: &[&str]) -> u64 {
135 match userid.parse::<Userid>() {
136 Ok(userid) => Self::lookup_privs(self, &userid, path),
137 Err(_) => 0,
138 }
139 }
140}