Skip to content

Commit 0cb0a15

Browse files
authored
feat(core): capabilities on multiwebview contexts (#8789)
* feat(core): capabilities on multiwebview contexts * fix cli * lint * sort
1 parent edb11c1 commit 0cb0a15

14 files changed

+208
-14
lines changed

.changes/capabilities-multiwebview.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"tauri": patch:enhance
3+
"tauri-utils": patch:enhance
4+
---
5+
6+
Add `webviews` array on the capability for usage on multiwebview contexts.

core/tauri-utils/src/acl/capability.rs

+8
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,15 @@ pub struct Capability {
6060
#[serde(default)]
6161
pub context: CapabilityContext,
6262
/// List of windows that uses this capability. Can be a glob pattern.
63+
///
64+
/// On multiwebview windows, prefer [`Self::webviews`] for a fine grained access control.
6365
pub windows: Vec<String>,
66+
/// List of webviews that uses this capability. Can be a glob pattern.
67+
///
68+
/// This is only required when using on multiwebview contexts, by default
69+
/// all child webviews of a window that matches [`Self::windows`] are linked.
70+
#[serde(default, skip_serializing_if = "Vec::is_empty")]
71+
pub webviews: Vec<String>,
6472
/// List of permissions attached to this capability. Must include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`.
6573
pub permissions: Vec<PermissionEntry>,
6674
/// Target platforms this capability applies. By default all platforms applies.

core/tauri-utils/src/acl/resolved.rs

+23-5
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ pub struct ResolvedCommand {
4141
pub referenced_by: Vec<ResolvedCommandReference>,
4242
/// The list of window label patterns that was resolved for this command.
4343
pub windows: Vec<glob::Pattern>,
44+
/// The list of webview label patterns that was resolved for this command.
45+
pub webviews: Vec<glob::Pattern>,
4446
/// The reference of the scope that is associated with this command. See [`Resolved#structfield.scopes`].
4547
pub scope: Option<ScopeKey>,
4648
}
@@ -49,6 +51,7 @@ impl fmt::Debug for ResolvedCommand {
4951
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5052
f.debug_struct("ResolvedCommand")
5153
.field("windows", &self.windows)
54+
.field("webviews", &self.webviews)
5255
.field("scope", &self.scope)
5356
.finish()
5457
}
@@ -271,7 +274,8 @@ impl Resolved {
271274
ResolvedCommand {
272275
#[cfg(debug_assertions)]
273276
referenced_by: cmd.referenced_by,
274-
windows: parse_window_patterns(cmd.windows)?,
277+
windows: parse_glob_patterns(cmd.windows)?,
278+
webviews: parse_glob_patterns(cmd.webviews)?,
275279
scope: cmd.resolved_scope_key,
276280
},
277281
))
@@ -285,7 +289,8 @@ impl Resolved {
285289
ResolvedCommand {
286290
#[cfg(debug_assertions)]
287291
referenced_by: cmd.referenced_by,
288-
windows: parse_window_patterns(cmd.windows)?,
292+
windows: parse_glob_patterns(cmd.windows)?,
293+
webviews: parse_glob_patterns(cmd.webviews)?,
289294
scope: cmd.resolved_scope_key,
290295
},
291296
))
@@ -299,11 +304,15 @@ impl Resolved {
299304
}
300305
}
301306

302-
fn parse_window_patterns(windows: HashSet<String>) -> Result<Vec<glob::Pattern>, Error> {
307+
fn parse_glob_patterns(raw: HashSet<String>) -> Result<Vec<glob::Pattern>, Error> {
308+
let mut raw = raw.into_iter().collect::<Vec<_>>();
309+
raw.sort();
310+
303311
let mut patterns = Vec::new();
304-
for window in windows {
305-
patterns.push(glob::Pattern::new(&window)?);
312+
for pattern in raw {
313+
patterns.push(glob::Pattern::new(&pattern)?);
306314
}
315+
307316
Ok(patterns)
308317
}
309318

@@ -312,6 +321,7 @@ struct ResolvedCommandTemp {
312321
#[cfg(debug_assertions)]
313322
pub referenced_by: Vec<ResolvedCommandReference>,
314323
pub windows: HashSet<String>,
324+
pub webviews: HashSet<String>,
315325
pub scope: Vec<ScopeKey>,
316326
pub resolved_scope_key: Option<ScopeKey>,
317327
}
@@ -351,6 +361,8 @@ fn resolve_command(
351361
});
352362

353363
resolved.windows.extend(capability.windows.clone());
364+
resolved.webviews.extend(capability.webviews.clone());
365+
354366
if let Some(id) = scope_id {
355367
resolved.scope.push(id);
356368
}
@@ -456,6 +468,10 @@ mod build {
456468
let w = window.as_str();
457469
quote!(#w.parse().unwrap())
458470
});
471+
let webviews = vec_lit(&self.webviews, |window| {
472+
let w = window.as_str();
473+
quote!(#w.parse().unwrap())
474+
});
459475
let scope = opt_lit(self.scope.as_ref());
460476

461477
#[cfg(debug_assertions)]
@@ -465,6 +481,7 @@ mod build {
465481
::tauri::utils::acl::resolved::ResolvedCommand,
466482
referenced_by,
467483
windows,
484+
webviews,
468485
scope
469486
)
470487
}
@@ -473,6 +490,7 @@ mod build {
473490
tokens,
474491
::tauri::utils::acl::resolved::ResolvedCommand,
475492
windows,
493+
webviews,
476494
scope
477495
)
478496
}

