]>
Commit | Line | Data |
---|---|---|
423e6561 DM |
1 | //! Cached user info for fast ACL permission checks |
2 | ||
6e695960 | 3 | use std::sync::{RwLock, Arc}; |
423e6561 | 4 | |
a737179e | 5 | use anyhow::{Error, bail}; |
423e6561 DM |
6 | |
7 | use proxmox::api::section_config::SectionConfigData; | |
6e695960 | 8 | use lazy_static::lazy_static; |
423e6561 DM |
9 | use proxmox::api::UserInformation; |
10 | ||
1347b115 | 11 | use super::acl::{AclTree, ROLE_NAMES, ROLE_ADMIN}; |
423e6561 | 12 | use super::user::User; |
e7cb4dc5 | 13 | use crate::api2::types::Userid; |
423e6561 DM |
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 | ||
6e695960 DM |
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 | ||
423e6561 DM |
34 | impl 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 | |
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 | } |