Skip to content

Commit

Permalink
feat(core): dynamic runtime capability (#9036)
Browse files Browse the repository at this point in the history
* feat(core): dynamic runtime capability

* local(), windows(), webviews()

* enhance identation
  • Loading branch information
lucasfernog authored Mar 1, 2024
1 parent a77be97 commit 03098b5
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 15 deletions.
5 changes: 5 additions & 0 deletions .changes/runtime-capability-dynamic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri": patch:enhance
---

`Manager::add_capability` now allows adding a dynamically defined capability instead of only relying on static strings.
149 changes: 143 additions & 6 deletions core/tauri/src/ipc/authority.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@ use std::fmt::{Debug, Display};
use std::sync::Arc;

use serde::de::DeserializeOwned;
use serde::Serialize;
use state::TypeMap;

use tauri_utils::acl::capability::CapabilityFile;
use tauri_utils::acl::manifest::Manifest;
use tauri_utils::acl::{
capability::{Capability, CapabilityFile, PermissionEntry},
manifest::Manifest,
Value, APP_ACL_KEY,
};
use tauri_utils::acl::{
resolved::{CommandKey, Resolved, ResolvedCommand, ResolvedScope, ScopeKey},
ExecutionContext,
ExecutionContext, Scopes,
};
use tauri_utils::acl::{Value, APP_ACL_KEY};

use crate::{ipc::InvokeError, sealed::ManagerBase, Runtime};
use crate::{AppHandle, Manager};
Expand Down Expand Up @@ -62,6 +65,140 @@ impl Origin {
}
}

/// A capability that can be added at runtime.
pub trait RuntimeCapability {
/// Creates the capability file.
fn build(self) -> CapabilityFile;
}

impl<T: AsRef<str>> RuntimeCapability for T {
fn build(self) -> CapabilityFile {
self.as_ref().parse().expect("invalid capability")
}
}

/// A builder for a [`Capability`].
pub struct CapabilityBuilder(Capability);

impl CapabilityBuilder {
/// Creates a new capability builder with a unique identifier.
pub fn new(identifier: impl Into<String>) -> Self {
Self(Capability {
identifier: identifier.into(),
description: "".into(),
remote: None,
local: true,
windows: Vec::new(),
webviews: Vec::new(),
permissions: Vec::new(),
platforms: Vec::new(),
})
}

/// Allows this capability to be used by a remote URL.
pub fn remote(mut self, url: String) -> Self {
self
.0
.remote
.get_or_insert_with(Default::default)
.urls
.push(url);
self
}

/// Whether this capability is applied on local app URLs or not. Defaults to `true`.
pub fn local(mut self, local: bool) -> Self {
self.0.local = local;
self
}

/// Link this capability to the given window label.
pub fn window(mut self, window: impl Into<String>) -> Self {
self.0.windows.push(window.into());
self
}

/// Link this capability to the a list of window labels.
pub fn windows(mut self, windows: impl IntoIterator<Item = impl Into<String>>) -> Self {
self.0.windows.extend(windows.into_iter().map(|w| w.into()));
self
}

/// Link this capability to the given webview label.
pub fn webview(mut self, webview: impl Into<String>) -> Self {
self.0.webviews.push(webview.into());
self
}

/// Link this capability to the a list of window labels.
pub fn webviews(mut self, webviews: impl IntoIterator<Item = impl Into<String>>) -> Self {
self
.0
.webviews
.extend(webviews.into_iter().map(|w| w.into()));
self
}

/// Add a new permission to this capability.
pub fn permission(mut self, permission: impl Into<String>) -> Self {
let permission = permission.into();
self.0.permissions.push(PermissionEntry::PermissionRef(
permission
.clone()
.try_into()
.unwrap_or_else(|_| panic!("invalid permission identifier '{permission}'")),
));
self
}

/// Add a new scoped permission to this capability.
pub fn permission_scoped<T: Serialize>(
mut self,
permission: impl Into<String>,
allowed: Vec<T>,
denied: Vec<T>,
) -> Self {
let permission = permission.into();
let identifier = permission
.clone()
.try_into()
.unwrap_or_else(|_| panic!("invalid permission identifier '{permission}'"));

let allowed_scope = allowed
.into_iter()
.map(|a| {
serde_json::to_value(a)
.expect("failed to serialize scope")
.into()
})
.collect();
let denied_scope = denied
.into_iter()
.map(|a| {
serde_json::to_value(a)
.expect("failed to serialize scope")
.into()
})
.collect();
let scope = Scopes {
allow: Some(allowed_scope),
deny: Some(denied_scope),
};

self
.0
.permissions
.push(PermissionEntry::ExtendedPermission { identifier, scope });
self
}
}

