refactor(core): allow configuring both local and remote URLs on capability (#8950)

This commit is contained in:
Lucas Fernandes Nogueira 2024-02-22 15:15:16 -03:00 committed by GitHub
parent e538ba586c
commit a76fb118ce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 99 additions and 123 deletions

View File

@ -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<String> }` and `local: bool (default: true)` instead of choosing one on the `context` field.

View File

@ -1085,15 +1085,22 @@
"default": "", "default": "",
"type": "string" "type": "string"
}, },
"context": { "remote": {
"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.", "description": "Configure remote URLs that can use the capability permissions.",
"default": "local", "anyOf": [
"allOf": [
{ {
"$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": { "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.", "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", "type": "array",
@ -1131,42 +1138,21 @@
} }
} }
}, },
"CapabilityContext": { "CapabilityRemote": {
"description": "Context of the capability.", "description": "Configuration for remote URLs that are associated with the capability.",
"oneOf": [ "type": "object",
{ "required": [
"description": "Capability refers to local URL usage.", "urls"
"type": "string", ],
"enum": [ "properties": {
"local" "urls": {
] "description": "Remote domains this capability refers to. Can use glob patterns.",
}, "type": "array",
{ "items": {
"description": "Capability refers to remote usage.", "type": "string"
"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
} }
] }
}, },
"PermissionEntry": { "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.", "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.",

View File

@ -56,11 +56,11 @@ pub struct Capability {
/// Description of the capability. /// Description of the capability.
#[serde(default)] #[serde(default)]
pub description: String, pub description: String,
/// Execution context of the capability. /// Configure remote URLs that can use the capability permissions.
/// pub remote: Option<CapabilityRemote>,
/// At runtime, Tauri filters the IPC command together with the context to determine whether it is allowed or not and its scope. /// Whether this capability is enabled for local app URLs or not. Defaults to `true`.
#[serde(default)] #[serde(default = "default_capability_local")]
pub context: CapabilityContext, pub local: bool,
/// List of windows that uses this capability. Can be a glob pattern. /// List of windows that uses this capability. Can be a glob pattern.
/// ///
/// On multiwebview windows, prefer [`Self::webviews`] for a fine grained access control. /// On multiwebview windows, prefer [`Self::webviews`] for a fine grained access control.
@ -78,6 +78,10 @@ pub struct Capability {
pub platforms: Vec<Target>, pub platforms: Vec<Target>,
} }
fn default_capability_local() -> bool {
true
}
fn default_platforms() -> Vec<Target> { fn default_platforms() -> Vec<Target> {
vec![ vec![
Target::Linux, Target::Linux,
@ -88,19 +92,13 @@ fn default_platforms() -> Vec<Target> {
] ]
} }
/// 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)] #[derive(Debug, Default, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub enum CapabilityContext { pub struct CapabilityRemote {
/// Capability refers to local URL usage. /// Remote domains this capability refers to. Can use glob patterns.
#[default] pub urls: Vec<String>,
Local,
/// Capability refers to remote usage.
Remote {
/// Remote domains this capability refers to. Can use glob patterns.
urls: Vec<String>,
},
} }
/// Capability formats accepted in a capability file. /// Capability formats accepted in a capability file.
@ -154,19 +152,14 @@ mod build {
use super::*; use super::*;
use crate::{literal_struct, tokens::*}; use crate::{literal_struct, tokens::*};
impl ToTokens for CapabilityContext { impl ToTokens for CapabilityRemote {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::acl::capability::CapabilityContext }; let urls = vec_lit(&self.urls, str_lit);
literal_struct!(
tokens.append_all(match self { tokens,
Self::Remote { urls } => { ::tauri::utils::acl::capability::CapabilityRemote,
let urls = vec_lit(urls, str_lit); urls
quote! { #prefix::Remote { urls: #urls } } );
}
Self::Local => {
quote! { #prefix::Local }
}
});
} }
} }
@ -192,7 +185,8 @@ mod build {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
let identifier = str_lit(&self.identifier); let identifier = str_lit(&self.identifier);
let description = str_lit(&self.description); 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 windows = vec_lit(&self.windows, str_lit);
let permissions = vec_lit(&self.permissions, identity); let permissions = vec_lit(&self.permissions, identity);
let platforms = vec_lit(&self.platforms, identity); let platforms = vec_lit(&self.platforms, identity);
@ -202,7 +196,8 @@ mod build {
::tauri::utils::acl::capability::Capability, ::tauri::utils::acl::capability::Capability,
identifier, identifier,
description, description,
context, remote,
local,
windows, windows,
permissions, permissions,
platforms platforms

View File

@ -15,7 +15,7 @@ use glob::Pattern;
use crate::platform::Target; use crate::platform::Target;
use super::{ use super::{
capability::{Capability, CapabilityContext, PermissionEntry}, capability::{Capability, PermissionEntry},
plugin::Manifest, plugin::Manifest,
Commands, Error, ExecutionContext, Permission, PermissionSet, Scopes, Value, Commands, Error, ExecutionContext, Permission, PermissionSet, Scopes, Value,
}; };
@ -346,18 +346,18 @@ fn resolve_command(
scope_id: Option<ScopeKey>, scope_id: Option<ScopeKey>,
#[cfg(debug_assertions)] referenced_by_permission_identifier: String, #[cfg(debug_assertions)] referenced_by_permission_identifier: String,
) { ) {
let contexts = match &capability.context { let mut contexts = Vec::new();
CapabilityContext::Local => { if capability.local {
vec![ExecutionContext::Local] contexts.push(ExecutionContext::Local);
} }
CapabilityContext::Remote { urls } => urls if let Some(remote) = &capability.remote {
.iter() contexts.extend(remote.urls.iter().map(|url| {
.map(|url| ExecutionContext::Remote { ExecutionContext::Remote {
url: Pattern::new(url) url: Pattern::new(url)
.unwrap_or_else(|e| panic!("invalid glob pattern for remote URL {url}: {e}")), .unwrap_or_else(|e| panic!("invalid glob pattern for remote URL {url}: {e}")),
}) }
.collect(), }));
}; }
for context in contexts { for context in contexts {
let resolved = commands let resolved = commands

View File

@ -2,5 +2,6 @@ identifier = "run-app"
description = "app capability" description = "app capability"
windows = ["main"] windows = ["main"]
permissions = ["fs:read", "fs:allow-app"] permissions = ["fs:read", "fs:allow-app"]
[context.remote] local = false
[remote]
urls = ["https://tauri.app"] urls = ["https://tauri.app"]

View File

@ -1085,15 +1085,22 @@
"default": "", "default": "",
"type": "string" "type": "string"
}, },
"context": { "remote": {
"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.", "description": "Configure remote URLs that can use the capability permissions.",
"default": "local", "anyOf": [
"allOf": [
{ {
"$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": { "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.", "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", "type": "array",
@ -1131,42 +1138,21 @@
} }
} }
}, },
"CapabilityContext": { "CapabilityRemote": {
"description": "Context of the capability.", "description": "Configuration for remote URLs that are associated with the capability.",
"oneOf": [ "type": "object",
{ "required": [
"description": "Capability refers to local URL usage.", "urls"
"type": "string", ],
"enum": [ "properties": {
"local" "urls": {
] "description": "Remote domains this capability refers to. Can use glob patterns.",
}, "type": "array",
{ "items": {
"description": "Capability refers to remote usage.", "type": "string"
"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
} }
] }
}, },
"PermissionEntry": { "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.", "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.",

View File

@ -7,7 +7,7 @@ use crate::Result;
use serde_json::{Map, Value}; use serde_json::{Map, Value};
use tauri_utils::{ use tauri_utils::{
acl::{ acl::{
capability::{Capability, CapabilityContext, PermissionEntry}, capability::{Capability, PermissionEntry},
Scopes, Value as AclValue, Scopes, Value as AclValue,
}, },
platform::Target, platform::Target,
@ -59,7 +59,8 @@ pub fn migrate(tauri_dir: &Path) -> Result<()> {
serde_json::to_string_pretty(&Capability { serde_json::to_string_pretty(&Capability {
identifier: "migrated".to_string(), identifier: "migrated".to_string(),
description: "permissions that were migrated from v1".into(), description: "permissions that were migrated from v1".into(),
context: CapabilityContext::Local, local: true,
remote: None,
windows: vec!["main".into()], windows: vec!["main".into()],
webviews: vec![], webviews: vec![],
permissions, permissions,