]> git.proxmox.com Git - proxmox-backup.git/blob - src/config/cached_user_info.rs
cleanup User configuration: use Updater
[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 use proxmox::tools::time::epoch_i64;
11
12 use pbs_api_types::{Authid, Userid, User, ApiToken, ROLE_ADMIN};
13 use pbs_config::acl::{AclTree, ROLE_NAMES};
14
15 use crate::tools::Memcom;
16
17 /// Cache User/Group/Token/Acl configuration data for fast permission tests
18 pub struct CachedUserInfo {
19 user_cfg: Arc<SectionConfigData>,
20 acl_tree: Arc<AclTree>,
21 }
22
23 struct ConfigCache {
24 data: Option<Arc<CachedUserInfo>>,
25 last_update: i64,
26 last_user_cache_generation: usize,
27 }
28
29 lazy_static! {
30 static ref CACHED_CONFIG: RwLock<ConfigCache> = RwLock::new(
31 ConfigCache { data: None, last_update: 0, last_user_cache_generation: 0 }
32 );
33 }
34
35 impl CachedUserInfo {
36
37 /// Returns a cached instance (up to 5 seconds old).
38 pub fn new() -> Result<Arc<Self>, Error> {
39 let now = epoch_i64();
40
41 let memcom = Memcom::new()?;
42 let user_cache_generation = memcom.user_cache_generation();
43
44 { // limit scope
45 let cache = CACHED_CONFIG.read().unwrap();
46 if (user_cache_generation == cache.last_user_cache_generation) &&
47 ((now - cache.last_update) < 5)
48 {
49 if let Some(ref config) = cache.data {
50 return Ok(config.clone());
51 }
52 }
53 }
54
55 let config = Arc::new(CachedUserInfo {
56 user_cfg: super::user::cached_config()?,
57 acl_tree: pbs_config::acl::cached_config()?,
58 });
59
60 let mut cache = CACHED_CONFIG.write().unwrap();
61 cache.last_update = now;
62 cache.last_user_cache_generation = user_cache_generation;
63 cache.data = Some(config.clone());
64
65 Ok(config)
66 }
67
68 #[cfg(test)]
69 pub(crate) fn test_new(user_cfg: SectionConfigData, acl_tree: AclTree) -> Self {
70 Self {
71 user_cfg: Arc::new(user_cfg),
72 acl_tree: Arc::new(acl_tree),
73 }
74 }
75
76 /// Test if a user_id is enabled and not expired
77 pub fn is_active_user_id(&self, userid: &Userid) -> bool {
78 if let Ok(info) = self.user_cfg.lookup::<User>("user", userid.as_str()) {
79 info.is_active()
80 } else {
81 false
82 }
83 }
84
85 /// Test if a authentication id is enabled and not expired
86 pub fn is_active_auth_id(&self, auth_id: &Authid) -> bool {
87 let userid = auth_id.user();
88
89 if !self.is_active_user_id(userid) {
90 return false;
91 }
92
93 if auth_id.is_token() {
94 if let Ok(info) = self.user_cfg.lookup::<ApiToken>("token", &auth_id.to_string()) {
95 return info.is_active();
96 } else {
97 return false;
98 }
99 }
100
101 true
102 }
103
104 pub fn check_privs(
105 &self,
106 auth_id: &Authid,
107 path: &[&str],
108 required_privs: u64,
109 partial: bool,
110 ) -> Result<(), Error> {
111 let privs = self.lookup_privs(&auth_id, path);
112 let allowed = if partial {
113 (privs & required_privs) != 0
114 } else {
115 (privs & required_privs) == required_privs
116 };
117 if !allowed {
118 // printing the path doesn't leaks any information as long as we
119 // always check privilege before resource existence
120 bail!("no permissions on '/{}'", path.join("/"));
121 }
122 Ok(())
123 }
124
125 pub fn is_superuser(&self, auth_id: &Authid) -> bool {
126 !auth_id.is_token() && auth_id.user() == "root@pam"
127 }
128
129 pub fn is_group_member(&self, _userid: &Userid, _group: &str) -> bool {
130 false
131 }
132
133 pub fn lookup_privs(&self, auth_id: &Authid, path: &[&str]) -> u64 {
134 let (privs, _) = self.lookup_privs_details(auth_id, path);
135 privs
136 }
137
138 pub fn lookup_privs_details(&self, auth_id: &Authid, path: &[&str]) -> (u64, u64) {
139 if self.is_superuser(auth_id) {
140 return (ROLE_ADMIN, ROLE_ADMIN);
141 }
142
143 let roles = self.acl_tree.roles(auth_id, path);
144 let mut privs: u64 = 0;
145 let mut propagated_privs: u64 = 0;
146 for (role, propagate) in roles {
147 if let Some((role_privs, _)) = ROLE_NAMES.get(role.as_str()) {
148 if propagate {
149 propagated_privs |= role_privs;
150 }
151 privs |= role_privs;
152 }
153 }
154
155 if auth_id.is_token() {
156 // limit privs to that of owning user
157 let user_auth_id = Authid::from(auth_id.user().clone());
158 privs &= self.lookup_privs(&user_auth_id, path);
159 let (owner_privs, owner_propagated_privs) = self.lookup_privs_details(&user_auth_id, path);
160 privs &= owner_privs;
161 propagated_privs &= owner_propagated_privs;
162 }
163
164 (privs, propagated_privs)
165 }
166
167 }
168
169 impl UserInformation for CachedUserInfo {
170 fn is_superuser(&self, userid: &str) -> bool {
171 userid == "root@pam"
172 }
173
174 fn is_group_member(&self, _userid: &str, _group: &str) -> bool {
175 false
176 }
177
178 fn lookup_privs(&self, auth_id: &str, path: &[&str]) -> u64 {
179 match auth_id.parse::<Authid>() {
180 Ok(auth_id) => Self::lookup_privs(self, &auth_id, path),
181 Err(_) => 0,
182 }
183 }
184 }