mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-08-16 19:30:27 +03:00
feat(core): capabilities on multiwebview contexts (#8789)
* feat(core): capabilities on multiwebview contexts * fix cli * lint * sort
This commit is contained in:
parent
edb11c138d
commit
0cb0a15ce2
6
.changes/capabilities-multiwebview.md
Normal file
6
.changes/capabilities-multiwebview.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
"tauri": patch:enhance
|
||||
"tauri-utils": patch:enhance
|
||||
---
|
||||
|
||||
Add `webviews` array on the capability for usage on multiwebview contexts.
|
@ -60,7 +60,15 @@ pub struct Capability {
|
||||
#[serde(default)]
|
||||
pub context: CapabilityContext,
|
||||
/// List of windows that uses this capability. Can be a glob pattern.
|
||||
///
|
||||
/// On multiwebview windows, prefer [`Self::webviews`] for a fine grained access control.
|
||||
pub windows: Vec<String>,
|
||||
/// List of webviews that uses this capability. Can be a glob pattern.
|
||||
///
|
||||
/// This is only required when using on multiwebview contexts, by default
|
||||
/// all child webviews of a window that matches [`Self::windows`] are linked.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub webviews: Vec<String>,
|
||||
/// 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<PermissionEntry>,
|
||||
/// Target platforms this capability applies. By default all platforms applies.
|
||||
|
@ -41,6 +41,8 @@ pub struct ResolvedCommand {
|
||||
pub referenced_by: Vec<ResolvedCommandReference>,
|
||||
/// The list of window label patterns that was resolved for this command.
|
||||
pub windows: Vec<glob::Pattern>,
|
||||
/// The list of webview label patterns that was resolved for this command.
|
||||
pub webviews: Vec<glob::Pattern>,
|
||||
/// The reference of the scope that is associated with this command. See [`Resolved#structfield.scopes`].
|
||||
pub scope: Option<ScopeKey>,
|
||||
}
|
||||
@ -49,6 +51,7 @@ impl fmt::Debug for ResolvedCommand {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ResolvedCommand")
|
||||
.field("windows", &self.windows)
|
||||
.field("webviews", &self.webviews)
|
||||
.field("scope", &self.scope)
|
||||
.finish()
|
||||
}
|
||||
@ -271,7 +274,8 @@ impl Resolved {
|
||||
ResolvedCommand {
|
||||
#[cfg(debug_assertions)]
|
||||
referenced_by: cmd.referenced_by,
|
||||
windows: parse_window_patterns(cmd.windows)?,
|
||||
windows: parse_glob_patterns(cmd.windows)?,
|
||||
webviews: parse_glob_patterns(cmd.webviews)?,
|
||||
scope: cmd.resolved_scope_key,
|
||||
},
|
||||
))
|
||||
@ -285,7 +289,8 @@ impl Resolved {
|
||||
ResolvedCommand {
|
||||
#[cfg(debug_assertions)]
|
||||
referenced_by: cmd.referenced_by,
|
||||
windows: parse_window_patterns(cmd.windows)?,
|
||||
windows: parse_glob_patterns(cmd.windows)?,
|
||||
webviews: parse_glob_patterns(cmd.webviews)?,
|
||||
scope: cmd.resolved_scope_key,
|
||||
},
|
||||
))
|
||||
@ -299,11 +304,15 @@ impl Resolved {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_window_patterns(windows: HashSet<String>) -> Result<Vec<glob::Pattern>, Error> {
|
||||
fn parse_glob_patterns(raw: HashSet<String>) -> Result<Vec<glob::Pattern>, Error> {
|
||||
let mut raw = raw.into_iter().collect::<Vec<_>>();
|
||||
raw.sort();
|
||||
|
||||
let mut patterns = Vec::new();
|
||||
for window in windows {
|
||||
patterns.push(glob::Pattern::new(&window)?);
|
||||
for pattern in raw {
|
||||
patterns.push(glob::Pattern::new(&pattern)?);
|
||||
}
|
||||
|
||||
Ok(patterns)
|
||||
}
|
||||
|
||||
@ -312,6 +321,7 @@ struct ResolvedCommandTemp {
|
||||
#[cfg(debug_assertions)]
|
||||
pub referenced_by: Vec<ResolvedCommandReference>,
|
||||
pub windows: HashSet<String>,
|
||||
pub webviews: HashSet<String>,
|
||||
pub scope: Vec<ScopeKey>,
|
||||
pub resolved_scope_key: Option<ScopeKey>,
|
||||
}
|
||||
@ -351,6 +361,8 @@ fn resolve_command(
|
||||
});
|
||||
|
||||
resolved.windows.extend(capability.windows.clone());
|
||||
resolved.webviews.extend(capability.webviews.clone());
|
||||
|
||||
if let Some(id) = scope_id {
|
||||
resolved.scope.push(id);
|
||||
}
|
||||
@ -456,6 +468,10 @@ mod build {
|
||||
let w = window.as_str();
|
||||
quote!(#w.parse().unwrap())
|
||||
});
|
||||
let webviews = vec_lit(&self.webviews, |window| {
|
||||
let w = window.as_str();
|
||||
quote!(#w.parse().unwrap())
|
||||
});
|
||||
let scope = opt_lit(self.scope.as_ref());
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
@ -465,6 +481,7 @@ mod build {
|
||||
::tauri::utils::acl::resolved::ResolvedCommand,
|
||||
referenced_by,
|
||||
windows,
|
||||
webviews,
|
||||
scope
|
||||
)
|
||||
}
|
||||
@ -473,6 +490,7 @@ mod build {
|
||||
tokens,
|
||||
::tauri::utils::acl::resolved::ResolvedCommand,
|
||||
windows,
|
||||
webviews,
|
||||
scope
|
||||
)
|
||||
}
|
||||
|
@ -90,6 +90,7 @@ impl RuntimeAuthority {
|
||||
plugin: &str,
|
||||
command_name: &str,
|
||||
window: &str,
|
||||
webview: &str,
|
||||
origin: &Origin,
|
||||
) -> String {
|
||||
fn print_references(resolved: &ResolvedCommand) -> String {
|
||||
@ -147,10 +148,16 @@ impl RuntimeAuthority {
|
||||
.iter()
|
||||
.find(|(cmd, _)| origin.matches(&cmd.context))
|
||||
{
|
||||
if resolved.windows.iter().any(|w| w.matches(window)) {
|
||||
if resolved.webviews.iter().any(|w| w.matches(webview))
|
||||
|| resolved.windows.iter().any(|w| w.matches(window))
|
||||
{
|
||||
"allowed".to_string()
|
||||
} else {
|
||||
format!("{plugin}.{command_name} not allowed on window {window}, expected one of {}, referenced by {}", resolved.windows.iter().map(|w| w.as_str()).collect::<Vec<_>>().join(", "), print_references(resolved))
|
||||
format!("{plugin}.{command_name} not allowed on window {window}, webview {webview}, allowed windows: {}, allowed webviews: {}, referenced by {}",
|
||||
resolved.windows.iter().map(|w| w.as_str()).collect::<Vec<_>>().join(", "),
|
||||
resolved.webviews.iter().map(|w| w.as_str()).collect::<Vec<_>>().join(", "),
|
||||
print_references(resolved)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
let permission_error_detail = if let Some(manifest) = self.acl.get(plugin) {
|
||||
@ -217,6 +224,7 @@ impl RuntimeAuthority {
|
||||
&self,
|
||||
command: &str,
|
||||
window: &str,
|
||||
webview: &str,
|
||||
origin: &Origin,
|
||||
) -> Option<&ResolvedCommand> {
|
||||
if self
|
||||
@ -231,7 +239,10 @@ impl RuntimeAuthority {
|
||||
.iter()
|
||||
.find(|(cmd, _)| cmd.name == command && origin.matches(&cmd.context))
|
||||
.map(|(_cmd, resolved)| resolved)
|
||||
.filter(|resolved| resolved.windows.iter().any(|w| w.matches(window)))
|
||||
.filter(|resolved| {
|
||||
resolved.webviews.iter().any(|w| w.matches(webview))
|
||||
|| resolved.windows.iter().any(|w| w.matches(window))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -467,6 +478,7 @@ mod tests {
|
||||
context: ExecutionContext::Local,
|
||||
};
|
||||
let window = "main-*";
|
||||
let webview = "other-*";
|
||||
|
||||
let resolved_cmd = ResolvedCommand {
|
||||
windows: vec![Pattern::new(window).unwrap()],
|
||||
@ -485,6 +497,41 @@ mod tests {
|
||||
authority.resolve_access(
|
||||
&command.name,
|
||||
&window.replace('*', "something"),
|
||||
webview,
|
||||
&Origin::Local
|
||||
),
|
||||
Some(&resolved_cmd)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn webview_glob_pattern_matches() {
|
||||
let command = CommandKey {
|
||||
name: "my-command".into(),
|
||||
context: ExecutionContext::Local,
|
||||
};
|
||||
let window = "other-*";
|
||||
let webview = "main-*";
|
||||
|
||||
let resolved_cmd = ResolvedCommand {
|
||||
windows: vec![Pattern::new(window).unwrap()],
|
||||
webviews: vec![Pattern::new(webview).unwrap()],
|
||||
..Default::default()
|
||||
};
|
||||
let allowed_commands = [(command.clone(), resolved_cmd.clone())]
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
let authority = RuntimeAuthority::new(Resolved {
|
||||
allowed_commands,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
authority.resolve_access(
|
||||
&command.name,
|
||||
window,
|
||||
&webview.replace('*', "something"),
|
||||
&Origin::Local
|
||||
),
|
||||
Some(&resolved_cmd)
|
||||
@ -501,6 +548,7 @@ mod tests {
|
||||
},
|
||||
};
|
||||
let window = "main";
|
||||
let webview = "main";
|
||||
|
||||
let resolved_cmd = ResolvedCommand {
|
||||
windows: vec![Pattern::new(window).unwrap()],
|
||||
@ -520,6 +568,7 @@ mod tests {
|
||||
authority.resolve_access(
|
||||
&command.name,
|
||||
window,
|
||||
webview,
|
||||
&Origin::Remote {
|
||||
domain: domain.into()
|
||||
}
|
||||
@ -538,6 +587,7 @@ mod tests {
|
||||
},
|
||||
};
|
||||
let window = "main";
|
||||
let webview = "main";
|
||||
|
||||
let resolved_cmd = ResolvedCommand {
|
||||
windows: vec![Pattern::new(window).unwrap()],
|
||||
@ -557,6 +607,7 @@ mod tests {
|
||||
authority.resolve_access(
|
||||
&command.name,
|
||||
window,
|
||||
webview,
|
||||
&Origin::Remote {
|
||||
domain: domain.replace('*', "studio")
|
||||
}
|
||||
@ -572,6 +623,7 @@ mod tests {
|
||||
context: ExecutionContext::Local,
|
||||
};
|
||||
let window = "main";
|
||||
let webview = "main";
|
||||
|
||||
let resolved_cmd = ResolvedCommand {
|
||||
windows: vec![Pattern::new(window).unwrap()],
|
||||
@ -591,6 +643,7 @@ mod tests {
|
||||
.resolve_access(
|
||||
&command.name,
|
||||
window,
|
||||
webview,
|
||||
&Origin::Remote {
|
||||
domain: "tauri.app".into()
|
||||
}
|
||||
@ -605,6 +658,7 @@ mod tests {
|
||||
context: ExecutionContext::Local,
|
||||
};
|
||||
let window = "main";
|
||||
let webview = "main";
|
||||
let windows = vec![Pattern::new(window).unwrap()];
|
||||
let allowed_commands = [(
|
||||
command.clone(),
|
||||
@ -632,7 +686,7 @@ mod tests {
|
||||
});
|
||||
|
||||
assert!(authority
|
||||
.resolve_access(&command.name, window, &Origin::Local)
|
||||
.resolve_access(&command.name, window, webview, &Origin::Local)
|
||||
.is_none());
|
||||
}
|
||||
}
|
||||
|
@ -1121,7 +1121,12 @@ fn main() {
|
||||
};
|
||||
let resolved_acl = manager
|
||||
.runtime_authority
|
||||
.resolve_access(&request.cmd, &message.webview.webview.label, &acl_origin)
|
||||
.resolve_access(
|
||||
&request.cmd,
|
||||
message.webview.label(),
|
||||
message.webview.window().label(),
|
||||
&acl_origin,
|
||||
)
|
||||
.cloned();
|
||||
|
||||
let mut invoke = Invoke {
|
||||
@ -1145,7 +1150,8 @@ fn main() {
|
||||
.reject(manager.runtime_authority.resolve_access_message(
|
||||
plugin,
|
||||
&command_name,
|
||||
&invoke.message.webview.webview.label,
|
||||
invoke.message.webview.window().label(),
|
||||
invoke.message.webview.label(),
|
||||
&acl_origin,
|
||||
));
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
identifier = "run-app"
|
||||
description = "app capability"
|
||||
windows = ["main"]
|
||||
webviews = ["child1", "child2"]
|
||||
permissions = ["ping:allow-ping"]
|
@ -0,0 +1 @@
|
||||
["ping"]
|
@ -1,6 +1,5 @@
|
||||
---
|
||||
source: core/tests/acl/src/lib.rs
|
||||
assertion_line: 59
|
||||
expression: resolved
|
||||
---
|
||||
Resolved {
|
||||
@ -29,6 +28,7 @@ Resolved {
|
||||
is_recursive: false,
|
||||
},
|
||||
],
|
||||
webviews: [],
|
||||
scope: None,
|
||||
},
|
||||
},
|
||||
|
@ -1,6 +1,5 @@
|
||||
---
|
||||
source: core/tests/acl/src/lib.rs
|
||||
assertion_line: 59
|
||||
expression: resolved
|
||||
---
|
||||
Resolved {
|
||||
@ -63,6 +62,7 @@ Resolved {
|
||||
is_recursive: false,
|
||||
},
|
||||
],
|
||||
webviews: [],
|
||||
scope: None,
|
||||
},
|
||||
CommandKey {
|
||||
@ -123,6 +123,7 @@ Resolved {
|
||||
is_recursive: false,
|
||||
},
|
||||
],
|
||||
webviews: [],
|
||||
scope: None,
|
||||
},
|
||||
},
|
||||
|
@ -1,6 +1,5 @@
|
||||
---
|
||||
source: core/tests/acl/src/lib.rs
|
||||
assertion_line: 59
|
||||
expression: resolved
|
||||
---
|
||||
Resolved {
|
||||
@ -29,6 +28,7 @@ Resolved {
|
||||
is_recursive: false,
|
||||
},
|
||||
],
|
||||
webviews: [],
|
||||
scope: None,
|
||||
},
|
||||
CommandKey {
|
||||
@ -55,6 +55,7 @@ Resolved {
|
||||
is_recursive: false,
|
||||
},
|
||||
],
|
||||
webviews: [],
|
||||
scope: None,
|
||||
},
|
||||
},
|
||||
|
@ -0,0 +1,87 @@
|
||||
---
|
||||
source: core/tests/acl/src/lib.rs
|
||||
expression: resolved
|
||||
---
|
||||
Resolved {
|
||||
allowed_commands: {
|
||||
CommandKey {
|
||||
name: "plugin:ping|ping",
|
||||
context: Local,
|
||||
}: ResolvedCommand {
|
||||
windows: [
|
||||
Pattern {
|
||||
original: "main",
|
||||
tokens: [
|
||||
Char(
|
||||
'm',
|
||||
),
|
||||
Char(
|
||||
'a',
|
||||
),
|
||||
Char(
|
||||
'i',
|
||||
),
|
||||
Char(
|
||||
'n',
|
||||
),
|
||||
],
|
||||
is_recursive: false,
|
||||
},
|
||||
],
|
||||
webviews: [
|
||||
Pattern {
|
||||
original: "child1",
|
||||
tokens: [
|
||||
Char(
|
||||
'c',
|
||||
),
|
||||
Char(
|
||||
'h',
|
||||
),
|
||||
Char(
|
||||
'i',
|
||||
),
|
||||
Char(
|
||||
'l',
|
||||
),
|
||||
Char(
|
||||
'd',
|
||||
),
|
||||
Char(
|
||||
'1',
|
||||
),
|
||||
],
|
||||
is_recursive: false,
|
||||
},
|
||||
Pattern {
|
||||
original: "child2",
|
||||
tokens: [
|
||||
Char(
|
||||
'c',
|
||||
),
|
||||
Char(
|
||||
'h',
|
||||
),
|
||||
Char(
|
||||
'i',
|
||||
),
|
||||
Char(
|
||||
'l',
|
||||
),
|
||||
Char(
|
||||
'd',
|
||||
),
|
||||
Char(
|
||||
'2',
|
||||
),
|
||||
],
|
||||
is_recursive: false,
|
||||
},
|
||||
],
|
||||
scope: None,
|
||||
},
|
||||
},
|
||||
denied_commands: {},
|
||||
command_scope: {},
|
||||
global_scope: {},
|
||||
}
|
@ -28,6 +28,7 @@ Resolved {
|
||||
is_recursive: false,
|
||||
},
|
||||
],
|
||||
webviews: [],
|
||||
scope: Some(
|
||||
9188997750422900590,
|
||||
),
|
||||
@ -56,6 +57,7 @@ Resolved {
|
||||
is_recursive: false,
|
||||
},
|
||||
],
|
||||
webviews: [],
|
||||
scope: Some(
|
||||
1349364295896631601,
|
||||
),
|
||||
@ -84,6 +86,7 @@ Resolved {
|
||||
is_recursive: false,
|
||||
},
|
||||
],
|
||||
webviews: [],
|
||||
scope: Some(
|
||||
8031926490300119127,
|
||||
),
|
||||
|
@ -28,6 +28,7 @@ Resolved {
|
||||
is_recursive: false,
|
||||
},
|
||||
],
|
||||
webviews: [],
|
||||
scope: Some(
|
||||
18088007599891946824,
|
||||
),
|
||||
@ -56,6 +57,7 @@ Resolved {
|
||||
is_recursive: false,
|
||||
},
|
||||
],
|
||||
webviews: [],
|
||||
scope: Some(
|
||||
5856262838373339618,
|
||||
),
|
||||
@ -84,6 +86,7 @@ Resolved {
|
||||
is_recursive: false,
|
||||
},
|
||||
],
|
||||
webviews: [],
|
||||
scope: Some(
|
||||
7912899488978770657,
|
||||
),
|
||||
|
@ -61,6 +61,7 @@ pub fn migrate(tauri_dir: &Path) -> Result<()> {
|
||||
description: "permissions that were migrated from v1".into(),
|
||||
context: CapabilityContext::Local,
|
||||
windows: vec!["main".into()],
|
||||
webviews: vec![],
|
||||
permissions,
|
||||
platforms: vec![
|
||||
Target::Linux,
|
||||
|
Loading…
Reference in New Issue
Block a user