src/config/cached_user_info.rs: cache it up to 5 seconds
This commit is contained in:
		@ -1,10 +1,11 @@
 | 
			
		||||
//! Cached user info for fast ACL permission checks
 | 
			
		||||
 | 
			
		||||
use std::sync::Arc;
 | 
			
		||||
use std::sync::{RwLock, Arc};
 | 
			
		||||
 | 
			
		||||
use anyhow::{Error, bail};
 | 
			
		||||
 | 
			
		||||
use proxmox::api::section_config::SectionConfigData;
 | 
			
		||||
use lazy_static::lazy_static;
 | 
			
		||||
use proxmox::api::UserInformation;
 | 
			
		||||
 | 
			
		||||
use super::acl::{AclTree, ROLE_NAMES};
 | 
			
		||||
@ -16,14 +17,43 @@ pub struct CachedUserInfo {
 | 
			
		||||
    acl_tree: Arc<AclTree>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn now() -> i64 { unsafe { libc::time(std::ptr::null_mut()) } }
 | 
			
		||||
 | 
			
		||||
struct ConfigCache {
 | 
			
		||||
    data: Option<Arc<CachedUserInfo>>,
 | 
			
		||||
    last_update: i64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
lazy_static! {
 | 
			
		||||
    static ref CACHED_CONFIG: RwLock<ConfigCache> = RwLock::new(
 | 
			
		||||
        ConfigCache { data: None, last_update: 0 }
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CachedUserInfo {
 | 
			
		||||
 | 
			
		||||
    /// Creates a new instance.
 | 
			
		||||
    pub fn new() -> Result<Self, Error> {
 | 
			
		||||
        Ok(CachedUserInfo {
 | 
			
		||||
    /// Returns a cached instance (up to 5 seconds old).
 | 
			
		||||
    pub fn new() -> Result<Arc<Self>, Error> {
 | 
			
		||||
        let now = now();
 | 
			
		||||
        { // limit scope
 | 
			
		||||
            let cache = CACHED_CONFIG.read().unwrap();
 | 
			
		||||
            if (now - cache.last_update) < 5 {
 | 
			
		||||
                if let Some(ref config) = cache.data {
 | 
			
		||||
                    return Ok(config.clone());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let config = Arc::new(CachedUserInfo {
 | 
			
		||||
            user_cfg: super::user::cached_config()?,
 | 
			
		||||
            acl_tree: super::acl::cached_config()?,
 | 
			
		||||
        })
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        let mut cache = CACHED_CONFIG.write().unwrap();
 | 
			
		||||
        cache.last_update = now;
 | 
			
		||||
        cache.data = Some(config.clone());
 | 
			
		||||
 | 
			
		||||
        Ok(config)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Test if a user account is enabled and not expired
 | 
			
		||||
@ -34,8 +64,7 @@ impl CachedUserInfo {
 | 
			
		||||
            }
 | 
			
		||||
            if let Some(expire) = info.expire {
 | 
			
		||||
                if expire > 0 {
 | 
			
		||||
                    let now = unsafe { libc::time(std::ptr::null_mut()) };
 | 
			
		||||
                    if expire <= now {
 | 
			
		||||
                    if expire <= now() {
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@ -562,7 +562,7 @@ pub async fn handle_request(api: Arc<ApiConfig>, req: Request<Body>) -> Result<R
 | 
			
		||||
                }
 | 
			
		||||
                Some(api_method) => {
 | 
			
		||||
                    let user = rpcenv.get_user();
 | 
			
		||||
                    if !check_api_permission(api_method.access.permission, user.as_deref(), &uri_param, &user_info) {
 | 
			
		||||
                    if !check_api_permission(api_method.access.permission, user.as_deref(), &uri_param, user_info.as_ref()) {
 | 
			
		||||
                        let err = http_err!(FORBIDDEN, format!("permission check failed"));
 | 
			
		||||
                        tokio::time::delay_until(Instant::from_std(access_forbidden_time)).await;
 | 
			
		||||
                        return Ok((formatter.format_error)(err));
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user