]> git.proxmox.com Git - proxmox-backup.git/blob - src/config/cached_user_info.rs
more clippy lints
[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 && expire <= now() {
68 return false;
69 }
70 }
71 return true;
72 } else {
73 return false;
74 }
75 }
76
77 pub fn check_privs(
78 &self,
79 userid: &Userid,
80 path: &[&str],
81 required_privs: u64,
82 partial: bool,
83 ) -> Result<(), Error> {
84 let user_privs = self.lookup_privs(&userid, path);
85 let allowed = if partial {
86 (user_privs & required_privs) != 0
87 } else {
88 (user_privs & required_privs) == required_privs
89 };
90 if !allowed {
91 // printing the path doesn't leaks any information as long as we
92 // always check privilege before resource existence
93 bail!("no permissions on '/{}'", path.join("/"));
94 }
95 Ok(())
96 }
97
98 pub fn is_superuser(&self, userid: &Userid) -> bool {
99 userid == "root@pam"
100 }
101
102 pub fn is_group_member(&self, _userid: &Userid, _group: &str) -> bool {
103 false
104 }
105
106 pub fn lookup_privs(&self, userid: &Userid, path: &[&str]) -> u64 {
107
108 if self.is_superuser(userid) {
109 return ROLE_ADMIN;
110 }
111
112 let roles = self.acl_tree.roles(userid, path);
113 let mut privs: u64 = 0;
114 for role in roles {
115 if let Some((role_privs, _)) = ROLE_NAMES.get(role.as_str()) {
116 privs |= role_privs;
117 }
118 }
119 privs
120 }
121 }
122
123 impl UserInformation for CachedUserInfo {
124 fn is_superuser(&self, userid: &str) -> bool {
125 userid == "root@pam"
126 }
127
128 fn is_group_member(&self, _userid: &str, _group: &str) -> bool {
129 false
130 }
131
132 fn lookup_privs(&self, userid: &str, path: &[&str]) -> u64 {
133 match userid.parse::<Userid>() {
134 Ok(userid) => Self::lookup_privs(self, &userid, path),
135 Err(_) => 0,
136 }
137 }
138 }