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
{
67 if expire
> 0 && expire
<= now() {
83 ) -> Result
<(), Error
> {
84 let user_privs
= self.lookup_privs(&userid
, path
);
85 let allowed
= if partial
{
86 (user_privs
& required_privs
) != 0
88 (user_privs
& required_privs
) == required_privs
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("/"));
98 pub fn is_superuser(&self, userid
: &Userid
) -> bool
{
102 pub fn is_group_member(&self, _userid
: &Userid
, _group
: &str) -> bool
{
106 pub fn lookup_privs(&self, userid
: &Userid
, path
: &[&str]) -> u64 {
108 if self.is_superuser(userid
) {
112 let roles
= self.acl_tree
.roles(userid
, path
);
113 let mut privs
: u64 = 0;
115 if let Some((role_privs
, _
)) = ROLE_NAMES
.get(role
.as_str()) {
123 impl UserInformation
for CachedUserInfo
{
124 fn is_superuser(&self, userid
: &str) -> bool
{
128 fn is_group_member(&self, _userid
: &str, _group
: &str) -> bool
{
132 fn lookup_privs(&self, userid
: &str, path
: &[&str]) -> u64 {
133 match userid
.parse
::<Userid
>() {
134 Ok(userid
) => Self::lookup_privs(self, &userid
, path
),