aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJae-Heon Ji <32578710+jaeheonji@users.noreply.github.com>2023-08-12 22:35:42 +0900
committerGitHub <noreply@github.com>2023-08-12 15:35:42 +0200
commitc8ddb23297e2f4fc900b8286d57e2808ae6a4fdb (patch)
treec96d26c8bfc24172374aefe88624b78c6890663f
parentdocs(changelog): protobuffers (diff)
downloadsl0ppij-c8ddb23297e2f4fc900b8286d57e2808ae6a4fdb.tar.gz
sl0ppij-c8ddb23297e2f4fc900b8286d57e2808ae6a4fdb.tar.xz
sl0ppij-c8ddb23297e2f4fc900b8286d57e2808ae6a4fdb.zip
feat: add plugin permission system (#2624)
* WIP: add exaple of permission ui * feat: add request permission ui * feat: add caching permission in memory * feat: add permission check * feat: add file caching * fix: changes request * feat(ui): new status bar mode (#2619) * supermode prototype * fix integration tests * fix tests * style(fmt): rustfmt * docs(changelog): status-bar supermode * fix(rendering): occasional glitches while resizing (#2621) * docs(changelog): resize glitches fix * chore(version): bump development version * Fix colored pane frames in mirrored sessions (#2625) * server/panes/tiled: Fix colored frames in mirrored sessions. Colored frames were previously ignored because they were treated like floating panes when rendering tiled panes. * CHANGELOG: Add PR #2625 * server/tab/unit: Fix unit tests for server. * fix(sessions): use custom lists of adjectives and nouns for generating session names (#2122) * Create custom lists of adjectives and nouns for generating session names * move word lists to const slices * add logic to retry name generation * refactor - reuse the name generator - iterator instead of for loop --------- Co-authored-by: Thomas Linford <linford.t@gmail.com> * docs(changelog): generate session names with custom words list * feat(plugins): make plugins configurable (#2646) * work * make every plugin entry point configurable * make integration tests pass * make e2e tests pass * add test for plugin configuration * add test snapshot * add plugin config parsing test * cleanups * style(fmt): rustfmt * style(comment): remove commented code * docs(changelog): configurable plugins * style(fmt): rustfmt * touch up ui * fix: don't save permission data in memory * feat: load cached permission * test: add example test (WIP) * fix: issue event are always denied * test: update snapshot * apply formatting * refactor: update default cache function * test: add more new test * apply formatting * Revert "apply formatting" This reverts commit a4e93703fbfdb6865131daa1c8b90fc5c36ab25e. * apply format * fix: update cache path * apply format * fix: cache path * fix: update log level * test for github workflow * Revert "test for github workflow" This reverts commit 01eff3bc5d1627a4e60bc6dac8ebe5500bc5b56e. * refactor: permission cache * fix(test): permission grant/deny race condition * style(fmt): rustfmt * style(fmt): rustfmt * configure permissions * permission denied test * snapshot * add ui for small plugins * style(fmt): rustfmt * some cleanups --------- Co-authored-by: Aram Drevekenin <aram@poor.dev> Co-authored-by: har7an <99636919+har7an@users.noreply.github.com> Co-authored-by: Kyle Sutherland-Cash <kyle.sutherlandcash@gmail.com> Co-authored-by: Thomas Linford <linford.t@gmail.com> Co-authored-by: Thomas Linford <tlinford@users.noreply.github.com>
-rw-r--r--default-plugins/fixture-plugin-for-tests/src/main.rs13
-rw-r--r--default-plugins/strider/src/main.rs4
-rw-r--r--zellij-server/src/panes/floating_panes/mod.rs8
-rw-r--r--zellij-server/src/panes/plugin_pane.rs101
-rw-r--r--zellij-server/src/panes/tiled_panes/mod.rs2
-rw-r--r--zellij-server/src/plugins/mod.rs36
-rw-r--r--zellij-server/src/plugins/plugin_loader.rs28
-rw-r--r--zellij-server/src/plugins/plugin_map.rs7
-rw-r--r--zellij-server/src/plugins/unit/plugin_tests.rs1151
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__denied_permission_request_result.snap7
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__granted_permission_request_result.snap15
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__request_plugin_permissions.snap17
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__switch_to_mode_plugin_command_permission_denied.snap6
-rw-r--r--zellij-server/src/plugins/wasm_bridge.rs138
-rw-r--r--zellij-server/src/plugins/zellij_exports.rs329
-rw-r--r--zellij-server/src/screen.rs27
-rw-r--r--zellij-server/src/tab/mod.rs54
-rw-r--r--zellij-tile/src/lib.rs1
-rw-r--r--zellij-tile/src/shim.rs7
-rwxr-xr-xzellij-utils/assets/plugins/compact-bar.wasmbin526730 -> 806599 bytes
-rwxr-xr-xzellij-utils/assets/plugins/fixture-plugin-for-tests.wasmbin469401 -> 768790 bytes
-rwxr-xr-xzellij-utils/assets/plugins/status-bar.wasmbin656048 -> 961879 bytes
-rwxr-xr-xzellij-utils/assets/plugins/strider.wasmbin1718164 -> 2025837 bytes
-rwxr-xr-xzellij-utils/assets/plugins/tab-bar.wasmbin497583 -> 781434 bytes
-rw-r--r--zellij-utils/src/consts.rs2
-rw-r--r--zellij-utils/src/data.rs70
-rw-r--r--zellij-utils/src/errors.rs2
-rw-r--r--zellij-utils/src/input/mod.rs1
-rw-r--r--zellij-utils/src/input/permission.rs60
-rw-r--r--zellij-utils/src/kdl/mod.rs52
-rw-r--r--zellij-utils/src/plugin_api/event.proto6
-rw-r--r--zellij-utils/src/plugin_api/event.rs30
-rw-r--r--zellij-utils/src/plugin_api/mod.rs1
-rw-r--r--zellij-utils/src/plugin_api/plugin_command.proto7
-rw-r--r--zellij-utils/src/plugin_api/plugin_command.rs34
-rw-r--r--zellij-utils/src/plugin_api/plugin_permission.proto12
-rw-r--r--zellij-utils/src/plugin_api/plugin_permission.rs44
37 files changed, 1935 insertions, 337 deletions
diff --git a/default-plugins/fixture-plugin-for-tests/src/main.rs b/default-plugins/fixture-plugin-for-tests/src/main.rs
index fe8b034b..811a4507 100644
--- a/default-plugins/fixture-plugin-for-tests/src/main.rs
+++ b/default-plugins/fixture-plugin-for-tests/src/main.rs
@@ -39,6 +39,16 @@ register_worker!(TestWorker, test_worker, TEST_WORKER);
impl ZellijPlugin for State {
fn load(&mut self, configuration: BTreeMap<String, String>) {
+ request_permission(&[
+ PermissionType::ChangeApplicationState,
+ PermissionType::ReadApplicationState,
+ PermissionType::ReadApplicationState,
+ PermissionType::ChangeApplicationState,
+ PermissionType::OpenFiles,
+ PermissionType::RunCommands,
+ PermissionType::OpenTerminalsOrPlugins,
+ PermissionType::WriteToStdin,
+ ]);
self.configuration = configuration;
subscribe(&[
EventType::InputReceived,
@@ -227,6 +237,9 @@ impl ZellijPlugin for State {
Key::Ctrl('z') => {
go_to_tab_name(&format!("{:?}", self.configuration));
},
+ Key::Ctrl('1') => {
+ request_permission(&[PermissionType::ReadApplicationState]);
+ },
_ => {},
},
Event::CustomMessage(message, payload) => {
diff --git a/default-plugins/strider/src/main.rs b/default-plugins/strider/src/main.rs
index e620930c..3d4c94a9 100644
--- a/default-plugins/strider/src/main.rs
+++ b/default-plugins/strider/src/main.rs
@@ -30,6 +30,7 @@ impl ZellijPlugin for State {
EventType::FileSystemCreate,
EventType::FileSystemUpdate,
EventType::FileSystemDelete,
+ EventType::PermissionRequestResult,
]);
post_message_to(PluginMessage {
worker_name: Some("file_name_search".into()),
@@ -54,6 +55,9 @@ impl ZellijPlugin for State {
};
self.ev_history.push_back((event.clone(), Instant::now()));
match event {
+ Event::PermissionRequestResult(_) => {
+ should_render = true;
+ },
Event::Timer(_elapsed) => {
if self.search_state.loading {
set_timeout(0.5);
diff --git a/zellij-server/src/panes/floating_panes/mod.rs b/zellij-server/src/panes/floating_panes/mod.rs
index 2de5b74e..1f2d770b 100644
--- a/zellij-server/src/panes/floating_panes/mod.rs
+++ b/zellij-server/src/panes/floating_panes/mod.rs
@@ -295,7 +295,7 @@ impl FloatingPanes {
pane.render_full_viewport();
}
}
- pub fn set_pane_frames(&mut self, os_api: &mut Box<dyn ServerOsApi>) -> Result<()> {
+ pub fn set_pane_frames(&mut self, _os_api: &mut Box<dyn ServerOsApi>) -> Result<()> {
let err_context =
|pane_id: &PaneId| format!("failed to activate frame on pane {pane_id:?}");
@@ -392,7 +392,7 @@ impl FloatingPanes {
self.set_force_render();
}
- pub fn resize_pty_all_panes(&mut self, os_api: &mut Box<dyn ServerOsApi>) -> Result<()> {
+ pub fn resize_pty_all_panes(&mut self, _os_api: &mut Box<dyn ServerOsApi>) -> Result<()> {
for pane in self.panes.values_mut() {
resize_pty!(pane, os_api, self.senders, self.character_cell_size)
.with_context(|| format!("failed to resize PTY in pane {:?}", pane.pid()))?;
@@ -403,7 +403,7 @@ impl FloatingPanes {
pub fn resize_active_pane(
&mut self,
client_id: ClientId,
- os_api: &mut Box<dyn ServerOsApi>,
+ _os_api: &mut Box<dyn ServerOsApi>,
strategy: &ResizeStrategy,
) -> Result<bool> {
// true => successfully resized
@@ -838,7 +838,7 @@ impl FloatingPanes {
self.focus_pane_for_all_clients(focused_pane);
}
}
- pub fn switch_active_pane_with(&mut self, os_api: &mut Box<dyn ServerOsApi>, pane_id: PaneId) {
+ pub fn switch_active_pane_with(&mut self, _os_api: &mut Box<dyn ServerOsApi>, pane_id: PaneId) {
if let Some(active_pane_id) = self.first_active_floating_pane_id() {
let current_position = self.panes.get(&active_pane_id).unwrap();
let prev_geom = current_position.position_and_size();
diff --git a/zellij-server/src/panes/plugin_pane.rs b/zellij-server/src/panes/plugin_pane.rs
index 46552960..b4e3461e 100644
--- a/zellij-server/src/panes/plugin_pane.rs
+++ b/zellij-server/src/panes/plugin_pane.rs
@@ -1,11 +1,11 @@
-use std::collections::HashMap;
+use std::collections::{BTreeSet, HashMap};
use std::time::Instant;
use crate::output::{CharacterChunk, SixelImageChunk};
use crate::panes::{grid::Grid, sixel::SixelImageStore, LinkHandler, PaneId};
use crate::plugins::PluginInstruction;
use crate::pty::VteBytes;
-use crate::tab::Pane;
+use crate::tab::{AdjustedInput, Pane};
use crate::ui::{
loading_indication::LoadingIndication,
pane_boundaries_frame::{FrameParams, PaneFrame},
@@ -13,6 +13,7 @@ use crate::ui::{
use crate::ClientId;
use std::cell::RefCell;
use std::rc::Rc;
+use zellij_utils::data::{PermissionStatus, PermissionType, PluginPermission};
use zellij_utils::pane_size::{Offset, SizeInPixels};
use zellij_utils::position::Position;
use zellij_utils::{
@@ -25,6 +26,15 @@ use zellij_utils::{
vte,
};
+macro_rules! style {
+ ($fg:expr) => {
+ ansi_term::Style::new().fg(match $fg {
+ PaletteColor::Rgb((r, g, b)) => ansi_term::Color::RGB(r, g, b),
+ PaletteColor::EightBit(color) => ansi_term::Color::Fixed(color),
+ })
+ };
+}
+
macro_rules! get_or_create_grid {
($self:ident, $client_id:ident) => {{
let rows = $self.get_content_rows();
@@ -73,6 +83,7 @@ pub(crate) struct PluginPane {
pane_frame_color_override: Option<(PaletteColor, Option<String>)>,
invoked_with: Option<Run>,
loading_indication: LoadingIndication,
+ requesting_permissions: Option<PluginPermission>,
debug: bool,
}
@@ -121,6 +132,7 @@ impl PluginPane {
pane_frame_color_override: None,
invoked_with,
loading_indication,
+ requesting_permissions: None,
debug,
};
for client_id in currently_connected_clients {
@@ -181,6 +193,14 @@ impl Pane for PluginPane {
}
fn handle_plugin_bytes(&mut self, client_id: ClientId, bytes: VteBytes) {
self.set_client_should_render(client_id, true);
+
+ let mut vte_bytes = bytes;
+ if let Some(plugin_permission) = &self.requesting_permissions {
+ vte_bytes = self
+ .display_request_permission_message(plugin_permission)
+ .into();
+ }
+
let grid = get_or_create_grid!(self, client_id);
// this is part of the plugin contract, whenever we update the plugin and call its render function, we delete the existing viewport
@@ -193,14 +213,36 @@ impl Pane for PluginPane {
.vte_parsers
.entry(client_id)
.or_insert_with(|| vte::Parser::new());
- for &byte in &bytes {
+
+ for &byte in &vte_bytes {
vte_parser.advance(grid, byte);
}
+
self.should_render.insert(client_id, true);
}
fn cursor_coordinates(&self) -> Option<(usize, usize)> {
None
}
+ fn adjust_input_to_terminal(&mut self, input_bytes: Vec<u8>) -> Option<AdjustedInput> {
+ if let Some(requesting_permissions) = &self.requesting_permissions {
+ let permissions = requesting_permissions.permissions.clone();
+ match input_bytes.as_slice() {
+ // Y or y
+ &[89] | &[121] => Some(AdjustedInput::PermissionRequestResult(
+ permissions,
+ PermissionStatus::Granted,
+ )),
+ // N or n
+ &[78] | &[110] => Some(AdjustedInput::PermissionRequestResult(
+ permissions,
+ PermissionStatus::Denied,
+ )),
+ _ => None,
+ }
+ } else {
+ Some(AdjustedInput::WriteBytesToTerminal(input_bytes))
+ }
+ }
fn position_and_size(&self) -> PaneGeom {
self.geom
}
@@ -233,6 +275,9 @@ impl Pane for PluginPane {
fn set_selectable(&mut self, selectable: bool) {
self.selectable = selectable;
}
+ fn request_permissions_from_user(&mut self, permissions: Option<PluginPermission>) {
+ self.requesting_permissions = permissions;
+ }
fn render(
&mut self,
client_id: Option<ClientId>,
@@ -595,4 +640,54 @@ impl PluginPane {
self.handle_plugin_bytes(client_id, bytes.clone());
}
}
+ fn display_request_permission_message(&self, plugin_permission: &PluginPermission) -> String {
+ let bold_white = style!(self.style.colors.white).bold();
+ let cyan = style!(self.style.colors.cyan).bold();
+ let orange = style!(self.style.colors.orange).bold();
+ let green = style!(self.style.colors.green).bold();
+
+ let mut messages = String::new();
+ let permissions: BTreeSet<PermissionType> =
+ plugin_permission.permissions.clone().into_iter().collect();
+
+ let min_row_count = permissions.len() + 4;
+
+ if self.rows() >= min_row_count {
+ messages.push_str(&format!(
+ "{} {} {}\n",
+ bold_white.paint("Plugin"),
+ cyan.paint(&plugin_permission.name),
+ bold_white.paint("asks permission to:"),
+ ));
+ permissions.iter().enumerate().for_each(|(i, p)| {
+ messages.push_str(&format!(
+ "\n\r{}. {}",
+ bold_white.paint(&format!("{}", i + 1)),
+ orange.paint(p.display_name())
+ ));
+ });
+
+ messages.push_str(&format!(
+ "\n\n\r{} {}",
+ bold_white.paint("Allow?"),
+ green.paint("(y/n)"),
+ ));
+ } else {
+ messages.push_str(&format!(
+ "{} {}. {} {}\n",
+ bold_white.paint("This plugin asks permission to:"),
+ orange.paint(
+ permissions
+ .iter()
+ .map(|p| p.to_string())
+ .collect::<Vec<_>>()
+ .join(", ")
+ ),
+ bold_white.paint("Allow?"),
+ green.paint("(y/n)"),
+ ));
+ }
+
+ messages
+ }
}
diff --git a/zellij-server/src/panes/tiled_panes/mod.rs b/zellij-server/src/panes/tiled_panes/mod.rs
index 9aca1af4..736c16bc 100644
--- a/zellij-server/src/panes/tiled_panes/mod.rs
+++ b/zellij-server/src/panes/tiled_panes/mod.rs
@@ -68,7 +68,6 @@ pub struct TiledPanes {
draw_pane_frames: bool,
panes_to_hide: HashSet<PaneId>,
fullscreen_is_active: bool,
- os_api: Box<dyn ServerOsApi>,
senders: ThreadSenders,
window_title: Option<String>,
client_id_to_boundaries: HashMap<ClientId, Boundaries>,
@@ -105,7 +104,6 @@ impl TiledPanes {
draw_pane_frames,
panes_to_hide: HashSet::new(),
fullscreen_is_active: false,
- os_api,
senders,
window_title: None,
client_id_to_boundaries: HashMap::new(),
diff --git a/zellij-server/src/plugins/mod.rs b/zellij-server/src/plugins/mod.rs
index f36bdc2d..4bef0683 100644
--- a/zellij-server/src/plugins/mod.rs
+++ b/zellij-server/src/plugins/mod.rs
@@ -18,7 +18,7 @@ use crate::{pty::PtyInstruction, thread_bus::Bus, ClientId, ServerInstruction};
use wasm_bridge::WasmBridge;
use zellij_utils::{
- data::{Event, EventType, PluginCapabilities},
+ data::{Event, EventType, PermissionStatus, PermissionType, PluginCapabilities},
errors::{prelude::*, ContextType, PluginContext},
input::{
command::TerminalAction,
@@ -79,6 +79,13 @@ pub enum PluginInstruction {
String, // serialized payload
),
PluginSubscribedToEvents(PluginId, ClientId, HashSet<EventType>),
+ PermissionRequestResult(
+ PluginId,
+ Option<ClientId>,
+ Vec<PermissionType>,
+ PermissionStatus,
+ Option<PathBuf>,
+ ),
Exit,
}
@@ -105,6 +112,9 @@ impl From<&PluginInstruction> for PluginContext {
PluginInstruction::PluginSubscribedToEvents(..) => {
PluginContext::PluginSubscribedToEvents
},
+ PluginInstruction::PermissionRequestResult(..) => {
+ PluginContext::PermissionRequestResult
+ },
}
}
}
@@ -287,6 +297,30 @@ pub(crate) fn plugin_thread_main(
}
}
},
+ PluginInstruction::PermissionRequestResult(
+ plugin_id,
+ client_id,
+ permissions,
+ status,
+ cache_path,
+ ) => {
+ if let Err(e) = wasm_bridge.cache_plugin_permissions(
+ plugin_id,
+ client_id,
+ permissions,
+ status,
+ cache_path,
+ ) {
+ log::error!("{}", e);
+ }
+
+ let updates = vec![(
+ Some(plugin_id),
+ client_id,
+ Event::PermissionRequestResult(status),
+ )];
+ wasm_bridge.update_plugins(updates)?;
+ },
PluginInstruction::Exit => {
wasm_bridge.cleanup();
break;
diff --git a/zellij-server/src/plugins/plugin_loader.rs b/zellij-server/src/plugins/plugin_loader.rs
index 92a4c048..ea0c2b6a 100644
--- a/zellij-server/src/plugins/plugin_loader.rs
+++ b/zellij-server/src/plugins/plugin_loader.rs
@@ -188,6 +188,7 @@ impl<'a> PluginLoader<'a> {
display_loading_stage!(end, loading_indication, senders, plugin_id);
Ok(())
}
+
pub fn add_client(
client_id: ClientId,
plugin_dir: PathBuf,
@@ -613,6 +614,19 @@ impl<'a> PluginLoader<'a> {
}
start_function.call(&[]).with_context(err_context)?;
+ plugin_map.lock().unwrap().insert(
+ self.plugin_id,
+ self.client_id,
+ Arc::new(Mutex::new(RunningPlugin::new(
+ main_user_instance,
+ main_user_env,
+ self.size.rows,
+ self.size.cols,
+ ))),
+ subscriptions.clone(),
+ workers,
+ );
+
let protobuf_plugin_configuration: ProtobufPluginConfiguration = self
.plugin
.userspace_configuration
@@ -640,18 +654,6 @@ impl<'a> PluginLoader<'a> {
self.senders,
self.plugin_id
);
- plugin_map.lock().unwrap().insert(
- self.plugin_id,
- self.client_id,
- Arc::new(Mutex::new(RunningPlugin::new(
- main_user_instance,
- main_user_env,
- self.size.rows,
- self.size.cols,
- ))),
- subscriptions.clone(),
- workers,
- );
display_loading_stage!(
indicate_writing_plugin_to_cache_success,
self.loading_indication,
@@ -764,13 +766,13 @@ impl<'a> PluginLoader<'a> {
})
.with_context(err_context)?;
let wasi = wasi_env.import_object(&module).with_context(err_context)?;
-
let mut mut_plugin = self.plugin.clone();
mut_plugin.set_tab_index(self.tab_index);
let plugin_env = PluginEnv {
plugin_id: self.plugin_id,
client_id: self.client_id,
plugin: mut_plugin,
+ permissions: Arc::new(Mutex::new(None)),
senders: self.senders.clone(),
wasi_env,
plugin_own_data_dir: self.plugin_own_data_dir.clone(),
diff --git a/zellij-server/src/plugins/plugin_map.rs b/zellij-server/src/plugins/plugin_map.rs
index 2f7454c7..0cf26943 100644
--- a/zellij-server/src/plugins/plugin_map.rs
+++ b/zellij-server/src/plugins/plugin_map.rs
@@ -11,7 +11,6 @@ use wasmer_wasi::WasiEnv;
use crate::{thread_bus::ThreadSenders, ClientId};
use zellij_utils::async_channel::Sender;
-use zellij_utils::errors::prelude::*;
use zellij_utils::{
data::EventType,
data::PluginCapabilities,
@@ -20,6 +19,7 @@ use zellij_utils::{
input::plugins::PluginConfig,
ipc::ClientAttributes,
};
+use zellij_utils::{data::PermissionType, errors::prelude::*};
// the idea here is to provide atomicity when adding/removing plugins from the map (eg. when a new
// client connects) but to also allow updates/renders not to block each other
@@ -193,6 +193,7 @@ pub type Subscriptions = HashSet<EventType>;
pub struct PluginEnv {
pub plugin_id: PluginId,
pub plugin: PluginConfig,
+ pub permissions: Arc<Mutex<Option<HashSet<PermissionType>>>>,
pub senders: ThreadSenders,
pub wasi_env: WasiEnv,
pub tab_index: usize,
@@ -215,6 +216,10 @@ impl PluginEnv {
self.plugin_id
)
}
+
+ pub fn set_permissions(&mut self, permissions: HashSet<PermissionType>) {
+ self.permissions.lock().unwrap().replace(permissions);
+ }
}
#[derive(Eq, PartialEq, Hash)]
diff --git a/zellij-server/src/plugins/unit/plugin_tests.rs b/zellij-server/src/plugins/unit/plugin_tests.rs
index 663ca94e..e995a07b 100644
--- a/zellij-server/src/plugins/unit/plugin_tests.rs
+++ b/zellij-server/src/plugins/unit/plugin_tests.rs
@@ -6,9 +6,10 @@ use std::collections::BTreeMap;
use std::path::PathBuf;
use tempfile::tempdir;
use wasmer::Store;
-use zellij_utils::data::{Event, Key, PluginCapabilities};
+use zellij_utils::data::{Event, Key, PermissionStatus, PermissionType, PluginCapabilities};
use zellij_utils::errors::ErrorContext;
use zellij_utils::input::layout::{Layout, PluginUserConfiguration, RunPlugin, RunPluginLocation};
+use zellij_utils::input::permission::PermissionCache;
use zellij_utils::input::plugins::PluginsConfig;
use zellij_utils::ipc::ClientAttributes;
use zellij_utils::lazy_static::lazy_static;
@@ -52,6 +53,157 @@ macro_rules! log_actions_in_thread {
};
}
+macro_rules! grant_permissions_and_log_actions_in_thread {
+ ( $arc_mutex_log:expr, $exit_event:path, $receiver:expr, $exit_after_count:expr, $permission_type:expr, $cache_path:expr, $plugin_thread_sender:expr, $client_id:expr ) => {
+ std::thread::Builder::new()
+ .name("fake_screen_thread".to_string())
+ .spawn({
+ let log = $arc_mutex_log.clone();
+ let mut exit_event_count = 0;
+ let cache_path = $cache_path.clone();
+ let plugin_thread_sender = $plugin_thread_sender.clone();
+ move || loop {
+ let (event, _err_ctx) = $receiver
+ .recv()
+ .expect("failed to receive event on channel");
+ match event {
+ $exit_event(..) => {
+ exit_event_count += 1;
+ log.lock().unwrap().push(event);
+ if exit_event_count == $exit_after_count {
+ break;
+ }
+ },
+ ScreenInstruction::RequestPluginPermissions(_, plugin_permission) => {
+ if plugin_permission.permissions.contains($permission_type) {
+ let _ = plugin_thread_sender.send(
+ PluginInstruction::PermissionRequestResult(
+ 0,
+ Some($client_id),
+ plugin_permission.permissions,
+ PermissionStatus::Granted,
+ Some(cache_path.clone()),
+ ),
+ );
+ } else {
+ let _ = plugin_thread_sender.send(
+ PluginInstruction::PermissionRequestResult(
+ 0,
+ Some($client_id),
+ plugin_permission.permissions,
+ PermissionStatus::Denied,
+ Some(cache_path.clone()),
+ ),
+ );
+ }
+ },
+ _ => {
+ log.lock().unwrap().push(event);
+ },
+ }
+ }
+ })
+ .unwrap()
+ };
+}
+
+macro_rules! deny_permissions_and_log_actions_in_thread {
+ ( $arc_mutex_log:expr, $exit_event:path, $receiver:expr, $exit_after_count:expr, $permission_type:expr, $cache_path:expr, $plugin_thread_sender:expr, $client_id:expr ) => {
+ std::thread::Builder::new()
+ .name("fake_screen_thread".to_string())
+ .spawn({
+ let log = $arc_mutex_log.clone();
+ let mut exit_event_count = 0;
+ let cache_path = $cache_path.clone();
+ let plugin_thread_sender = $plugin_thread_sender.clone();
+ move || loop {
+ let (event, _err_ctx) = $receiver
+ .recv()
+ .expect("failed to receive event on channel");
+ match event {
+ $exit_event(..) => {
+ exit_event_count += 1;
+ log.lock().unwrap().push(event);
+ if exit_event_count == $exit_after_count {
+ break;
+ }
+ },
+ ScreenInstruction::RequestPluginPermissions(_, plugin_permission) => {
+ let _ = plugin_thread_sender.send(
+ PluginInstruction::PermissionRequestResult(
+ 0,
+ Some($client_id),
+ plugin_permission.permissions,
+ PermissionStatus::Denied,
+ Some(cache_path.clone()),
+ ),
+ );
+ break;
+ },
+ _ => {
+ log.lock().unwrap().push(event);
+ },
+ }
+ }
+ })
+ .unwrap()
+ };
+}
+
+macro_rules! grant_permissions_and_log_actions_in_thread_naked_variant {
+ ( $arc_mutex_log:expr, $exit_event:path, $receiver:expr, $exit_after_count:expr, $permission_type:expr, $cache_path:expr, $plugin_thread_sender:expr, $client_id:expr ) => {
+ std::thread::Builder::new()
+ .name("fake_screen_thread".to_string())
+ .spawn({
+ let log = $arc_mutex_log.clone();
+ let mut exit_event_count = 0;
+ let cache_path = $cache_path.clone();
+ let plugin_thread_sender = $plugin_thread_sender.clone();
+ move || loop {
+ let (event, _err_ctx) = $receiver
+ .recv()
+ .expect("failed to receive event on channel");
+ match event {
+ $exit_event => {
+ exit_event_count += 1;
+ log.lock().unwrap().push(event);
+ if exit_event_count == $exit_after_count {
+ break;
+ }
+ },
+ ScreenInstruction::RequestPluginPermissions(_, plugin_permission) => {
+ if plugin_permission.permissions.contains($permission_type) {
+ let _ = plugin_thread_sender.send(
+ PluginInstruction::PermissionRequestResult(
+ 0,
+ Some($client_id),
+ plugin_permission.permissions,
+ PermissionStatus::Granted,
+ Some(cache_path.clone()),
+ ),
+ );
+ } else {
+ let _ = plugin_thread_sender.send(
+ PluginInstruction::PermissionRequestResult(
+ 0,
+ Some($client_id),
+ plugin_permission.permissions,
+ PermissionStatus::Denied,
+ Some(cache_path.clone()),
+ ),
+ );
+ }
+ },
+ _ => {
+ log.lock().unwrap().push(event);
+ },
+ }
+ }
+ })
+ .unwrap()
+ };
+}
+
macro_rules! log_actions_in_thread_naked_variant {
( $arc_mutex_log:expr, $exit_event:path, $receiver:expr, $exit_after_count:expr ) => {
std::thread::Builder::new()
@@ -167,14 +319,14 @@ fn create_plugin_thread_with_server_receiver(
) -> (
SenderWithContext<PluginInstruction>,
Receiver<(ServerInstruction, ErrorContext)>,
+ Receiver<(ScreenInstruction, ErrorContext)>,
Box<dyn FnMut()>,
) {
let zellij_cwd = zellij_cwd.unwrap_or_else(|| PathBuf::from("."));
let (to_server, server_receiver): ChannelWithContext<ServerInstruction> = channels::bounded(50);
let to_server = SenderWithContext::new(to_server);
- let (to_screen, _screen_receiver): ChannelWithContext<ScreenInstruction> =
- channels::unbounded();
+ let (to_screen, screen_receiver): ChannelWithContext<ScreenInstruction> = channels::unbounded();
let to_screen = SenderWithContext::new(to_screen);
let (to_plugin, plugin_receiver): ChannelWithContext<PluginInstruction> = channels::unbounded();
@@ -240,7 +392,12 @@ fn create_plugin_thread_with_server_receiver(
// the plugin cache
}
};
- (to_plugin, server_receiver, Box::new(teardown))
+ (
+ to_plugin,
+ server_receiver,
+ screen_receiver,
+ Box::new(teardown),
+ )
}
fn create_plugin_thread_with_pty_receiver(
@@ -248,6 +405,7 @@ fn create_plugin_thread_with_pty_receiver(
) -> (
SenderWithContext<PluginInstruction>,
Receiver<(PtyInstruction, ErrorContext)>,
+ Receiver<(ScreenInstruction, ErrorContext)>,
Box<dyn FnMut()>,
) {
let zellij_cwd = zellij_cwd.unwrap_or_else(|| PathBuf::from("."));
@@ -255,8 +413,7 @@ fn create_plugin_thread_with_pty_receiver(
channels::bounded(50);
let to_server = SenderWithContext::new(to_server);
- let (to_screen, _screen_receiver): ChannelWithContext<ScreenInstruction> =
- channels::unbounded();
+ let (to_screen, screen_receiver): ChannelWithContext<ScreenInstruction> = channels::unbounded();
let to_screen = SenderWithContext::new(to_screen);
let (to_plugin, plugin_receiver): ChannelWithContext<PluginInstruction> = channels::unbounded();
@@ -322,7 +479,7 @@ fn create_plugin_thread_with_pty_receiver(
// the plugin cache
}
};
- (to_plugin, pty_receiver, Box::new(teardown))
+ (to_plugin, pty_receiver, screen_receiver, Box::new(teardown))
}
lazy_static! {
@@ -344,6 +501,9 @@ pub fn load_new_plugin_from_hd() {
// message (this is what the fixture plugin does)
// we then listen on our mock screen receiver to make sure we got a PluginBytes instruction
// that contains said render, and assert against it
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) = create_plugin_thread(None);
let plugin_should_float = Some(false);
let plugin_title = Some("test_plugin".to_owned());
@@ -359,11 +519,15 @@ pub fn load_new_plugin_from_hd() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::PluginBytes,
screen_receiver,
- 2
+ 2,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -403,8 +567,12 @@ pub fn load_new_plugin_from_hd() {
#[test]
#[ignore]
pub fn plugin_workers() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
let (plugin_thread_sender, screen_receiver, mut teardown) = create_plugin_thread(None);
let plugin_should_float = Some(false);
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let plugin_title = Some("test_plugin".to_owned());
let run_plugin = RunPlugin {
_allow_exec_host_cmd: false,
@@ -418,11 +586,15 @@ pub fn plugin_workers() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::PluginBytes,
screen_receiver,
- 3
+ 3,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -465,8 +637,12 @@ pub fn plugin_workers() {
#[test]
#[ignore]
pub fn plugin_workers_persist_state() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
let (plugin_thread_sender, screen_receiver, mut teardown) = create_plugin_thread(None);
let plugin_should_float = Some(false);
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let plugin_title = Some("test_plugin".to_owned());
let run_plugin = RunPlugin {
_allow_exec_host_cmd: false,
@@ -480,11 +656,15 @@ pub fn plugin_workers_persist_state() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::PluginBytes,
screen_receiver,
- 5
+ 5,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -603,6 +783,7 @@ pub fn switch_to_mode_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -619,11 +800,82 @@ pub fn switch_to_mode_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::ChangeMode,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ plugin_title,
+ run_plugin,
+ tab_index,
+ client_id,
+ size,
+ ));
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(Key::Char('a')), // this triggers a SwitchToMode(Tab) command in the fixture
+ // plugin
+ )]));
+ std::thread::sleep(std::time::Duration::from_millis(100));
+ screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ teardown();
+ let switch_to_mode_event = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::ChangeMode(..) = i {
+ Some(i.clone())
+ } else {
+ None
+ }
+ })
+ .clone();
+ assert_snapshot!(format!("{:#?}", switch_to_mode_event));
+}
+
+#[test]
+#[ignore]
+pub fn switch_to_mode_plugin_command_permission_denied() {
+ let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
+ // destructor removes the directory
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, screen_receiver, mut teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ };
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let screen_thread = deny_permissions_and_log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::ChangeMode,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -665,6 +917,7 @@ pub fn new_tabs_with_layout_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -681,11 +934,15 @@ pub fn new_tabs_with_layout_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::NewTab,
screen_receiver,
- 2
+ 2,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -741,6 +998,7 @@ pub fn new_tab_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -757,11 +1015,15 @@ pub fn new_tab_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::NewTab,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -803,6 +1065,7 @@ pub fn go_to_next_tab_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -819,11 +1082,15 @@ pub fn go_to_next_tab_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::SwitchTabNext,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -864,6 +1131,7 @@ pub fn go_to_previous_tab_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -880,11 +1148,15 @@ pub fn go_to_previous_tab_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::SwitchTabPrev,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -925,6 +1197,7 @@ pub fn resize_focused_pane_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -941,11 +1214,15 @@ pub fn resize_focused_pane_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::Resize,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -986,6 +1263,7 @@ pub fn resize_focused_pane_with_direction_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -1002,11 +1280,15 @@ pub fn resize_focused_pane_with_direction_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::Resize,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -1047,6 +1329,7 @@ pub fn focus_next_pane_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -1063,11 +1346,15 @@ pub fn focus_next_pane_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::FocusNextPane,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -1108,6 +1395,7 @@ pub fn focus_previous_pane_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -1124,11 +1412,15 @@ pub fn focus_previous_pane_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::FocusPreviousPane,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -1169,6 +1461,7 @@ pub fn move_focus_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -1185,11 +1478,15 @@ pub fn move_focus_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::MoveFocusLeft,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -1230,6 +1527,7 @@ pub fn move_focus_or_tab_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -1246,11 +1544,15 @@ pub fn move_focus_or_tab_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::MoveFocusLeftOrPreviousTab,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -1291,6 +1593,7 @@ pub fn edit_scrollback_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -1307,11 +1610,15 @@ pub fn edit_scrollback_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::EditScrollback,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -1352,6 +1659,7 @@ pub fn write_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -1368,11 +1676,15 @@ pub fn write_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::WriteCharacter,
screen_receiver,
- 1
+ 1,
+ &PermissionType::WriteToStdin,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -1413,6 +1725,7 @@ pub fn write_chars_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -1429,11 +1742,15 @@ pub fn write_chars_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::WriteCharacter,
screen_receiver,
- 1
+ 1,
+ &PermissionType::WriteToStdin,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -1474,6 +1791,7 @@ pub fn toggle_tab_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -1490,11 +1808,15 @@ pub fn toggle_tab_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::ToggleTab,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -1535,6 +1857,7 @@ pub fn move_pane_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -1551,11 +1874,15 @@ pub fn move_pane_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::MovePane,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -1596,6 +1923,7 @@ pub fn move_pane_with_direction_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -1612,11 +1940,15 @@ pub fn move_pane_with_direction_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::MovePaneLeft,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -1657,6 +1989,7 @@ pub fn clear_screen_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -1673,11 +2006,16 @@ pub fn clear_screen_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::ClearScreen,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -1689,6 +2027,7 @@ pub fn clear_screen_plugin_command() {
client_id,
size,
));
+ std::thread::sleep(std::time::Duration::from_millis(100));
let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
None,
Some(client_id),
@@ -1718,6 +2057,7 @@ pub fn scroll_up_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -1734,11 +2074,16 @@ pub fn scroll_up_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::ScrollUp,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -1779,6 +2124,7 @@ pub fn scroll_down_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -1795,11 +2141,15 @@ pub fn scroll_down_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::ScrollDown,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -1840,6 +2190,7 @@ pub fn scroll_to_top_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -1856,11 +2207,15 @@ pub fn scroll_to_top_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::ScrollToTop,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -1901,6 +2256,7 @@ pub fn scroll_to_bottom_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -1917,11 +2273,15 @@ pub fn scroll_to_bottom_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::ScrollToBottom,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -1962,6 +2322,7 @@ pub fn page_scroll_up_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -1978,11 +2339,15 @@ pub fn page_scroll_up_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::PageScrollUp,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -2023,6 +2388,7 @@ pub fn page_scroll_down_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -2039,11 +2405,15 @@ pub fn page_scroll_down_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::PageScrollDown,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -2084,6 +2454,7 @@ pub fn toggle_focus_fullscreen_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -2100,11 +2471,15 @@ pub fn toggle_focus_fullscreen_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::ToggleActiveTerminalFullscreen,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -2145,6 +2520,7 @@ pub fn toggle_pane_frames_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -2161,11 +2537,15 @@ pub fn toggle_pane_frames_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread_naked_variant!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!(
received_screen_instructions,
ScreenInstruction::TogglePaneFrames,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -2206,6 +2586,7 @@ pub fn toggle_pane_embed_or_eject_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -2222,11 +2603,15 @@ pub fn toggle_pane_embed_or_eject_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::TogglePaneEmbedOrFloating,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -2267,6 +2652,7 @@ pub fn undo_rename_pane_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -2283,11 +2669,15 @@ pub fn undo_rename_pane_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::UndoRenamePane,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -2328,6 +2718,7 @@ pub fn close_focus_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -2344,11 +2735,15 @@ pub fn close_focus_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::CloseFocusedPane,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -2389,6 +2784,7 @@ pub fn toggle_active_tab_sync_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -2405,11 +2801,15 @@ pub fn toggle_active_tab_sync_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::ToggleActiveSyncTab,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -2450,6 +2850,7 @@ pub fn close_focused_tab_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -2466,11 +2867,15 @@ pub fn close_focused_tab_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::CloseTab,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -2511,6 +2916,7 @@ pub fn undo_rename_tab_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -2527,11 +2933,15 @@ pub fn undo_rename_tab_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::UndoRenameTab,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -2572,6 +2982,7 @@ pub fn previous_swap_layout_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -2588,11 +2999,15 @@ pub fn previous_swap_layout_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::PreviousSwapLayout,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -2633,6 +3048,7 @@ pub fn next_swap_layout_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -2649,11 +3065,15 @@ pub fn next_swap_layout_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::NextSwapLayout,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -2694,6 +3114,7 @@ pub fn go_to_tab_name_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -2710,11 +3131,15 @@ pub fn go_to_tab_name_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::GoToTabName,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -2755,6 +3180,7 @@ pub fn focus_or_create_tab_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -2771,11 +3197,15 @@ pub fn focus_or_create_tab_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::GoToTabName,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -2816,6 +3246,7 @@ pub fn go_to_tab() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -2832,11 +3263,15 @@ pub fn go_to_tab() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::GoToTab,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -2877,6 +3312,7 @@ pub fn start_or_reload_plugin() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -2893,11 +3329,15 @@ pub fn start_or_reload_plugin() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::StartOrReloadPluginPane,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -2938,7 +3378,8 @@ pub fn quit_zellij_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
- let (plugin_thread_sender, server_receiver, mut teardown) =
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, server_receiver, screen_receiver, mut teardown) =
create_plugin_thread_with_server_receiver(Some(plugin_host_folder));
let plugin_should_float = Some(false);
let plugin_title = Some("test_plugin".to_owned());
@@ -2954,12 +3395,23 @@ pub fn quit_zellij_plugin_command() {
rows: 20,
};
let received_server_instruction = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let server_thread = log_actions_in_thread!(
received_server_instruction,
ServerInstruction::ClientExit,
server_receiver,
1
);
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!(
+ received_screen_instructions,
+ ScreenInstruction::Exit,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
let _ = plugin_thread_sender.send(PluginInstruction::Load(
@@ -2976,7 +3428,7 @@ pub fn quit_zellij_plugin_command() {
Event::Key(Key::Char('8')), // this triggers the enent in the fixture plugin
)]));
std::thread::sleep(std::time::Duration::from_millis(100));
- screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ server_thread.join().unwrap(); // this might take a while if the cache is cold
teardown();
let new_tab_event = received_server_instruction
.lock()
@@ -2999,7 +3451,8 @@ pub fn detach_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
- let (plugin_thread_sender, server_receiver, mut teardown) =
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, server_receiver, screen_receiver, mut teardown) =
create_plugin_thread_with_server_receiver(Some(plugin_host_folder));
let plugin_should_float = Some(false);
let plugin_title = Some("test_plugin".to_owned());
@@ -3015,12 +3468,23 @@ pub fn detach_plugin_command() {
rows: 20,
};
let received_server_instruction = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let server_thread = log_actions_in_thread!(
received_server_instruction,
ServerInstruction::DetachSession,
server_receiver,
1
);
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!(
+ received_screen_instructions,
+ ScreenInstruction::Exit,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
let _ = plugin_thread_sender.send(PluginInstruction::Load(
@@ -3037,7 +3501,7 @@ pub fn detach_plugin_command() {
Event::Key(Key::Char('l')), // this triggers the enent in the fixture plugin
)]));
std::thread::sleep(std::time::Duration::from_millis(100));
- screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ server_thread.join().unwrap(); // this might take a while if the cache is cold
teardown();
let new_tab_event = received_server_instruction
.lock()
@@ -3060,7 +3524,8 @@ pub fn open_file_floating_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
- let (plugin_thread_sender, pty_receiver, mut teardown) =
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, pty_receiver, screen_receiver, mut teardown) =
create_plugin_thread_with_pty_receiver(Some(plugin_host_folder));
let plugin_should_float = Some(false);
let plugin_title = Some("test_plugin".to_owned());
@@ -3075,13 +3540,24 @@ pub fn open_file_floating_plugin_command() {
cols: 121,
rows: 20,
};
- let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
- received_screen_instructions,
+ let received_pty_instructions = Arc::new(Mutex::new(vec![]));
+ let pty_thread = log_actions_in_thread!(
+ received_pty_instructions,
PtyInstruction::SpawnTerminal,
pty_receiver,
1
);
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!(
+ received_screen_instructions,
+ ScreenInstruction::Exit,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
let _ = plugin_thread_sender.send(PluginInstruction::Load(
@@ -3098,9 +3574,9 @@ pub fn open_file_floating_plugin_command() {
Event::Key(Key::Ctrl('h')), // this triggers the enent in the fixture plugin
)]));
std::thread::sleep(std::time::Duration::from_millis(100));
- screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ pty_thread.join().unwrap(); // this might take a while if the cache is cold
teardown();
- let new_tab_event = received_screen_instructions
+ let new_tab_event = received_pty_instructions
.lock()
.unwrap()
.iter()
@@ -3121,7 +3597,8 @@ pub fn open_file_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
- let (plugin_thread_sender, pty_receiver, mut teardown) =
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, pty_receiver, screen_receiver, mut teardown) =
create_plugin_thread_with_pty_receiver(Some(plugin_host_folder));
let plugin_should_float = Some(false);
let plugin_title = Some("test_plugin".to_owned());
@@ -3136,13 +3613,24 @@ pub fn open_file_plugin_command() {
cols: 121,
rows: 20,
};
- let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
- received_screen_instructions,
+ let received_pty_instructions = Arc::new(Mutex::new(vec![]));
+ let pty_thread = log_actions_in_thread!(
+ received_pty_instructions,
PtyInstruction::SpawnTerminal,
pty_receiver,
1
);
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!(
+ received_screen_instructions,
+ ScreenInstruction::Exit,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
let _ = plugin_thread_sender.send(PluginInstruction::Load(
@@ -3159,9 +3647,9 @@ pub fn open_file_plugin_command() {
Event::Key(Key::Ctrl('g')), // this triggers the enent in the fixture plugin
)]));
std::thread::sleep(std::time::Duration::from_millis(100));
- screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ pty_thread.join().unwrap(); // this might take a while if the cache is cold
teardown();
- let new_tab_event = received_screen_instructions
+ let new_tab_event = received_pty_instructions
.lock()
.unwrap()
.iter()
@@ -3182,7 +3670,8 @@ pub fn open_file_with_line_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
- let (plugin_thread_sender, pty_receiver, mut teardown) =
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, pty_receiver, screen_receiver, mut teardown) =
create_plugin_thread_with_pty_receiver(Some(plugin_host_folder));
let plugin_should_float = Some(false);
let plugin_title = Some("test_plugin".to_owned());
@@ -3197,14 +3686,26 @@ pub fn open_file_with_line_plugin_command() {
cols: 121,
rows: 20,
};
- let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
- received_screen_instructions,
+ let received_pty_instructions = Arc::new(Mutex::new(vec![]));
+ let pty_thread = log_actions_in_thread!(
+ received_pty_instructions,
PtyInstruction::SpawnTerminal,
pty_receiver,
1
);
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!(
+ received_screen_instructions,
+ ScreenInstruction::Exit,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
+
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
let _ = plugin_thread_sender.send(PluginInstruction::Load(
plugin_should_float,
@@ -3220,9 +3721,9 @@ pub fn open_file_with_line_plugin_command() {
Event::Key(Key::Ctrl('i')), // this triggers the enent in the fixture plugin
)]));
std::thread::sleep(std::time::Duration::from_millis(100));
- screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ pty_thread.join().unwrap(); // this might take a while if the cache is cold
teardown();
- let new_tab_event = received_screen_instructions
+ let new_tab_event = received_pty_instructions
.lock()
.unwrap()
.iter()
@@ -3243,7 +3744,8 @@ pub fn open_file_with_line_floating_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
- let (plugin_thread_sender, pty_receiver, mut teardown) =
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, pty_receiver, screen_receiver, mut teardown) =
create_plugin_thread_with_pty_receiver(Some(plugin_host_folder));
let plugin_should_float = Some(false);
let plugin_title = Some("test_plugin".to_owned());
@@ -3258,13 +3760,24 @@ pub fn open_file_with_line_floating_plugin_command() {
cols: 121,
rows: 20,
};
- let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
- received_screen_instructions,
+ let received_pty_instructions = Arc::new(Mutex::new(vec![]));
+ let pty_thread = log_actions_in_thread!(
+ received_pty_instructions,
PtyInstruction::SpawnTerminal,
pty_receiver,
1
);
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!(
+ received_screen_instructions,
+ ScreenInstruction::Exit,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
let _ = plugin_thread_sender.send(PluginInstruction::Load(
@@ -3281,9 +3794,9 @@ pub fn open_file_with_line_floating_plugin_command() {
Event::Key(Key::Ctrl('j')), // this triggers the enent in the fixture plugin
)]));
std::thread::sleep(std::time::Duration::from_millis(100));
- screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ pty_thread.join().unwrap(); // this might take a while if the cache is cold
teardown();
- let new_tab_event = received_screen_instructions
+ let new_tab_event = received_pty_instructions
.lock()
.unwrap()
.iter()
@@ -3304,7 +3817,8 @@ pub fn open_terminal_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
- let (plugin_thread_sender, pty_receiver, mut teardown) =
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, pty_receiver, screen_receiver, mut teardown) =
create_plugin_thread_with_pty_receiver(Some(plugin_host_folder));
let plugin_should_float = Some(false);
let plugin_title = Some("test_plugin".to_owned());
@@ -3319,13 +3833,24 @@ pub fn open_terminal_plugin_command() {
cols: 121,
rows: 20,
};
- let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
- received_screen_instructions,
+ let received_pty_instructions = Arc::new(Mutex::new(vec![]));
+ let pty_thread = log_actions_in_thread!(
+ received_pty_instructions,
PtyInstruction::SpawnTerminal,
pty_receiver,
1
);
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!(
+ received_screen_instructions,
+ ScreenInstruction::Exit,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
let _ = plugin_thread_sender.send(PluginInstruction::Load(
@@ -3342,9 +3867,9 @@ pub fn open_terminal_plugin_command() {
Event::Key(Key::Ctrl('k')), // this triggers the enent in the fixture plugin
)]));
std::thread::sleep(std::time::Duration::from_millis(100));
- screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ pty_thread.join().unwrap(); // this might take a while if the cache is cold
teardown();
- let new_tab_event = received_screen_instructions
+ let new_tab_event = received_pty_instructions
.lock()
.unwrap()
.iter()
@@ -3365,7 +3890,8 @@ pub fn open_terminal_floating_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
- let (plugin_thread_sender, pty_receiver, mut teardown) =
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, pty_receiver, screen_receiver, mut teardown) =
create_plugin_thread_with_pty_receiver(Some(plugin_host_folder));
let plugin_should_float = Some(false);
let plugin_title = Some("test_plugin".to_owned());
@@ -3380,13 +3906,24 @@ pub fn open_terminal_floating_plugin_command() {
cols: 121,
rows: 20,
};
- let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
- received_screen_instructions,
+ let received_pty_instructions = Arc::new(Mutex::new(vec![]));
+ let pty_thread = log_actions_in_thread!(
+ received_pty_instructions,
PtyInstruction::SpawnTerminal,
pty_receiver,
1
);
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!(
+ received_screen_instructions,
+ ScreenInstruction::Exit,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
let _ = plugin_thread_sender.send(PluginInstruction::Load(
@@ -3403,9 +3940,9 @@ pub fn open_terminal_floating_plugin_command() {
Event::Key(Key::Ctrl('l')), // this triggers the enent in the fixture plugin
)]));
std::thread::sleep(std::time::Duration::from_millis(100));
- screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ pty_thread.join().unwrap(); // this might take a while if the cache is cold
teardown();
- let new_tab_event = received_screen_instructions
+ let new_tab_event = received_pty_instructions
.lock()
.unwrap()
.iter()
@@ -3426,7 +3963,8 @@ pub fn open_command_pane_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
- let (plugin_thread_sender, pty_receiver, mut teardown) =
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, pty_receiver, screen_receiver, mut teardown) =
create_plugin_thread_with_pty_receiver(Some(plugin_host_folder));
let plugin_should_float = Some(false);
let plugin_title = Some("test_plugin".to_owned());
@@ -3441,13 +3979,24 @@ pub fn open_command_pane_plugin_command() {
cols: 121,
rows: 20,
};
- let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
- received_screen_instructions,
+ let received_pty_instructions = Arc::new(Mutex::new(vec![]));
+ let pty_thread = log_actions_in_thread!(
+ received_pty_instructions,
PtyInstruction::SpawnTerminal,
pty_receiver,
1
);
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!(
+ received_screen_instructions,
+ ScreenInstruction::Exit,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
let _ = plugin_thread_sender.send(PluginInstruction::Load(
@@ -3464,9 +4013,9 @@ pub fn open_command_pane_plugin_command() {
Event::Key(Key::Ctrl('m')), // this triggers the enent in the fixture plugin
)]));
std::thread::sleep(std::time::Duration::from_millis(100));
- screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ pty_thread.join().unwrap(); // this might take a while if the cache is cold
teardown();
- let new_tab_event = received_screen_instructions
+ let new_tab_event = received_pty_instructions
.lock()
.unwrap()
.iter()
@@ -3487,7 +4036,8 @@ pub fn open_command_pane_floating_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
- let (plugin_thread_sender, pty_receiver, mut teardown) =
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+ let (plugin_thread_sender, pty_receiver, screen_receiver, mut teardown) =
create_plugin_thread_with_pty_receiver(Some(plugin_host_folder));
let plugin_should_float = Some(false);
let plugin_title = Some("test_plugin".to_owned());
@@ -3502,13 +4052,24 @@ pub fn open_command_pane_floating_plugin_command() {
cols: 121,
rows: 20,
};
- let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
- received_screen_instructions,
+ let received_pty_instructions = Arc::new(Mutex::new(vec![]));
+ let pty_thread = log_actions_in_thread!(
+ received_pty_instructions,
PtyInstruction::SpawnTerminal,
pty_receiver,
1
);
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!(
+ received_screen_instructions,
+ ScreenInstruction::Exit,
+ screen_receiver,
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
+ );
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
let _ = plugin_thread_sender.send(PluginInstruction::Load(
@@ -3525,9 +4086,9 @@ pub fn open_command_pane_floating_plugin_command() {
Event::Key(Key::Ctrl('n')), // this triggers the enent in the fixture plugin
)]));
std::thread::sleep(std::time::Duration::from_millis(100));
- screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ pty_thread.join().unwrap(); // this might take a while if the cache is cold
teardown();
- let new_tab_event = received_screen_instructions
+ let new_tab_event = received_pty_instructions
.lock()
.unwrap()
.iter()
@@ -3548,6 +4109,7 @@ pub fn switch_to_tab_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -3564,11 +4126,15 @@ pub fn switch_to_tab_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::GoToTab,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -3731,6 +4297,7 @@ pub fn close_terminal_pane_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -3747,11 +4314,15 @@ pub fn close_terminal_pane_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::ClosePane,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -3792,6 +4363,7 @@ pub fn close_plugin_pane_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -3808,11 +4380,15 @@ pub fn close_plugin_pane_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::ClosePane,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -3853,6 +4429,7 @@ pub fn focus_terminal_pane_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -3869,11 +4446,15 @@ pub fn focus_terminal_pane_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::FocusPaneWithId,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -3914,6 +4495,7 @@ pub fn focus_plugin_pane_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -3930,11 +4512,15 @@ pub fn focus_plugin_pane_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::FocusPaneWithId,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -3975,6 +4561,7 @@ pub fn rename_terminal_pane_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -3991,11 +4578,15 @@ pub fn rename_terminal_pane_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::RenamePane,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -4036,6 +4627,7 @@ pub fn rename_plugin_pane_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -4052,11 +4644,15 @@ pub fn rename_plugin_pane_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::RenamePane,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -4097,6 +4693,7 @@ pub fn rename_tab_plugin_command() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -4113,11 +4710,15 @@ pub fn rename_tab_plugin_command() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::RenameTab,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -4158,6 +4759,7 @@ pub fn send_configuration_to_plugins() {
let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its
// destructor removes the directory
let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
let (plugin_thread_sender, screen_receiver, mut teardown) =
create_plugin_thread(Some(plugin_host_folder));
let plugin_should_float = Some(false);
@@ -4183,11 +4785,15 @@ pub fn send_configuration_to_plugins() {
rows: 20,
};
let received_screen_instructions = Arc::new(Mutex::new(vec![]));
- let screen_thread = log_actions_in_thread!(
+ let screen_thread = grant_permissions_and_log_actions_in_thread!(
received_screen_instructions,
ScreenInstruction::GoToTabName,
screen_receiver,
- 1
+ 1,
+ &PermissionType::ChangeApplicationState,
+ cache_path,
+ plugin_thread_sender,
+ client_id
);
let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
@@ -4224,3 +4830,226 @@ pub fn send_configuration_to_plugins() {
.clone();
assert_snapshot!(format!("{:#?}", go_to_tab_event));
}
+
+#[test]
+#[ignore]
+pub fn request_plugin_permissions() {
+ let temp_folder = tempdir().unwrap();
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let (plugin_thread_sender, screen_receiver, mut teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ };
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+ let received_screen_instructions = Arc::new(Mutex::new(vec![]));
+ let screen_thread = log_actions_in_thread!(
+ received_screen_instructions,
+ ScreenInstruction::RequestPluginPermissions,
+ screen_receiver,
+ 1
+ );
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ plugin_title,
+ run_plugin,
+ tab_index,
+ client_id,
+ size,
+ ));
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(Key::Ctrl('1')), // this triggers the enent in the fixture plugin
+ )]));
+ std::thread::sleep(std::time::Duration::from_millis(100));
+ screen_thread.join().unwrap(); // this might take a while if the cache is cold
+ teardown();
+ let new_tab_event = received_screen_instructions
+ .lock()
+ .unwrap()
+ .iter()
+ .find_map(|i| {
+ if let ScreenInstruction::RequestPluginPermissions(_, plugin_permission) = i {
+ Some(plugin_permission.permissions.clone())
+ } else {
+ None
+ }
+ })
+ .clone();
+ assert_snapshot!(format!("{:#?}", new_tab_event));
+}
+
+#[test]
+#[ignore]
+pub fn granted_permission_request_result() {
+ let temp_folder = tempdir().unwrap();
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+
+ let (plugin_thread_sender, screen_receiver, mut teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ };
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+
+ // here we create a fake screen thread that will send a PermissionStatus::Granted
+ // message for every permission request it gets
+ let screen_thread = std::thread::Builder::new()
+ .name("fake_screen_thread".to_string())
+ .spawn({
+ let cache_path = cache_path.clone();
+ let plugin_thread_sender = plugin_thread_sender.clone();
+ move || loop {
+ let (event, _err_ctx) = screen_receiver
+ .recv()
+ .expect("failed to receive event on channel");
+ match event {
+ ScreenInstruction::RequestPluginPermissions(_, plugin_permission) => {
+ let _ =
+ plugin_thread_sender.send(PluginInstruction::PermissionRequestResult(
+ 0,
+ Some(client_id),
+ plugin_permission.permissions,
+ PermissionStatus::Granted,
+ Some(cache_path.clone()),
+ ));
+ break;
+ },
+ ScreenInstruction::Exit => {
+ break;
+ },
+ _ => {},
+ }
+ }
+ })
+ .unwrap();
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ plugin_title,
+ run_plugin.clone(),
+ tab_index,
+ client_id,
+ size,
+ ));
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(Key::Ctrl('1')), // this triggers the enent in the fixture plugin
+ )]));
+ screen_thread.join().unwrap();
+ teardown();
+
+ let permission_cache = PermissionCache::from_path_or_default(Some(cache_path));
+ let mut permissions = permission_cache
+ .get_permissions(run_plugin.location.to_string())
+ .clone();
+ let permissions = permissions.as_mut().map(|p| {
+ let mut permissions = p.clone();
+ permissions.sort_unstable();
+ permissions
+ });
+
+ assert_snapshot!(format!("{:#?}", permissions));
+}
+
+#[test]
+#[ignore]
+pub fn denied_permission_request_result() {
+ let temp_folder = tempdir().unwrap();
+ let plugin_host_folder = PathBuf::from(temp_folder.path());
+ let cache_path = plugin_host_folder.join("permissions_test.kdl");
+
+ let (plugin_thread_sender, screen_receiver, mut teardown) =
+ create_plugin_thread(Some(plugin_host_folder));
+ let plugin_should_float = Some(false);
+ let plugin_title = Some("test_plugin".to_owned());
+ let run_plugin = RunPlugin {
+ _allow_exec_host_cmd: false,
+ location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)),
+ configuration: Default::default(),
+ };
+ let tab_index = 1;
+ let client_id = 1;
+ let size = Size {
+ cols: 121,
+ rows: 20,
+ };
+
+ // here we create a fake screen thread that will send a PermissionStatus::Granted
+ // message for every permission request it gets
+ let screen_thread = std::thread::Builder::new()
+ .name("fake_screen_thread".to_string())
+ .spawn({
+ let cache_path = cache_path.clone();
+ let plugin_thread_sender = plugin_thread_sender.clone();
+ move || loop {
+ let (event, _err_ctx) = screen_receiver
+ .recv()
+ .expect("failed to receive event on channel");
+ match event {
+ ScreenInstruction::RequestPluginPermissions(_, plugin_permission) => {
+ let _ =
+ plugin_thread_sender.send(PluginInstruction::PermissionRequestResult(
+ 0,
+ Some(client_id),
+ plugin_permission.permissions,
+ PermissionStatus::Denied,
+ Some(cache_path.clone()),
+ ));
+ break;
+ },
+ ScreenInstruction::Exit => {
+ break;
+ },
+ _ => {},
+ }
+ }
+ })
+ .unwrap();
+
+ let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id));
+ let _ = plugin_thread_sender.send(PluginInstruction::Load(
+ plugin_should_float,
+ plugin_title,
+ run_plugin.clone(),
+ tab_index,
+ client_id,
+ size,
+ ));
+ let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(
+ None,
+ Some(client_id),
+ Event::Key(Key::Ctrl('1')), // this triggers the enent in the fixture plugin
+ )]));
+ screen_thread.join().unwrap();
+ teardown();
+
+ let permission_cache = PermissionCache::from_path_or_default(Some(cache_path));
+ let permissions = permission_cache.get_permissions(run_plugin.location.to_string());
+
+ assert_snapshot!(format!("{:#?}", permissions));
+}
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__denied_permission_request_result.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__denied_permission_request_result.snap
new file mode 100644
index 00000000..d7dfeb16
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__denied_permission_request_result.snap
@@ -0,0 +1,7 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+expression: "format!(\"{:#?}\", permissions)"
+---
+Some(
+ [],
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__granted_permission_request_result.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__granted_permission_request_result.snap
new file mode 100644
index 00000000..beaa4adc
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__granted_permission_request_result.snap
@@ -0,0 +1,15 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+assertion_line: 4864
+expression: "format!(\"{:#?}\", permissions)"
+---
+Some(
+ [
+ ReadApplicationState,
+ ChangeApplicationState,
+ OpenFiles,
+ RunCommands,
+ OpenTerminalsOrPlugins,
+ WriteToStdin,
+ ],
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__request_plugin_permissions.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__request_plugin_permissions.snap
new file mode 100644
index 00000000..23fa6c22
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__request_plugin_permissions.snap
@@ -0,0 +1,17 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+assertion_line: 4767
+expression: "format!(\"{:#?}\", new_tab_event)"
+---
+Some(
+ [
+ ChangeApplicationState,
+ ReadApplicationState,
+ ReadApplicationState,
+ ChangeApplicationState,
+ OpenFiles,
+ RunCommands,
+ OpenTerminalsOrPlugins,
+ WriteToStdin,
+ ],
+)
diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__switch_to_mode_plugin_command_permission_denied.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__switch_to_mode_plugin_command_permission_denied.snap
new file mode 100644
index 00000000..57afe877
--- /dev/null
+++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__switch_to_mode_plugin_command_permission_denied.snap
@@ -0,0 +1,6 @@
+---
+source: zellij-server/src/plugins/./unit/plugin_tests.rs
+assertion_line: 903
+expression: "format!(\"{:#?}\", switch_to_mode_event)"
+---
+None
diff --git a/zellij-server/src/plugins/wasm_bridge.rs b/zellij-server/src/plugins/wasm_bridge.rs
index d6fcf1fc..5c3c7c0d 100644
--- a/zellij-server/src/plugins/wasm_bridge.rs
+++ b/zellij-server/src/plugins/wasm_bridge.rs
@@ -13,6 +13,8 @@ use std::{
};
use wasmer::{Instance, Module, Store, Value};
use zellij_utils::async_std::task::{self, JoinHandle};
+use zellij_utils::data::{PermissionStatus, PermissionType};
+use zellij_utils::input::permission::PermissionCache;
use zellij_utils::notify_debouncer_full::{notify::RecommendedWatcher, Debouncer, FileIdMap};
use zellij_utils::plugin_api::event::ProtobufEvent;
@@ -706,6 +708,44 @@ impl WasmBridge {
};
}
}
+ pub fn cache_plugin_permissions(
+ &mut self,
+ plugin_id: PluginId,
+ client_id: Option<ClientId>,
+ permissions: Vec<PermissionType>,
+ status: PermissionStatus,
+ cache_path: Option<PathBuf>,
+ ) -> Result<()> {
+ if let Some(running_plugin) = self
+ .plugin_map
+ .lock()
+ .unwrap()
+ .get_running_plugin(plugin_id, client_id)
+ {
+ let err_context = || format!("Failed to write plugin permission {plugin_id}");
+
+ let mut running_plugin = running_plugin.lock().unwrap();
+ let permissions = if status == PermissionStatus::Granted {
+ permissions
+ } else {
+ vec![]
+ };
+
+ running_plugin
+ .plugin_env
+ .set_permissions(HashSet::from_iter(permissions.clone()));
+
+ let mut permission_cache = PermissionCache::from_path_or_default(cache_path);
+ permission_cache.cache(
+ running_plugin.plugin_env.plugin.location.to_string(),
+ permissions,
+ );
+
+ permission_cache.write_to_file().with_context(err_context)?;
+ }
+
+ Ok(())
+ }
}
fn handle_plugin_successful_loading(senders: &ThreadSenders, plugin_id: PluginId) {
@@ -728,6 +768,35 @@ fn handle_plugin_loading_failure(
));
}
+// TODO: move to permissions?
+fn check_event_permission(
+ plugin_env: &PluginEnv,
+ event: &Event,
+) -> (PermissionStatus, Option<PermissionType>) {
+ if plugin_env.plugin.is_builtin() {
+ // built-in plugins can do all the things because they're part of the application and
+ // there's no use to deny them anything
+ return (PermissionStatus::Granted, None);
+ }
+ let permission = match event {
+ Event::ModeUpdate(..)
+ | Event::TabUpdate(..)
+ | Event::PaneUpdate(..)
+ | Event::CopyToClipboard(..)
+ | Event::SystemClipboardFailure
+ | Event::InputReceived => PermissionType::ReadApplicationState,
+ _ => return (PermissionStatus::Granted, None),
+ };
+
+ if let Some(permissions) = plugin_env.permissions.lock().unwrap().as_ref() {
+ if permissions.contains(&permission) {
+ return (PermissionStatus::Granted, None);
+ }
+ }
+
+ (PermissionStatus::Denied, Some(permission))
+}
+
pub fn apply_event_to_plugin(
plugin_id: PluginId,
client_id: ClientId,
@@ -739,35 +808,48 @@ pub fn apply_event_to_plugin(
plugin_bytes: &mut Vec<(PluginId, ClientId, Vec<u8>)>,
) -> Result<()> {
let err_context = || format!("Failed to apply event to plugin {plugin_id}");
- let protobuf_event: ProtobufEvent = event
- .clone()
- .try_into()
- .map_err(|e| anyhow!("Failed to convert to protobuf: {:?}", e))?;
- let update = instance
- .exports
- .get_function("update")
- .with_context(err_context)?;
- wasi_write_object(&plugin_env.wasi_env, &protobuf_event.encode_to_vec())
- .with_context(err_context)?;
- let update_return = update.call(&[]).with_context(err_context)?;
- let should_render = match update_return.get(0) {
- Some(Value::I32(n)) => *n == 1,
- _ => false,
- };
-
- if rows > 0 && columns > 0 && should_render {
- let rendered_bytes = instance
- .exports
- .get_function("render")
- .map_err(anyError::new)
- .and_then(|render| {
- render
- .call(&[Value::I32(rows as i32), Value::I32(columns as i32)])
+ match check_event_permission(plugin_env, event) {
+ (PermissionStatus::Granted, _) => {
+ let protobuf_event: ProtobufEvent = event
+ .clone()
+ .try_into()
+ .map_err(|e| anyhow!("Failed to convert to protobuf: {:?}", e))?;
+ let update = instance
+ .exports
+ .get_function("update")
+ .with_context(err_context)?;
+ wasi_write_object(&plugin_env.wasi_env, &protobuf_event.encode_to_vec())
+ .with_context(err_context)?;
+ let update_return = update.call(&[]).with_context(err_context)?;
+ let should_render = match update_return.get(0) {
+ Some(Value::I32(n)) => *n == 1,
+ _ => false,
+ };
+ if rows > 0 && columns > 0 && should_render {
+ let rendered_bytes = instance
+ .exports
+ .get_function("render")
.map_err(anyError::new)
- })
- .and_then(|_| wasi_read_string(&plugin_env.wasi_env))
- .with_context(err_context)?;
- plugin_bytes.push((plugin_id, client_id, rendered_bytes.as_bytes().to_vec()));
+ .and_then(|render| {
+ render
+ .call(&[Value::I32(rows as i32), Value::I32(columns as i32)])
+ .map_err(anyError::new)
+ })
+ .and_then(|_| wasi_read_string(&plugin_env.wasi_env))
+ .with_context(err_context)?;
+ plugin_bytes.push((plugin_id, client_id, rendered_bytes.as_bytes().to_vec()));
+ }
+ },
+ (PermissionStatus::Denied, permission) => {
+ log::error!(
+ "PluginId '{}' permission '{}' is not allowed - Event '{:?}' denied",
+ plugin_id,
+ permission
+ .map(|p| p.to_string())
+ .unwrap_or("UNKNOWN".to_owned()),
+ EventType::from_str(&event.to_string()).with_context(err_context)?
+ );
+ },
}
Ok(())
}
diff --git a/zellij-server/src/plugins/zellij_exports.rs b/zellij-server/src/plugins/zellij_exports.rs
index 275de13e..f2effa19 100644
--- a/zellij-server/src/plugins/zellij_exports.rs
+++ b/zellij-server/src/plugins/zellij_exports.rs
@@ -8,12 +8,15 @@ use std::{
collections::{BTreeMap, HashSet},
path::PathBuf,
process,
+ str::FromStr,
sync::{Arc, Mutex},
thread,
time::{Duration, Instant},
};
use wasmer::{imports, Function, ImportObject, Store, WasmerEnv};
use wasmer_wasi::WasiEnv;
+use zellij_utils::data::{CommandType, PermissionStatus, PermissionType, PluginPermission};
+use zellij_utils::input::permission::PermissionCache;
use url::Url;
@@ -86,118 +89,134 @@ impl ForeignFunctionEnv {
}
fn host_run_plugin_command(env: &ForeignFunctionEnv) {
+ let err_context = || format!("failed to run plugin command {}", env.plugin_env.name());
wasi_read_bytes(&env.plugin_env.wasi_env)
.and_then(|bytes| {
let command: ProtobufPluginCommand = ProtobufPluginCommand::decode(bytes.as_slice())?;
let command: PluginCommand = command
.try_into()
.map_err(|e| anyhow!("failed to convert serialized command: {}", e))?;
- match command {
- PluginCommand::Subscribe(event_list) => subscribe(env, event_list)?,
- PluginCommand::Unsubscribe(event_list) => unsubscribe(env, event_list)?,
- PluginCommand::SetSelectable(selectable) => set_selectable(env, selectable),
- PluginCommand::GetPluginIds => get_plugin_ids(env),
- PluginCommand::GetZellijVersion => get_zellij_version(env),
- PluginCommand::OpenFile(file_to_open) => open_file(env, file_to_open),
- PluginCommand::OpenFileFloating(file_to_open) => {
- open_file_floating(env, file_to_open)
+ match check_command_permission(&env.plugin_env, &command) {
+ (PermissionStatus::Granted, _) => match command {
+ PluginCommand::Subscribe(event_list) => subscribe(env, event_list)?,
+ PluginCommand::Unsubscribe(event_list) => unsubscribe(env, event_list)?,
+ PluginCommand::SetSelectable(selectable) => set_selectable(env, selectable),
+ PluginCommand::GetPluginIds => get_plugin_ids(env),
+ PluginCommand::GetZellijVersion => get_zellij_version(env),
+ PluginCommand::OpenFile(file_to_open) => open_file(env, file_to_open),
+ PluginCommand::OpenFileFloating(file_to_open) => {
+ open_file_floating(env, file_to_open)
+ },
+ PluginCommand::OpenTerminal(cwd) => open_terminal(env, cwd.path.try_into()?),
+ PluginCommand::OpenTerminalFloating(cwd) => {
+ open_terminal_floating(env, cwd.path.try_into()?)
+ },
+ PluginCommand::OpenCommandPane(command_to_run) => {
+ open_command_pane(env, command_to_run)
+ },
+ PluginCommand::OpenCommandPaneFloating(command_to_run) => {
+ open_command_pane_floating(env, command_to_run)
+ },
+ PluginCommand::SwitchTabTo(tab_index) => switch_tab_to(env, tab_index),
+ PluginCommand::SetTimeout(seconds) => set_timeout(env, seconds),
+ PluginCommand::ExecCmd(command_line) => exec_cmd(env, command_line),
+ PluginCommand::PostMessageTo(plugin_message) => {
+ post_message_to(env, plugin_message)?
+ },
+ PluginCommand::PostMessageToPlugin(plugin_message) => {
+ post_message_to_plugin(env, plugin_message)?
+ },
+ PluginCommand::HideSelf => hide_self(env)?,
+ PluginCommand::ShowSelf(should_float_if_hidden) => {
+ show_self(env, should_float_if_hidden)
+ },
+ PluginCommand::SwitchToMode(input_mode) => {
+ switch_to_mode(env, input_mode.try_into()?)
+ },
+ PluginCommand::NewTabsWithLayout(raw_layout) => {
+ new_tabs_with_layout(env, &raw_layout)?
+ },
+ PluginCommand::NewTab => new_tab(env),
+ PluginCommand::GoToNextTab => go_to_next_tab(env),
+ PluginCommand::GoToPreviousTab => go_to_previous_tab(env),
+ PluginCommand::Resize(resize_payload) => resize(env, resize_payload),
+ PluginCommand::ResizeWithDirection(resize_strategy) => {
+ resize_with_direction(env, resize_strategy)
+ },
+ PluginCommand::FocusNextPane => focus_next_pane(env),
+ PluginCommand::FocusPreviousPane => focus_previous_pane(env),
+ PluginCommand::MoveFocus(direction) => move_focus(env, direction),
+ PluginCommand::MoveFocusOrTab(direction) => move_focus_or_tab(env, direction),
+ PluginCommand::Detach => detach(env),
+ PluginCommand::EditScrollback => edit_scrollback(env),
+ PluginCommand::Write(bytes) => write(env, bytes),
+ PluginCommand::WriteChars(chars) => write_chars(env, chars),
+ PluginCommand::ToggleTab => toggle_tab(env),
+ PluginCommand::MovePane => move_pane(env),
+ PluginCommand::MovePaneWithDirection(direction) => {
+ move_pane_with_direction(env, direction)
+ },
+ PluginCommand::ClearScreen => clear_screen(env),
+ PluginCommand::ScrollUp => scroll_up(env),
+ PluginCommand::ScrollDown => scroll_down(env),
+ PluginCommand::ScrollToTop => scroll_to_top(env),
+ PluginCommand::ScrollToBottom => scroll_to_bottom(env),
+ PluginCommand::PageScrollUp => page_scroll_up(env),
+ PluginCommand::PageScrollDown => page_scroll_down(env),
+ PluginCommand::ToggleFocusFullscreen => toggle_focus_fullscreen(env),
+ PluginCommand::TogglePaneFrames => toggle_pane_frames(env),
+ PluginCommand::TogglePaneEmbedOrEject => toggle_pane_embed_or_eject(env),
+ PluginCommand::UndoRenamePane => undo_rename_pane(env),
+ PluginCommand::CloseFocus => close_focus(env),
+ PluginCommand::ToggleActiveTabSync => toggle_active_tab_sync(env),
+ PluginCommand::CloseFocusedTab => close_focused_tab(env),
+ PluginCommand::UndoRenameTab => undo_rename_tab(env),
+ PluginCommand::QuitZellij => quit_zellij(env),
+ PluginCommand::PreviousSwapLayout => previous_swap_layout(env),
+ PluginCommand::NextSwapLayout => next_swap_layout(env),
+ PluginCommand::GoToTabName(tab_name) => go_to_tab_name(env, tab_name),
+ PluginCommand::FocusOrCreateTab(tab_name) => focus_or_create_tab(env, tab_name),
+ PluginCommand::GoToTab(tab_index) => go_to_tab(env, tab_index),
+ PluginCommand::StartOrReloadPlugin(plugin_url) => {
+ start_or_reload_plugin(env, &plugin_url)?
+ },
+ PluginCommand::CloseTerminalPane(terminal_pane_id) => {
+ close_terminal_pane(env, terminal_pane_id)
+ },
+ PluginCommand::ClosePluginPane(plugin_pane_id) => {
+ close_plugin_pane(env, plugin_pane_id)
+ },
+ PluginCommand::FocusTerminalPane(terminal_pane_id, should_float_if_hidden) => {
+ focus_terminal_pane(env, terminal_pane_id, should_float_if_hidden)
+ },
+ PluginCommand::FocusPluginPane(plugin_pane_id, should_float_if_hidden) => {
+ focus_plugin_pane(env, plugin_pane_id, should_float_if_hidden)
+ },
+ PluginCommand::RenameTerminalPane(terminal_pane_id, new_name) => {
+ rename_terminal_pane(env, terminal_pane_id, &new_name)
+ },
+ PluginCommand::RenamePluginPane(plugin_pane_id, new_name) => {
+ rename_plugin_pane(env, plugin_pane_id, &new_name)
+ },
+ PluginCommand::RenameTab(tab_index, new_name) => {
+ rename_tab(env, tab_index, &new_name)
+ },
+ PluginCommand::ReportPanic(crash_payload) => report_panic(env, &crash_payload),
+ PluginCommand::RequestPluginPermissions(permissions) => {
+ request_permission(env, permissions)?
+ },
},
- PluginCommand::OpenTerminal(cwd) => open_terminal(env, cwd.path.try_into()?),
- PluginCommand::OpenTerminalFloating(cwd) => {
- open_terminal_floating(env, cwd.path.try_into()?)
+ (PermissionStatus::Denied, permission) => {
+ log::error!(
+ "Plugin '{}' permission '{}' denied - Command '{:?}' denied",
+ env.plugin_env.name(),
+ permission
+ .map(|p| p.to_string())
+ .unwrap_or("UNKNOWN".to_owned()),
+ CommandType::from_str(&command.to_string()).with_context(err_context)?
+ );
},
- PluginCommand::OpenCommandPane(command_to_run) => {
- open_command_pane(env, command_to_run)
- },
- PluginCommand::OpenCommandPaneFloating(command_to_run) => {
- open_command_pane_floating(env, command_to_run)
- },
- PluginCommand::SwitchTabTo(tab_index) => switch_tab_to(env, tab_index),
- PluginCommand::SetTimeout(seconds) => set_timeout(env, seconds),
- PluginCommand::ExecCmd(command_line) => exec_cmd(env, command_line),
- PluginCommand::PostMessageTo(plugin_message) => {
- post_message_to(env, plugin_message)?
- },
- PluginCommand::PostMessageToPlugin(plugin_message) => {
- post_message_to_plugin(env, plugin_message)?
- },
- PluginCommand::HideSelf => hide_self(env)?,
- PluginCommand::ShowSelf(should_float_if_hidden) => {
- show_self(env, should_float_if_hidden)
- },
- PluginCommand::SwitchToMode(input_mode) => {
- switch_to_mode(env, input_mode.try_into()?)
- },
- PluginCommand::NewTabsWithLayout(raw_layout) => {
- new_tabs_with_layout(env, &raw_layout)?
- },
- PluginCommand::NewTab => new_tab(env),
- PluginCommand::GoToNextTab => go_to_next_tab(env),
- PluginCommand::GoToPreviousTab => go_to_previous_tab(env),
- PluginCommand::Resize(resize_payload) => resize(env, resize_payload),
- PluginCommand::ResizeWithDirection(resize_strategy) => {
- resize_with_direction(env, resize_strategy)
- },
- PluginCommand::FocusNextPane => focus_next_pane(env),
- PluginCommand::FocusPreviousPane => focus_previous_pane(env),
- PluginCommand::MoveFocus(direction) => move_focus(env, direction),
- PluginCommand::MoveFocusOrTab(direction) => move_focus_or_tab(env, direction),
- PluginCommand::Detach => detach(env),
- PluginCommand::EditScrollback => edit_scrollback(env),
- PluginCommand::Write(bytes) => write(env, bytes),
- PluginCommand::WriteChars(chars) => write_chars(env, chars),
- PluginCommand::ToggleTab => toggle_tab(env),
- PluginCommand::MovePane => move_pane(env),
- PluginCommand::MovePaneWithDirection(direction) => {
- move_pane_with_direction(env, direction)
- },
- PluginCommand::ClearScreen => clear_screen(env),
- PluginCommand::ScrollUp => scroll_up(env),
- PluginCommand::ScrollDown => scroll_down(env),
- PluginCommand::ScrollToTop => scroll_to_top(env),
- PluginCommand::ScrollToBottom => scroll_to_bottom(env),
- PluginCommand::PageScrollUp => page_scroll_up(env),
- PluginCommand::PageScrollDown => page_scroll_down(env),
- PluginCommand::ToggleFocusFullscreen => toggle_focus_fullscreen(env),
- PluginCommand::TogglePaneFrames => toggle_pane_frames(env),
- PluginCommand::TogglePaneEmbedOrEject => toggle_pane_embed_or_eject(env),
- PluginCommand::UndoRenamePane => undo_rename_pane(env),
- PluginCommand::CloseFocus => close_focus(env),
- PluginCommand::ToggleActiveTabSync => toggle_active_tab_sync(env),
- PluginCommand::CloseFocusedTab => close_focused_tab(env),
- PluginCommand::UndoRenameTab => undo_rename_tab(env),
- PluginCommand::QuitZellij => quit_zellij(env),
- PluginCommand::PreviousSwapLayout => previous_swap_layout(env),
- PluginCommand::NextSwapLayout => next_swap_layout(env),
- PluginCommand::GoToTabName(tab_name) => go_to_tab_name(env, tab_name),
- PluginCommand::FocusOrCreateTab(tab_name) => focus_or_create_tab(env, tab_name),
- PluginCommand::GoToTab(tab_index) => go_to_tab(env, tab_index),
- PluginCommand::StartOrReloadPlugin(plugin_url) => {
- start_or_reload_plugin(env, &plugin_url)?
- },
- PluginCommand::CloseTerminalPane(terminal_pane_id) => {
- close_terminal_pane(env, terminal_pane_id)
- },
- PluginCommand::ClosePluginPane(plugin_pane_id) => {
- close_plugin_pane(env, plugin_pane_id)
- },
- PluginCommand::FocusTerminalPane(terminal_pane_id, should_float_if_hidden) => {
- focus_terminal_pane(env, terminal_pane_id, should_float_if_hidden)
- },
- PluginCommand::FocusPluginPane(plugin_pane_id, should_float_if_hidden) => {
- focus_plugin_pane(env, plugin_pane_id, should_float_if_hidden)
- },
- PluginCommand::RenameTerminalPane(terminal_pane_id, new_name) => {
- rename_terminal_pane(env, terminal_pane_id, &new_name)
- },
- PluginCommand::RenamePluginPane(plugin_pane_id, new_name) => {
- rename_plugin_pane(env, plugin_pane_id, &new_name)
- },
- PluginCommand::RenameTab(tab_index, new_name) => {
- rename_tab(env, tab_index, &new_name)
- },
- PluginCommand::ReportPanic(crash_payload) => report_panic(env, &crash_payload),
- }
+ };
Ok(())
})
.with_context(|| format!("failed to run plugin command {}", env.plugin_env.name()))
@@ -255,6 +274,30 @@ fn set_selectable(env: &ForeignFunctionEnv, selectable: bool) {
}
}
+fn request_permission(env: &ForeignFunctionEnv, permissions: Vec<PermissionType>) -> Result<()> {
+ if PermissionCache::from_path_or_default(None)
+ .check_permissions(env.plugin_env.plugin.location.to_string(), &permissions)
+ {
+ return env
+ .plugin_env
+ .senders
+ .send_to_plugin(PluginInstruction::PermissionRequestResult(
+ env.plugin_env.plugin_id,
+ Some(env.plugin_env.client_id),
+ permissions.to_vec(),
+ PermissionStatus::Granted,
+ None,
+ ));
+ }
+
+ env.plugin_env
+ .senders
+ .send_to_screen(ScreenInstruction::RequestPluginPermissions(
+ env.plugin_env.plugin_id,
+ PluginPermission::new(env.plugin_env.plugin.location.to_string(), permissions),
+ ))
+}
+
fn get_plugin_ids(env: &ForeignFunctionEnv) {
let ids = PluginIds {
plugin_id: env.plugin_env.plugin_id,
@@ -1001,3 +1044,81 @@ pub fn wasi_read_bytes(wasi_env: &WasiEnv) -> Result<Vec<u8>> {
.and_then(|string| serde_json::from_str(&string).map_err(anyError::new))
.with_context(|| format!("failed to deserialize object from WASI env '{wasi_env:?}'"))
}
+
+// TODO: move to permissions?
+fn check_command_permission(
+ plugin_env: &PluginEnv,
+ command: &PluginCommand,
+) -> (PermissionStatus, Option<PermissionType>) {
+ if plugin_env.plugin.is_builtin() {
+ // built-in plugins can do all the things because they're part of the application and
+ // there's no use to deny them anything
+ return (PermissionStatus::Granted, None);
+ }
+ let permission = match command {
+ PluginCommand::OpenFile(..) | PluginCommand::OpenFileFloating(..) => {
+ PermissionType::OpenFiles
+ },
+ PluginCommand::OpenTerminal(..)
+ | PluginCommand::StartOrReloadPlugin(..)
+ | PluginCommand::OpenTerminalFloating(..) => PermissionType::OpenTerminalsOrPlugins,
+ PluginCommand::OpenCommandPane(..)
+ | PluginCommand::OpenCommandPaneFloating(..)
+ | PluginCommand::ExecCmd(..) => PermissionType::RunCommands,
+ PluginCommand::Write(..) | PluginCommand::WriteChars(..) => PermissionType::WriteToStdin,
+ PluginCommand::SwitchTabTo(..)
+ | PluginCommand::SwitchToMode(..)
+ | PluginCommand::NewTabsWithLayout(..)
+ | PluginCommand::NewTab
+ | PluginCommand::GoToNextTab
+ | PluginCommand::GoToPreviousTab
+ | PluginCommand::Resize(..)
+ | PluginCommand::ResizeWithDirection(..)
+ | PluginCommand::FocusNextPane
+ | PluginCommand::MoveFocus(..)
+ | PluginCommand::MoveFocusOrTab(..)
+ | PluginCommand::Detach
+ | PluginCommand::EditScrollback
+ | PluginCommand::ToggleTab
+ | PluginCommand::MovePane
+ | PluginCommand::MovePaneWithDirection(..)
+ | PluginCommand::ClearScreen
+ | PluginCommand::ScrollUp
+ | PluginCommand::ScrollDown
+ | PluginCommand::ScrollToTop
+ | PluginCommand::ScrollToBottom
+ | PluginCommand::PageScrollUp
+ | PluginCommand::PageScrollDown
+ | PluginCommand::ToggleFocusFullscreen
+ | PluginCommand::TogglePaneFrames
+ | PluginCommand::TogglePaneEmbedOrEject
+ | PluginCommand::UndoRenamePane
+ | PluginCommand::CloseFocus
+ | PluginCommand::ToggleActiveTabSync
+ | PluginCommand::CloseFocusedTab
+ | PluginCommand::UndoRenameTab
+ | PluginCommand::QuitZellij
+ | PluginCommand::PreviousSwapLayout
+ | PluginCommand::NextSwapLayout
+ | PluginCommand::GoToTabName(..)
+ | PluginCommand::FocusOrCreateTab(..)
+ | PluginCommand::GoToTab(..)
+ | PluginCommand::CloseTerminalPane(..)
+ | PluginCommand::ClosePluginPane(..)
+ | PluginCommand::FocusTerminalPane(..)
+ | PluginCommand::FocusPluginPane(..)
+ | PluginCommand::RenameTerminalPane(..)
+ | PluginCommand::RenamePluginPane(..)
+ | PluginCommand::RenameTab(..) => PermissionType::ChangeApplicationState,
+ _ => return (PermissionStatus::Granted, None),
+ };
+
+ log::info!("plugin permissions: {:?}", plugin_env.permissions);
+ if let Some(permissions) = plugin_env.permissions.lock().unwrap().as_ref() {
+ if permissions.contains(&permission) {
+ return (PermissionStatus::Granted, None);
+ }
+ }
+
+ (PermissionStatus::Denied, Some(permission))
+}
diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs
index 4d5775f7..456bea09 100644
--- a/zellij-server/src/screen.rs
+++ b/zellij-server/src/screen.rs
@@ -6,7 +6,7 @@ use std::path::PathBuf;
use std::rc::Rc;
use std::str;
-use zellij_utils::data::{Direction, PaneManifest, Resize, ResizeStrategy};
+use zellij_utils::data::{Direction, PaneManifest, PluginPermission, Resize, ResizeStrategy};
use zellij_utils::errors::prelude::*;
use zellij_utils::input::command::RunCommand;
use zellij_utils::input::options::Clipboard;
@@ -280,6 +280,10 @@ pub enum ScreenInstruction {
FocusPaneWithId(PaneId, bool, ClientId), // bool is should_float
RenamePane(PaneId, Vec<u8>),
RenameTab(usize, Vec<u8>),
+ RequestPluginPermissions(
+ u32, // u32 - plugin_id
+ PluginPermission,
+ ),
BreakPane(Box<Layout>, Option<TerminalAction>, ClientId),
BreakPaneRight(ClientId),
BreakPaneLeft(ClientId),
@@ -450,6 +454,9 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::FocusPaneWithId(..) => ScreenContext::FocusPaneWithId,
ScreenInstruction::RenamePane(..) => ScreenContext::RenamePane,
ScreenInstruction::RenameTab(..) => ScreenContext::RenameTab,
+ ScreenInstruction::RequestPluginPermissions(..) => {
+ ScreenContext::RequestPluginPermissions
+ },
ScreenInstruction::BreakPane(..) => ScreenContext::BreakPane,
ScreenInstruction::BreakPaneRight(..) => ScreenContext::BreakPaneRight,
ScreenInstruction::BreakPaneLeft(..) => ScreenContext::BreakPaneLeft,
@@ -2958,6 +2965,24 @@ pub(crate) fn screen_thread_main(
}
screen.report_tab_state()?;
},
+ ScreenInstruction::RequestPluginPermissions(plugin_id, plugin_permission) => {
+ let all_tabs = screen.get_tabs_mut();
+ let found = all_tabs.values_mut().any(|tab| {
+ if tab.has_plugin(plugin_id) {
+ tab.request_plugin_permissions(plugin_id, Some(plugin_permission.clone()));
+ true
+ } else {
+ false
+ }
+ });
+
+ if !found {
+ log::error!(
+ "PluginId '{}' not found - cannot request permissions",
+ plugin_id
+ );
+ }
+ },
ScreenInstruction::BreakPane(default_layout, default_shell, client_id) => {
screen.break_pane(default_shell, default_layout, client_id)?;
},
diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs
index 20fbaaca..fb6d4e17 100644
--- a/zellij-server/src/tab/mod.rs
+++ b/zellij-server/src/tab/mod.rs
@@ -9,7 +9,9 @@ mod swap_layouts;
use copy_command::CopyCommand;
use std::env::temp_dir;
use uuid::Uuid;
-use zellij_utils::data::{Direction, PaneInfo, ResizeStrategy};
+use zellij_utils::data::{
+ Direction, PaneInfo, PermissionStatus, PermissionType, PluginPermission, ResizeStrategy,
+};
use zellij_utils::errors::prelude::*;
use zellij_utils::input::command::RunCommand;
use zellij_utils::position::{Column, Line};
@@ -220,6 +222,7 @@ pub trait Pane {
fn set_should_render_boundaries(&mut self, _should_render: bool) {}
fn selectable(&self) -> bool;
fn set_selectable(&mut self, selectable: bool);
+ fn request_permissions_from_user(&mut self, _permissions: Option<PluginPermission>) {}
fn render(
&mut self,
client_id: Option<ClientId>,
@@ -473,6 +476,7 @@ pub trait Pane {
pub enum AdjustedInput {
WriteBytesToTerminal(Vec<u8>),
ReRunCommandInThisPane(RunCommand),
+ PermissionRequestResult(Vec<PermissionType>, PermissionStatus),
CloseThisPane,
}
pub fn get_next_terminal_position(
@@ -1560,17 +1564,35 @@ impl Tab {
self.close_pane(PaneId::Terminal(active_terminal_id), false, None);
should_update_ui = true;
},
+ Some(_) => {},
None => {},
}
},
- PaneId::Plugin(pid) => {
- let mut plugin_updates = vec![];
- for key in parse_keys(&input_bytes) {
- plugin_updates.push((Some(pid), client_id, Event::Key(key)));
- }
- self.senders
- .send_to_plugin(PluginInstruction::Update(plugin_updates))
- .with_context(err_context)?;
+ PaneId::Plugin(pid) => match active_terminal.adjust_input_to_terminal(input_bytes) {
+ Some(AdjustedInput::WriteBytesToTerminal(adjusted_input)) => {
+ let mut plugin_updates = vec![];
+ for key in parse_keys(&adjusted_input) {
+ plugin_updates.push((Some(pid), client_id, Event::Key(key)));
+ }
+ self.senders
+ .send_to_plugin(PluginInstruction::Update(plugin_updates))
+ .with_context(err_context)?;
+ },
+ Some(AdjustedInput::PermissionRequestResult(permissions, status)) => {
+ self.request_plugin_permissions(pid, None);
+ self.senders
+ .send_to_plugin(PluginInstruction::PermissionRequestResult(
+ pid,
+ client_id,
+ permissions,
+ status,
+ None,
+ ))
+ .with_context(err_context)?;
+ should_update_ui = true;
+ },
+ Some(_) => {},
+ None => {},
},
}
Ok(should_update_ui)
@@ -3437,6 +3459,20 @@ impl Tab {
}
Ok(())
}
+ pub fn request_plugin_permissions(&mut self, pid: u32, permissions: Option<PluginPermission>) {
+ if let Some(plugin_pane) = self
+ .tiled_panes
+ .get_pane_mut(PaneId::Plugin(pid))
+ .or_else(|| self.floating_panes.get_pane_mut(PaneId::Plugin(pid)))
+ .or_else(|| {
+ self.suppressed_panes
+ .values_mut()
+ .find(|s_p| s_p.pid() == PaneId::Plugin(pid))
+ })
+ {
+ plugin_pane.request_permissions_from_user(permissions);
+ }
+ }
}
pub fn pane_info_for_pane(pane_id: &PaneId, pane: &Box<dyn Pane>) -> PaneInfo {
diff --git a/zellij-tile/src/lib.rs b/zellij-tile/src/lib.rs
index 67b4a551..df99e9ae 100644
--- a/zellij-tile/src/lib.rs
+++ b/zellij-tile/src/lib.rs
@@ -107,6 +107,7 @@ macro_rules! register_plugin {
fn load() {
STATE.with(|state| {
use std::collections::BTreeMap;
+ use std::convert::TryFrom;
use std::convert::TryInto;
use zellij_tile::shim::plugin_api::action::ProtobufPluginConfiguration;
use zellij_tile::shim::prost::Message;
diff --git a/zellij-tile/src/shim.rs b/zellij-tile/src/shim.rs
index c3a772f9..6204767a 100644
--- a/zellij-tile/src/shim.rs
+++ b/zellij-tile/src/shim.rs
@@ -39,6 +39,13 @@ pub fn set_selectable(selectable: bool) {
unsafe { host_run_plugin_command() };
}
+pub fn request_permission(permissions: &[PermissionType]) {
+ let plugin_command = PluginCommand::RequestPluginPermissions(permissions.into());
+ let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap();
+ object_to_stdout(&protobuf_plugin_command.encode_to_vec());
+ unsafe { host_run_plugin_command() };
+}
+
// Query Functions
/// Returns the unique Zellij pane ID for the plugin as well as the Zellij process id.
pub fn get_plugin_ids() -> PluginIds {
diff --git a/zellij-utils/assets/plugins/compact-bar.wasm b/zellij-utils/assets/plugins/compact-bar.wasm
index f38c500f..9e2112cf 100755
--- a/zellij-utils/assets/plugins/compact-bar.wasm
+++ b/zellij-utils/assets/plugins/compact-bar.wasm
Binary files differ
diff --git a/zellij-utils/assets/plugins/fixture-plugin-for-tests.wasm b/zellij-utils/assets/plugins/fixture-plugin-for-tests.wasm
index 80a6776e..ff13cefd 100755
--- a/zellij-utils/assets/plugins/fixture-plugin-for-tests.wasm
+++ b/zellij-utils/assets/plugins/fixture-plugin-for-tests.wasm
Binary files differ
diff --git a/zellij-utils/assets/plugins/status-bar.wasm b/zellij-utils/assets/plugins/status-bar.wasm
index 58beaacc..e18d4edf 100755
--- a/zellij-utils/assets/plugins/status-bar.wasm
+++ b/zellij-utils/assets/plugins/status-bar.wasm
Binary files differ
diff --git a/zellij-utils/assets/plugins/strider.wasm b/zellij-utils/assets/plugins/strider.wasm
index db61209a..3aff9cc3 100755
--- a/zellij-utils/assets/plugins/strider.wasm
+++ b/zellij-utils/assets/plugins/strider.wasm
Binary files differ
diff --git a/zellij-utils/assets/plugins/tab-bar.wasm b/zellij-utils/assets/plugins/tab-bar.wasm
index 1a2919f6..ee53ba18 100755
--- a/zellij-utils/assets/plugins/tab-bar.wasm
+++ b/zellij-utils/assets/plugins/tab-bar.wasm
Binary files differ
diff --git a/zellij-utils/src/consts.rs b/zellij-utils/src/consts.rs
index 50600a93..a2c3a42a 100644
--- a/zellij-utils/src/consts.rs
+++ b/zellij-utils/src/consts.rs
@@ -36,6 +36,8 @@ lazy_static! {
.cache_dir()
.to_path_buf()
.join(format!("{}", Uuid::new_v4()));
+ pub static ref ZELLIJ_PLUGIN_PERMISSIONS_CACHE: PathBuf =
+ ZELLIJ_CACHE_DIR.join("permissions.kdl");
}
pub const FEATURES: &[&str] = &[
diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs
index 9c1c8e2f..a08e642f 100644
--- a/zellij-utils/src/data.rs
+++ b/zellij-utils/src/data.rs
@@ -6,7 +6,7 @@ use std::collections::{HashMap, HashSet};
use std::fmt;
use std::path::{Path, PathBuf};
use std::str::FromStr;
-use strum_macros::{EnumDiscriminants, EnumIter, EnumString, ToString};
+use strum_macros::{Display, EnumDiscriminants, EnumIter, EnumString, ToString};
pub type ClientId = u16; // TODO: merge with crate type?
@@ -493,6 +493,63 @@ pub enum Event {
FileSystemUpdate(Vec<PathBuf>),
/// A file was deleted somewhere in the Zellij CWD folder
FileSystemDelete(Vec<PathBuf>),
+ /// A Result of plugin permission request
+ PermissionRequestResult(PermissionStatus),
+}
+
+#[derive(
+ Debug,
+ PartialEq,
+ Eq,
+ Hash,
+ Copy,
+ Clone,
+ EnumDiscriminants,
+ ToString,
+ Serialize,
+ Deserialize,
+ PartialOrd,
+ Ord,
+)]
+#[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize, Display, PartialOrd, Ord))]
+#[strum_discriminants(name(PermissionType))]
+#[non_exhaustive]
+pub enum Permission {
+ ReadApplicationState,
+ ChangeApplicationState,
+ OpenFiles,
+ RunCommands,
+ OpenTerminalsOrPlugins,
+ WriteToStdin,
+}
+
+impl PermissionType {
+ pub fn display_name(&self) -> String {
+ match self {
+ PermissionType::ReadApplicationState => {
+ "Access Zellij state (Panes, Tabs and UI)".to_owned()
+ },
+ PermissionType::ChangeApplicationState => {
+ "Change Zellij state (Panes, Tabs and UI)".to_owned()
+ },
+ PermissionType::OpenFiles => "Open files (eg. for editing)".to_owned(),
+ PermissionType::RunCommands => "Run commands".to_owned(),
+ PermissionType::OpenTerminalsOrPlugins => "Start new terminals and plugins".to_owned(),
+ PermissionType::WriteToStdin => "Write to standard input (STDIN)".to_owned(),
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct PluginPermission {
+ pub name: String,
+ pub permissions: Vec<PermissionType>,
+}
+
+impl PluginPermission {
+ pub fn new(name: String, permissions: Vec<PermissionType>) -> Self {
+ PluginPermission { name, permissions }
+ }
}
/// Describes the different input modes, which change the way that keystrokes will be interpreted.
@@ -811,6 +868,12 @@ pub enum CopyDestination {
System,
}
+#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
+pub enum PermissionStatus {
+ Granted,
+ Denied,
+}
+
#[derive(Debug, Default, Clone)]
pub struct FileToOpen {
pub path: PathBuf,
@@ -882,7 +945,9 @@ impl PluginMessage {
}
}
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, EnumDiscriminants, ToString)]
+#[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize))]
+#[strum_discriminants(name(CommandType))]
pub enum PluginCommand {
Subscribe(HashSet<EventType>),
Unsubscribe(HashSet<EventType>),
@@ -950,4 +1015,5 @@ pub enum PluginCommand {
RenamePluginPane(u32, String), // plugin pane id, new name
RenameTab(u32, String), // tab index, new name
ReportPanic(String), // stringified panic
+ RequestPluginPermissions(Vec<PermissionType>),
}
diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs
index df5d0278..306db448 100644
--- a/zellij-utils/src/errors.rs
+++ b/zellij-utils/src/errors.rs
@@ -338,6 +338,7 @@ pub enum ScreenContext {
FocusPaneWithId,
RenamePane,
RenameTab,
+ RequestPluginPermissions,
BreakPane,
BreakPaneRight,
BreakPaneLeft,
@@ -377,6 +378,7 @@ pub enum PluginContext {
PostMessageToPluginWorker,
PostMessageToPlugin,
PluginSubscribedToEvents,
+ PermissionRequestResult,
}
/// Stack call representations corresponding to the different types of [`ClientInstruction`]s.
diff --git a/zellij-utils/src/input/mod.rs b/zellij-utils/src/input/mod.rs
index 6722cfd8..1c95f348 100644
--- a/zellij-utils/src/input/mod.rs
+++ b/zellij-utils/src/input/mod.rs
@@ -4,6 +4,7 @@ pub mod config;
pub mod keybinds;
pub mod layout;
pub mod options;
+pub mod permission;
pub mod plugins;
pub mod theme;
diff --git a/zellij-utils/src/input/permission.rs b/zellij-utils/src/input/permission.rs
new file mode 100644
index 00000000..1755fe1e
--- /dev/null
+++ b/zellij-utils/src/input/permission.rs
@@ -0,0 +1,60 @@
+use std::{
+ collections::HashMap,
+ fs::{self, File},
+ io::Write,
+ path::PathBuf,
+};
+
+use crate::{consts::ZELLIJ_PLUGIN_PERMISSIONS_CACHE, data::PermissionType};
+
+pub type GrantedPermission = HashMap<String, Vec<PermissionType>>;
+
+#[derive(Default, Debug)]
+pub struct PermissionCache {
+ path: PathBuf,
+ granted: GrantedPermission,
+}
+
+impl PermissionCache {
+ pub fn cache(&mut self, plugin_name: String, permissions: Vec<PermissionType>) {
+ self.granted.insert(plugin_name, permissions);
+ }
+
+ pub fn get_permissions(&self, plugin_name: String) -> Option<&Vec<PermissionType>> {
+ self.granted.get(&plugin_name)
+ }
+
+ pub fn check_permissions(
+ &self,
+ plugin_name: String,
+ permissions: &Vec<PermissionType>,
+ ) -> bool {
+ if let Some(target) = self.granted.get(&plugin_name) {
+ if target == permissions {
+ return true;
+ }
+ }
+
+ false
+ }
+
+ pub fn from_path_or_default(cache_path: Option<PathBuf>) -> Self {
+ let cache_path = cache_path.unwrap_or(ZELLIJ_PLUGIN_PERMISSIONS_CACHE.to_path_buf());
+
+ let granted = match fs::read_to_string(cache_path.clone()) {
+ Ok(raw_string) => PermissionCache::from_string(raw_string).unwrap_or_default(),
+ Err(_) => GrantedPermission::default(),
+ };
+
+ PermissionCache {
+ path: cache_path,
+ granted,
+ }
+ }
+
+ pub fn write_to_file(&self) -> std::io::Result<()> {
+ let mut f = File::create(&self.path)?;
+ write!(f, "{}", PermissionCache::to_string(&self.granted))?;
+ Ok(())
+ }
+}
diff --git a/zellij-utils/src/kdl/mod.rs b/zellij-utils/src/kdl/mod.rs
index a75a4a63..13a03ad1 100644
--- a/zellij-utils/src/kdl/mod.rs
+++ b/zellij-utils/src/kdl/mod.rs
@@ -1,15 +1,16 @@
mod kdl_layout_parser;
-use crate::data::{Direction, InputMode, Key, Palette, PaletteColor, Resize};
+use crate::data::{Direction, InputMode, Key, Palette, PaletteColor, PermissionType, Resize};
use crate::envs::EnvironmentVariables;
use crate::input::config::{Config, ConfigError, KdlError};
use crate::input::keybinds::Keybinds;
use crate::input::layout::{Layout, PluginUserConfiguration, RunPlugin, RunPluginLocation};
use crate::input::options::{Clipboard, OnForceClose, Options};
+use crate::input::permission::{GrantedPermission, PermissionCache};
use crate::input::plugins::{PluginConfig, PluginTag, PluginType, PluginsConfig};
use crate::input::theme::{FrameConfig, Theme, Themes, UiConfig};
use crate::setup::{find_default_config_dir, get_layout_dir};
use kdl_layout_parser::KdlLayoutParser;
-use std::collections::{BTreeMap, HashMap};
+use std::collections::{BTreeMap, HashMap, HashSet};
use strum::IntoEnumIterator;
use miette::NamedSource;
@@ -1793,6 +1794,53 @@ impl Themes {
}
}
+impl PermissionCache {
+ pub fn from_string(raw_string: String) -> Result<GrantedPermission, ConfigError> {
+ let kdl_document: KdlDocument = raw_string.parse()?;
+
+ let mut granted_permission = GrantedPermission::default();
+
+ for node in kdl_document.nodes() {
+ if let Some(children) = node.children() {
+ let key = kdl_name!(node);
+ let permissions: Vec<PermissionType> = children
+ .nodes()
+ .iter()
+ .filter_map(|p| {
+ let v = kdl_name!(p);
+ PermissionType::from_str(v).ok()
+ })
+ .collect();
+
+ granted_permission.insert(key.into(), permissions);
+ }
+ }
+
+ Ok(granted_permission)
+ }
+
+ pub fn to_string(granted: &GrantedPermission) -> String {
+ let mut kdl_doucment = KdlDocument::new();
+
+ granted.iter().for_each(|(k, v)| {
+ let mut node = KdlNode::new(k.as_str());
+ let mut children = KdlDocument::new();
+
+ let permissions: HashSet<PermissionType> = v.clone().into_iter().collect();
+ permissions.iter().for_each(|f| {
+ let n = KdlNode::new(f.to_string().as_str());
+ children.nodes_mut().push(n);
+ });
+
+ node.set_children(children);
+ kdl_doucment.nodes_mut().push(node);
+ });
+
+ kdl_doucment.fmt();
+ kdl_doucment.to_string()
+ }
+}
+
pub fn parse_plugin_user_configuration(
plugin_block: &KdlNode,
) -> Result<BTreeMap<String, String>, ConfigError> {
diff --git a/zellij-utils/src/plugin_api/event.proto b/zellij-utils/src/plugin_api/event.proto
index 95928ae3..3a9838fa 100644
--- a/zellij-utils/src/plugin_api/event.proto
+++ b/zellij-utils/src/plugin_api/event.proto
@@ -38,6 +38,7 @@ enum EventType {
FileSystemUpdate = 13;
/// A file was deleted somewhere in the Zellij CWD folder
FileSystemDelete = 14;
+ PermissionRequestResult = 15;
}
message EventNameList {
@@ -57,9 +58,14 @@ message Event {
bool visible_payload = 9;
CustomMessagePayload custom_message_payload = 10;
FileListPayload file_list_payload = 11;
+ PermissionRequestResultPayload permission_request_result_payload = 12;
}
}
+message PermissionRequestResultPayload {
+ bool granted = 1;
+}
+
message FileListPayload {
repeated string paths = 1;
}
diff --git a/zellij-utils/src/plugin_api/event.rs b/zellij-utils/src/plugin_api/event.rs
index 315fa54b..6115fe1e 100644
--- a/zellij-utils/src/plugin_api/event.rs
+++ b/zellij-utils/src/plugin_api/event.rs
@@ -13,9 +13,10 @@ pub use super::generated_api::api::{
style::Style as ProtobufStyle,
};
use crate::data::{
- CopyDestination, Direction, Event, EventType, InputMode, Key, ModeInfo, Mouse, Palette,
- PaletteColor, PaneInfo, PaneManifest, PluginCapabilities, Style, TabInfo, ThemeHue,
+ CopyDestination, Event, EventType, InputMode, Key, ModeInfo, Mouse, PaneInfo, PaneManifest,
+ PermissionStatus, PluginCapabilities, Style, TabInfo,
};
+
use crate::errors::prelude::*;
use crate::input::actions::Action;
@@ -160,6 +161,16 @@ impl TryFrom<ProtobufEvent> for Event {
},
_ => Err("Malformed payload for the file system delete Event"),
},
+ Some(ProtobufEventType::PermissionRequestResult) => match protobuf_event.payload {
+ Some(ProtobufEventPayload::PermissionRequestResultPayload(payload)) => {
+ if payload.granted {
+ Ok(Event::PermissionRequestResult(PermissionStatus::Granted))
+ } else {
+ Ok(Event::PermissionRequestResult(PermissionStatus::Denied))
+ }
+ },
+ _ => Err("Malformed payload for the file system delete Event"),
+ },
None => Err("Unknown Protobuf Event"),
}
}
@@ -290,6 +301,18 @@ impl TryFrom<Event> for ProtobufEvent {
payload: Some(event::Payload::FileListPayload(file_list_payload)),
})
},
+ Event::PermissionRequestResult(permission_status) => {
+ let granted = match permission_status {
+ PermissionStatus::Granted => true,
+ PermissionStatus::Denied => false,
+ };
+ Ok(ProtobufEvent {
+ name: ProtobufEventType::PermissionRequestResult as i32,
+ payload: Some(event::Payload::PermissionRequestResultPayload(
+ PermissionRequestResultPayload { granted },
+ )),
+ })
+ },
}
}
}
@@ -673,6 +696,7 @@ impl TryFrom<ProtobufEventType> for EventType {
ProtobufEventType::FileSystemRead => EventType::FileSystemRead,
ProtobufEventType::FileSystemUpdate => EventType::FileSystemUpdate,
ProtobufEventType::FileSystemDelete => EventType::FileSystemDelete,
+ ProtobufEventType::PermissionRequestResult => EventType::PermissionRequestResult,
})
}
}
@@ -696,6 +720,7 @@ impl TryFrom<EventType> for ProtobufEventType {
EventType::FileSystemRead => ProtobufEventType::FileSystemRead,
EventType::FileSystemUpdate => ProtobufEventType::FileSystemUpdate,
EventType::FileSystemDelete => ProtobufEventType::FileSystemDelete,
+ EventType::PermissionRequestResult => ProtobufEventType::PermissionRequestResult,
})
}
}
@@ -717,6 +742,7 @@ fn serialize_mode_update_event() {
#[test]
fn serialize_mode_update_event_with_non_default_values() {
+ use crate::data::{Direction, Palette, PaletteColor, ThemeHue};
use prost::Message;
let mode_update_event = Event::ModeUpdate(ModeInfo {
mode: InputMode::Locked,
diff --git a/zellij-utils/src/plugin_api/mod.rs b/zellij-utils/src/plugin_api/mod.rs
index 55ace500..4812d468 100644
--- a/zellij-utils/src/plugin_api/mod.rs
+++ b/zellij-utils/src/plugin_api/mod.rs
@@ -7,6 +7,7 @@ pub mod key;
pub mod message;
pub mod plugin_command;
pub mod plugin_ids;
+pub mod plugin_permission;
pub mod resize;
pub mod style;
pub mod generated_api {
diff --git a/zellij-utils/src/plugin_api/plugin_command.proto b/zellij-utils/src/plugin_api/plugin_command.proto
index f09ccf4b..9ba8bb7f 100644
--- a/zellij-utils/src/plugin_api/plugin_command.proto
+++ b/zellij-utils/src/plugin_api/plugin_command.proto
@@ -6,6 +6,7 @@ import "command.proto";
import "message.proto";
import "input_mode.proto";
import "resize.proto";
+import "plugin_permission.proto";
package api.plugin_command;
@@ -76,6 +77,7 @@ enum CommandName {
RenamePluginPane = 63;
RenameTab = 64;
ReportCrash = 65;
+ RequestPluginPermissions = 66;
}
message PluginCommand {
@@ -117,9 +119,14 @@ message PluginCommand {
IdAndNewName rename_plugin_pane_payload = 35;
IdAndNewName rename_tab_payload = 36;
string report_crash_payload = 37;
+ RequestPluginPermissionPayload request_plugin_permission_payload = 38;
}
}
+message RequestPluginPermissionPayload {
+ repeated plugin_permission.PermissionType permissions = 1;
+}
+
message SubscribePayload {
event.EventNameList subscriptions = 1;
}
diff --git a/zellij-utils/src/plugin_api/plugin_command.rs b/zellij-utils/src/plugin_api/plugin_command.rs
index bf359dd2..ff50a6a7 100644
--- a/zellij-utils/src/plugin_api/plugin_command.rs
+++ b/zellij-utils/src/plugin_api/plugin_command.rs
@@ -3,14 +3,15 @@ pub use super::generated_api::api::{
plugin_command::{
plugin_command::Payload, CommandName, ExecCmdPayload, IdAndNewName, MovePayload,
OpenCommandPanePayload, OpenFilePayload, PaneIdAndShouldFloat,
- PluginCommand as ProtobufPluginCommand, PluginMessagePayload, ResizePayload,
- SetTimeoutPayload, SubscribePayload, SwitchTabToPayload, SwitchToModePayload,
- UnsubscribePayload,
+ PluginCommand as ProtobufPluginCommand, PluginMessagePayload,
+ RequestPluginPermissionPayload, ResizePayload, SetTimeoutPayload, SubscribePayload,
+ SwitchTabToPayload, SwitchToModePayload, UnsubscribePayload,
},
+ plugin_permission::PermissionType as ProtobufPermissionType,
resize::ResizeAction as ProtobufResizeAction,
};
-use crate::data::PluginCommand;
+use crate::data::{PermissionType, PluginCommand};
use std::convert::TryFrom;
@@ -486,6 +487,19 @@ impl TryFrom<ProtobufPluginCommand> for PluginCommand {
},
_ => Err("Mismatched payload for ReportCrash"),
},
+ Some(CommandName::RequestPluginPermissions) => match protobuf_plugin_command.payload {
+ Some(Payload::RequestPluginPermissionPayload(payload)) => {
+ Ok(PluginCommand::RequestPluginPermissions(
+ payload
+ .permissions
+ .iter()
+ .filter_map(|p| ProtobufPermissionType::from_i32(*p))
+ .filter_map(|p| PermissionType::try_from(p).ok())
+ .collect(),
+ ))
+ },
+ _ => Err("Mismatched payload for RequestPluginPermission"),
+ },
None => Err("Unrecognized plugin command"),
}
}
@@ -820,6 +834,18 @@ impl TryFrom<PluginCommand> for ProtobufPluginCommand {
name: CommandName::ReportCrash as i32,
payload: Some(Payload::ReportCrashPayload(payload)),
}),
+ PluginCommand::RequestPluginPermissions(permissions) => Ok(ProtobufPluginCommand {
+ name: CommandName::RequestPluginPermissions as i32,
+ payload: Some(Payload::RequestPluginPermissionPayload(
+ RequestPluginPermissionPayload {
+ permissions: permissions
+ .iter()
+ .filter_map(|p| ProtobufPermissionType::try_from(*p).ok())
+ .map(|p| p as i32)
+ .collect(),
+ },
+ )),
+ }),
}
}
}
diff --git a/zellij-utils/src/plugin_api/plugin_permission.proto b/zellij-utils/src/plugin_api/plugin_permission.proto
new file mode 100644
index 00000000..50072b1b
--- /dev/null
+++ b/zellij-utils/src/plugin_api/plugin_permission.proto
@@ -0,0 +1,12 @@
+syntax = "proto3";
+
+package api.plugin_permission;
+
+enum PermissionType {
+ ReadApplicationState = 0;
+ ChangeApplicationState = 1;
+ OpenFiles = 2;
+ RunCommands = 3;
+ OpenTerminalsOrPlugins = 4;
+ WriteToStdin = 5;
+}
diff --git a/zellij-utils/src/plugin_api/plugin_permission.rs b/zellij-utils/src/plugin_api/plugin_permission.rs
new file mode 100644
index 00000000..d2839fac
--- /dev/null
+++ b/zellij-utils/src/plugin_api/plugin_permission.rs
@@ -0,0 +1,44 @@
+pub use super::generated_api::api::plugin_permission::PermissionType as ProtobufPermissionType;
+use crate::data::PermissionType;
+
+use std::convert::TryFrom;
+
+impl TryFrom<ProtobufPermissionType> for PermissionType {
+ type Error = &'static str;
+ fn try_from(protobuf_permission: ProtobufPermissionType) -> Result<Self, &'static str> {
+ match protobuf_permission {
+ ProtobufPermissionType::ReadApplicationState => {
+ Ok(PermissionType::ReadApplicationState)
+ },
+ ProtobufPermissionType::ChangeApplicationState => {
+ Ok(PermissionType::ChangeApplicationState)
+ },
+ ProtobufPermissionType::OpenFiles => Ok(PermissionType::OpenFiles),
+ ProtobufPermissionType::RunCommands => Ok(PermissionType::RunCommands),
+ ProtobufPermissionType::OpenTerminalsOrPlugins => {
+ Ok(PermissionType::OpenTerminalsOrPlugins)
+ },
+ ProtobufPermissionType::WriteToStdin => Ok(PermissionType::WriteToStdin),
+ }
+ }
+}
+
+impl TryFrom<PermissionType> for ProtobufPermissionType {
+ type Error = &'static str;
+ fn try_from(permission: PermissionType) -> Result<Self, &'static str> {
+ match permission {
+ PermissionType::ReadApplicationState => {
+ Ok(ProtobufPermissionType::ReadApplicationState)
+ },
+ PermissionType::ChangeApplicationState => {
+ Ok(ProtobufPermissionType::ChangeApplicationState)
+ },
+ PermissionType::OpenFiles => Ok(ProtobufPermissionType::OpenFiles),
+ PermissionType::RunCommands => Ok(ProtobufPermissionType::RunCommands),
+ PermissionType::OpenTerminalsOrPlugins => {
+ Ok(ProtobufPermissionType::OpenTerminalsOrPlugins)
+ },
+ PermissionType::WriteToStdin => Ok(ProtobufPermissionType::WriteToStdin),
+ }
+ }
+}