Gate plugins in private profiles

This commit is contained in:
2026-05-09 00:44:15 -04:00
parent 7797203bbf
commit 96732c8932
8 changed files with 236 additions and 31 deletions
+2
View File
@@ -103,6 +103,7 @@ pub struct BrowserSnapshot {
pub active_profile_id: ProfileId,
pub active_space_name: String,
pub active_profile_name: String,
pub active_profile_kind: ProfileKind,
pub active_download_policy: DownloadPolicy,
pub search_engine: SearchEngine,
pub new_tab_destination: NewTabDestination,
@@ -362,6 +363,7 @@ impl BrowserCore {
active_profile_id: self.active_profile_id.clone(),
active_space_name: active_space.name().to_string(),
active_profile_name: active_profile.name().to_string(),
active_profile_kind: active_profile.kind().clone(),
active_download_policy: active_profile.download_policy().clone(),
search_engine: self.search_engine,
new_tab_destination: self.new_tab_destination,
+54 -2
View File
@@ -1,6 +1,6 @@
use std::time::SystemTime;
use ely_domain::{PluginId, PluginManifest};
use ely_domain::{PluginId, PluginManifest, ProfileKind};
use crate::CoreError;
@@ -10,6 +10,7 @@ use super::BrowserCore;
pub struct InstalledPlugin {
manifest: PluginManifest,
enabled: bool,
private_window_allowed: bool,
high_risk_confirmed: bool,
installed_at: SystemTime,
}
@@ -19,6 +20,8 @@ pub enum PluginAuditAction {
Installed,
Enabled,
Disabled,
PrivateWindowAllowed,
PrivateWindowBlocked,
Uninstalled,
}
@@ -31,13 +34,23 @@ pub struct PluginAuditEvent {
impl InstalledPlugin {
fn new(manifest: PluginManifest, high_risk_confirmed: bool, installed_at: SystemTime) -> Self {
Self { manifest, enabled: true, high_risk_confirmed, installed_at }
Self {
manifest,
enabled: true,
private_window_allowed: false,
high_risk_confirmed,
installed_at,
}
}
fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
fn set_private_window_allowed(&mut self, allowed: bool) {
self.private_window_allowed = allowed;
}
#[must_use]
pub fn manifest(&self) -> &PluginManifest {
&self.manifest
@@ -53,6 +66,20 @@ impl InstalledPlugin {
self.enabled
}
#[must_use]
pub fn private_window_allowed(&self) -> bool {
self.private_window_allowed
}
#[must_use]
pub fn enabled_for_profile(&self, profile_kind: &ProfileKind) -> bool {
self.enabled
&& match profile_kind {
ProfileKind::Standard => true,
ProfileKind::Private => self.private_window_allowed,
}
}
#[must_use]
pub fn high_risk_confirmed(&self) -> bool {
self.high_risk_confirmed
@@ -120,6 +147,31 @@ impl BrowserCore {
Ok(())
}
pub fn set_plugin_private_window_allowed(
&mut self,
plugin_id: &PluginId,
allowed: bool,
) -> Result<(), CoreError> {
self.plugin_mut(plugin_id)?.set_private_window_allowed(allowed);
let action = if allowed {
PluginAuditAction::PrivateWindowAllowed
} else {
PluginAuditAction::PrivateWindowBlocked
};
self.record_plugin_audit_event(plugin_id.clone(), action);
Ok(())
}
pub fn enabled_plugins_for_active_profile(&self) -> Result<Vec<InstalledPlugin>, CoreError> {
let profile_kind = self.active_profile()?.kind();
Ok(self
.installed_plugins
.iter()
.filter(|plugin| plugin.enabled_for_profile(profile_kind))
.cloned()
.collect())
}
pub fn uninstall_plugin(&mut self, plugin_id: &PluginId) -> Result<(), CoreError> {
let plugin_index = self
.installed_plugins
+52
View File
@@ -30,6 +30,7 @@ fn installs_standard_plugin_and_records_audit_event() -> Result<(), Box<dyn Erro
assert_eq!(snapshot.installed_plugins.len(), 1);
assert_eq!(snapshot.installed_plugins[0].id(), &plugin_id);
assert!(snapshot.installed_plugins[0].enabled());
assert!(!snapshot.installed_plugins[0].private_window_allowed());
assert!(!snapshot.installed_plugins[0].high_risk_confirmed());
assert_eq!(snapshot.plugin_audit_events.len(), 1);
assert_eq!(snapshot.plugin_audit_events[0].plugin_id(), &plugin_id);
@@ -144,6 +145,57 @@ fn allows_reinstall_after_plugin_uninstall() -> Result<(), Box<dyn Error>> {
Ok(())
}
#[test]
fn private_profile_disables_plugins_until_allowed() -> Result<(), Box<dyn Error>> {
let mut core = BrowserCore::new(InitialBrowserConfig::private_window()?)?;
let plugin_id =
core.install_plugin(plugin_manifest("com.elydora.reader", &["page:metadata"])?, false)?;
assert!(core.enabled_plugins_for_active_profile()?.is_empty());
assert!(!core.snapshot()?.installed_plugins[0].private_window_allowed());
core.set_plugin_private_window_allowed(&plugin_id, true)?;
let allowed_plugins = core.enabled_plugins_for_active_profile()?;
let snapshot = core.snapshot()?;
assert_eq!(allowed_plugins.len(), 1);
assert_eq!(allowed_plugins[0].id(), &plugin_id);
assert!(snapshot.installed_plugins[0].private_window_allowed());
assert_eq!(snapshot.plugin_audit_events[1].action(), &PluginAuditAction::PrivateWindowAllowed);
Ok(())
}
#[test]
fn disabled_plugin_overrides_private_permission() -> Result<(), Box<dyn Error>> {
let mut core = BrowserCore::new(InitialBrowserConfig::private_window()?)?;
let plugin_id =
core.install_plugin(plugin_manifest("com.elydora.reader", &["page:metadata"])?, false)?;
core.set_plugin_private_window_allowed(&plugin_id, true)?;
core.disable_plugin(&plugin_id)?;
assert!(core.enabled_plugins_for_active_profile()?.is_empty());
assert_eq!(core.snapshot()?.plugin_audit_events[2].action(), &PluginAuditAction::Disabled);
Ok(())
}
#[test]
fn blocks_unknown_plugin_private_window_permission() -> Result<(), Box<dyn Error>> {
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
let plugin_id = PluginId::parse("com.elydora.missing")?;
let error = core
.set_plugin_private_window_allowed(&plugin_id, true)
.err()
.ok_or_else(|| io::Error::other("missing plugin permission update succeeded"))?;
assert!(matches!(
error,
CoreError::PluginNotFound { id } if id.as_str() == "com.elydora.missing"
));
Ok(())
}
#[test]
fn rejects_unknown_plugin_state_change() -> Result<(), Box<dyn Error>> {
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;