diff --git a/.changes/capability-context-refactor.md b/.changes/capability-context-refactor.md new file mode 100644 index 000000000..62d1c9a7e --- /dev/null +++ b/.changes/capability-context-refactor.md @@ -0,0 +1,7 @@ +--- +"tauri-utils": patch:breaking +"tauri-cli": patch:breaking +"@tauri-apps/cli": patch:breaking +--- + +Changed the capability format to allow configuring both `remote: { urls: Vec }` and `local: bool (default: true)` instead of choosing one on the `context` field. diff --git a/core/tauri-config-schema/schema.json b/core/tauri-config-schema/schema.json index 591a6a77f..87d9e0bb8 100644 --- a/core/tauri-config-schema/schema.json +++ b/core/tauri-config-schema/schema.json @@ -1085,15 +1085,22 @@ "default": "", "type": "string" }, - "context": { - "description": "Execution context of the capability.\n\nAt runtime, Tauri filters the IPC command together with the context to determine whether it is allowed or not and its scope.", - "default": "local", - "allOf": [ + "remote": { + "description": "Configure remote URLs that can use the capability permissions.", + "anyOf": [ { - "$ref": "#/definitions/CapabilityContext" + "$ref": "#/definitions/CapabilityRemote" + }, + { + "type": "null" } ] }, + "local": { + "description": "Whether this capability is enabled for local app URLs or not. Defaults to `true`.", + "default": true, + "type": "boolean" + }, "windows": { "description": "List of windows that uses this capability. Can be a glob pattern.\n\nOn multiwebview windows, prefer [`Self::webviews`] for a fine grained access control.", "type": "array", @@ -1131,42 +1138,21 @@ } } }, - "CapabilityContext": { - "description": "Context of the capability.", - "oneOf": [ - { - "description": "Capability refers to local URL usage.", - "type": "string", - "enum": [ - "local" - ] - }, - { - "description": "Capability refers to remote usage.", - "type": "object", - "required": [ - "remote" - ], - "properties": { - "remote": { - "type": "object", - "required": [ - "urls" - ], - "properties": { - "urls": { - "description": "Remote domains this capability refers to. Can use glob patterns.", - "type": "array", - "items": { - "type": "string" - } - } - } - } - }, - "additionalProperties": false + "CapabilityRemote": { + "description": "Configuration for remote URLs that are associated with the capability.", + "type": "object", + "required": [ + "urls" + ], + "properties": { + "urls": { + "description": "Remote domains this capability refers to. Can use glob patterns.", + "type": "array", + "items": { + "type": "string" + } } - ] + } }, "PermissionEntry": { "description": "An entry for a permission value in a [`Capability`] can be either a raw permission [`Identifier`] or an object that references a permission and extends its scope.", diff --git a/core/tauri-utils/src/acl/capability.rs b/core/tauri-utils/src/acl/capability.rs index 48f62b4a6..e5dd69cfa 100644 --- a/core/tauri-utils/src/acl/capability.rs +++ b/core/tauri-utils/src/acl/capability.rs @@ -56,11 +56,11 @@ pub struct Capability { /// Description of the capability. #[serde(default)] pub description: String, - /// Execution context of the capability. - /// - /// At runtime, Tauri filters the IPC command together with the context to determine whether it is allowed or not and its scope. - #[serde(default)] - pub context: CapabilityContext, + /// Configure remote URLs that can use the capability permissions. + pub remote: Option, + /// Whether this capability is enabled for local app URLs or not. Defaults to `true`. + #[serde(default = "default_capability_local")] + pub local: bool, /// List of windows that uses this capability. Can be a glob pattern. /// /// On multiwebview windows, prefer [`Self::webviews`] for a fine grained access control. @@ -78,6 +78,10 @@ pub struct Capability { pub platforms: Vec, } +fn default_capability_local() -> bool { + true +} + fn default_platforms() -> Vec { vec![ Target::Linux, @@ -88,19 +92,13 @@ fn default_platforms() -> Vec { ] } -/// Context of the capability. +/// Configuration for remote URLs that are associated with the capability. #[derive(Debug, Default, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[serde(rename_all = "camelCase")] -pub enum CapabilityContext { - /// Capability refers to local URL usage. - #[default] - Local, - /// Capability refers to remote usage. - Remote { - /// Remote domains this capability refers to. Can use glob patterns. - urls: Vec, - }, +pub struct CapabilityRemote { + /// Remote domains this capability refers to. Can use glob patterns. + pub urls: Vec, } /// Capability formats accepted in a capability file. @@ -154,19 +152,14 @@ mod build { use super::*; use crate::{literal_struct, tokens::*}; - impl ToTokens for CapabilityContext { + impl ToTokens for CapabilityRemote { fn to_tokens(&self, tokens: &mut TokenStream) { - let prefix = quote! { ::tauri::utils::acl::capability::CapabilityContext }; - - tokens.append_all(match self { - Self::Remote { urls } => { - let urls = vec_lit(urls, str_lit); - quote! { #prefix::Remote { urls: #urls } } - } - Self::Local => { - quote! { #prefix::Local } - } - }); + let urls = vec_lit(&self.urls, str_lit); + literal_struct!( + tokens, + ::tauri::utils::acl::capability::CapabilityRemote, + urls + ); } } @@ -192,7 +185,8 @@ mod build { fn to_tokens(&self, tokens: &mut TokenStream) { let identifier = str_lit(&self.identifier); let description = str_lit(&self.description); - let context = &self.context; + let remote = &self.remote; + let local = self.local; let windows = vec_lit(&self.windows, str_lit); let permissions = vec_lit(&self.permissions, identity); let platforms = vec_lit(&self.platforms, identity); @@ -202,7 +196,8 @@ mod build { ::tauri::utils::acl::capability::Capability, identifier, description, - context, + remote, + local, windows, permissions, platforms diff --git a/core/tauri-utils/src/acl/resolved.rs b/core/tauri-utils/src/acl/resolved.rs index 1cd6f107a..ac0adb59a 100644 --- a/core/tauri-utils/src/acl/resolved.rs +++ b/core/tauri-utils/src/acl/resolved.rs @@ -15,7 +15,7 @@ use glob::Pattern; use crate::platform::Target; use super::{ - capability::{Capability, CapabilityContext, PermissionEntry}, + capability::{Capability, PermissionEntry}, plugin::Manifest, Commands, Error, ExecutionContext, Permission, PermissionSet, Scopes, Value, }; @@ -346,18 +346,18 @@ fn resolve_command( scope_id: Option, #[cfg(debug_assertions)] referenced_by_permission_identifier: String, ) { - let contexts = match &capability.context { - CapabilityContext::Local => { - vec![ExecutionContext::Local] - } - CapabilityContext::Remote { urls } => urls - .iter() - .map(|url| ExecutionContext::Remote { + let mut contexts = Vec::new(); + if capability.local { + contexts.push(ExecutionContext::Local); + } + if let Some(remote) = &capability.remote { + contexts.extend(remote.urls.iter().map(|url| { + ExecutionContext::Remote { url: Pattern::new(url) .unwrap_or_else(|e| panic!("invalid glob pattern for remote URL {url}: {e}")), - }) - .collect(), - }; + } + })); + } for context in contexts { let resolved = commands diff --git a/core/tests/acl/fixtures/capabilities/file-explorer-remote/cap.toml b/core/tests/acl/fixtures/capabilities/file-explorer-remote/cap.toml index b027d6e0a..242461ce8 100644 --- a/core/tests/acl/fixtures/capabilities/file-explorer-remote/cap.toml +++ b/core/tests/acl/fixtures/capabilities/file-explorer-remote/cap.toml @@ -2,5 +2,6 @@ identifier = "run-app" description = "app capability" windows = ["main"] permissions = ["fs:read", "fs:allow-app"] -[context.remote] +local = false +[remote] urls = ["https://tauri.app"] diff --git a/tooling/cli/schema.json b/tooling/cli/schema.json index 591a6a77f..87d9e0bb8 100644 --- a/tooling/cli/schema.json +++ b/tooling/cli/schema.json @@ -1085,15 +1085,22 @@ "default": "", "type": "string" }, - "context": { - "description": "Execution context of the capability.\n\nAt runtime, Tauri filters the IPC command together with the context to determine whether it is allowed or not and its scope.", - "default": "local", - "allOf": [ + "remote": { + "description": "Configure remote URLs that can use the capability permissions.", + "anyOf": [ { - "$ref": "#/definitions/CapabilityContext" + "$ref": "#/definitions/CapabilityRemote" + }, + { + "type": "null" } ] }, + "local": { + "description": "Whether this capability is enabled for local app URLs or not. Defaults to `true`.", + "default": true, + "type": "boolean" + }, "windows": { "description": "List of windows that uses this capability. Can be a glob pattern.\n\nOn multiwebview windows, prefer [`Self::webviews`] for a fine grained access control.", "type": "array", @@ -1131,42 +1138,21 @@ } } }, - "CapabilityContext": { - "description": "Context of the capability.", - "oneOf": [ - { - "description": "Capability refers to local URL usage.", - "type": "string", - "enum": [ - "local" - ] - }, - { - "description": "Capability refers to remote usage.", - "type": "object", - "required": [ - "remote" - ], - "properties": { - "remote": { - "type": "object", - "required": [ - "urls" - ], - "properties": { - "urls": { - "description": "Remote domains this capability refers to. Can use glob patterns.", - "type": "array", - "items": { - "type": "string" - } - } - } - } - }, - "additionalProperties": false + "CapabilityRemote": { + "description": "Configuration for remote URLs that are associated with the capability.", + "type": "object", + "required": [ + "urls" + ], + "properties": { + "urls": { + "description": "Remote domains this capability refers to. Can use glob patterns.", + "type": "array", + "items": { + "type": "string" + } } - ] + } }, "PermissionEntry": { "description": "An entry for a permission value in a [`Capability`] can be either a raw permission [`Identifier`] or an object that references a permission and extends its scope.", diff --git a/tooling/cli/src/migrate/config.rs b/tooling/cli/src/migrate/config.rs index 2f0201cb9..2f2960917 100644 --- a/tooling/cli/src/migrate/config.rs +++ b/tooling/cli/src/migrate/config.rs @@ -7,7 +7,7 @@ use crate::Result; use serde_json::{Map, Value}; use tauri_utils::{ acl::{ - capability::{Capability, CapabilityContext, PermissionEntry}, + capability::{Capability, PermissionEntry}, Scopes, Value as AclValue, }, platform::Target, @@ -59,7 +59,8 @@ pub fn migrate(tauri_dir: &Path) -> Result<()> { serde_json::to_string_pretty(&Capability { identifier: "migrated".to_string(), description: "permissions that were migrated from v1".into(), - context: CapabilityContext::Local, + local: true, + remote: None, windows: vec!["main".into()], webviews: vec![], permissions,