core/tauri/src/ipc/authority.rs

+58-4
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ impl RuntimeAuthority {
9090
plugin: &str,
9191
command_name: &str,
9292
window: &str,
93+
webview: &str,
9394
origin: &Origin,
9495
) -> String {
9596
fn print_references(resolved: &ResolvedCommand) -> String {
@@ -147,10 +148,16 @@ impl RuntimeAuthority {
147148
.iter()
148149
.find(|(cmd, _)| origin.matches(&cmd.context))
149150
{
150-
if resolved.windows.iter().any(|w| w.matches(window)) {
151+
if resolved.webviews.iter().any(|w| w.matches(webview))
152+
|| resolved.windows.iter().any(|w| w.matches(window))
153+
{
151154
"allowed".to_string()
152155
} else {
153-
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))
156+
format!("{plugin}.{command_name} not allowed on window {window}, webview {webview}, allowed windows: {}, allowed webviews: {}, referenced by {}",
157+
resolved.windows.iter().map(|w| w.as_str()).collect::<Vec<_>>().join(", "),
158+
resolved.webviews.iter().map(|w| w.as_str()).collect::<Vec<_>>().join(", "),
159+
print_references(resolved)
160+
)
154161
}
155162
} else {
156163
let permission_error_detail = if let Some(manifest) = self.acl.get(plugin) {
@@ -217,6 +224,7 @@ impl RuntimeAuthority {
217224
&self,
218225
command: &str,
219226
window: &str,
227+
webview: &str,
220228
origin: &Origin,
221229
) -> Option<&ResolvedCommand> {
222230
if self
@@ -231,7 +239,10 @@ impl RuntimeAuthority {
231239
.iter()
232240
.find(|(cmd, _)| cmd.name == command && origin.matches(&cmd.context))
233241
.map(|(_cmd, resolved)| resolved)
234-
.filter(|resolved| resolved.windows.iter().any(|w| w.matches(window)))
242+
.filter(|resolved| {
243+
resolved.webviews.iter().any(|w| w.matches(webview))
244+
|| resolved.windows.iter().any(|w| w.matches(window))
245+
})
235246
}
236247
}
237248
}
@@ -467,6 +478,7 @@ mod tests {
467478
context: ExecutionContext::Local,
468479
};
469480
let window = "main-*";
481+
let webview = "other-*";
470482

471483
let resolved_cmd = ResolvedCommand {
472484
windows: vec![Pattern::new(window).unwrap()],
@@ -485,6 +497,41 @@ mod tests {
485497
authority.resolve_access(
486498
&command.name,
487499
&window.replace('*', "something"),
500+
webview,
501+
&Origin::Local
502+
),
503+
Some(&resolved_cmd)
504+
);
505+
}
506+
507+
#[test]
508+
fn webview_glob_pattern_matches() {
509+
let command = CommandKey {
510+
name: "my-command".into(),
511+
context: ExecutionContext::Local,
512+
};
513+
let window = "other-*";
514+
let webview = "main-*";
515+
516+
let resolved_cmd = ResolvedCommand {
517+
windows: vec![Pattern::new(window).unwrap()],
518+
webviews: vec![Pattern::new(webview).unwrap()],
519+
..Default::default()
520+
};
521+
let allowed_commands = [(command.clone(), resolved_cmd.clone())]
522+
.into_iter()
523+
.collect();
524+
525+
let authority = RuntimeAuthority::new(Resolved {
526+
allowed_commands,
527+
..Default::default()
528+
});
529+
530+
assert_eq!(
531+
authority.resolve_access(
532+
&command.name,
533+
window,
534+
&webview.replace('*', "something"),
488535
&Origin::Local
489536
),
490537
Some(&resolved_cmd)
@@ -501,6 +548,7 @@ mod tests {
501548
},
502549
};
503550
let window = "main";
551+
let webview = "main";
504552

505553
let resolved_cmd = ResolvedCommand {
506554
windows: vec![Pattern::new(window).unwrap()],
@@ -520,6 +568,7 @@ mod tests {
520568
authority.resolve_access(
521569
&command.name,
522570
window,
571+
webview,
523572
&Origin::Remote {
524573
domain: domain.into()
525574
}
@@ -538,6 +587,7 @@ mod tests {
538587
},
539588
};
540589
let window = "main";
590+
let webview = "main";
541591

542592
let resolved_cmd = ResolvedCommand {
543593
windows: vec![Pattern::new(window).unwrap()],
@@ -557,6 +607,7 @@ mod tests {
557607
authority.resolve_access(
558608
&command.name,
559609
window,
610+
webview,
560611
&Origin::Remote {
561612
domain: domain.replace('*', "studio")
562613
}
@@ -572,6 +623,7 @@ mod tests {
572623
context: ExecutionContext::Local,
573624
};
574625
let window = "main";
626+
let webview = "main";
575627

576628
let resolved_cmd = ResolvedCommand {
577629
windows: vec![Pattern::new(window).unwrap()],
@@ -591,6 +643,7 @@ mod tests {
591643
.resolve_access(
592644
&command.name,
593645
window,
646+
webview,
594647
&Origin::Remote {
595648
domain: "tauri.app".into()
596649
}
@@ -605,6 +658,7 @@ mod tests {
605658
context: ExecutionContext::Local,
606659
};
607660
let window = "main";
661+
let webview = "main";
608662
let windows = vec![Pattern::new(window).unwrap()];
609663
let allowed_commands = [(
610664
command.clone(),
@@ -632,7 +686,7 @@ mod tests {
632686
});
633687

634688
assert!(authority
635-
.resolve_access(&command.name, window, &Origin::Local)
689+
.resolve_access(&command.name, window, webview, &Origin::Local)
636690
.is_none());
637691
}
638692
}

core/tauri/src/webview/mod.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -1121,7 +1121,12 @@ fn main() {
11211121
};
11221122
let resolved_acl = manager
11231123
.runtime_authority
1124-
.resolve_access(&request.cmd, &message.webview.webview.label, &acl_origin)
1124+
.resolve_access(
1125+
&request.cmd,
1126+
message.webview.label(),
1127+
message.webview.window().label(),
1128+
&acl_origin,
1129+
)
11251130
.cloned();
11261131

11271132
let mut invoke = Invoke {
@@ -1145,7 +1150,8 @@ fn main() {
11451150
.reject(manager.runtime_authority.resolve_access_message(
11461151
plugin,
11471152
&command_name,
1148-
&invoke.message.webview.webview.label,
1153+
invoke.message.webview.window().label(),
1154+
invoke.message.webview.label(),
11491155
&acl_origin,
11501156
));
11511157
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
identifier = "run-app"
2+
description = "app capability"
3+
windows = ["main"]
4+
webviews = ["child1", "child2"]
5+
permissions = ["ping:allow-ping"]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
["ping"]

core/tests/acl/fixtures/snapshots/acl_tests__tests__basic-ping.snap

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
---
22
source: core/tests/acl/src/lib.rs
3-
assertion_line: 59
43
expression: resolved
54
---
65
Resolved {
@@ -29,6 +28,7 @@ Resolved {
2928
is_recursive: false,
3029
},
3130
],
31+
webviews: [],
3232
scope: None,
3333
},
3434
},

core/tests/acl/fixtures/snapshots/acl_tests__tests__file-explorer-remote.snap

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
---
22
source: core/tests/acl/src/lib.rs
3-
assertion_line: 59
43
expression: resolved
54
---
65
Resolved {
@@ -63,6 +62,7 @@ Resolved {
6362
is_recursive: false,
6463
},
6564
],
65+
webviews: [],
6666
scope: None,
6767
},
6868
CommandKey {
@@ -123,6 +123,7 @@ Resolved {
123123
is_recursive: false,
124124
},
125125
],
126+
webviews: [],
126127
scope: None,
127128
},
128129
},

core/tests/acl/fixtures/snapshots/acl_tests__tests__file-explorer.snap

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
---
22
source: core/tests/acl/src/lib.rs
3-
assertion_line: 59
43
expression: resolved
54
---
65
Resolved {
@@ -29,6 +28,7 @@ Resolved {
2928
is_recursive: false,
3029
},
3130
],
31+
webviews: [],
3232
scope: None,
3333
},
3434
CommandKey {
@@ -55,6 +55,7 @@ Resolved {
5555
is_recursive: false,
5656
},
5757
],
58+
webviews: [],
5859
scope: None,
5960
},
6061
},

0 commit comments

Comments
 (0)