Skip to content

Commit 03098b5

Browse files
authored
feat(core): dynamic runtime capability (#9036)
* feat(core): dynamic runtime capability * local(), windows(), webviews() * enhance identation
1 parent a77be97 commit 03098b5

File tree

5 files changed

+157
-15
lines changed

5 files changed

+157
-15
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri": patch:enhance
3+
---
4+
5+
`Manager::add_capability` now allows adding a dynamically defined capability instead of only relying on static strings.

core/tauri/src/ipc/authority.rs

Lines changed: 143 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,18 @@ use std::fmt::{Debug, Display};
77
use std::sync::Arc;
88

99
use serde::de::DeserializeOwned;
10+
use serde::Serialize;
1011
use state::TypeMap;
1112

12-
use tauri_utils::acl::capability::CapabilityFile;
13-
use tauri_utils::acl::manifest::Manifest;
13+
use tauri_utils::acl::{
14+
capability::{Capability, CapabilityFile, PermissionEntry},
15+
manifest::Manifest,
16+
Value, APP_ACL_KEY,
17+
};
1418
use tauri_utils::acl::{
1519
resolved::{CommandKey, Resolved, ResolvedCommand, ResolvedScope, ScopeKey},
16-
ExecutionContext,
20+
ExecutionContext, Scopes,
1721
};
18-
use tauri_utils::acl::{Value, APP_ACL_KEY};
1922

2023
use crate::{ipc::InvokeError, sealed::ManagerBase, Runtime};
2124
use crate::{AppHandle, Manager};
@@ -62,6 +65,140 @@ impl Origin {
6265
}
6366
}
6467

