diff --git a/.changes/permission-platforms.md b/.changes/permission-platforms.md new file mode 100644 index 000000000..575e8b48a --- /dev/null +++ b/.changes/permission-platforms.md @@ -0,0 +1,6 @@ +--- +"tauri": patch:feat +"tauri-utils": patch:feat +--- + +Allow defining a permission that only applies to a set of target platforms via the `platforms` configuration option. diff --git a/core/tauri-config-schema/schema.json b/core/tauri-config-schema/schema.json index 87d9e0bb8..58dd4b891 100644 --- a/core/tauri-config-schema/schema.json +++ b/core/tauri-config-schema/schema.json @@ -1123,7 +1123,7 @@ } }, "platforms": { - "description": "Target platforms this capability applies. By default all platforms applies.", + "description": "Target platforms this capability applies. By default all platforms are affected by this capability.", "default": [ "linux", "macOS", diff --git a/core/tauri-utils/src/acl/capability.rs b/core/tauri-utils/src/acl/capability.rs index 927a317c9..aa6e8d9e6 100644 --- a/core/tauri-utils/src/acl/capability.rs +++ b/core/tauri-utils/src/acl/capability.rs @@ -74,7 +74,7 @@ pub struct Capability { pub webviews: Vec, /// List of permissions attached to this capability. Must include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. pub permissions: Vec, - /// Target platforms this capability applies. By default all platforms applies. + /// Target platforms this capability applies. By default all platforms are affected by this capability. #[serde(default = "default_platforms", skip_serializing_if = "Vec::is_empty")] pub platforms: Vec, } diff --git a/core/tauri-utils/src/acl/mod.rs b/core/tauri-utils/src/acl/mod.rs index d5204dad8..63bf917c2 100644 --- a/core/tauri-utils/src/acl/mod.rs +++ b/core/tauri-utils/src/acl/mod.rs @@ -9,6 +9,8 @@ use serde::{Deserialize, Serialize}; use std::num::NonZeroU64; use thiserror::Error; +use crate::platform::Target; + pub use self::{identifier::*, value::*}; /// Known filename of the permission schema JSON file @@ -172,6 +174,20 @@ pub struct Permission { /// Allowed or denied scoped when using this permission. #[serde(default, skip_serializing_if = "Scopes::is_empty")] pub scope: Scopes, + + /// Target platforms this permission applies. By default all platforms are affected by this permission. + #[serde(default = "default_platforms", skip_serializing_if = "Vec::is_empty")] + pub platforms: Vec, +} + +fn default_platforms() -> Vec { + vec![ + Target::Linux, + Target::MacOS, + Target::Windows, + Target::Android, + Target::Ios, + ] } /// A set of direct permissions grouped together under a new name. @@ -252,6 +268,8 @@ mod build_ { let description = opt_str_lit(self.description.as_ref()); let commands = &self.commands; let scope = &self.scope; + let platforms = vec_lit(&self.platforms, identity); + literal_struct!( tokens, ::tauri::utils::acl::Permission, @@ -259,7 +277,8 @@ mod build_ { identifier, description, commands, - scope + scope, + platforms ) } } diff --git a/core/tauri-utils/src/acl/resolved.rs b/core/tauri-utils/src/acl/resolved.rs index 845d436e4..1f6f9812d 100644 --- a/core/tauri-utils/src/acl/resolved.rs +++ b/core/tauri-utils/src/acl/resolved.rs @@ -112,6 +112,7 @@ impl Resolved { with_resolved_permissions( capability, acl, + target, |ResolvedPermission { key, permission_name, @@ -273,6 +274,7 @@ struct ResolvedPermission<'a> { fn with_resolved_permissions)>( capability: &Capability, acl: &BTreeMap, + target: Target, mut f: F, ) -> Result<(), Error> { for permission_entry in &capability.permissions { @@ -281,7 +283,10 @@ fn with_resolved_permissions)>( let key = permission_id.get_prefix().unwrap_or(APP_ACL_KEY); - let permissions = get_permissions(key, permission_name, acl)?; + let permissions = get_permissions(key, permission_name, acl)? + .into_iter() + .filter(|p| p.platforms.contains(&target)) + .collect::>(); let mut resolved_scope = Scopes::default(); let mut commands = Commands::default(); diff --git a/core/tests/acl/fixtures/capabilities/platform-specific-permissions/cap.toml b/core/tests/acl/fixtures/capabilities/platform-specific-permissions/cap.toml new file mode 100644 index 000000000..f39ea410e --- /dev/null +++ b/core/tests/acl/fixtures/capabilities/platform-specific-permissions/cap.toml @@ -0,0 +1,9 @@ +identifier = "run-app" +description = "app capability" +windows = ["main"] +permissions = [ + "os:allow-apt-linux", + "os:allow-library-folder-macos", + "os:deny-webview-folder-windows", + "os:open-browser", +] diff --git a/core/tests/acl/fixtures/capabilities/platform-specific-permissions/required-plugins.json b/core/tests/acl/fixtures/capabilities/platform-specific-permissions/required-plugins.json new file mode 100644 index 000000000..349a761c0 --- /dev/null +++ b/core/tests/acl/fixtures/capabilities/platform-specific-permissions/required-plugins.json @@ -0,0 +1 @@ +["os"] diff --git a/core/tests/acl/fixtures/plugins/os/linux.toml b/core/tests/acl/fixtures/plugins/os/linux.toml new file mode 100644 index 000000000..89787e121 --- /dev/null +++ b/core/tests/acl/fixtures/plugins/os/linux.toml @@ -0,0 +1,7 @@ +[[permission]] +identifier = "allow-apt-linux" +platforms = ["linux"] +description = "Allows spawning the apt command on Linux" +commands.allow = ["spawn"] +[[permission.scope.allow]] +command = "apt" diff --git a/core/tests/acl/fixtures/plugins/os/macos.toml b/core/tests/acl/fixtures/plugins/os/macos.toml new file mode 100644 index 000000000..1b59b0b1b --- /dev/null +++ b/core/tests/acl/fixtures/plugins/os/macos.toml @@ -0,0 +1,7 @@ + +[[permission]] +identifier = "allow-library-folder-macos" +platforms = ["macOS"] +description = "Allows access to the $HOME/Library folder on maOS" +[[permission.scope.allow]] +path = "$HOME/Library/**" diff --git a/core/tests/acl/fixtures/plugins/os/open-browser.toml b/core/tests/acl/fixtures/plugins/os/open-browser.toml new file mode 100644 index 000000000..b72436924 --- /dev/null +++ b/core/tests/acl/fixtures/plugins/os/open-browser.toml @@ -0,0 +1,28 @@ +[[permission]] +identifier = "allow-servo-linux" +platforms = ["linux"] +description = "Allows starting servo on Linux" +commands.allow = ["spawn"] +[[permission.scope.allow]] +command = "servo" + +[[permission]] +identifier = "allow-edge-windows" +platforms = ["windows"] +description = "Allows starting edge on Windows" +commands.allow = ["spawn"] +[[permission.scope.allow]] +command = "edge" + +[[permission]] +identifier = "allow-safari-macos" +platforms = ["macOS"] +description = "Allows starting safari on macOS" +commands.allow = ["spawn"] +[[permission.scope.allow]] +command = "safari" + +[[set]] +identifier = "open-browser" +description = "allows opening a URL on the platform browser" +permissions = ["allow-servo-linux", "allow-edge-windows", "allow-safari-macos"] diff --git a/core/tests/acl/fixtures/plugins/os/windows.toml b/core/tests/acl/fixtures/plugins/os/windows.toml new file mode 100644 index 000000000..577b0c967 --- /dev/null +++ b/core/tests/acl/fixtures/plugins/os/windows.toml @@ -0,0 +1,6 @@ +[[permission]] +identifier = "deny-webview-folder-windows" +platforms = ["windows"] +description = "Denies access to the webview folder on Windows" +[[permission.scope.deny]] +path = "$APP/EBWebView/**" diff --git a/core/tests/acl/fixtures/snapshots/linux/acl_tests__tests__platform-specific-permissions.snap b/core/tests/acl/fixtures/snapshots/linux/acl_tests__tests__platform-specific-permissions.snap new file mode 100644 index 000000000..e6d2be783 --- /dev/null +++ b/core/tests/acl/fixtures/snapshots/linux/acl_tests__tests__platform-specific-permissions.snap @@ -0,0 +1,65 @@ +--- +source: core/tests/acl/src/lib.rs +expression: resolved +--- +Resolved { + allowed_commands: { + CommandKey { + name: "plugin:os|spawn", + context: Local, + }: ResolvedCommand { + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope: Some( + 8031926490300119127, + ), + }, + }, + denied_commands: {}, + command_scope: { + 8031926490300119127: ResolvedScope { + allow: [ + Map( + { + "command": String( + "apt", + ), + }, + ), + Map( + { + "command": String( + "servo", + ), + }, + ), + ], + deny: [], + }, + }, + global_scope: { + "os": ResolvedScope { + allow: [], + deny: [], + }, + }, +} diff --git a/core/tests/acl/fixtures/snapshots/macOS/acl_tests__tests__platform-specific-permissions.snap b/core/tests/acl/fixtures/snapshots/macOS/acl_tests__tests__platform-specific-permissions.snap new file mode 100644 index 000000000..4b053c850 --- /dev/null +++ b/core/tests/acl/fixtures/snapshots/macOS/acl_tests__tests__platform-specific-permissions.snap @@ -0,0 +1,66 @@ +--- +source: core/tests/acl/src/lib.rs +expression: resolved +--- +Resolved { + allowed_commands: { + CommandKey { + name: "plugin:os|spawn", + context: Local, + }: ResolvedCommand { + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope: Some( + 7912899488978770657, + ), + }, + }, + denied_commands: {}, + command_scope: { + 7912899488978770657: ResolvedScope { + allow: [ + Map( + { + "command": String( + "safari", + ), + }, + ), + ], + deny: [], + }, + }, + global_scope: { + "os": ResolvedScope { + allow: [ + Map( + { + "path": String( + "$HOME/Library/**", + ), + }, + ), + ], + deny: [], + }, + }, +} diff --git a/core/tests/acl/fixtures/snapshots/windows/acl_tests__tests__platform-specific-permissions.snap b/core/tests/acl/fixtures/snapshots/windows/acl_tests__tests__platform-specific-permissions.snap new file mode 100644 index 000000000..a1780ba3e --- /dev/null +++ b/core/tests/acl/fixtures/snapshots/windows/acl_tests__tests__platform-specific-permissions.snap @@ -0,0 +1,66 @@ +--- +source: core/tests/acl/src/lib.rs +expression: resolved +--- +Resolved { + allowed_commands: { + CommandKey { + name: "plugin:os|spawn", + context: Local, + }: ResolvedCommand { + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope: Some( + 7912899488978770657, + ), + }, + }, + denied_commands: {}, + command_scope: { + 7912899488978770657: ResolvedScope { + allow: [ + Map( + { + "command": String( + "edge", + ), + }, + ), + ], + deny: [], + }, + }, + global_scope: { + "os": ResolvedScope { + allow: [], + deny: [ + Map( + { + "path": String( + "$APP/EBWebView/**", + ), + }, + ), + ], + }, + }, +} diff --git a/core/tests/acl/src/lib.rs b/core/tests/acl/src/lib.rs index 4eb6fff22..9eef40983 100644 --- a/core/tests/acl/src/lib.rs +++ b/core/tests/acl/src/lib.rs @@ -41,14 +41,21 @@ mod tests { #[test] fn resolve_acl() { - let mut settings = insta::Settings::clone_current(); - settings.set_snapshot_path("../fixtures/snapshots"); - let _guard = settings.bind_to_scope(); - let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); let fixtures_path = manifest_dir.join("fixtures").join("capabilities"); for fixture_path in read_dir(fixtures_path).expect("failed to read fixtures") { let fixture_entry = fixture_path.expect("failed to read fixture entry"); + + let mut settings = insta::Settings::clone_current(); + settings.set_snapshot_path( + if fixture_entry.path().file_name().unwrap() == "platform-specific-permissions" { + Path::new("../fixtures/snapshots").join(Target::current().to_string()) + } else { + Path::new("../fixtures/snapshots").to_path_buf() + }, + ); + let _guard = settings.bind_to_scope(); + let fixture_plugins_str = read_to_string(fixture_entry.path().join("required-plugins.json")) .expect("failed to read fixture required-plugins.json file"); let fixture_plugins: Vec = serde_json::from_str(&fixture_plugins_str) diff --git a/examples/api/src-tauri/tauri-plugin-sample/permissions/schemas/schema.json b/examples/api/src-tauri/tauri-plugin-sample/permissions/schemas/schema.json index 79b1967f9..5b7e7edf6 100644 --- a/examples/api/src-tauri/tauri-plugin-sample/permissions/schemas/schema.json +++ b/examples/api/src-tauri/tauri-plugin-sample/permissions/schemas/schema.json @@ -136,6 +136,20 @@ "$ref": "#/definitions/Scopes" } ] + }, + "platforms": { + "description": "Target platforms this permission applies. By default all platforms are affected by this permission.", + "default": [ + "linux", + "macOS", + "windows", + "android", + "iOS" + ], + "type": "array", + "items": { + "$ref": "#/definitions/Target" + } } } }, @@ -241,6 +255,46 @@ } ] }, + "Target": { + "description": "Platform target.", + "oneOf": [ + { + "description": "MacOS.", + "type": "string", + "enum": [ + "macOS" + ] + }, + { + "description": "Windows.", + "type": "string", + "enum": [ + "windows" + ] + }, + { + "description": "Linux.", + "type": "string", + "enum": [ + "linux" + ] + }, + { + "description": "Android.", + "type": "string", + "enum": [ + "android" + ] + }, + { + "description": "iOS.", + "type": "string", + "enum": [ + "iOS" + ] + } + ] + }, "PermissionKind": { "type": "string", "oneOf": [ diff --git a/examples/plugins/tauri-plugin-example/permissions/home-config.toml b/examples/plugins/tauri-plugin-example/permissions/home-config.toml index 307c2f1bd..72b308940 100644 --- a/examples/plugins/tauri-plugin-example/permissions/home-config.toml +++ b/examples/plugins/tauri-plugin-example/permissions/home-config.toml @@ -5,5 +5,5 @@ version = 1 identifier = "deny-home-dir-config" description = "Denies read access to the complete $HOME folder." -[[scope.deny]] +[[permission.scope.deny]] path = "$HOME/.config" diff --git a/examples/plugins/tauri-plugin-example/permissions/home-dir.toml b/examples/plugins/tauri-plugin-example/permissions/home-dir.toml index b4c23cf27..a091545a5 100644 --- a/examples/plugins/tauri-plugin-example/permissions/home-dir.toml +++ b/examples/plugins/tauri-plugin-example/permissions/home-dir.toml @@ -6,5 +6,5 @@ identifier = "allow-home-dir" description = "Allows read access to the complete $HOME folder." commands.allow = ["readDirectory", "readFile"] -[[scope.allow]] +[[permission.scope.allow]] path = "$HOME/**" diff --git a/tooling/cli/schema.json b/tooling/cli/schema.json index 87d9e0bb8..58dd4b891 100644 --- a/tooling/cli/schema.json +++ b/tooling/cli/schema.json @@ -1123,7 +1123,7 @@ } }, "platforms": { - "description": "Target platforms this capability applies. By default all platforms applies.", + "description": "Target platforms this capability applies. By default all platforms are affected by this capability.", "default": [ "linux", "macOS", diff --git a/tooling/cli/src/acl/permission/new.rs b/tooling/cli/src/acl/permission/new.rs index f9cefb1ae..930e267cf 100644 --- a/tooling/cli/src/acl/permission/new.rs +++ b/tooling/cli/src/acl/permission/new.rs @@ -63,6 +63,7 @@ pub fn command(options: Options) -> Result<()> { description, commands: Commands { allow, deny }, scope: Default::default(), + platforms: Default::default(), }; let path = match options.out {