From b59b29a534578cb1910a21a394d0279cb423c2b2 Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Fri, 20 Oct 2023 14:20:00 +0200 Subject: [PATCH] feat(plugins): web requests api (#2879) * feat(plugins): web requests api * fix e2e tests * fix e2e tests again --- Cargo.lock | 694 +++++++++++++++--- .../fixture-plugin-for-tests/src/main.rs | 17 + zellij-server/src/background_jobs.rs | 80 +- .../src/plugins/unit/plugin_tests.rs | 76 ++ ...ts__granted_permission_request_result.snap | 3 +- ...gin_tests__request_plugin_permissions.snap | 3 +- ...gin_tests__web_request_plugin_command.snap | 26 + zellij-server/src/plugins/zellij_exports.rs | 34 +- zellij-tile/src/shim.rs | 15 + zellij-utils/Cargo.toml | 1 + zellij-utils/assets/prost/api.event.rs | 27 +- .../assets/prost/api.plugin_command.rs | 53 +- .../assets/prost/api.plugin_permission.rs | 3 + zellij-utils/src/data.rs | 41 +- zellij-utils/src/errors.rs | 1 + zellij-utils/src/lib.rs | 3 +- zellij-utils/src/plugin_api/event.proto | 14 + zellij-utils/src/plugin_api/event.rs | 41 ++ .../src/plugin_api/plugin_command.proto | 17 + zellij-utils/src/plugin_api/plugin_command.rs | 80 +- .../src/plugin_api/plugin_permission.proto | 1 + .../src/plugin_api/plugin_permission.rs | 2 + 22 files changed, 1124 insertions(+), 108 deletions(-) create mode 100644 zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__web_request_plugin_command.snap diff --git a/Cargo.lock b/Cargo.lock index 05d6fbe6..0f02d18b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,60 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aead" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" +dependencies = [ + "generic-array 0.14.5", +] + +[[package]] +name = "aes" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" +dependencies = [ + "aes-soft", + "aesni", + "cipher", +] + +[[package]] +name = "aes-gcm" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aes-soft" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" +dependencies = [ + "cipher", + "opaque-debug 0.3.0", +] + +[[package]] +name = "aesni" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" +dependencies = [ + "cipher", + "opaque-debug 0.3.0", +] + [[package]] name = "ahash" version = "0.7.6" @@ -93,6 +147,16 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-dup" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7427a12b8dc09291528cfb1da2447059adb4a257388c2acd6497a79d55cf6f7c" +dependencies = [ + "futures-io", + "simple-mutex", +] + [[package]] name = "async-executor" version = "1.4.1" @@ -109,9 +173,9 @@ dependencies = [ [[package]] name = "async-global-executor" -version = "2.1.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd8b508d585e01084059b60f06ade4cb7415cd2e4084b71dd1cb44e7d3fb9880" +checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" dependencies = [ "async-channel", "async-executor", @@ -123,29 +187,47 @@ dependencies = [ ] [[package]] -name = "async-io" -version = "1.7.0" +name = "async-h1" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5e18f61464ae81cde0a23e713ae8fd299580c54d697a35820cfd0625b8b0e07" +checksum = "5d1d1dae8cb2c4258a79d6ed088b7fb9b4763bf4e9b22d040779761e046a2971" dependencies = [ - "concurrent-queue 1.2.2", + "async-channel", + "async-dup", + "async-global-executor", + "async-io", + "futures-lite", + "http-types", + "httparse", + "log", + "pin-project", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock", + "autocfg", + "cfg-if 1.0.0", + "concurrent-queue 2.2.0", "futures-lite", - "libc", "log", - "once_cell", "parking", "polling", + "rustix", "slab", "socket2", "waker-fn", - "winapi", ] [[package]] name = "async-lock" -version = "2.5.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" dependencies = [ "event-listener", ] @@ -201,6 +283,19 @@ version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9" +[[package]] +name = "async-tls" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d85a97c4a0ecce878efd3f945f119c78a646d8975340bca0398f9bb05c30cc52" +dependencies = [ + "futures-core", + "futures-io", + "rustls", + "webpki", + "webpki-roots", +] + [[package]] name = "async-trait" version = "0.1.56" @@ -265,6 +360,12 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + [[package]] name = "base64" version = "0.13.0" @@ -431,6 +532,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array 0.14.5", +] + [[package]] name = "clap" version = "3.2.4" @@ -541,6 +651,17 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "config" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3" +dependencies = [ + "lazy_static", + "nom 5.1.2", + "serde", +] + [[package]] name = "console" version = "0.15.0" @@ -562,6 +683,23 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" +[[package]] +name = "cookie" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951" +dependencies = [ + "aes-gcm", + "base64 0.13.0", + "hkdf", + "hmac", + "percent-encoding", + "rand 0.8.5", + "sha2", + "time 0.2.27", + "version_check", +] + [[package]] name = "core-foundation-sys" version = "0.8.3" @@ -590,6 +728,12 @@ dependencies = [ "libc", ] +[[package]] +name = "cpuid-bool" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" + [[package]] name = "cranelift-bforest" version = "0.86.1" @@ -724,6 +868,16 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "crypto-mac" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" +dependencies = [ + "generic-array 0.14.5", + "subtle", +] + [[package]] name = "csscolorparser" version = "0.6.2" @@ -744,6 +898,15 @@ dependencies = [ "syn 1.0.96", ] +[[package]] +name = "ctr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" +dependencies = [ + "cipher", +] + [[package]] name = "daemonize" version = "0.4.1" @@ -788,6 +951,33 @@ dependencies = [ "syn 1.0.96", ] +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if 1.0.0", + "hashbrown 0.14.1", + "lock_api", + "once_cell", + "parking_lot_core 0.9.9", +] + +[[package]] +name = "deadpool" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d126179d86aee4556e54f5f3c6bf6d9884e7cc52cef82f77ee6f90a7747616d" +dependencies = [ + "async-trait", + "config", + "crossbeam-queue", + "num_cpus", + "serde", + "tokio", +] + [[package]] name = "deltae" version = "0.3.0" @@ -1078,7 +1268,7 @@ checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.2.13", "windows-sys 0.48.0", ] @@ -1129,31 +1319,58 @@ dependencies = [ ] [[package]] -name = "futures-channel" -version = "0.3.21" +name = "futures" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", + "futures-sink", ] [[package]] name = "futures-core" -version = "0.3.21" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] [[package]] name = "futures-io" -version = "0.3.21" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-lite" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ "fastrand", "futures-core", @@ -1164,6 +1381,47 @@ dependencies = [ "waker-fn", ] +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "fuzzy-matcher" version = "0.3.7" @@ -1241,6 +1499,16 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "ghash" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375" +dependencies = [ + "opaque-debug 0.3.0", + "polyval", +] + [[package]] name = "ghost" version = "0.1.4" @@ -1303,6 +1571,12 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" + [[package]] name = "heck" version = "0.3.3" @@ -1345,6 +1619,73 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3461b968f695ca312b968503261f5a345de0f02a39dbaa3021f20d53b426395d" +[[package]] +name = "hkdf" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" +dependencies = [ + "digest 0.9.0", + "hmac", +] + +[[package]] +name = "hmac" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + +[[package]] +name = "http-client" +version = "6.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1947510dc91e2bf586ea5ffb412caad7673264e14bb39fb9078da114a94ce1a5" +dependencies = [ + "async-h1", + "async-std", + "async-tls", + "async-trait", + "cfg-if 1.0.0", + "dashmap", + "deadpool", + "futures", + "http-types", + "log", + "rustls", +] + +[[package]] +name = "http-types" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9b187a72d63adbfba487f48095306ac823049cb504ee195541e91c7775f5ad" +dependencies = [ + "anyhow", + "async-channel", + "async-std", + "base64 0.13.0", + "cookie", + "futures-lite", + "infer", + "pin-project-lite", + "rand 0.7.3", + "serde", + "serde_json", + "serde_qs", + "serde_urlencoded", + "url", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + [[package]] name = "humantime" version = "2.1.0" @@ -1420,6 +1761,12 @@ dependencies = [ "hashbrown 0.11.2", ] +[[package]] +name = "infer" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e9829a50b42bb782c1df523f78d332fe371b10c661e78b7a3c34b0198e9fac" + [[package]] name = "inotify" version = "0.9.6" @@ -1616,6 +1963,19 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72d9d1bd215936bc8647ad92986bb56f3f216550b53c44ab785e3217ae33625e" +[[package]] +name = "lexical-core" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" +dependencies = [ + "arrayvec 0.5.2", + "bitflags", + "cfg-if 1.0.0", + "ryu", + "static_assertions", +] + [[package]] name = "libc" version = "0.2.140" @@ -1662,9 +2022,9 @@ checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -1796,6 +2156,22 @@ dependencies = [ "syn 2.0.15", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1897,6 +2273,7 @@ version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" dependencies = [ + "lexical-core", "memchr", "version_check", ] @@ -2020,11 +2397,10 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl-sys" -version = "0.9.74" +version = "0.9.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835363342df5fba8354c5b453325b110ffd54044e588c539cf2f20a8014e4cb1" +checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", @@ -2091,7 +2467,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.3", + "parking_lot_core 0.9.9", ] [[package]] @@ -2103,22 +2479,22 @@ dependencies = [ "cfg-if 1.0.0", "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.13", "smallvec", "winapi", ] [[package]] name = "parking_lot_core" -version = "0.9.3" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.4.1", "smallvec", - "windows-sys 0.36.1", + "windows-targets 0.48.0", ] [[package]] @@ -2284,6 +2660,26 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -2315,6 +2711,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "polyval" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd" +dependencies = [ + "cpuid-bool", + "opaque-debug 0.3.0", + "universal-hash", +] + [[package]] name = "ppv-lite86" version = "0.2.16" @@ -2373,9 +2780,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -2588,6 +2995,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -2595,7 +3011,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom 0.2.7", - "redox_syscall", + "redox_syscall 0.2.13", "thiserror", ] @@ -2658,6 +3074,21 @@ dependencies = [ "bytecheck", ] +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + [[package]] name = "rkyv" version = "0.7.39" @@ -2744,6 +3175,19 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "rustls" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81" +dependencies = [ + "base64 0.12.3", + "log", + "ring", + "sct", + "webpki", +] + [[package]] name = "ryu" version = "1.0.10" @@ -2765,6 +3209,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "sct" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "seahash" version = "4.1.0" @@ -2862,6 +3316,29 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_qs" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" +dependencies = [ + "percent-encoding", + "serde", + "thiserror", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_yaml" version = "0.8.24" @@ -2975,6 +3452,15 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e24979f63a11545f5f2c60141afe249d4f19f84581ea2138065e400941d83d3" +[[package]] +name = "simple-mutex" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38aabbeafa6f6dead8cebf246fe9fae1f9215c8d29b3a69f93bd62a9e4a3dcd6" +dependencies = [ + "event-listener", +] + [[package]] name = "siphasher" version = "0.3.10" @@ -3026,14 +3512,20 @@ checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043" [[package]] name = "socket2" -version = "0.4.4" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "spinning" version = "0.1.0" @@ -3070,6 +3562,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "status-bar" version = "0.1.0" @@ -3184,6 +3682,12 @@ dependencies = [ "syn 1.0.96", ] +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + [[package]] name = "suggest" version = "0.4.0" @@ -3222,6 +3726,28 @@ dependencies = [ "is-terminal", ] +[[package]] +name = "surf" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718b1ae6b50351982dedff021db0def601677f2120938b070eadb10ba4038dd7" +dependencies = [ + "async-std", + "async-trait", + "cfg-if 1.0.0", + "futures-util", + "getrandom 0.2.7", + "http-client", + "http-types", + "log", + "mime_guess", + "once_cell", + "pin-project-lite", + "rustls", + "serde", + "serde_json", +] + [[package]] name = "syn" version = "1.0.96" @@ -3285,7 +3811,7 @@ dependencies = [ "cfg-if 1.0.0", "fastrand", "libc", - "redox_syscall", + "redox_syscall 0.2.13", "remove_dir_all", "winapi", ] @@ -3410,7 +3936,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fdfe0627923f7411a43ec9ec9c39c3a9b4151be313e0922042581fb6c9b717f" dependencies = [ "libc", - "redox_syscall", + "redox_syscall 0.2.13", "winapi", ] @@ -3494,6 +4020,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" +[[package]] +name = "tokio" +version = "1.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +dependencies = [ + "autocfg", + "backtrace", + "pin-project-lite", +] + [[package]] name = "toml" version = "0.5.10" @@ -3638,6 +4175,16 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array 0.14.5", + "subtle", +] + [[package]] name = "unsafe-any-ors" version = "1.0.0" @@ -3647,6 +4194,12 @@ dependencies = [ "destructure_traitobject", ] +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "url" version = "2.2.2" @@ -4199,6 +4752,25 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f20dea7535251981a9670857150d571846545088359b28e4951d350bdaf179f" +dependencies = [ + "webpki", +] + [[package]] name = "wepoll-ffi" version = "0.1.2" @@ -4309,19 +4881,6 @@ dependencies = [ "windows_x86_64_msvc 0.33.0", ] -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", -] - [[package]] name = "windows-sys" version = "0.45.0" @@ -4388,12 +4947,6 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" -[[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -4412,12 +4965,6 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" -[[package]] -name = "windows_i686_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -4436,12 +4983,6 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" -[[package]] -name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -4460,12 +5001,6 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" -[[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -4496,12 +5031,6 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" -[[package]] -name = "windows_x86_64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -4689,6 +5218,7 @@ dependencies = [ "strip-ansi-escapes", "strum", "strum_macros", + "surf", "tempfile", "termwiz", "thiserror", diff --git a/default-plugins/fixture-plugin-for-tests/src/main.rs b/default-plugins/fixture-plugin-for-tests/src/main.rs index cdbdc9f2..79783a1b 100644 --- a/default-plugins/fixture-plugin-for-tests/src/main.rs +++ b/default-plugins/fixture-plugin-for-tests/src/main.rs @@ -48,6 +48,7 @@ impl ZellijPlugin for State { PermissionType::RunCommands, PermissionType::OpenTerminalsOrPlugins, PermissionType::WriteToStdin, + PermissionType::WebAccess, ]); self.configuration = configuration; subscribe(&[ @@ -257,6 +258,22 @@ impl ZellijPlugin for State { context, ); }, + Key::Ctrl('4') => { + let mut headers = BTreeMap::new(); + let mut context = BTreeMap::new(); + let body = vec![1, 2, 3]; + headers.insert("header1".to_owned(), "value1".to_owned()); + headers.insert("header2".to_owned(), "value2".to_owned()); + context.insert("user_key_1".to_owned(), "user_value1".to_owned()); + context.insert("user_key_2".to_owned(), "user_value2".to_owned()); + web_request( + "https://example.com/foo?arg1=val1&arg2=val2", + HttpVerb::Post, + headers, + body, + context, + ); + }, _ => {}, }, Event::CustomMessage(message, payload) => { diff --git a/zellij-server/src/background_jobs.rs b/zellij-server/src/background_jobs.rs index 57e13364..3f2777b7 100644 --- a/zellij-server/src/background_jobs.rs +++ b/zellij-server/src/background_jobs.rs @@ -3,8 +3,13 @@ use zellij_utils::consts::{ session_info_cache_file_name, session_info_folder_for_session, session_layout_cache_file_name, ZELLIJ_SOCK_DIR, }; -use zellij_utils::data::{Event, SessionInfo}; +use zellij_utils::data::{Event, HttpVerb, SessionInfo}; use zellij_utils::errors::{prelude::*, BackgroundJobContext, ContextType}; +use zellij_utils::surf::{ + self, + http::{Method, Url}, + RequestBuilder, +}; use std::collections::{BTreeMap, HashMap}; use std::fs; @@ -40,6 +45,15 @@ pub enum BackgroundJob { PathBuf, BTreeMap, ), // command, args, env_variables, cwd, context + WebRequest( + PluginId, + ClientId, + String, // url + HttpVerb, + BTreeMap, // headers + Vec, // body + BTreeMap, // context + ), Exit, } @@ -57,6 +71,7 @@ impl From<&BackgroundJob> for BackgroundJobContext { BackgroundJob::ReportSessionInfo(..) => BackgroundJobContext::ReportSessionInfo, BackgroundJob::ReportLayoutInfo(..) => BackgroundJobContext::ReportLayoutInfo, BackgroundJob::RunCommand(..) => BackgroundJobContext::RunCommand, + BackgroundJob::WebRequest(..) => BackgroundJobContext::WebRequest, BackgroundJob::Exit => BackgroundJobContext::Exit, } } @@ -285,6 +300,69 @@ pub(crate) fn background_jobs_main(bus: Bus) -> Result<()> { } }); }, + BackgroundJob::WebRequest(plugin_id, client_id, url, verb, headers, body, context) => { + task::spawn({ + let senders = bus.senders.clone(); + async move { + async fn web_request( + url: String, + verb: HttpVerb, + headers: BTreeMap, + body: Vec, + ) -> Result< + (u16, BTreeMap, Vec), // status_code, headers, body + zellij_utils::surf::Error, + > { + let url = Url::parse(&url)?; + let http_method = match verb { + HttpVerb::Get => Method::Get, + HttpVerb::Post => Method::Post, + HttpVerb::Put => Method::Put, + HttpVerb::Delete => Method::Delete, + }; + let mut req = RequestBuilder::new(http_method, url); + if !body.is_empty() { + req = req.body(body); + } + for (header, value) in headers { + req = req.header(header.as_str(), value); + } + let mut res = req.await?; + let status_code = res.status(); + let headers: BTreeMap = res + .iter() + .map(|(name, value)| (name.to_string(), value.to_string())) + .collect(); + let body = res.take_body().into_bytes().await?; + Ok((status_code as u16, headers, body)) + } + + match web_request(url, verb, headers, body).await { + Ok((status, headers, body)) => { + let _ = senders.send_to_plugin(PluginInstruction::Update(vec![( + Some(plugin_id), + Some(client_id), + Event::WebRequestResult(status, headers, body, context), + )])); + }, + Err(e) => { + log::error!("Failed to send web request: {}", e); + let error_body = e.to_string().as_bytes().to_vec(); + let _ = senders.send_to_plugin(PluginInstruction::Update(vec![( + Some(plugin_id), + Some(client_id), + Event::WebRequestResult( + 400, + BTreeMap::new(), + error_body, + context, + ), + )])); + }, + } + } + }); + }, BackgroundJob::Exit => { for loading_plugin in loading_plugins.values() { loading_plugin.store(false, Ordering::SeqCst); diff --git a/zellij-server/src/plugins/unit/plugin_tests.rs b/zellij-server/src/plugins/unit/plugin_tests.rs index e866987d..d9d2229c 100644 --- a/zellij-server/src/plugins/unit/plugin_tests.rs +++ b/zellij-server/src/plugins/unit/plugin_tests.rs @@ -546,6 +546,7 @@ fn create_plugin_thread_with_background_jobs_receiver( let _ = to_screen.send(ScreenInstruction::Exit); let _ = to_server.send(ServerInstruction::KillSession); let _ = to_plugin.send(PluginInstruction::Exit); + let _ = to_background_jobs.send(BackgroundJob::Exit); let _ = plugin_thread.join(); } }; @@ -5418,3 +5419,78 @@ pub fn run_command_with_env_vars_and_cwd_plugin_command() { .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } + +#[test] +#[ignore] +pub fn web_request_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, background_jobs_receiver, screen_receiver, teardown) = + create_plugin_thread_with_background_jobs_receiver(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_background_jobs_instructions = Arc::new(Mutex::new(vec![])); + let background_jobs_thread = log_actions_in_thread!( + received_background_jobs_instructions, + BackgroundJob::WebRequest, + background_jobs_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::WebAccess, + 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, + false, + plugin_title, + run_plugin, + tab_index, + None, + client_id, + size, + )); + std::thread::sleep(std::time::Duration::from_millis(500)); + let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( + None, + Some(client_id), + Event::Key(Key::Ctrl('4')), // this triggers the enent in the fixture plugin + )])); + background_jobs_thread.join().unwrap(); // this might take a while if the cache is cold + teardown(); + let new_tab_event = received_background_jobs_instructions + .lock() + .unwrap() + .iter() + .find_map(|i| { + if let BackgroundJob::WebRequest(..) = i { + Some(i.clone()) + } else { + None + } + }) + .clone(); + assert_snapshot!(format!("{:#?}", new_tab_event)); +} 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 index beaa4adc..cc51bef2 100644 --- 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 @@ -1,6 +1,6 @@ --- source: zellij-server/src/plugins/./unit/plugin_tests.rs -assertion_line: 4864 +assertion_line: 5189 expression: "format!(\"{:#?}\", permissions)" --- Some( @@ -11,5 +11,6 @@ Some( RunCommands, OpenTerminalsOrPlugins, WriteToStdin, + WebAccess, ], ) 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 index 23fa6c22..d9a92db5 100644 --- 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 @@ -1,6 +1,6 @@ --- source: zellij-server/src/plugins/./unit/plugin_tests.rs -assertion_line: 4767 +assertion_line: 5101 expression: "format!(\"{:#?}\", new_tab_event)" --- Some( @@ -13,5 +13,6 @@ Some( RunCommands, OpenTerminalsOrPlugins, WriteToStdin, + WebAccess, ], ) diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__web_request_plugin_command.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__web_request_plugin_command.snap new file mode 100644 index 00000000..4a97e368 --- /dev/null +++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__web_request_plugin_command.snap @@ -0,0 +1,26 @@ +--- +source: zellij-server/src/plugins/./unit/plugin_tests.rs +assertion_line: 5494 +expression: "format!(\"{:#?}\", new_tab_event)" +--- +Some( + WebRequest( + 0, + 1, + "https://example.com/foo?arg1=val1&arg2=val2", + Post, + { + "header1": "value1", + "header2": "value2", + }, + [ + 1, + 2, + 3, + ], + { + "user_key_1": "user_value1", + "user_key_2": "user_value2", + }, + ), +) diff --git a/zellij-server/src/plugins/zellij_exports.rs b/zellij-server/src/plugins/zellij_exports.rs index 496c7d64..524d0a8f 100644 --- a/zellij-server/src/plugins/zellij_exports.rs +++ b/zellij-server/src/plugins/zellij_exports.rs @@ -18,7 +18,7 @@ use std::{ use wasmer::{imports, AsStoreMut, Function, FunctionEnv, FunctionEnvMut, Imports}; use wasmer_wasi::WasiEnv; use zellij_utils::data::{ - CommandType, ConnectToSession, PermissionStatus, PermissionType, PluginPermission, + CommandType, ConnectToSession, HttpVerb, PermissionStatus, PermissionType, PluginPermission, }; use zellij_utils::input::permission::PermissionCache; @@ -130,6 +130,9 @@ fn host_run_plugin_command(env: FunctionEnvMut) { PluginCommand::RunCommand(command_line, env_variables, cwd, context) => { run_command(env, command_line, env_variables, cwd, context) }, + PluginCommand::WebRequest(url, verb, headers, body, context) => { + web_request(env, url, verb, headers, body, context) + }, PluginCommand::PostMessageTo(plugin_message) => { post_message_to(env, plugin_message)? }, @@ -607,12 +610,6 @@ fn run_command( cwd: PathBuf, context: BTreeMap, ) { - let err_context = || { - format!( - "failed to execute command on host for plugin '{}'", - env.plugin_env.name() - ) - }; if command_line.is_empty() { log::error!("Command cannot be empty"); } else { @@ -632,6 +629,28 @@ fn run_command( } } +fn web_request( + env: &ForeignFunctionEnv, + url: String, + verb: HttpVerb, + headers: BTreeMap, + body: Vec, + context: BTreeMap, +) { + let _ = env + .plugin_env + .senders + .send_to_background_jobs(BackgroundJob::WebRequest( + env.plugin_env.plugin_id, + env.plugin_env.client_id, + url, + verb, + headers, + body, + context, + )); +} + fn post_message_to(env: &ForeignFunctionEnv, plugin_message: PluginMessage) -> Result<()> { let worker_name = plugin_message .worker_name @@ -1198,6 +1217,7 @@ fn check_command_permission( | PluginCommand::OpenCommandPaneInPlace(..) | PluginCommand::RunCommand(..) | PluginCommand::ExecCmd(..) => PermissionType::RunCommands, + PluginCommand::WebRequest(..) => PermissionType::WebAccess, PluginCommand::Write(..) | PluginCommand::WriteChars(..) => PermissionType::WriteToStdin, PluginCommand::SwitchTabTo(..) | PluginCommand::SwitchToMode(..) diff --git a/zellij-tile/src/shim.rs b/zellij-tile/src/shim.rs index 97065a2a..86fe6040 100644 --- a/zellij-tile/src/shim.rs +++ b/zellij-tile/src/shim.rs @@ -207,6 +207,21 @@ pub fn run_command_with_env_variables_and_cwd( unsafe { host_run_plugin_command() }; } +pub fn web_request>( + url: S, + verb: HttpVerb, + headers: BTreeMap, + body: Vec, + context: BTreeMap, +) where + S: ToString, +{ + let plugin_command = PluginCommand::WebRequest(url.to_string(), verb, headers, body, context); + let protobuf_plugin_command: ProtobufPluginCommand = plugin_command.try_into().unwrap(); + object_to_stdout(&protobuf_plugin_command.encode_to_vec()); + unsafe { host_run_plugin_command() }; +} + /// Hide the plugin pane (suppress it) from the UI pub fn hide_self() { let plugin_command = PluginCommand::HideSelf; diff --git a/zellij-utils/Cargo.toml b/zellij-utils/Cargo.toml index 55a596fb..8845b64f 100644 --- a/zellij-utils/Cargo.toml +++ b/zellij-utils/Cargo.toml @@ -54,6 +54,7 @@ interprocess = "1.2.1" async-std = { version = "1.3.0", features = ["unstable"] } notify-debouncer-full = "0.1.0" humantime = "2.1.0" +surf = { version = "2.3.2", default-features = false, features = ["h1-client-rustls"] } [dev-dependencies] insta = { version = "1.6.0", features = ["backtrace"] } diff --git a/zellij-utils/assets/prost/api.event.rs b/zellij-utils/assets/prost/api.event.rs index 9b0097a5..0f6221df 100644 --- a/zellij-utils/assets/prost/api.event.rs +++ b/zellij-utils/assets/prost/api.event.rs @@ -11,7 +11,7 @@ pub struct Event { pub name: i32, #[prost( oneof = "event::Payload", - tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14" + tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15" )] pub payload: ::core::option::Option, } @@ -46,6 +46,8 @@ pub mod event { SessionUpdatePayload(super::SessionUpdatePayload), #[prost(message, tag = "14")] RunCommandResultPayload(super::RunCommandResultPayload), + #[prost(message, tag = "15")] + WebRequestResultPayload(super::WebRequestResultPayload), } } #[allow(clippy::derive_partial_eq_without_eq)] @@ -68,6 +70,18 @@ pub struct RunCommandResultPayload { } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] +pub struct WebRequestResultPayload { + #[prost(int32, tag = "1")] + pub status: i32, + #[prost(message, repeated, tag = "2")] + pub headers: ::prost::alloc::vec::Vec
, + #[prost(bytes = "vec", tag = "3")] + pub body: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "4")] + pub context: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] pub struct ContextItem { #[prost(string, tag = "1")] pub name: ::prost::alloc::string::String, @@ -76,6 +90,14 @@ pub struct ContextItem { } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] +pub struct Header { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub value: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] pub struct PermissionRequestResultPayload { #[prost(bool, tag = "1")] pub granted: bool, @@ -287,6 +309,7 @@ pub enum EventType { PermissionRequestResult = 15, SessionUpdate = 16, RunCommandResult = 17, + WebRequestResult = 18, } impl EventType { /// String value of the enum field names used in the ProtoBuf definition. @@ -313,6 +336,7 @@ impl EventType { EventType::PermissionRequestResult => "PermissionRequestResult", EventType::SessionUpdate => "SessionUpdate", EventType::RunCommandResult => "RunCommandResult", + EventType::WebRequestResult => "WebRequestResult", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -336,6 +360,7 @@ impl EventType { "PermissionRequestResult" => Some(Self::PermissionRequestResult), "SessionUpdate" => Some(Self::SessionUpdate), "RunCommandResult" => Some(Self::RunCommandResult), + "WebRequestResult" => Some(Self::WebRequestResult), _ => None, } } diff --git a/zellij-utils/assets/prost/api.plugin_command.rs b/zellij-utils/assets/prost/api.plugin_command.rs index d8cd13c1..a97032b4 100644 --- a/zellij-utils/assets/prost/api.plugin_command.rs +++ b/zellij-utils/assets/prost/api.plugin_command.rs @@ -5,7 +5,7 @@ pub struct PluginCommand { pub name: i32, #[prost( oneof = "plugin_command::Payload", - tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43" + tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44" )] pub payload: ::core::option::Option, } @@ -98,6 +98,8 @@ pub mod plugin_command { OpenCommandPaneInPlacePayload(super::OpenCommandPanePayload), #[prost(message, tag = "43")] RunCommandPayload(super::RunCommandPayload), + #[prost(message, tag = "44")] + WebRequestPayload(super::WebRequestPayload), } } #[allow(clippy::derive_partial_eq_without_eq)] @@ -178,6 +180,20 @@ pub struct RunCommandPayload { } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] +pub struct WebRequestPayload { + #[prost(string, tag = "1")] + pub url: ::prost::alloc::string::String, + #[prost(enumeration = "HttpVerb", tag = "2")] + pub verb: i32, + #[prost(message, repeated, tag = "3")] + pub headers: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "4")] + pub body: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "5")] + pub context: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] pub struct EnvVariable { #[prost(string, tag = "1")] pub name: ::prost::alloc::string::String, @@ -294,6 +310,7 @@ pub enum CommandName { OpenCommandInPlace = 69, OpenFileInPlace = 70, RunCommand = 71, + WebRequest = 72, } impl CommandName { /// String value of the enum field names used in the ProtoBuf definition. @@ -374,6 +391,7 @@ impl CommandName { CommandName::OpenCommandInPlace => "OpenCommandInPlace", CommandName::OpenFileInPlace => "OpenFileInPlace", CommandName::RunCommand => "RunCommand", + CommandName::WebRequest => "WebRequest", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -451,6 +469,39 @@ impl CommandName { "OpenCommandInPlace" => Some(Self::OpenCommandInPlace), "OpenFileInPlace" => Some(Self::OpenFileInPlace), "RunCommand" => Some(Self::RunCommand), + "WebRequest" => Some(Self::WebRequest), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum HttpVerb { + Get = 0, + Post = 1, + Put = 2, + Delete = 3, +} +impl HttpVerb { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + HttpVerb::Get => "Get", + HttpVerb::Post => "Post", + HttpVerb::Put => "Put", + HttpVerb::Delete => "Delete", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "Get" => Some(Self::Get), + "Post" => Some(Self::Post), + "Put" => Some(Self::Put), + "Delete" => Some(Self::Delete), _ => None, } } diff --git a/zellij-utils/assets/prost/api.plugin_permission.rs b/zellij-utils/assets/prost/api.plugin_permission.rs index dfe931f8..d33fa950 100644 --- a/zellij-utils/assets/prost/api.plugin_permission.rs +++ b/zellij-utils/assets/prost/api.plugin_permission.rs @@ -7,6 +7,7 @@ pub enum PermissionType { RunCommands = 3, OpenTerminalsOrPlugins = 4, WriteToStdin = 5, + WebAccess = 6, } impl PermissionType { /// String value of the enum field names used in the ProtoBuf definition. @@ -21,6 +22,7 @@ impl PermissionType { PermissionType::RunCommands => "RunCommands", PermissionType::OpenTerminalsOrPlugins => "OpenTerminalsOrPlugins", PermissionType::WriteToStdin => "WriteToStdin", + PermissionType::WebAccess => "WebAccess", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -32,6 +34,7 @@ impl PermissionType { "RunCommands" => Some(Self::RunCommands), "OpenTerminalsOrPlugins" => Some(Self::OpenTerminalsOrPlugins), "WriteToStdin" => Some(Self::WriteToStdin), + "WebAccess" => Some(Self::WebAccess), _ => None, } } diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs index 8ff43836..91cf1308 100644 --- a/zellij-utils/src/data.rs +++ b/zellij-utils/src/data.rs @@ -497,7 +497,16 @@ pub enum Event { PermissionRequestResult(PermissionStatus), SessionUpdate(Vec), RunCommandResult(Option, Vec, Vec, BTreeMap), // exit_code, STDOUT, STDERR, - // context + // context + WebRequestResult( + u16, + BTreeMap, + Vec, + BTreeMap, + ), // status, + // headers, + // body, + // context } #[derive( @@ -524,6 +533,7 @@ pub enum Permission { RunCommands, OpenTerminalsOrPlugins, WriteToStdin, + WebAccess, } impl PermissionType { @@ -539,6 +549,7 @@ impl PermissionType { PermissionType::RunCommands => "Run commands".to_owned(), PermissionType::OpenTerminalsOrPlugins => "Start new terminals and plugins".to_owned(), PermissionType::WriteToStdin => "Write to standard input (STDIN)".to_owned(), + PermissionType::WebAccess => "Make web requests".to_owned(), } } } @@ -991,6 +1002,14 @@ impl PluginMessage { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum HttpVerb { + Get, + Post, + Put, + Delete, +} + #[derive(Debug, Clone, EnumDiscriminants, ToString)] #[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize))] #[strum_discriminants(name(CommandType))] @@ -1067,12 +1086,16 @@ pub enum PluginCommand { OpenFileInPlace(FileToOpen), OpenCommandPaneInPlace(CommandToRun), RunCommand( - Vec, - BTreeMap, - PathBuf, - BTreeMap, - ), // command, - // env_Variables, - // cwd, - // context + Vec, // command + BTreeMap, // env_variables + PathBuf, // cwd + BTreeMap, // context + ), + WebRequest( + String, // url + HttpVerb, + BTreeMap, // headers + Vec, // body + BTreeMap, // context + ), } diff --git a/zellij-utils/src/errors.rs b/zellij-utils/src/errors.rs index 2f5cfc2d..1e4eab8a 100644 --- a/zellij-utils/src/errors.rs +++ b/zellij-utils/src/errors.rs @@ -447,6 +447,7 @@ pub enum BackgroundJobContext { ReportSessionInfo, ReportLayoutInfo, RunCommand, + WebRequest, Exit, } diff --git a/zellij-utils/src/lib.rs b/zellij-utils/src/lib.rs index b301e1c6..5438f323 100644 --- a/zellij-utils/src/lib.rs +++ b/zellij-utils/src/lib.rs @@ -24,7 +24,8 @@ pub mod logging; // Requires log4rs #[cfg(not(target_family = "wasm"))] pub use ::{ anyhow, async_channel, async_std, clap, common_path, humantime, interprocess, lazy_static, - libc, miette, nix, notify_debouncer_full, regex, serde, signal_hook, tempfile, termwiz, vte, + libc, miette, nix, notify_debouncer_full, regex, serde, signal_hook, surf, tempfile, termwiz, + vte, }; pub use ::prost; diff --git a/zellij-utils/src/plugin_api/event.proto b/zellij-utils/src/plugin_api/event.proto index 1463d8f0..cfffe06f 100644 --- a/zellij-utils/src/plugin_api/event.proto +++ b/zellij-utils/src/plugin_api/event.proto @@ -41,6 +41,7 @@ enum EventType { PermissionRequestResult = 15; SessionUpdate = 16; RunCommandResult = 17; + WebRequestResult = 18; } message EventNameList { @@ -63,6 +64,7 @@ message Event { PermissionRequestResultPayload permission_request_result_payload = 12; SessionUpdatePayload session_update_payload = 13; RunCommandResultPayload run_command_result_payload = 14; + WebRequestResultPayload web_request_result_payload = 15; } } @@ -77,11 +79,23 @@ message RunCommandResultPayload { repeated ContextItem context = 4; } +message WebRequestResultPayload { + int32 status = 1; + repeated Header headers = 2; + bytes body = 3; + repeated ContextItem context = 4; +} + message ContextItem { string name = 1; string value = 2; } +message Header { + string name = 1; + string value = 2; +} + message PermissionRequestResultPayload { bool granted = 1; } diff --git a/zellij-utils/src/plugin_api/event.rs b/zellij-utils/src/plugin_api/event.rs index 6547f383..a322ec3b 100644 --- a/zellij-utils/src/plugin_api/event.rs +++ b/zellij-utils/src/plugin_api/event.rs @@ -198,6 +198,25 @@ impl TryFrom for Event { }, _ => Err("Malformed payload for the RunCommandResult Event"), }, + Some(ProtobufEventType::WebRequestResult) => match protobuf_event.payload { + Some(ProtobufEventPayload::WebRequestResultPayload(web_request_result_payload)) => { + Ok(Event::WebRequestResult( + web_request_result_payload.status as u16, + web_request_result_payload + .headers + .into_iter() + .map(|h| (h.name, h.value)) + .collect(), + web_request_result_payload.body, + web_request_result_payload + .context + .into_iter() + .map(|c_i| (c_i.name, c_i.value)) + .collect(), + )) + }, + _ => Err("Malformed payload for the WebRequestResult Event"), + }, None => Err("Unknown Protobuf Event"), } } @@ -370,6 +389,26 @@ impl TryFrom for ProtobufEvent { )), }) }, + Event::WebRequestResult(status, headers, body, context) => { + let web_request_result_payload = WebRequestResultPayload { + status: status as i32, + headers: headers + .into_iter() + .map(|(name, value)| Header { name, value }) + .collect(), + body, + context: context + .into_iter() + .map(|(name, value)| ContextItem { name, value }) + .collect(), + }; + Ok(ProtobufEvent { + name: ProtobufEventType::WebRequestResult as i32, + payload: Some(event::Payload::WebRequestResultPayload( + web_request_result_payload, + )), + }) + }, } } } @@ -816,6 +855,7 @@ impl TryFrom for EventType { ProtobufEventType::PermissionRequestResult => EventType::PermissionRequestResult, ProtobufEventType::SessionUpdate => EventType::SessionUpdate, ProtobufEventType::RunCommandResult => EventType::RunCommandResult, + ProtobufEventType::WebRequestResult => EventType::WebRequestResult, }) } } @@ -842,6 +882,7 @@ impl TryFrom for ProtobufEventType { EventType::PermissionRequestResult => ProtobufEventType::PermissionRequestResult, EventType::SessionUpdate => ProtobufEventType::SessionUpdate, EventType::RunCommandResult => ProtobufEventType::RunCommandResult, + EventType::WebRequestResult => ProtobufEventType::WebRequestResult, }) } } diff --git a/zellij-utils/src/plugin_api/plugin_command.proto b/zellij-utils/src/plugin_api/plugin_command.proto index e0b26184..93e7d6b2 100644 --- a/zellij-utils/src/plugin_api/plugin_command.proto +++ b/zellij-utils/src/plugin_api/plugin_command.proto @@ -83,6 +83,7 @@ enum CommandName { OpenCommandInPlace = 69; OpenFileInPlace = 70; RunCommand = 71; + WebRequest = 72; } message PluginCommand { @@ -130,6 +131,7 @@ message PluginCommand { OpenFilePayload open_terminal_in_place_payload = 41; OpenCommandPanePayload open_command_pane_in_place_payload = 42; RunCommandPayload run_command_payload = 43; + WebRequestPayload web_request_payload = 44; } } @@ -179,6 +181,21 @@ message RunCommandPayload { repeated ContextItem context = 4; } +message WebRequestPayload { + string url = 1; + HttpVerb verb = 2; + repeated event.Header headers = 3; + bytes body = 4; + repeated ContextItem context = 5; +} + +enum HttpVerb { + Get = 0; + Post = 1; + Put = 2; + Delete = 3; +} + message EnvVariable { string name = 1; string value = 2; diff --git a/zellij-utils/src/plugin_api/plugin_command.rs b/zellij-utils/src/plugin_api/plugin_command.rs index 181953d4..84c083ad 100644 --- a/zellij-utils/src/plugin_api/plugin_command.rs +++ b/zellij-utils/src/plugin_api/plugin_command.rs @@ -1,24 +1,47 @@ pub use super::generated_api::api::{ action::{PaneIdAndShouldFloat, SwitchToModePayload}, - event::EventNameList as ProtobufEventNameList, + event::{EventNameList as ProtobufEventNameList, Header}, input_mode::InputMode as ProtobufInputMode, plugin_command::{ plugin_command::Payload, CommandName, ContextItem, EnvVariable, ExecCmdPayload, - IdAndNewName, MovePayload, OpenCommandPanePayload, OpenFilePayload, - PluginCommand as ProtobufPluginCommand, PluginMessagePayload, + HttpVerb as ProtobufHttpVerb, IdAndNewName, MovePayload, OpenCommandPanePayload, + OpenFilePayload, PluginCommand as ProtobufPluginCommand, PluginMessagePayload, RequestPluginPermissionPayload, ResizePayload, RunCommandPayload, SetTimeoutPayload, SubscribePayload, SwitchSessionPayload, SwitchTabToPayload, UnsubscribePayload, + WebRequestPayload, }, plugin_permission::PermissionType as ProtobufPermissionType, resize::ResizeAction as ProtobufResizeAction, }; -use crate::data::{ConnectToSession, PermissionType, PluginCommand}; +use crate::data::{ConnectToSession, HttpVerb, PermissionType, PluginCommand}; use std::collections::BTreeMap; use std::convert::TryFrom; use std::path::PathBuf; +impl Into for ProtobufHttpVerb { + fn into(self) -> HttpVerb { + match self { + ProtobufHttpVerb::Get => HttpVerb::Get, + ProtobufHttpVerb::Post => HttpVerb::Post, + ProtobufHttpVerb::Put => HttpVerb::Put, + ProtobufHttpVerb::Delete => HttpVerb::Delete, + } + } +} + +impl Into for HttpVerb { + fn into(self) -> ProtobufHttpVerb { + match self { + HttpVerb::Get => ProtobufHttpVerb::Get, + HttpVerb::Post => ProtobufHttpVerb::Post, + HttpVerb::Put => ProtobufHttpVerb::Put, + HttpVerb::Delete => ProtobufHttpVerb::Delete, + } + } +} + impl TryFrom for PluginCommand { type Error = &'static str; fn try_from(protobuf_plugin_command: ProtobufPluginCommand) -> Result { @@ -577,6 +600,34 @@ impl TryFrom for PluginCommand { }, _ => Err("Mismatched payload for RunCommand"), }, + Some(CommandName::WebRequest) => match protobuf_plugin_command.payload { + Some(Payload::WebRequestPayload(web_request_payload)) => { + let context: BTreeMap = web_request_payload + .context + .into_iter() + .map(|e| (e.name, e.value)) + .collect(); + let headers: BTreeMap = web_request_payload + .headers + .into_iter() + .map(|e| (e.name, e.value)) + .collect(); + let verb = match ProtobufHttpVerb::from_i32(web_request_payload.verb) { + Some(verb) => verb.into(), + None => { + return Err("Unrecognized http verb"); + }, + }; + Ok(PluginCommand::WebRequest( + web_request_payload.url, + verb, + headers, + web_request_payload.body, + context, + )) + }, + _ => Err("Mismatched payload for WebRequest"), + }, None => Err("Unrecognized plugin command"), } } @@ -972,6 +1023,27 @@ impl TryFrom for ProtobufPluginCommand { })), }) }, + PluginCommand::WebRequest(url, verb, headers, body, context) => { + let context: Vec<_> = context + .into_iter() + .map(|(name, value)| ContextItem { name, value }) + .collect(); + let headers: Vec<_> = headers + .into_iter() + .map(|(name, value)| Header { name, value }) + .collect(); + let verb: ProtobufHttpVerb = verb.into(); + Ok(ProtobufPluginCommand { + name: CommandName::WebRequest as i32, + payload: Some(Payload::WebRequestPayload(WebRequestPayload { + url, + verb: verb as i32, + body, + headers, + context, + })), + }) + }, } } } diff --git a/zellij-utils/src/plugin_api/plugin_permission.proto b/zellij-utils/src/plugin_api/plugin_permission.proto index 50072b1b..a796c748 100644 --- a/zellij-utils/src/plugin_api/plugin_permission.proto +++ b/zellij-utils/src/plugin_api/plugin_permission.proto @@ -9,4 +9,5 @@ enum PermissionType { RunCommands = 3; OpenTerminalsOrPlugins = 4; WriteToStdin = 5; + WebAccess = 6; } diff --git a/zellij-utils/src/plugin_api/plugin_permission.rs b/zellij-utils/src/plugin_api/plugin_permission.rs index d2839fac..c9f0d49f 100644 --- a/zellij-utils/src/plugin_api/plugin_permission.rs +++ b/zellij-utils/src/plugin_api/plugin_permission.rs @@ -19,6 +19,7 @@ impl TryFrom for PermissionType { Ok(PermissionType::OpenTerminalsOrPlugins) }, ProtobufPermissionType::WriteToStdin => Ok(PermissionType::WriteToStdin), + ProtobufPermissionType::WebAccess => Ok(PermissionType::WebAccess), } } } @@ -39,6 +40,7 @@ impl TryFrom for ProtobufPermissionType { Ok(ProtobufPermissionType::OpenTerminalsOrPlugins) }, PermissionType::WriteToStdin => Ok(ProtobufPermissionType::WriteToStdin), + PermissionType::WebAccess => Ok(ProtobufPermissionType::WebAccess), } } }