1 //! Cached user info for fast ACL permission checks
3 use std
::sync
::{RwLock, Arc}
;
5 use anyhow
::{Error, bail}
;
7 use proxmox
::api
::section_config
::SectionConfigData
;
8 use lazy_static
::lazy_static
;
9 use proxmox
::api
::UserInformation
;
11 use super::acl
::{AclTree, ROLE_NAMES, ROLE_ADMIN}
;
12 use super::user
::User
;
13 use crate::api2
::types
::Userid
;
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
>,
21 fn now() -> i64 { unsafe { libc::time(std::ptr::null_mut()) }
}
24 data
: Option
<Arc
<CachedUserInfo
>>,
29 static ref CACHED_CONFIG
: RwLock
<ConfigCache
> = RwLock
::new(
30 ConfigCache { data: None, last_update: 0 }
36 /// Returns a cached instance (up to 5 seconds old).
37 pub fn new() -> Result
<Arc
<Self>, Error
> {
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());
48 let config
= Arc
::new(CachedUserInfo
{
49 user_cfg
: super::user
::cached_config()?
,
50 acl_tree
: super::acl
::cached_config()?
,
53 let mut cache
= CACHED_CONFIG
.write().unwrap();
54 cache
.last_update
= now
;
55 cache
.data
= Some(config
.clone());
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) {
66 if let Some(expire
) = info
.expire
{
85 ) -> Result
<(), Error
> {
86 let user_privs
= self.lookup_privs(&userid
, path
);
87 let allowed
= if partial
{
88 (user_privs
& required_privs
) != 0
90 (user_privs
& required_privs
) == required_privs
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("/"));
100 pub fn is_superuser(&self, userid
: &Userid
) -> bool
{
104 pub fn is_group_member(&self, _userid
: &Userid
, _group
: &str) -> bool
{
108 pub fn lookup_privs(&self, userid
: &Userid
, path
: &[&str]) -> u64 {
110 if self.is_superuser(userid
) {
114 let roles
= self.acl_tree
.roles(userid
, path
);
115 let mut privs
: u64 = 0;
117 if let Some((role_privs
, _
)) = ROLE_NAMES
.get(role
.as_str()) {
125 impl UserInformation
for CachedUserInfo
{
126 fn is_superuser(&self, userid
: &str) -> bool
{
130 fn is_group_member(&self, _userid
: &str, _group
: &str) -> bool
{
134 fn lookup_privs(&self, userid
: &str, path
: &[&str]) -> u64 {
135 match userid
.parse
::<Userid
>() {
136 Ok(userid
) => Self::lookup_privs(self, &userid
, path
),