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
;
10 use proxmox
::tools
::time
::epoch_i64
;
12 use pbs_api_types
::{Authid, Userid, User, ApiToken, ROLE_ADMIN}
;
13 use pbs_config
::acl
::{AclTree, ROLE_NAMES}
;
15 use crate::tools
::Memcom
;
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
>,
24 data
: Option
<Arc
<CachedUserInfo
>>,
26 last_user_cache_generation
: usize,
30 static ref CACHED_CONFIG
: RwLock
<ConfigCache
> = RwLock
::new(
31 ConfigCache { data: None, last_update: 0, last_user_cache_generation: 0 }
37 /// Returns a cached instance (up to 5 seconds old).
38 pub fn new() -> Result
<Arc
<Self>, Error
> {
39 let now
= epoch_i64();
41 let memcom
= Memcom
::new()?
;
42 let user_cache_generation
= memcom
.user_cache_generation();
45 let cache
= CACHED_CONFIG
.read().unwrap();
46 if (user_cache_generation
== cache
.last_user_cache_generation
) &&
47 ((now
- cache
.last_update
) < 5)
49 if let Some(ref config
) = cache
.data
{
50 return Ok(config
.clone());
55 let config
= Arc
::new(CachedUserInfo
{
56 user_cfg
: super::user
::cached_config()?
,
57 acl_tree
: pbs_config
::acl
::cached_config()?
,
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());
69 pub(crate) fn test_new(user_cfg
: SectionConfigData
, acl_tree
: AclTree
) -> Self {
71 user_cfg
: Arc
::new(user_cfg
),
72 acl_tree
: Arc
::new(acl_tree
),
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()) {
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();
89 if !self.is_active_user_id(userid
) {
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();
110 ) -> Result
<(), Error
> {
111 let privs
= self.lookup_privs(&auth_id
, path
);
112 let allowed
= if partial
{
113 (privs
& required_privs
) != 0
115 (privs
& required_privs
) == required_privs
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("/"));
125 pub fn is_superuser(&self, auth_id
: &Authid
) -> bool
{
126 !auth_id
.is_token() && auth_id
.user() == "root@pam"
129 pub fn is_group_member(&self, _userid
: &Userid
, _group
: &str) -> bool
{
133 pub fn lookup_privs(&self, auth_id
: &Authid
, path
: &[&str]) -> u64 {
134 let (privs
, _
) = self.lookup_privs_details(auth_id
, path
);
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
);
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()) {
149 propagated_privs
|= role_privs
;
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
;
164 (privs
, propagated_privs
)
169 impl UserInformation
for CachedUserInfo
{
170 fn is_superuser(&self, userid
: &str) -> bool
{
174 fn is_group_member(&self, _userid
: &str, _group
: &str) -> bool
{
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
),