Gate plugins in private profiles
This commit is contained in:
@@ -166,25 +166,24 @@ fn render_plugin_catalog_list(
|
|||||||
.overflow_y_scrollbar()
|
.overflow_y_scrollbar()
|
||||||
.border_t_1()
|
.border_t_1()
|
||||||
.border_color(rgb(colors::HAIRLINE))
|
.border_color(rgb(colors::HAIRLINE))
|
||||||
.children(
|
.children(snapshot.installed_plugins.iter().enumerate().map(|(index, plugin)| {
|
||||||
snapshot
|
render_plugin_catalog_row(index, plugin, &snapshot.active_profile_kind, cx)
|
||||||
.installed_plugins
|
}))
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(index, plugin)| render_plugin_catalog_row(index, plugin, cx)),
|
|
||||||
)
|
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_plugin_catalog_row(
|
fn render_plugin_catalog_row(
|
||||||
index: usize,
|
index: usize,
|
||||||
plugin: &InstalledPlugin,
|
plugin: &InstalledPlugin,
|
||||||
|
profile_kind: &ely_domain::ProfileKind,
|
||||||
cx: &mut Context<ElyShell>,
|
cx: &mut Context<ElyShell>,
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
let detail_route = format!("ely://plugin/{}", plugin.id().as_str());
|
let detail_route = format!("ely://plugin/{}", plugin.id().as_str());
|
||||||
let high_risk_count = plugin.manifest().high_risk_permissions().count();
|
let high_risk_count = plugin.manifest().high_risk_permissions().count();
|
||||||
let status_color = if plugin.enabled() { colors::SUCCESS } else { colors::MUTED };
|
let status_color =
|
||||||
let status_label = if plugin.enabled() { "Enabled" } else { "Disabled" };
|
if plugin.enabled_for_profile(profile_kind) { colors::SUCCESS } else { colors::MUTED };
|
||||||
|
let status_label =
|
||||||
|
if plugin.enabled_for_profile(profile_kind) { "Enabled" } else { "Disabled" };
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.py_3()
|
.py_3()
|
||||||
@@ -251,7 +250,11 @@ fn render_plugin_catalog_row(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn enabled_plugin_count(snapshot: &BrowserSnapshot) -> usize {
|
fn enabled_plugin_count(snapshot: &BrowserSnapshot) -> usize {
|
||||||
snapshot.installed_plugins.iter().filter(|plugin| plugin.enabled()).count()
|
snapshot
|
||||||
|
.installed_plugins
|
||||||
|
.iter()
|
||||||
|
.filter(|plugin| plugin.enabled_for_profile(&snapshot.active_profile_kind))
|
||||||
|
.count()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn high_risk_plugin_count(snapshot: &BrowserSnapshot) -> usize {
|
fn high_risk_plugin_count(snapshot: &BrowserSnapshot) -> usize {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use ely_browser_core::{BrowserSnapshot, InstalledPlugin};
|
use ely_browser_core::{BrowserSnapshot, InstalledPlugin};
|
||||||
use ely_design_system::colors;
|
use ely_design_system::colors;
|
||||||
use ely_domain::{PluginId, PluginManifest, PluginPermission, PluginPermissionRisk};
|
use ely_domain::{PluginId, PluginManifest, PluginPermission, PluginPermissionRisk, ProfileKind};
|
||||||
use gpui::prelude::FluentBuilder;
|
use gpui::prelude::FluentBuilder;
|
||||||
use gpui::{AnyElement, Context, IntoElement, ParentElement, Styled, div, px, rgb};
|
use gpui::{AnyElement, Context, IntoElement, ParentElement, Styled, div, px, rgb};
|
||||||
use gpui_component::{
|
use gpui_component::{
|
||||||
@@ -30,7 +30,11 @@ impl ElyShell {
|
|||||||
.flex_col()
|
.flex_col()
|
||||||
.gap_5()
|
.gap_5()
|
||||||
.when_some(plugin, |this, plugin| {
|
.when_some(plugin, |this, plugin| {
|
||||||
this.child(self.render_plugin_detail_header(plugin, cx))
|
this.child(self.render_plugin_detail_header(
|
||||||
|
plugin,
|
||||||
|
&snapshot.active_profile_kind,
|
||||||
|
cx,
|
||||||
|
))
|
||||||
.child(render_plugin_security_summary(plugin))
|
.child(render_plugin_security_summary(plugin))
|
||||||
.child(render_plugin_manifest_rows(plugin.manifest()))
|
.child(render_plugin_manifest_rows(plugin.manifest()))
|
||||||
.child(render_plugin_permission_list(plugin.manifest()))
|
.child(render_plugin_permission_list(plugin.manifest()))
|
||||||
@@ -44,14 +48,19 @@ impl ElyShell {
|
|||||||
fn render_plugin_detail_header(
|
fn render_plugin_detail_header(
|
||||||
&mut self,
|
&mut self,
|
||||||
plugin: &InstalledPlugin,
|
plugin: &InstalledPlugin,
|
||||||
|
profile_kind: &ProfileKind,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
let plugin_id = plugin.id().clone();
|
let plugin_id = plugin.id().clone();
|
||||||
let target_enabled = !plugin.enabled();
|
let target_enabled = !plugin.enabled();
|
||||||
let status_label = if plugin.enabled() { "Enabled" } else { "Disabled" };
|
let private_allowed = plugin.private_window_allowed();
|
||||||
let status_color = if plugin.enabled() { colors::SUCCESS } else { colors::MUTED };
|
let status_label = plugin_status_label(plugin, profile_kind);
|
||||||
|
let status_color =
|
||||||
|
if plugin.enabled_for_profile(profile_kind) { colors::SUCCESS } else { colors::MUTED };
|
||||||
let action_label = if plugin.enabled() { "Disable" } else { "Enable" };
|
let action_label = if plugin.enabled() { "Disable" } else { "Enable" };
|
||||||
let action_icon = if plugin.enabled() { IconName::CircleX } else { IconName::Check };
|
let action_icon = if plugin.enabled() { IconName::CircleX } else { IconName::Check };
|
||||||
|
let private_action_label = if private_allowed { "Block Private" } else { "Allow Private" };
|
||||||
|
let private_action_icon = if private_allowed { IconName::CircleX } else { IconName::Check };
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
@@ -82,6 +91,24 @@ impl ElyShell {
|
|||||||
.gap_3()
|
.gap_3()
|
||||||
.text_xs()
|
.text_xs()
|
||||||
.child(div().font_semibold().text_color(rgb(status_color)).child(status_label))
|
.child(div().font_semibold().text_color(rgb(status_color)).child(status_label))
|
||||||
|
.child(
|
||||||
|
Button::new("toggle-plugin-detail-private")
|
||||||
|
.ghost()
|
||||||
|
.xsmall()
|
||||||
|
.icon(private_action_icon)
|
||||||
|
.label(private_action_label)
|
||||||
|
.tooltip("Set Private Window Permission")
|
||||||
|
.on_click(cx.listener({
|
||||||
|
let plugin_id = plugin_id.clone();
|
||||||
|
move |shell, _, _, cx| {
|
||||||
|
shell.set_plugin_private_window_allowed(
|
||||||
|
plugin_id.clone(),
|
||||||
|
!private_allowed,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
)
|
||||||
.child(
|
.child(
|
||||||
Button::new("toggle-plugin-detail-enabled")
|
Button::new("toggle-plugin-detail-enabled")
|
||||||
.xsmall()
|
.xsmall()
|
||||||
@@ -108,6 +135,17 @@ impl ElyShell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn plugin_status_label(plugin: &InstalledPlugin, profile_kind: &ProfileKind) -> &'static str {
|
||||||
|
if !plugin.enabled() {
|
||||||
|
return "Disabled";
|
||||||
|
}
|
||||||
|
match profile_kind {
|
||||||
|
ProfileKind::Standard => "Enabled",
|
||||||
|
ProfileKind::Private if plugin.private_window_allowed() => "Private Enabled",
|
||||||
|
ProfileKind::Private => "Private Off",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render_plugin_security_summary(plugin: &InstalledPlugin) -> AnyElement {
|
fn render_plugin_security_summary(plugin: &InstalledPlugin) -> AnyElement {
|
||||||
let manifest = plugin.manifest();
|
let manifest = plugin.manifest();
|
||||||
div()
|
div()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use ely_browser_core::{BrowserSnapshot, InstalledPlugin, PluginAuditAction, PluginAuditEvent};
|
use ely_browser_core::{BrowserSnapshot, InstalledPlugin, PluginAuditAction, PluginAuditEvent};
|
||||||
use ely_design_system::colors;
|
use ely_design_system::colors;
|
||||||
use ely_domain::PluginId;
|
use ely_domain::{PluginId, ProfileKind};
|
||||||
use gpui::prelude::FluentBuilder;
|
use gpui::prelude::FluentBuilder;
|
||||||
use gpui::{AnyElement, Context, IntoElement, ParentElement, Styled, div, px, rgb};
|
use gpui::{AnyElement, Context, IntoElement, ParentElement, Styled, div, px, rgb};
|
||||||
use gpui_component::{
|
use gpui_component::{
|
||||||
@@ -252,7 +252,7 @@ fn render_plugin_list(snapshot: &BrowserSnapshot, cx: &mut Context<ElyShell>) ->
|
|||||||
.installed_plugins
|
.installed_plugins
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(index, plugin)| render_plugin_row(index, plugin, cx))
|
.map(|(index, plugin)| render_plugin_row(index, plugin, &snapshot.active_profile_kind, cx))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
div()
|
div()
|
||||||
@@ -267,11 +267,13 @@ fn render_plugin_list(snapshot: &BrowserSnapshot, cx: &mut Context<ElyShell>) ->
|
|||||||
fn render_plugin_row(
|
fn render_plugin_row(
|
||||||
index: usize,
|
index: usize,
|
||||||
plugin: &InstalledPlugin,
|
plugin: &InstalledPlugin,
|
||||||
|
profile_kind: &ProfileKind,
|
||||||
cx: &mut Context<ElyShell>,
|
cx: &mut Context<ElyShell>,
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
let high_risk_count = plugin.manifest().high_risk_permissions().count();
|
let high_risk_count = plugin.manifest().high_risk_permissions().count();
|
||||||
let status_color = if plugin.enabled() { colors::SUCCESS } else { colors::MUTED };
|
let status_color =
|
||||||
let status_label = if plugin.enabled() { "Enabled" } else { "Disabled" };
|
if plugin.enabled_for_profile(profile_kind) { colors::SUCCESS } else { colors::MUTED };
|
||||||
|
let status_label = plugin_status_label(plugin, profile_kind);
|
||||||
let plugin_id = plugin.id().clone();
|
let plugin_id = plugin.id().clone();
|
||||||
let plugin_name = plugin.manifest().name().to_string();
|
let plugin_name = plugin.manifest().name().to_string();
|
||||||
let target_enabled = !plugin.enabled();
|
let target_enabled = !plugin.enabled();
|
||||||
@@ -323,11 +325,23 @@ fn render_plugin_row(
|
|||||||
target_enabled,
|
target_enabled,
|
||||||
cx,
|
cx,
|
||||||
))
|
))
|
||||||
|
.child(render_plugin_private_button(index, plugin, plugin_id.clone(), cx))
|
||||||
.child(render_plugin_uninstall_button(index, plugin_id, plugin_name, cx)),
|
.child(render_plugin_uninstall_button(index, plugin_id, plugin_name, cx)),
|
||||||
)
|
)
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn plugin_status_label(plugin: &InstalledPlugin, profile_kind: &ProfileKind) -> &'static str {
|
||||||
|
if !plugin.enabled() {
|
||||||
|
return "Disabled";
|
||||||
|
}
|
||||||
|
match profile_kind {
|
||||||
|
ProfileKind::Standard => "Enabled",
|
||||||
|
ProfileKind::Private if plugin.private_window_allowed() => "Private Enabled",
|
||||||
|
ProfileKind::Private => "Private Off",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render_plugin_state_button(
|
fn render_plugin_state_button(
|
||||||
index: usize,
|
index: usize,
|
||||||
plugin: &InstalledPlugin,
|
plugin: &InstalledPlugin,
|
||||||
@@ -354,6 +368,28 @@ fn render_plugin_state_button(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_plugin_private_button(
|
||||||
|
index: usize,
|
||||||
|
plugin: &InstalledPlugin,
|
||||||
|
plugin_id: PluginId,
|
||||||
|
cx: &mut Context<ElyShell>,
|
||||||
|
) -> AnyElement {
|
||||||
|
let allowed = plugin.private_window_allowed();
|
||||||
|
let label = if allowed { "Block Private" } else { "Allow Private" };
|
||||||
|
let icon = if allowed { IconName::CircleX } else { IconName::Check };
|
||||||
|
|
||||||
|
Button::new(("set-plugin-private", index))
|
||||||
|
.ghost()
|
||||||
|
.xsmall()
|
||||||
|
.icon(icon)
|
||||||
|
.label(label)
|
||||||
|
.tooltip("Set Private Window Permission")
|
||||||
|
.on_click(cx.listener(move |shell, _, _, cx| {
|
||||||
|
shell.set_plugin_private_window_allowed(plugin_id.clone(), !allowed, cx);
|
||||||
|
}))
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
fn render_plugin_uninstall_button(
|
fn render_plugin_uninstall_button(
|
||||||
index: usize,
|
index: usize,
|
||||||
plugin_id: PluginId,
|
plugin_id: PluginId,
|
||||||
@@ -423,6 +459,8 @@ fn plugin_audit_action_label(action: &PluginAuditAction) -> &'static str {
|
|||||||
PluginAuditAction::Installed => "Installed",
|
PluginAuditAction::Installed => "Installed",
|
||||||
PluginAuditAction::Enabled => "Enabled",
|
PluginAuditAction::Enabled => "Enabled",
|
||||||
PluginAuditAction::Disabled => "Disabled",
|
PluginAuditAction::Disabled => "Disabled",
|
||||||
|
PluginAuditAction::PrivateWindowAllowed => "Private Allowed",
|
||||||
|
PluginAuditAction::PrivateWindowBlocked => "Private Blocked",
|
||||||
PluginAuditAction::Uninstalled => "Uninstalled",
|
PluginAuditAction::Uninstalled => "Uninstalled",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,11 +119,9 @@ fn render_task_table(snapshot: &BrowserSnapshot) -> AnyElement {
|
|||||||
})
|
})
|
||||||
.when(!snapshot.installed_plugins.is_empty(), |this| {
|
.when(!snapshot.installed_plugins.is_empty(), |this| {
|
||||||
this.child(render_section_header("Plugins")).children(
|
this.child(render_section_header("Plugins")).children(
|
||||||
snapshot
|
snapshot.installed_plugins.iter().enumerate().map(|(index, plugin)| {
|
||||||
.installed_plugins
|
render_plugin_task_row(index, plugin, &snapshot.active_profile_kind)
|
||||||
.iter()
|
}),
|
||||||
.enumerate()
|
|
||||||
.map(|(index, plugin)| render_plugin_task_row(index, plugin)),
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
@@ -169,9 +167,14 @@ fn render_download_task_row(index: usize, entry: &DownloadEntry) -> AnyElement {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_plugin_task_row(index: usize, plugin: &InstalledPlugin) -> AnyElement {
|
fn render_plugin_task_row(
|
||||||
let status = if plugin.enabled() { "Enabled" } else { "Disabled" };
|
index: usize,
|
||||||
let status_color = if plugin.enabled() { colors::SUCCESS } else { colors::MUTED };
|
plugin: &InstalledPlugin,
|
||||||
|
profile_kind: &ely_domain::ProfileKind,
|
||||||
|
) -> AnyElement {
|
||||||
|
let status = if plugin.enabled_for_profile(profile_kind) { "Enabled" } else { "Disabled" };
|
||||||
|
let status_color =
|
||||||
|
if plugin.enabled_for_profile(profile_kind) { colors::SUCCESS } else { colors::MUTED };
|
||||||
let detail = format!(
|
let detail = format!(
|
||||||
"{} permissions - {} contributions",
|
"{} permissions - {} contributions",
|
||||||
plugin.manifest().permissions().len(),
|
plugin.manifest().permissions().len(),
|
||||||
|
|||||||
@@ -170,6 +170,23 @@ impl ElyShell {
|
|||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn set_plugin_private_window_allowed(
|
||||||
|
&mut self,
|
||||||
|
plugin_id: PluginId,
|
||||||
|
allowed: bool,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
let result = match &mut self.state {
|
||||||
|
ShellState::Ready(core) => core
|
||||||
|
.set_plugin_private_window_allowed(&plugin_id, allowed)
|
||||||
|
.map_err(|error| error.to_string()),
|
||||||
|
ShellState::StartupError(message) => Err(message.clone()),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.plugin_install_error = result.err();
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
fn uninstall_plugin(&mut self, plugin_id: PluginId, cx: &mut Context<Self>) {
|
fn uninstall_plugin(&mut self, plugin_id: PluginId, cx: &mut Context<Self>) {
|
||||||
let result = match &mut self.state {
|
let result = match &mut self.state {
|
||||||
ShellState::Ready(core) => PluginPackageStore::application()
|
ShellState::Ready(core) => PluginPackageStore::application()
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ pub struct BrowserSnapshot {
|
|||||||
pub active_profile_id: ProfileId,
|
pub active_profile_id: ProfileId,
|
||||||
pub active_space_name: String,
|
pub active_space_name: String,
|
||||||
pub active_profile_name: String,
|
pub active_profile_name: String,
|
||||||
|
pub active_profile_kind: ProfileKind,
|
||||||
pub active_download_policy: DownloadPolicy,
|
pub active_download_policy: DownloadPolicy,
|
||||||
pub search_engine: SearchEngine,
|
pub search_engine: SearchEngine,
|
||||||
pub new_tab_destination: NewTabDestination,
|
pub new_tab_destination: NewTabDestination,
|
||||||
@@ -362,6 +363,7 @@ impl BrowserCore {
|
|||||||
active_profile_id: self.active_profile_id.clone(),
|
active_profile_id: self.active_profile_id.clone(),
|
||||||
active_space_name: active_space.name().to_string(),
|
active_space_name: active_space.name().to_string(),
|
||||||
active_profile_name: active_profile.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(),
|
active_download_policy: active_profile.download_policy().clone(),
|
||||||
search_engine: self.search_engine,
|
search_engine: self.search_engine,
|
||||||
new_tab_destination: self.new_tab_destination,
|
new_tab_destination: self.new_tab_destination,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
use ely_domain::{PluginId, PluginManifest};
|
use ely_domain::{PluginId, PluginManifest, ProfileKind};
|
||||||
|
|
||||||
use crate::CoreError;
|
use crate::CoreError;
|
||||||
|
|
||||||
@@ -10,6 +10,7 @@ use super::BrowserCore;
|
|||||||
pub struct InstalledPlugin {
|
pub struct InstalledPlugin {
|
||||||
manifest: PluginManifest,
|
manifest: PluginManifest,
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
|
private_window_allowed: bool,
|
||||||
high_risk_confirmed: bool,
|
high_risk_confirmed: bool,
|
||||||
installed_at: SystemTime,
|
installed_at: SystemTime,
|
||||||
}
|
}
|
||||||
@@ -19,6 +20,8 @@ pub enum PluginAuditAction {
|
|||||||
Installed,
|
Installed,
|
||||||
Enabled,
|
Enabled,
|
||||||
Disabled,
|
Disabled,
|
||||||
|
PrivateWindowAllowed,
|
||||||
|
PrivateWindowBlocked,
|
||||||
Uninstalled,
|
Uninstalled,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,13 +34,23 @@ pub struct PluginAuditEvent {
|
|||||||
|
|
||||||
impl InstalledPlugin {
|
impl InstalledPlugin {
|
||||||
fn new(manifest: PluginManifest, high_risk_confirmed: bool, installed_at: SystemTime) -> Self {
|
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) {
|
fn set_enabled(&mut self, enabled: bool) {
|
||||||
self.enabled = enabled;
|
self.enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_private_window_allowed(&mut self, allowed: bool) {
|
||||||
|
self.private_window_allowed = allowed;
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn manifest(&self) -> &PluginManifest {
|
pub fn manifest(&self) -> &PluginManifest {
|
||||||
&self.manifest
|
&self.manifest
|
||||||
@@ -53,6 +66,20 @@ impl InstalledPlugin {
|
|||||||
self.enabled
|
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]
|
#[must_use]
|
||||||
pub fn high_risk_confirmed(&self) -> bool {
|
pub fn high_risk_confirmed(&self) -> bool {
|
||||||
self.high_risk_confirmed
|
self.high_risk_confirmed
|
||||||
@@ -120,6 +147,31 @@ impl BrowserCore {
|
|||||||
Ok(())
|
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> {
|
pub fn uninstall_plugin(&mut self, plugin_id: &PluginId) -> Result<(), CoreError> {
|
||||||
let plugin_index = self
|
let plugin_index = self
|
||||||
.installed_plugins
|
.installed_plugins
|
||||||
|
|||||||
@@ -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.len(), 1);
|
||||||
assert_eq!(snapshot.installed_plugins[0].id(), &plugin_id);
|
assert_eq!(snapshot.installed_plugins[0].id(), &plugin_id);
|
||||||
assert!(snapshot.installed_plugins[0].enabled());
|
assert!(snapshot.installed_plugins[0].enabled());
|
||||||
|
assert!(!snapshot.installed_plugins[0].private_window_allowed());
|
||||||
assert!(!snapshot.installed_plugins[0].high_risk_confirmed());
|
assert!(!snapshot.installed_plugins[0].high_risk_confirmed());
|
||||||
assert_eq!(snapshot.plugin_audit_events.len(), 1);
|
assert_eq!(snapshot.plugin_audit_events.len(), 1);
|
||||||
assert_eq!(snapshot.plugin_audit_events[0].plugin_id(), &plugin_id);
|
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(())
|
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]
|
#[test]
|
||||||
fn rejects_unknown_plugin_state_change() -> Result<(), Box<dyn Error>> {
|
fn rejects_unknown_plugin_state_change() -> Result<(), Box<dyn Error>> {
|
||||||
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||||
|
|||||||
Reference in New Issue
Block a user