feat(plugin): cache permissions approval/decline

This commit is contained in:
Alexander Medvedev 2026-05-16 17:41:22 +02:00
parent aad2e477d9
commit c786bb0dc2
2 changed files with 117 additions and 2 deletions

View File

@ -0,0 +1,43 @@
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::collections::HashMap;
use std::path::Path;
use tokio::fs;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PermissionCacheEntry {
pub permissions_requested: Vec<String>,
pub approved: bool,
}
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct PermissionCache {
pub entries: HashMap<String, PermissionCacheEntry>, // Key is the hex hash of the plugin
}
impl PermissionCache {
pub async fn load(path: &Path) -> Self {
if let Ok(data) = fs::read_to_string(path).await {
serde_json::from_str(&data).unwrap_or_default()
} else {
Self::default()
}
}
pub async fn save(&self, path: &Path) -> tokio::io::Result<()> {
let data = serde_json::to_string_pretty(self).unwrap();
fs::write(path, data).await
}
}
pub async fn calculate_hash(path: &Path) -> tokio::io::Result<String> {
let bytes = fs::read(path).await?;
let mut hasher = Sha256::new();
hasher.update(&bytes);
let result = hasher.finalize();
Ok(result
.iter()
.map(|b| format!("{b:02x}"))
.collect::<Vec<String>>()
.join(""))
}

View File

@ -17,6 +17,7 @@ use tokio::{
use tracing::{debug, error, info, warn};
pub mod api;
pub mod cache;
pub mod loader;
/// Constants for plugin permissions.
///
@ -671,6 +672,9 @@ impl PluginManager {
return Ok(std::time::Duration::ZERO);
}
let cache_path = path.join("permission_cache.json");
let mut cache = cache::PermissionCache::load(&cache_path).await;
let mut prepared_plugins = Vec::new();
for entry in std::fs::read_dir(path)? {
@ -746,7 +750,40 @@ impl PluginManager {
for name in sorted_names {
if let Some((instance, metadata, loader_data, loader, path)) = plugins_map.remove(&name)
{
let (allowed, wait_time) = Self::ask_permission_confirmation(&metadata);
let hash = cache::calculate_hash(&path).await.unwrap_or_default();
let allowed;
let mut wait_time = std::time::Duration::ZERO;
if let Some(entry) = cache.entries.get(&hash) {
if entry.permissions_requested == metadata.permissions {
allowed = entry.approved;
info!(
"Found cached permission decision for plugin \"{}\" (approved: {})",
metadata.name, allowed
);
} else {
(allowed, wait_time) = Self::ask_permission_confirmation(&metadata);
cache.entries.insert(
hash,
cache::PermissionCacheEntry {
permissions_requested: metadata.permissions.clone(),
approved: allowed,
},
);
let _ = cache.save(&cache_path).await;
}
} else {
(allowed, wait_time) = Self::ask_permission_confirmation(&metadata);
cache.entries.insert(
hash,
cache::PermissionCacheEntry {
permissions_requested: metadata.permissions.clone(),
approved: allowed,
},
);
let _ = cache.save(&cache_path).await;
}
total_wait_time += wait_time;
if !allowed {
@ -784,7 +821,42 @@ impl PluginManager {
if loader.can_load(path) {
let (instance, metadata, loader_data) = loader.load(path).await?;
let (allowed, _wait_time) = Self::ask_permission_confirmation(&metadata);
let cache_path = Path::new(PLUGIN_DIR).join("permission_cache.json");
let mut cache = cache::PermissionCache::load(&cache_path).await;
let hash = cache::calculate_hash(path).await.unwrap_or_default();
let allowed;
if let Some(entry) = cache.entries.get(&hash) {
if entry.permissions_requested == metadata.permissions {
allowed = entry.approved;
info!(
"Found cached permission decision for plugin \"{}\" (approved: {})",
metadata.name, allowed
);
} else {
(allowed, _) = Self::ask_permission_confirmation(&metadata);
cache.entries.insert(
hash,
cache::PermissionCacheEntry {
permissions_requested: metadata.permissions.clone(),
approved: allowed,
},
);
let _ = cache.save(&cache_path).await;
}
} else {
(allowed, _) = Self::ask_permission_confirmation(&metadata);
cache.entries.insert(
hash,
cache::PermissionCacheEntry {
permissions_requested: metadata.permissions.clone(),
approved: allowed,
},
);
let _ = cache.save(&cache_path).await;
}
if !allowed {
warn!(
"Permission denied for plugin \"{}\", skipping loading.",