impl RuntimeCapability for CapabilityBuilder {
fn build(self) -> CapabilityFile {
CapabilityFile::Capability(self.0)
}
}

impl RuntimeAuthority {
#[doc(hidden)]
pub fn new(acl: BTreeMap<String, Manifest>, resolved_acl: Resolved) -> Self {
Expand Down Expand Up @@ -102,9 +239,9 @@ impl RuntimeAuthority {
}

/// Adds the given capability to the runtime authority.
pub fn add_capability(&mut self, capability: CapabilityFile) -> crate::Result<()> {
pub fn add_capability(&mut self, capability: impl RuntimeCapability) -> crate::Result<()> {
let mut capabilities = BTreeMap::new();
match capability {
match capability.build() {
CapabilityFile::Capability(c) => {
capabilities.insert(c.identifier.clone(), c);
}
Expand Down
5 changes: 4 additions & 1 deletion core/tauri/src/ipc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ mod command;
pub(crate) mod format_callback;
pub(crate) mod protocol;

pub use authority::{CommandScope, GlobalScope, Origin, RuntimeAuthority, ScopeObject, ScopeValue};
pub use authority::{
CapabilityBuilder, CommandScope, GlobalScope, Origin, RuntimeAuthority, RuntimeCapability,
ScopeObject, ScopeValue,
};
pub use channel::{Channel, JavaScriptChannelId};
pub use command::{private, CommandArg, CommandItem};

Expand Down
6 changes: 3 additions & 3 deletions core/tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ pub use cocoa;
#[doc(hidden)]
pub use embed_plist;
pub use error::{Error, Result};
use ipc::RuntimeAuthority;
use ipc::{RuntimeAuthority, RuntimeCapability};
pub use resources::{Resource, ResourceId, ResourceTable};
#[cfg(target_os = "ios")]
#[doc(hidden)]
Expand Down Expand Up @@ -888,13 +888,13 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
/// Ok(())
/// });
/// ```
fn add_capability(&self, capability: &'static str) -> Result<()> {
fn add_capability(&self, capability: impl RuntimeCapability) -> Result<()> {
self
.manager()
.runtime_authority
.lock()
.unwrap()
.add_capability(capability.parse().expect("invalid capability"))
.add_capability(capability)
}
}

Expand Down
7 changes: 2 additions & 5 deletions core/tauri/src/webview/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ use tauri_runtime::{
window::dpi::{PhysicalPosition, PhysicalSize, Position, Size},
WindowDispatch,
};
use tauri_utils::{
acl::APP_ACL_KEY,
config::{WebviewUrl, WindowConfig},
};
use tauri_utils::config::{WebviewUrl, WindowConfig};
pub use url::Url;

use crate::{
Expand Down Expand Up @@ -1192,7 +1189,7 @@ fn main() {
{
let (key, command_name) = plugin_command
.clone()
.unwrap_or_else(|| (APP_ACL_KEY, request.cmd.clone()));
.unwrap_or_else(|| (tauri_utils::acl::APP_ACL_KEY, request.cmd.clone()));
invoke.resolver.reject(
manager
.runtime_authority
Expand Down

0 comments on commit 03098b5

Please sign in to comment.