refactor(core): split allowlist configuration per module (#1263)

* refactor(core): split allowlist configuration per module

* fix: build with all features

* fix(cli): run fmt

* fix(core): run fmt
This commit is contained in:
Lucas Fernandes Nogueira 2021-02-20 14:09:18 -03:00 committed by GitHub
parent 75eaaf0132
commit e0be59ea26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 729 additions and 160 deletions

View File

@ -0,0 +1,5 @@
---
"tauri": minor
---
The `allowlist` configuration now has one object per module.

View File

@ -78,7 +78,7 @@ The code for the bundler is located in `[Tauri repo root]/cli/tauri-bundler`. Bu
### Developing Tauri Core
The code for Tauri core is located in `[Tauri repo root]/tauri`. The easiest way to test your changes is to use the `[Tauri repo root]/tauri/examples/communication` app. It automatically rebuilds and uses your local codebase. Just run `yarn tauri build` or `yarn tauri dev` in the communication app directory after making changes to test them out. To use your local changes in another project, edit its `src-tauri/Cargo.toml` file so that the `tauri` key looks like `tauri = { path = "PATH", features = [ "all-api", "cli" ] }`, where `PATH` is the relative path to `[Tauri repo root]/tauri`.
The code for Tauri core is located in `[Tauri repo root]/tauri`. The easiest way to test your changes is to use the `[Tauri repo root]/tauri/examples/communication` app. It automatically rebuilds and uses your local codebase. Just run `yarn tauri build` or `yarn tauri dev` in the communication app directory after making changes to test them out. To use your local changes in another project, edit its `src-tauri/Cargo.toml` file so that the `tauri` key looks like `tauri = { path = "PATH", features = [ "api-all", "cli" ] }`, where `PATH` is the relative path to `[Tauri repo root]/tauri`.
## Financial Contribution

View File

@ -62,7 +62,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
feature: [embedded-server, all-api]
feature: [embedded-server, api-all]
steps:
- uses: actions/checkout@v2

7
cli/core/Cargo.lock generated
View File

@ -329,12 +329,6 @@ version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6"
[[package]]
name = "convert_case"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]]
name = "core-foundation"
version = "0.9.1"
@ -1999,7 +1993,6 @@ dependencies = [
"anyhow",
"clap 3.0.0-beta.2",
"colored",
"convert_case",
"json-patch",
"notify",
"once_cell",

View File

@ -21,7 +21,6 @@ serde_json = "1.0"
notify = "4.0"
shared_child = "0.3"
toml_edit = "0.2"
convert_case = "0.4"
json-patch = "0.2"
schemars = "0.8"
valico = "3.5"

View File

@ -250,6 +250,232 @@ pub struct SecurityConfig {
csp: Option<String>,
}
trait Allowlist {
fn to_features(&self) -> Vec<&str>;
}
macro_rules! check_feature {
($self:ident, $features:ident, $flag:ident, $feature_name: expr) => {
if $self.$flag {
$features.push($feature_name)
}
};
}
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct FsAllowlistConfig {
#[serde(default)]
all: bool,
#[serde(default)]
read_text_file: bool,
#[serde(default)]
read_binary_file: bool,
#[serde(default)]
write_file: bool,
#[serde(default)]
write_binary_file: bool,
#[serde(default)]
read_dir: bool,
#[serde(default)]
copy_file: bool,
#[serde(default)]
create_dir: bool,
#[serde(default)]
remove_dir: bool,
#[serde(default)]
remove_file: bool,
#[serde(default)]
rename_file: bool,
#[serde(default)]
path: bool,
}
impl Allowlist for FsAllowlistConfig {
fn to_features(&self) -> Vec<&str> {
if self.all {
vec!["fs-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, read_text_file, "fs-read-text-file");
check_feature!(self, features, read_binary_file, "fs-read-binary-file");
check_feature!(self, features, write_file, "fs-write-file");
check_feature!(self, features, write_binary_file, "fs-write-binary-file");
check_feature!(self, features, read_dir, "fs-read-dir");
check_feature!(self, features, copy_file, "fs-copy-file");
check_feature!(self, features, create_dir, "fs-create-dir");
check_feature!(self, features, remove_dir, "fs-remove-dir");
check_feature!(self, features, remove_file, "fs-remove-file");
check_feature!(self, features, rename_file, "fs-rename-file");
check_feature!(self, features, path, "fs-path");
features
}
}
}
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct WindowAllowlistConfig {
#[serde(default)]
all: bool,
#[serde(default)]
create: bool,
}
impl Allowlist for WindowAllowlistConfig {
fn to_features(&self) -> Vec<&str> {
if self.all {
vec!["window-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, create, "window-create");
features
}
}
}
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct ShellAllowlistConfig {
#[serde(default)]
all: bool,
#[serde(default)]
execute: bool,
#[serde(default)]
open: bool,
}
impl Allowlist for ShellAllowlistConfig {
fn to_features(&self) -> Vec<&str> {
if self.all {
vec!["shell-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, execute, "shell-execute");
check_feature!(self, features, open, "shell-open");
features
}
}
}
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct DialogAllowlistConfig {
#[serde(default)]
all: bool,
#[serde(default)]
open: bool,
#[serde(default)]
save: bool,
}
impl Allowlist for DialogAllowlistConfig {
fn to_features(&self) -> Vec<&str> {
if self.all {
vec!["dialog-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, open, "dialog-open");
check_feature!(self, features, save, "dialog-save");
features
}
}
}
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct HttpAllowlistConfig {
#[serde(default)]
all: bool,
#[serde(default)]
request: bool,
}
impl Allowlist for HttpAllowlistConfig {
fn to_features(&self) -> Vec<&str> {
if self.all {
vec!["http-all"]
} else {
let mut features = Vec::new();
check_feature!(self, features, request, "http-request");
features
}
}
}
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct NotificationAllowlistConfig {
#[serde(default)]
all: bool,
}
impl Allowlist for NotificationAllowlistConfig {
fn to_features(&self) -> Vec<&str> {
if self.all {
vec!["notification-all"]
} else {
vec![]
}
}
}
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct GlobalShortcutAllowlistConfig {
#[serde(default)]
all: bool,
}
impl Allowlist for GlobalShortcutAllowlistConfig {
fn to_features(&self) -> Vec<&str> {
if self.all {
vec!["global-shortcut-all"]
} else {
vec![]
}
}
}
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct AllowlistConfig {
#[serde(default)]
all: bool,
#[serde(default)]
fs: FsAllowlistConfig,
#[serde(default)]
window: WindowAllowlistConfig,
#[serde(default)]
shell: ShellAllowlistConfig,
#[serde(default)]
dialog: DialogAllowlistConfig,
#[serde(default)]
http: HttpAllowlistConfig,
#[serde(default)]
notification: NotificationAllowlistConfig,
#[serde(default)]
global_shortcut: GlobalShortcutAllowlistConfig,
}
impl Allowlist for AllowlistConfig {
fn to_features(&self) -> Vec<&str> {
if self.all {
vec!["api-all"]
} else {
let mut features = Vec::new();
features.extend(self.fs.to_features());
features.extend(self.window.to_features());
features.extend(self.shell.to_features());
features.extend(self.dialog.to_features());
features.extend(self.http.to_features());
features.extend(self.notification.to_features());
features.extend(self.global_shortcut.to_features());
features
}
}
}
/// The Tauri configuration object.
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
@ -265,10 +491,17 @@ pub struct TauriConfig {
#[serde(default)]
pub bundle: BundleConfig,
#[serde(default)]
pub allowlist: HashMap<String, bool>,
allowlist: AllowlistConfig,
pub security: Option<SecurityConfig>,
}
impl TauriConfig {
#[allow(dead_code)]
pub fn features(&self) -> Vec<&str> {
self.allowlist.to_features()
}
}
/// The Build configuration object.
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]

View File

@ -31,7 +31,47 @@
"tauri": {
"description": "The Tauri configuration.",
"default": {
"allowlist": {},
"allowlist": {
"all": false,
"dialog": {
"all": false,
"open": false,
"save": false
},
"fs": {
"all": false,
"copyFile": false,
"createDir": false,
"path": false,
"readBinaryFile": false,
"readDir": false,
"readTextFile": false,
"removeDir": false,
"removeFile": false,
"renameFile": false,
"writeBinaryFile": false,
"writeFile": false
},
"globalShortcut": {
"all": false
},
"http": {
"all": false,
"request": false
},
"notification": {
"all": false
},
"shell": {
"all": false,
"execute": false,
"open": false
},
"window": {
"all": false,
"create": false
}
},
"bundle": {
"active": false,
"category": null,
@ -76,6 +116,103 @@
},
"additionalProperties": false,
"definitions": {
"AllowlistConfig": {
"type": "object",
"properties": {
"all": {
"default": false,
"type": "boolean"
},
"dialog": {
"default": {
"all": false,
"open": false,
"save": false
},
"allOf": [
{
"$ref": "#/definitions/DialogAllowlistConfig"
}
]
},
"fs": {
"default": {
"all": false,
"copyFile": false,
"createDir": false,
"path": false,
"readBinaryFile": false,
"readDir": false,
"readTextFile": false,
"removeDir": false,
"removeFile": false,
"renameFile": false,
"writeBinaryFile": false,
"writeFile": false
},
"allOf": [
{
"$ref": "#/definitions/FsAllowlistConfig"
}
]
},
"globalShortcut": {
"default": {
"all": false
},
"allOf": [
{
"$ref": "#/definitions/GlobalShortcutAllowlistConfig"
}
]
},
"http": {
"default": {
"all": false,
"request": false
},
"allOf": [
{
"$ref": "#/definitions/HttpAllowlistConfig"
}
]
},
"notification": {
"default": {
"all": false
},
"allOf": [
{
"$ref": "#/definitions/NotificationAllowlistConfig"
}
]
},
"shell": {
"default": {
"all": false,
"execute": false,
"open": false
},
"allOf": [
{
"$ref": "#/definitions/ShellAllowlistConfig"
}
]
},
"window": {
"default": {
"all": false,
"create": false
},
"allOf": [
{
"$ref": "#/definitions/WindowAllowlistConfig"
}
]
}
},
"additionalProperties": false
},
"BuildConfig": {
"description": "The Build configuration object.",
"type": "object",
@ -524,6 +661,24 @@
},
"additionalProperties": false
},
"DialogAllowlistConfig": {
"type": "object",
"properties": {
"all": {
"default": false,
"type": "boolean"
},
"open": {
"default": false,
"type": "boolean"
},
"save": {
"default": false,
"type": "boolean"
}
},
"additionalProperties": false
},
"EmbeddedServerConfig": {
"description": "The embeddedServer configuration object.",
"type": "object",
@ -556,6 +711,94 @@
},
"additionalProperties": false
},
"FsAllowlistConfig": {
"type": "object",
"properties": {
"all": {
"default": false,
"type": "boolean"
},
"copyFile": {
"default": false,
"type": "boolean"
},
"createDir": {
"default": false,
"type": "boolean"
},
"path": {
"default": false,
"type": "boolean"
},
"readBinaryFile": {
"default": false,
"type": "boolean"
},
"readDir": {
"default": false,
"type": "boolean"
},
"readTextFile": {
"default": false,
"type": "boolean"
},
"removeDir": {
"default": false,
"type": "boolean"
},
"removeFile": {
"default": false,
"type": "boolean"
},
"renameFile": {
"default": false,
"type": "boolean"
},
"writeBinaryFile": {
"default": false,
"type": "boolean"
},
"writeFile": {
"default": false,
"type": "boolean"
}
},
"additionalProperties": false
},
"GlobalShortcutAllowlistConfig": {
"type": "object",
"properties": {
"all": {
"default": false,
"type": "boolean"
}
},
"additionalProperties": false
},
"HttpAllowlistConfig": {
"type": "object",
"properties": {
"all": {
"default": false,
"type": "boolean"
},
"request": {
"default": false,
"type": "boolean"
}
},
"additionalProperties": false
},
"NotificationAllowlistConfig": {
"type": "object",
"properties": {
"all": {
"default": false,
"type": "boolean"
}
},
"additionalProperties": false
},
"OsxConfig": {
"type": "object",
"properties": {
@ -619,16 +862,75 @@
},
"additionalProperties": false
},
"ShellAllowlistConfig": {
"type": "object",
"properties": {
"all": {
"default": false,
"type": "boolean"
},
"execute": {
"default": false,
"type": "boolean"
},
"open": {
"default": false,
"type": "boolean"
}
},
"additionalProperties": false
},
"TauriConfig": {
"description": "The Tauri configuration object.",
"type": "object",
"properties": {
"allowlist": {
"default": {},
"type": "object",
"additionalProperties": {
"type": "boolean"
}
"default": {
"all": false,
"dialog": {
"all": false,
"open": false,
"save": false
},
"fs": {
"all": false,
"copyFile": false,
"createDir": false,
"path": false,
"readBinaryFile": false,
"readDir": false,
"readTextFile": false,
"removeDir": false,
"removeFile": false,
"renameFile": false,
"writeBinaryFile": false,
"writeFile": false
},
"globalShortcut": {
"all": false
},
"http": {
"all": false,
"request": false
},
"notification": {
"all": false
},
"shell": {
"all": false,
"execute": false,
"open": false
},
"window": {
"all": false,
"create": false
}
},
"allOf": [
{
"$ref": "#/definitions/AllowlistConfig"
}
]
},
"bundle": {
"description": "The bundler configuration.",
@ -708,6 +1010,20 @@
},
"additionalProperties": false
},
"WindowAllowlistConfig": {
"type": "object",
"properties": {
"all": {
"default": false,
"type": "boolean"
},
"create": {
"default": false,
"type": "boolean"
}
},
"additionalProperties": false
},
"WindowConfig": {
"description": "The window configuration object.",
"type": "object",

View File

@ -1,6 +1,5 @@
use super::{app_paths::tauri_dir, config::ConfigHandle};
use convert_case::{Case, Casing};
use toml_edit::{Array, Document, Value};
use std::{
@ -27,21 +26,13 @@ pub fn rewrite_manifest(config: ConfigHandle) -> crate::Result<()> {
let config = config_guard.as_ref().unwrap();
if let Some(tauri) = tauri {
let mut features: Array = Default::default();
let allowlist = &config.tauri.allowlist;
if *allowlist.get("all").unwrap_or(&false) {
features.push("all-api".to_string()).unwrap();
} else {
for (feature, enabled) in allowlist.iter() {
if *enabled {
features.push(feature.to_case(Case::Kebab)).unwrap();
}
}
let allowlist_features = config.tauri.features();
let mut features = Array::default();
for feature in allowlist_features {
features.push(feature).unwrap();
}
if config.tauri.cli.is_some() {
features.push("cli".to_string()).unwrap();
features.push("cli").unwrap();
}
match tauri {

View File

@ -24,7 +24,7 @@ icon = [
serde_json = "1.0.62"
serde = "1.0"
serde_derive = "1.0"
tauri = { path = "../../../../../../../tauri", features =["all-api"]}
tauri = { path = "../../../../../../../tauri", features =["api-all"]}
[features]
embedded-server = [ "tauri/embedded-server" ]

View File

@ -15,7 +15,7 @@ edition = "2018"
exclude = [ "test/fixture/**" ]
[package.metadata.docs.rs]
features = [ "all-api" ]
features = [ "api-all" ]
[dependencies]
serde_json = "1.0"
@ -44,49 +44,52 @@ cfg_aliases = "0.1.1"
[dev-dependencies]
proptest = "0.10.1"
serde_json = "1.0"
tauri = { path = ".", features = [ "all-api" ] }
tauri = { path = ".", features = [ "api-all" ] }
serde = { version = "1.0", features = [ "derive" ] }
[features]
cli = [ "tauri-api/cli" ]
embedded-server = [ "tiny_http" ]
all-api = [ "tauri-api/notification", "tauri-api/global-shortcut" ]
api-all = [ "tauri-api/notification", "tauri-api/global-shortcut" ]
updater = [ ]
# FS
read-text-file = [ ]
read-binary-file = [ ]
write-file = [ ]
write-binary-file = [ ]
read-dir = [ ]
copy-file = [ ]
create-dir = [ ]
remove-dir = [ ]
remove-file = [ ]
rename-file = [ ]
path-api = [ ]
event = [ ]
fs-all = [ ]
fs-read-text-file = [ ]
fs-read-binary-file = [ ]
fs-write-file = [ ]
fs-write-binary-file = [ ]
fs-read-dir = [ ]
fs-copy-file = [ ]
fs-create-dir = [ ]
fs-remove-dir = [ ]
fs-remove-file = [ ]
fs-rename-file = [ ]
fs-path-api = [ ]
# window
window = [ ]
create-window = [ ]
window-all = [ ]
window-create = [ ]
#shell
execute = [ ]
open = [ ]
shell-all = [ ]
shell-execute = [ ]
shell-open = [ ]
# dialog
open-dialog = [ ]
save-dialog = [ ]
dialog-all = [ ]
dialog-open = [ ]
dialog-save = [ ]
# HTTP
http-all = [ ]
http-request = [ ]
# notification
notification = [ "tauri-api/notification" ]
notification-all = [ "tauri-api/notification" ]
# global shortcut
global-shortcut = [ "tauri-api/global-shortcut" ]
global-shortcut-all = [ "tauri-api/global-shortcut" ]
[[example]]
name = "communication"

View File

@ -5,45 +5,47 @@ fn main() {
embedded_server: { feature = "embedded-server" },
dev: { not(feature = "embedded-server") },
all_api: { feature = "all-api" },
api_all: { feature = "api-all" },
// fs
read_text_file: { any(all_api, feature = "read-text-file") },
read_binary_file: { any(all_api, feature = "read-binary-file") },
write_file: { any(all_api, feature = "write-file") },
write_binary_file: { any(all_api, feature = "write-binary-file") },
read_dir: { any(all_api, feature = "read-dir") },
copy_file: { any(all_api, feature = "copy-file") },
create_dir: { any(all_api, feature = "create_dir") },
remove_dir: { any(all_api, feature = "remove-dir") },
remove_file: { any(all_api, feature = "remove-file") },
rename_file: { any(all_api, feature = "rename-file") },
// js path api
path_api: { any(all_api, feature = "path-api") },
fs_all: { any(api_all, feature = "fs-all") },
fs_read_text_file: { any(fs_all, feature = "fs-read-text-file") },
fs_read_binary_file: { any(fs_all, feature = "fs-read-binary-file") },
fs_write_file: { any(fs_all, feature = "fs-write-file") },
fs_write_binary_file: { any(fs_all, feature = "fs-write-binary-file") },
fs_read_dir: { any(fs_all, feature = "fs-read-dir") },
fs_copy_file: { any(fs_all, feature = "fs-copy-file") },
fs_create_dir: { any(fs_all, feature = "fs-create_dir") },
fs_remove_dir: { any(fs_all, feature = "fs-remove-dir") },
fs_remove_file: { any(fs_all, feature = "fs-remove-file") },
fs_rename_file: { any(fs_all, feature = "fs-rename-file") },
fs_path: { any(fs_all, feature = "fs-path") },
// window
window: { any(all_api, feature = "window") },
create_window: { any(all_api, feature = "create-window") },
window_all: { any(api_all, feature = "window-all") },
window_create: { any(window_all, feature = "window-create") },
// shell
open: { any(all_api, feature = "open") },
execute: { any(all_api, feature = "execute") },
shell_all: { any(api_all, feature = "shell-all") },
shell_open: { any(shell_all, feature = "shell-open") },
shell_execute: { any(shell_all, feature = "shell-execute") },
// dialog
open_dialog: { any(all_api, feature = "open-dialog") },
save_dialog: { any(all_api, feature = "save-dialog") },
dialog_all: { any(api_all, feature = "dialog-all") },
dialog_open: { any(dialog_all, feature = "dialog-open") },
dialog_save: { any(dialog_all, feature = "dialog-save") },
// http
http_request: { any(all_api, feature = "http-request") },
http_all: { any(api_all, feature = "http-all") },
http_request: { any(http_all, feature = "http-request") },
// cli
cli: { feature = "cli" },
// notification
notification: { any(all_api, feature = "notification") },
notification_all: { any(api_all, feature = "notification-all") },
// global shortcut
global_shortcut: { any(all_api, feature = "global_shortcut" )},
global_shortcut_all: { any(api_all, feature = "global_shortcut-all") },
}
}

View File

@ -14,7 +14,7 @@ build = "src/build.rs"
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = [ "derive" ] }
tauri = { path = "../../..", features =["all-api", "cli"]}
tauri = { path = "../../..", features =["api-all", "cli"]}
[target."cfg(windows)".build-dependencies]
winres = "0.1"

View File

@ -24,7 +24,7 @@ icon = [
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = [ "derive" ] }
tauri = { path = "../../..", features =["all-api", "cli"]}
tauri = { path = "../../..", features =["api-all", "cli"]}
[target."cfg(windows)".build-dependencies]
winres = "0.1"

View File

@ -22,7 +22,7 @@ icon = [
]
[dependencies]
tauri = { path = "../../..", features =["all-api"]}
tauri = { path = "../../..", features =["api-all"]}
[target."cfg(windows)".build-dependencies]
winres = "0.1"

View File

@ -3,12 +3,9 @@ mod dialog;
mod event;
#[allow(unused_imports)]
mod file_system;
#[cfg(global_shortcut)]
mod global_shortcut;
#[cfg(http_request)]
mod http;
mod internal;
#[cfg(notification)]
mod notification;
mod shell;
mod window;

View File

@ -1,5 +1,7 @@
#[cfg(any(dialog_open, dialog_save))]
use crate::api::dialog::FileDialogBuilder;
use crate::{
api::dialog::{ask as ask_dialog, message as message_dialog, AskResponse, FileDialogBuilder},
api::dialog::{ask as ask_dialog, message as message_dialog, AskResponse},
app::InvokeResponse,
};
use serde::Deserialize;
@ -66,16 +68,16 @@ impl Cmd {
pub async fn run(self) -> crate::Result<InvokeResponse> {
match self {
Self::OpenDialog { options } => {
#[cfg(open_dialog)]
#[cfg(dialog_open)]
return open(options);
#[cfg(not(open_dialog))]
Err(crate::Error::ApiNotAllowlisted("title".to_string()));
#[cfg(not(dialog_open))]
return Err(crate::Error::ApiNotAllowlisted("dialog > open".to_string()));
}
Self::SaveDialog { options } => {
#[cfg(save_dialog)]
#[cfg(dialog_save)]
return save(options);
#[cfg(not(save_dialog))]
Err(crate::Error::ApiNotAllowlisted("saveDialog".to_string()));
#[cfg(not(dialog_save))]
return Err(crate::Error::ApiNotAllowlisted("dialog > save".to_string()));
}
Self::MessageDialog { message } => {
let exe = std::env::current_exe()?;
@ -106,7 +108,7 @@ impl Cmd {
}
/// Shows an open dialog.
#[cfg(open_dialog)]
#[cfg(dialog_open)]
pub fn open(options: OpenDialogOptions) -> crate::Result<InvokeResponse> {
let mut dialog_builder = FileDialogBuilder::new();
if let Some(default_path) = options.default_path {
@ -127,7 +129,7 @@ pub fn open(options: OpenDialogOptions) -> crate::Result<InvokeResponse> {
}
/// Shows a save dialog.
#[cfg(save_dialog)]
#[cfg(dialog_save)]
pub fn save(options: SaveDialogOptions) -> crate::Result<InvokeResponse> {
let mut dialog_builder = FileDialogBuilder::new();
if let Some(default_path) = options.default_path {

View File

@ -94,15 +94,17 @@ impl Cmd {
pub async fn run(self) -> crate::Result<InvokeResponse> {
match self {
Self::ReadTextFile { path, options } => {
#[cfg(read_text_file)]
#[cfg(fs_read_text_file)]
return read_text_file(path, options).await.map(Into::into);
#[cfg(not(read_text_file))]
Err(crate::Error::ApiNotAllowlisted("readTextFile".to_string()))
#[cfg(not(fs_read_text_file))]
Err(crate::Error::ApiNotAllowlisted(
"fs > readTextFile".to_string(),
))
}
Self::ReadBinaryFile { path, options } => {
#[cfg(read_binary_file)]
#[cfg(fs_read_binary_file)]
return read_binary_file(path, options).await.map(Into::into);
#[cfg(not(read_binary_file))]
#[cfg(not(fs_read_binary_file))]
Err(crate::Error::ApiNotAllowlisted(
"readBinaryFile".to_string(),
))
@ -112,85 +114,95 @@ impl Cmd {
contents,
options,
} => {
#[cfg(write_file)]
#[cfg(fs_write_file)]
return write_file(path, contents, options).await.map(Into::into);
#[cfg(not(write_file))]
Err(crate::Error::ApiNotAllowlisted("writeFile".to_string()))
#[cfg(not(fs_write_file))]
Err(crate::Error::ApiNotAllowlisted(
"fs > writeFile".to_string(),
))
}
Self::WriteBinaryFile {
path,
contents,
options,
} => {
#[cfg(write_binary_file)]
#[cfg(fs_write_binary_file)]
return write_binary_file(path, contents, options)
.await
.map(Into::into);
#[cfg(not(write_binary_file))]
#[cfg(not(fs_write_binary_file))]
Err(crate::Error::ApiNotAllowlisted(
"writeBinaryFile".to_string(),
))
}
Self::ReadDir { path, options } => {
#[cfg(read_dir)]
#[cfg(fs_read_dir)]
return read_dir(path, options).await.map(Into::into);
#[cfg(not(read_dir))]
Err(crate::Error::ApiNotAllowlisted("readDir".to_string()))
#[cfg(not(fs_read_dir))]
Err(crate::Error::ApiNotAllowlisted("fs > readDir".to_string()))
}
Self::CopyFile {
source,
destination,
options,
} => {
#[cfg(copy_file)]
#[cfg(fs_copy_file)]
return copy_file(source, destination, options)
.await
.map(Into::into);
#[cfg(not(copy_file))]
Err(crate::Error::ApiNotAllowlisted("copyFile".to_string()))
#[cfg(not(fs_copy_file))]
Err(crate::Error::ApiNotAllowlisted("fs > copyFile".to_string()))
}
Self::CreateDir { path, options } => {
#[cfg(create_dir)]
#[cfg(fs_create_dir)]
return create_dir(path, options).await.map(Into::into);
#[cfg(not(create_dir))]
Err(crate::Error::ApiNotAllowlisted("createDir".to_string()))
#[cfg(not(fs_create_dir))]
Err(crate::Error::ApiNotAllowlisted(
"fs > createDir".to_string(),
))
}
Self::RemoveDir { path, options } => {
#[cfg(remove_dir)]
#[cfg(fs_remove_dir)]
return remove_dir(path, options).await.map(Into::into);
#[cfg(not(remove_dir))]
Err(crate::Error::ApiNotAllowlisted("removeDir".to_string()))
#[cfg(not(fs_remove_dir))]
Err(crate::Error::ApiNotAllowlisted(
"fs > removeDir".to_string(),
))
}
Self::RemoveFile { path, options } => {
#[cfg(remove_file)]
#[cfg(fs_remove_file)]
return remove_file(path, options).await.map(Into::into);
#[cfg(not(remove_file))]
Err(crate::Error::ApiNotAllowlisted("removeFile".to_string()))
#[cfg(not(fs_remove_file))]
Err(crate::Error::ApiNotAllowlisted(
"fs > removeFile".to_string(),
))
}
Self::RenameFile {
old_path,
new_path,
options,
} => {
#[cfg(rename_file)]
#[cfg(fs_rename_file)]
return rename_file(old_path, new_path, options)
.await
.map(Into::into);
#[cfg(not(rename_file))]
Err(crate::Error::ApiNotAllowlisted("renameFile".to_string()))
#[cfg(not(fs_rename_file))]
Err(crate::Error::ApiNotAllowlisted(
"fs > renameFile".to_string(),
))
}
Self::ResolvePath { path, directory } => {
#[cfg(path_api)]
#[cfg(fs_path)]
return resolve_path_handler(path, directory).await.map(Into::into);
#[cfg(not(path_api))]
Err(crate::Error::ApiNotAllowlisted("pathApi".to_string()))
#[cfg(not(fs_path))]
Err(crate::Error::ApiNotAllowlisted("fs > pathApi".to_string()))
}
}
}
}
/// Reads a directory.
#[cfg(read_dir)]
#[cfg(fs_read_dir)]
pub async fn read_dir(
path: PathBuf,
options: Option<DirOperationOptions>,
@ -204,7 +216,7 @@ pub async fn read_dir(
}
/// Copies a file.
#[cfg(copy_file)]
#[cfg(fs_copy_file)]
pub async fn copy_file(
source: PathBuf,
destination: PathBuf,
@ -222,7 +234,7 @@ pub async fn copy_file(
}
/// Creates a directory.
#[cfg(create_dir)]
#[cfg(fs_create_dir)]
pub async fn create_dir(path: PathBuf, options: Option<DirOperationOptions>) -> crate::Result<()> {
let (recursive, dir) = if let Some(options_value) = options {
(options_value.recursive, options_value.dir)
@ -240,7 +252,7 @@ pub async fn create_dir(path: PathBuf, options: Option<DirOperationOptions>) ->
}
/// Removes a directory.
#[cfg(remove_dir)]
#[cfg(fs_remove_dir)]
pub async fn remove_dir(path: PathBuf, options: Option<DirOperationOptions>) -> crate::Result<()> {
let (recursive, dir) = if let Some(options_value) = options {
(options_value.recursive, options_value.dir)
@ -258,7 +270,7 @@ pub async fn remove_dir(path: PathBuf, options: Option<DirOperationOptions>) ->
}
/// Removes a file
#[cfg(remove_file)]
#[cfg(fs_remove_file)]
pub async fn remove_file(
path: PathBuf,
options: Option<FileOperationOptions>,
@ -269,7 +281,7 @@ pub async fn remove_file(
}
/// Renames a file.
#[cfg(rename_file)]
#[cfg(fs_rename_file)]
pub async fn rename_file(
old_path: PathBuf,
new_path: PathBuf,
@ -286,7 +298,7 @@ pub async fn rename_file(
}
/// Writes a text file.
#[cfg(write_file)]
#[cfg(fs_write_file)]
pub async fn write_file(
path: PathBuf,
contents: String,
@ -299,7 +311,7 @@ pub async fn write_file(
}
/// Writes a binary file.
#[cfg(write_binary_file)]
#[cfg(fs_write_binary_file)]
pub async fn write_binary_file(
path: PathBuf,
contents: String,
@ -316,7 +328,7 @@ pub async fn write_binary_file(
}
/// Reads a text file.
#[cfg(read_text_file)]
#[cfg(fs_read_text_file)]
pub async fn read_text_file(
path: PathBuf,
options: Option<FileOperationOptions>,
@ -326,7 +338,7 @@ pub async fn read_text_file(
}
/// Reads a binary file.
#[cfg(read_binary_file)]
#[cfg(fs_read_binary_file)]
pub async fn read_binary_file(
path: PathBuf,
options: Option<FileOperationOptions>,
@ -335,6 +347,7 @@ pub async fn read_binary_file(
.map_err(crate::Error::FailedToExecuteApi)
}
#[cfg(fs_path)]
pub async fn resolve_path_handler(
path: String,
directory: Option<BaseDirectory>,

View File

@ -1,5 +1,6 @@
#[cfg(global_shortcut_all)]
use crate::api::shortcuts::ShortcutManager;
use crate::{
api::shortcuts::ShortcutManager,
app::{InvokeResponse, WebviewDispatcher},
async_runtime::Mutex,
};
@ -8,8 +9,10 @@ use serde::Deserialize;
use std::sync::Arc;
#[cfg(global_shortcut_all)]
type ShortcutManagerHandle = Arc<Mutex<ShortcutManager>>;
#[cfg(global_shortcut_all)]
pub fn manager_handle() -> &'static ShortcutManagerHandle {
static MANAGER: Lazy<ShortcutManagerHandle> = Lazy::new(Default::default);
&MANAGER
@ -34,6 +37,7 @@ pub enum Cmd {
IsRegistered { shortcut: String },
}
#[cfg(global_shortcut_all)]
fn register_shortcut<A: crate::ApplicationDispatcherExt + 'static>(
dispatcher: WebviewDispatcher<A>,
manager: &mut ShortcutManager,
@ -55,11 +59,11 @@ impl Cmd {
self,
webview_manager: &crate::WebviewManager<A>,
) -> crate::Result<InvokeResponse> {
#[cfg(not(global_shortcut))]
#[cfg(not(global_shortcut_all))]
return Err(crate::Error::ApiNotAllowlisted(
"globalShortcut".to_string(),
"globalShortcut > all".to_string(),
));
#[cfg(global_shortcut)]
#[cfg(global_shortcut_all)]
match self {
Self::Register { shortcut, handler } => {
let dispatcher = webview_manager.current_webview().await?.clone();

View File

@ -48,13 +48,16 @@ impl Cmd {
#[cfg(http_request)]
return make_request(client, *options).await.map(Into::into);
#[cfg(not(http_request))]
Err(crate::Error::ApiNotAllowlisted("httpRequest".to_string()))
Err(crate::Error::ApiNotAllowlisted(
"http > request".to_string(),
))
}
}
}
}
/// Makes an HTTP request and resolves the response to the webview
#[cfg(http_request)]
pub async fn make_request(
client_id: ClientId,
options: HttpRequestBuilder,

View File

@ -1,5 +1,6 @@
use crate::app::InvokeResponse;
use serde::Deserialize;
#[cfg(notification_all)]
use tauri_api::{config::Config, notification::Notification};
/// The options for the notification API.
@ -29,27 +30,28 @@ impl Cmd {
pub async fn run(self, context: &crate::app::Context) -> crate::Result<InvokeResponse> {
match self {
Self::Notification { options } => {
#[cfg(notification)]
#[cfg(notification_all)]
return send(options, &context.config).await.map(Into::into);
#[cfg(not(notification))]
#[cfg(not(notification_all))]
Err(crate::Error::ApiNotAllowlisted("notification".to_string()))
}
Self::IsNotificationPermissionGranted => {
#[cfg(notification)]
#[cfg(notification_all)]
return is_permission_granted().await.map(Into::into);
#[cfg(not(notification))]
#[cfg(not(notification_all))]
Err(crate::Error::ApiNotAllowlisted("notification".to_string()))
}
Self::RequestNotificationPermission => {
#[cfg(notification)]
#[cfg(notification_all)]
return request_permission().map(Into::into);
#[cfg(not(notification))]
#[cfg(not(notification_all))]
Err(crate::Error::ApiNotAllowlisted("notification".to_string()))
}
}
}
}
#[cfg(notification_all)]
pub async fn send(options: NotificationOptions, config: &Config) -> crate::Result<InvokeResponse> {
let identifier = config.tauri.bundle.identifier.clone();
let mut notification = Notification::new(identifier).title(options.title);
@ -63,6 +65,7 @@ pub async fn send(options: NotificationOptions, config: &Config) -> crate::Resul
Ok(().into())
}
#[cfg(notification_all)]
pub async fn is_permission_granted() -> crate::Result<InvokeResponse> {
let settings = crate::settings::read_settings()?;
if let Some(allow_notification) = settings.allow_notification {
@ -72,6 +75,7 @@ pub async fn is_permission_granted() -> crate::Result<InvokeResponse> {
}
}
#[cfg(notification_all)]
pub fn request_permission() -> crate::Result<String> {
let mut settings = crate::settings::read_settings()?;
let granted = "granted".to_string();

View File

@ -18,28 +18,30 @@ impl Cmd {
command: _,
args: _,
} => {
#[cfg(execute)]
#[cfg(shell_execute)]
{
//TODO
Ok(().into())
}
#[cfg(not(execute))]
Err(crate::Error::ApiNotAllowlisted("execute".to_string()))
#[cfg(not(shell_execute))]
Err(crate::Error::ApiNotAllowlisted(
"shell > execute".to_string(),
))
}
Self::Open { uri } => {
#[cfg(open)]
#[cfg(shell_open)]
{
open_browser(uri);
Ok(().into())
}
#[cfg(not(open))]
Err(crate::Error::ApiNotAllowlisted("open".to_string()))
#[cfg(not(shell_open))]
Err(crate::Error::ApiNotAllowlisted("shell > open".to_string()))
}
}
}
}
#[cfg(open)]
#[cfg(shell_open)]
pub fn open_browser(uri: String) {
#[cfg(test)]
assert!(uri.contains("http://"));
@ -53,7 +55,7 @@ mod test {
use proptest::prelude::*;
// Test the open func to see if proper uris can be opened by the browser.
proptest! {
#[cfg(open)]
#[cfg(shell_open)]
#[test]
fn check_open(uri in r"(http://)([\\w\\d\\.]+([\\w]{2,6})?)") {
super::open_browser(uri);

View File

@ -84,7 +84,7 @@ pub enum Cmd {
},
}
#[cfg(create_window)]
#[cfg(window_create)]
#[derive(Clone, serde::Serialize)]
struct WindowCreatedEvent {
label: String,
@ -95,15 +95,17 @@ impl Cmd {
self,
webview_manager: &crate::WebviewManager<A>,
) -> crate::Result<InvokeResponse> {
if cfg!(not(window)) {
Err(crate::Error::ApiNotAllowlisted("setTitle".to_string()))
if cfg!(not(window_all)) {
Err(crate::Error::ApiNotAllowlisted("window > all".to_string()))
} else {
let current_webview = webview_manager.current_webview().await?;
match self {
Self::CreateWebview { options } => {
#[cfg(not(create_window))]
return Err(crate::Error::ApiNotAllowlisted("createWindow".to_string()));
#[cfg(create_window)]
#[cfg(not(window_create))]
return Err(crate::Error::ApiNotAllowlisted(
"window > create".to_string(),
));
#[cfg(window_create)]
{
let label = options.label.to_string();
webview_manager

View File

@ -13,7 +13,7 @@ use tauri_api::{
#[derive(Default, Deserialize, Serialize)]
pub struct Settings {
/// Whether the user allows notifications or not.
#[cfg(notification)]
#[cfg(notification_all)]
pub allow_notification: Option<bool>,
}