68+
/// A capability that can be added at runtime.
69+
pub trait RuntimeCapability {
70+
/// Creates the capability file.
71+
fn build(self) -> CapabilityFile;
72+
}
73+
74+
impl<T: AsRef<str>> RuntimeCapability for T {
75+
fn build(self) -> CapabilityFile {
76+
self.as_ref().parse().expect("invalid capability")
77+
}
78+
}
79+
80+
/// A builder for a [`Capability`].
81+
pub struct CapabilityBuilder(Capability);
82+
83+
impl CapabilityBuilder {
84+
/// Creates a new capability builder with a unique identifier.
85+
pub fn new(identifier: impl Into<String>) -> Self {
86+
Self(Capability {
87+
identifier: identifier.into(),
88+
description: "".into(),
89+
remote: None,
90+
local: true,
91+
windows: Vec::new(),
92+
webviews: Vec::new(),
93+
permissions: Vec::new(),
94+
platforms: Vec::new(),
95+
})
96+
}
97+
98+
/// Allows this capability to be used by a remote URL.
99+
pub fn remote(mut self, url: String) -> Self {
100+
self
101+
.0
102+
.remote
103+
.get_or_insert_with(Default::default)
104+
.urls
105+
.push(url);
106+
self
107+
}
108+
109+
/// Whether this capability is applied on local app URLs or not. Defaults to `true`.
110+
pub fn local(mut self, local: bool) -> Self {
111+
self.0.local = local;
112+
self
113+
}
114+
115+
/// Link this capability to the given window label.
116+
pub fn window(mut self, window: impl Into<String>) -> Self {
117+
self.0.windows.push(window.into());
118+
self
119+
}
120+
121+
/// Link this capability to the a list of window labels.
122+
pub fn windows(mut self, windows: impl IntoIterator<Item = impl Into<String>>) -> Self {
123+
self.0.windows.extend(windows.into_iter().map(|w| w.into()));
124+
self
125+
}
126+
127+
/// Link this capability to the given webview label.
128+
pub fn webview(mut self, webview: impl Into<String>) -> Self {
129+
self.0.webviews.push(webview.into());
130+
self
131+
}
132+
133+
/// Link this capability to the a list of window labels.
134+
pub fn webviews(mut self, webviews: impl IntoIterator<Item = impl Into<String>>) -> Self {
135+
self
136+
.0
137+
.webviews
138+
.extend(webviews.into_iter().map(|w| w.into()));
139+
self
140+
}
141+
142+
/// Add a new permission to this capability.
143+
pub fn permission(mut self, permission: impl Into<String>) -> Self {
144+
let permission = permission.into();
145+
self.0.permissions.push(PermissionEntry::PermissionRef(
146+
permission
147+
.clone()
148+
.try_into()
149+
.unwrap_or_else(|_| panic!("invalid permission identifier '{permission}'")),
150+
));
151+
self
152+
}
153+
154+
/// Add a new scoped permission to this capability.
155+
pub fn permission_scoped<T: Serialize>(
156+
mut self,
157+
permission: impl Into<String>,
158+
allowed: Vec<T>,
159+
denied: Vec<T>,
160+
) -> Self {
161+
let permission = permission.into();
162+
let identifier = permission
163+
.clone()
164+
.try_into()
165+
.unwrap_or_else(|_| panic!("invalid permission identifier '{permission}'"));
166+
167+
let allowed_scope = allowed
168+
.into_iter()
169+
.map(|a| {
170+
serde_json::to_value(a)
171+
.expect("failed to serialize scope")
172+
.into()
173+
})
174+
.collect();
175+
let denied_scope = denied
176+
.into_iter()
177+
.map(|a| {
178+
serde_json::to_value(a)
179+
.expect("failed to serialize scope")
180+
.into()
181+
})
182+
.collect();
183+
let scope = Scopes {
184+
allow: Some(allowed_scope),
185+
deny: Some(denied_scope),
186+
};
187+
188+
self
189+
.0
190+
.permissions
191+
.push(PermissionEntry::ExtendedPermission { identifier, scope });
192+
self
193+
}
194+
}
195+
196+
impl RuntimeCapability for CapabilityBuilder {
197+
fn build(self) -> CapabilityFile {
198+
CapabilityFile::Capability(self.0)
199+
}
200+
}
201+
65202
impl RuntimeAuthority {
66203
#[doc(hidden)]
67204
pub fn new(acl: BTreeMap<String, Manifest>, resolved_acl: Resolved) -> Self {
@@ -102,9 +239,9 @@ impl RuntimeAuthority {
102239
}
103240

104241
/// Adds the given capability to the runtime authority.
105-
pub fn add_capability(&mut self, capability: CapabilityFile) -> crate::Result<()> {
242+
pub fn add_capability(&mut self, capability: impl RuntimeCapability) -> crate::Result<()> {
106243
let mut capabilities = BTreeMap::new();
107-
match capability {
244+
match capability.build() {
108245
CapabilityFile::Capability(c) => {
109246
capabilities.insert(c.identifier.clone(), c);
110247
}

core/tauri/src/ipc/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ mod command;
2424
pub(crate) mod format_callback;
2525
pub(crate) mod protocol;
2626

27-
pub use authority::{CommandScope, GlobalScope, Origin, RuntimeAuthority, ScopeObject, ScopeValue};
27+
pub use authority::{
28+
CapabilityBuilder, CommandScope, GlobalScope, Origin, RuntimeAuthority, RuntimeCapability,
29+
ScopeObject, ScopeValue,
30+
};
2831
pub use channel::{Channel, JavaScriptChannelId};
2932
pub use command::{private, CommandArg, CommandItem};
3033

core/tauri/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ pub use cocoa;
6969
#[doc(hidden)]
7070
pub use embed_plist;
7171
pub use error::{Error, Result};
72-
use ipc::RuntimeAuthority;
72+
use ipc::{RuntimeAuthority, RuntimeCapability};
7373
pub use resources::{Resource, ResourceId, ResourceTable};
7474
#[cfg(target_os = "ios")]
7575
#[doc(hidden)]
@@ -888,13 +888,13 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
888888
/// Ok(())
889889
/// });
890890
/// ```
891-
fn add_capability(&self, capability: &'static str) -> Result<()> {
891+
fn add_capability(&self, capability: impl RuntimeCapability) -> Result<()> {
892892
self
893893
.manager()
894894
.runtime_authority
895895
.lock()
896896
.unwrap()
897-
.add_capability(capability.parse().expect("invalid capability"))
897+
.add_capability(capability)
898898
}
899899
}
900900

core/tauri/src/webview/mod.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,7 @@ use tauri_runtime::{
2222
window::dpi::{PhysicalPosition, PhysicalSize, Position, Size},
2323
WindowDispatch,
2424
};
25-
use tauri_utils::{
26-
acl::APP_ACL_KEY,
27-
config::{WebviewUrl, WindowConfig},
28-
};
25+
use tauri_utils::config::{WebviewUrl, WindowConfig};
2926
pub use url::Url;
3027

3128
use crate::{
@@ -1192,7 +1189,7 @@ fn main() {
11921189
{
11931190
let (key, command_name) = plugin_command
11941191
.clone()
1195-
.unwrap_or_else(|| (APP_ACL_KEY, request.cmd.clone()));
1192+
.unwrap_or_else(|| (tauri_utils::acl::APP_ACL_KEY, request.cmd.clone()));
11961193
invoke.resolver.reject(
11971194
manager
11981195
.runtime_authority

0 commit comments

Comments
 (0)