]> git.proxmox.com Git - proxmox-backup.git/blob - src/config/cached_user_info.rs
fix #2847: api: datastore: change backup owner
[proxmox-backup.git] / src / config / cached_user_info.rs
1 //! Cached user info for fast ACL permission checks
2
3 use std::sync::{RwLock, Arc};
4
5 use anyhow::{Error, bail};
6
7 use proxmox::api::section_config::SectionConfigData;
8 use lazy_static::lazy_static;
9 use proxmox::api::UserInformation;
10
11 use super::acl::{AclTree, ROLE_NAMES, ROLE_ADMIN};
12 use super::user::User;
13 use crate::api2::types::Userid;
14
15 /// Cache User/Group/Acl configuration data for fast permission tests
16 pub struct CachedUserInfo {
17 user_cfg: Arc<SectionConfigData>,
18 acl_tree: Arc<AclTree>,
19 }
20
21 fn now() -> i64 { unsafe { libc::time(std::ptr::null_mut()) } }
22
23 struct ConfigCache {
24 data: Option<Arc<CachedUserInfo>>,
25 last_update: i64,
26 }
27
28 lazy_static! {
29 static ref CACHED_CONFIG: RwLock<ConfigCache> = RwLock::new(
30 ConfigCache { data: None, last_update: 0 }
31 );
32 }
33
34 impl CachedUserInfo {
35
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 {
49 user_cfg: super::user::cached_config()?,
50 acl_tree: super::acl::cached_config()?,
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)
58 }
59
60 /// Test if a user account is enabled and not expired
61 pub fn is_active_user(&self, userid: &Userid) -> bool {
62 if let Ok(info) = self.user_cfg.lookup::<User>("user", userid.as_str()) {
63 if !info.enable.unwrap_or(true) {
64 return false;
65 }
66 if let Some(expire) = info.expire {
67 if expire > 0 {
68 if expire <= now() {
69 return false;
70 }
71 }
72 }
73 return true;
74 } else {
75 return false;
76 }
77 }
78
79 pub fn check_privs(
80 &self,
81 userid: &Userid,
82 path: &[&str],
83 required_privs: u64,
84 partial: bool,
85 ) -> Result<(), Error> {
86 let user_privs = self.lookup_privs(&userid, path);
87 let allowed = if partial {
88 (user_privs & required_privs) != 0
89 } else {
90 (user_privs & required_privs) == required_privs
91 };
92 if !allowed {
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("/"));
96 }
97 Ok(())
98 }
99
100 pub fn is_superuser(&self, userid: &Userid) -> bool {
101 userid == "root@pam"
102 }
103
104 pub fn is_group_member(&self, _userid: &Userid, _group: &str) -> bool {
105 false
106 }
107
108 pub fn lookup_privs(&self, userid: &Userid, path: &[&str]) -> u64 {
109
110 if self.is_superuser(userid) {
111 return ROLE_ADMIN;
112 }
113
114 let roles = self.acl_tree.roles(userid, path);
115 let mut privs: u64 = 0;
116 for role in roles {
117 if let Some((role_privs, _)) = ROLE_NAMES.get(role.as_str()) {
118 privs |= role_privs;
119 }
120 }
121 privs
122 }
123 }
124
125 impl 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 }