Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add plugin permission system #2624

Merged
merged 48 commits into from
Aug 12, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
22cb57c
WIP: add exaple of permission ui
jaeheonji Jul 13, 2023
3448c7b
feat: add request permission ui
jaeheonji Jul 16, 2023
52b7cae
feat: add caching permission in memory
jaeheonji Jul 16, 2023
06b9db4
feat: add permission check
jaeheonji Jul 18, 2023
31d65e9
feat: add file caching
jaeheonji Jul 21, 2023
9b95472
fix: changes request
jaeheonji Jul 24, 2023
9ff7b64
feat(ui): new status bar mode (#2619)
imsnif Jul 12, 2023
67a9353
docs(changelog): status-bar supermode
imsnif Jul 12, 2023
c339a81
fix(rendering): occasional glitches while resizing (#2621)
imsnif Jul 12, 2023
9c79832
docs(changelog): resize glitches fix
imsnif Jul 12, 2023
5f52c34
chore(version): bump development version
imsnif Jul 12, 2023
e5ad994
Fix colored pane frames in mirrored sessions (#2625)
har7an Jul 16, 2023
0d8efd8
fix(sessions): use custom lists of adjectives and nouns for generatin…
khs26 Jul 18, 2023
b1285f6
docs(changelog): generate session names with custom words list
tlinford Jul 18, 2023
37ce6b3
feat(plugins): make plugins configurable (#2646)
imsnif Jul 25, 2023
01123c0
docs(changelog): configurable plugins
imsnif Jul 25, 2023
b1181c3
Merge branch 'main' into feature/permission
imsnif Jul 25, 2023
c216829
style(fmt): rustfmt
imsnif Jul 25, 2023
82b16da
touch up ui
imsnif Jul 25, 2023
1e05d56
fix: don't save permission data in memory
jaeheonji Jul 25, 2023
cdde362
feat: load cached permission
jaeheonji Jul 26, 2023
5af95ad
test: add example test (WIP)
jaeheonji Jul 28, 2023
d7470df
fix: issue event are always denied
jaeheonji Jul 29, 2023
041085e
test: update snapshot
jaeheonji Jul 29, 2023
29a2fad
apply formatting
jaeheonji Jul 29, 2023
171a5e8
refactor: update default cache function
jaeheonji Jul 31, 2023
14f1084
test: add more new test
jaeheonji Jul 31, 2023
a4e9370
apply formatting
jaeheonji Jul 31, 2023
b8e6f1f
Revert "apply formatting"
jaeheonji Jul 31, 2023
1c17476
apply format
jaeheonji Jul 31, 2023
8781687
fix: update cache path
jaeheonji Jul 31, 2023
3c53f43
apply format
jaeheonji Jul 31, 2023
53685d2
fix: cache path
jaeheonji Jul 31, 2023
0c879f2
fix: update log level
jaeheonji Aug 6, 2023
01eff3b
test for github workflow
jaeheonji Aug 6, 2023
e67d8a8
Merge remote-tracking branch 'origin/main' into feature/permission
jaeheonji Aug 6, 2023
87839e3
Revert "test for github workflow"
jaeheonji Aug 6, 2023
f38b914
refactor: permission cache
jaeheonji Aug 7, 2023
8f033bb
fix(test): permission grant/deny race condition
imsnif Aug 10, 2023
da098a4
style(fmt): rustfmt
imsnif Aug 10, 2023
dc502ef
Merge branch 'main' into jaeheonji-feature/permission
imsnif Aug 10, 2023
e733120
style(fmt): rustfmt
imsnif Aug 10, 2023
3a9b8bd
configure permissions
imsnif Aug 12, 2023
52aabe9
permission denied test
imsnif Aug 12, 2023
325af5e
snapshot
imsnif Aug 12, 2023
d6e0239
add ui for small plugins
imsnif Aug 12, 2023
fe69f3a
style(fmt): rustfmt
imsnif Aug 12, 2023
27b8464
some cleanups
imsnif Aug 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions default-plugins/strider/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ register_worker!(

impl ZellijPlugin for State {
fn load(&mut self) {
// Example
request_permission(&[PermissionType::KeyboardInput]);

refresh_directory(self);
self.search_state.loading = true;
subscribe(&[
Expand All @@ -29,6 +32,7 @@ impl ZellijPlugin for State {
EventType::FileSystemCreate,
EventType::FileSystemUpdate,
EventType::FileSystemDelete,
EventType::PermissionRequestResult,
]);
post_message_to(
"file_name_search",
Expand All @@ -53,6 +57,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);
Expand Down
66 changes: 63 additions & 3 deletions zellij-server/src/panes/plugin_pane.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
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},
};
use crate::ClientId;
use std::cell::RefCell;
use std::rc::Rc;
use zellij_utils::data::{PermissionType, PluginPermission};
use zellij_utils::pane_size::{Offset, SizeInPixels};
use zellij_utils::position::Position;
use zellij_utils::{
Expand All @@ -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();
Expand Down Expand Up @@ -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,
}

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand All @@ -193,14 +213,30 @@ 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, true)),
// N or n
&[78] | &[110] => Some(AdjustedInput::PermissionRequestResult(permissions, false)),
_ => None,
}
} else {
Some(AdjustedInput::WriteBytesToTerminal(input_bytes))
}
}
fn position_and_size(&self) -> PaneGeom {
self.geom
}
Expand Down Expand Up @@ -233,6 +269,9 @@ impl Pane for PluginPane {
fn set_selectable(&mut self, selectable: bool) {
self.selectable = selectable;
}
fn set_plugin_permissions(&mut self, permissions: Option<PluginPermission>) {
jaeheonji marked this conversation as resolved.
Show resolved Hide resolved
self.requesting_permissions = permissions;
}
fn render(
&mut self,
client_id: Option<ClientId>,
Expand Down Expand Up @@ -595,4 +634,25 @@ impl PluginPane {
self.handle_plugin_bytes(client_id, bytes.clone());
}
}
fn display_request_permission_message(&self, plugin_permission: &PluginPermission) -> String {
let cyan = style!(self.terminal_emulator_colors.borrow().cyan).bold();

let mut messages = String::new();
let permissions: HashSet<PermissionType> =
plugin_permission.permissions.clone().into_iter().collect();

messages.push_str(&format!(
"Plugin {} would like permission to:",
cyan.paint(&plugin_permission.name),
));
jaeheonji marked this conversation as resolved.
Show resolved Hide resolved
permissions.iter().enumerate().for_each(|(i, p)| {
messages.push_str(&format!("\n\r{}. {}", i + 1, p));
});

messages.push_str(&format!(
"\n\r\n\rWould you like to grant these permissions? (y/n)"
));

messages
}
}
37 changes: 36 additions & 1 deletion zellij-server/src/plugins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ use crate::{pty::PtyInstruction, thread_bus::Bus, ClientId, ServerInstruction};
use wasm_bridge::WasmBridge;

use zellij_utils::{
data::{Event, EventType, PluginCapabilities},
consts::ZELLIJ_PLUGIN_PERMISSIONS_FILE,
data::{Event, EventType, PermissionType, PluginCapabilities},
errors::{prelude::*, ContextType, PluginContext},
input::{
command::TerminalAction,
layout::{FloatingPaneLayout, Layout, Run, RunPlugin, RunPluginLocation, TiledPaneLayout},
permission::GrantedPermission,
plugins::PluginsConfig,
},
ipc::ClientAttributes,
Expand Down Expand Up @@ -79,6 +81,7 @@ pub enum PluginInstruction {
String, // serialized payload
),
PluginSubscribedToEvents(PluginId, ClientId, HashSet<EventType>),
PermissionRequestResult(PluginId, Option<ClientId>, Vec<PermissionType>, bool),
Exit,
}

Expand All @@ -105,6 +108,9 @@ impl From<&PluginInstruction> for PluginContext {
PluginInstruction::PluginSubscribedToEvents(..) => {
PluginContext::PluginSubscribedToEvents
},
PluginInstruction::PermissionRequestResult(..) => {
PluginContext::PermissionRequestResult
},
}
}
}
Expand All @@ -126,11 +132,21 @@ pub(crate) fn plugin_thread_main(
let plugin_dir = data_dir.join("plugins/");
let plugin_global_data_dir = plugin_dir.join("data");

let granted_permission =
match fs::read_to_string(plugin_dir.join(ZELLIJ_PLUGIN_PERMISSIONS_FILE)) {
Ok(s) => match GrantedPermission::from_string(s) {
Ok(p) => p,
Err(_) => GrantedPermission::default(),
},
Err(_) => GrantedPermission::default(),
};

let mut wasm_bridge = WasmBridge::new(
plugins,
bus.senders.clone(),
store,
plugin_dir,
granted_permission,
path_to_default_shell,
zellij_cwd,
capabilities,
Expand Down Expand Up @@ -286,6 +302,25 @@ pub(crate) fn plugin_thread_main(
}
}
},
PluginInstruction::PermissionRequestResult(
plugin_id,
client_id,
permissions,
result,
) => {
let permissions = if result { permissions } else { Vec::new() };
match wasm_bridge.caching_plugin_permissions(plugin_id, client_id, permissions) {
Ok(_) => {},
Err(e) => log::error!("{}", e),
}

let updates = vec![(
Some(plugin_id),
client_id,
Event::PermissionRequestResult(result),
)];
wasm_bridge.update_plugins(updates)?;
},
PluginInstruction::Exit => {
wasm_bridge.cleanup();
break;
Expand Down
Loading