Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): add Manager::add_capability, closes #8799 #8806

Merged
merged 9 commits into from
Feb 19, 2024
5 changes: 5 additions & 0 deletions .changes/acl-scope-refactor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri": patch:breaking
---

Removed the lifetime parameter from `ipc::GlobalScope` and `ipc::CommandScope`.
5 changes: 5 additions & 0 deletions .changes/runtime-add-capability.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri": patch:enhance
---

Added `Manager::add_capability` to add a capability file at runtime.
1 change: 1 addition & 0 deletions core/tauri-utils/src/acl/resolved.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ struct ResolvedCommandTemp {
pub scope: Vec<ScopeKey>,
pub resolved_scope_key: Option<ScopeKey>,
}

fn resolve_command(
commands: &mut BTreeMap<CommandKey, ResolvedCommandTemp>,
command: String,
Expand Down
88 changes: 45 additions & 43 deletions core/tauri/src/ipc/authority.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

use std::collections::BTreeMap;
use std::fmt::{Debug, Display};
use std::{collections::BTreeMap, ops::Deref};
use std::sync::Arc;

use serde::de::DeserializeOwned;
use state::TypeMap;
Expand Down Expand Up @@ -335,11 +336,18 @@ impl RuntimeAuthority {
/// List of allowed and denied objects that match either the command-specific or plugin global scope criterias.
#[derive(Debug)]
pub struct ScopeValue<T: ScopeObject> {
allow: Vec<T>,
deny: Vec<T>,
allow: Arc<Vec<T>>,
deny: Arc<Vec<T>>,
}

impl<T: ScopeObject> ScopeValue<T> {
fn clone(&self) -> Self {
Self {
allow: self.allow.clone(),
deny: self.deny.clone(),
}
}

/// What this access scope allows.
pub fn allows(&self) -> &Vec<T> {
&self.allow
Expand All @@ -351,27 +359,11 @@ impl<T: ScopeObject> ScopeValue<T> {
}
}

#[derive(Debug)]
enum OwnedOrRef<'a, T: Debug> {
Owned(T),
Ref(&'a T),
}

impl<'a, T: Debug> Deref for OwnedOrRef<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self {
Self::Owned(t) => t,
Self::Ref(r) => r,
}
}
}

/// Access scope for a command that can be retrieved directly in the command function.
#[derive(Debug)]
pub struct CommandScope<'a, T: ScopeObject>(OwnedOrRef<'a, ScopeValue<T>>);
pub struct CommandScope<T: ScopeObject>(ScopeValue<T>);

impl<'a, T: ScopeObject> CommandScope<'a, T> {
impl<T: ScopeObject> CommandScope<T> {
/// What this access scope allows.
pub fn allows(&self) -> &Vec<T> {
&self.0.allow
Expand All @@ -383,33 +375,35 @@ impl<'a, T: ScopeObject> CommandScope<'a, T> {
}
}

impl<'a, R: Runtime, T: ScopeObject> CommandArg<'a, R> for CommandScope<'a, T> {
impl<'a, R: Runtime, T: ScopeObject> CommandArg<'a, R> for CommandScope<T> {
/// Grabs the [`ResolvedScope`] from the [`CommandItem`] and returns the associated [`CommandScope`].
fn from_command(command: CommandItem<'a, R>) -> Result<Self, InvokeError> {
if let Some(scope_id) = command.acl.as_ref().and_then(|resolved| resolved.scope) {
Ok(CommandScope(OwnedOrRef::Ref(
Ok(CommandScope(
command
.message
.webview
.manager()
.runtime_authority
.lock()
.unwrap()
.scope_manager
.get_command_scope_typed(command.message.webview.app_handle(), &scope_id)?,
)))
))
} else {
Ok(CommandScope(OwnedOrRef::Owned(ScopeValue {
allow: Vec::new(),
deny: Vec::new(),
})))
Ok(CommandScope(ScopeValue {
allow: Default::default(),
deny: Default::default(),
}))
}
}
}

/// Global access scope that can be retrieved directly in the command function.
#[derive(Debug)]
pub struct GlobalScope<'a, T: ScopeObject>(&'a ScopeValue<T>);
pub struct GlobalScope<T: ScopeObject>(ScopeValue<T>);

impl<'a, T: ScopeObject> GlobalScope<'a, T> {
impl<T: ScopeObject> GlobalScope<T> {
/// What this access scope allows.
pub fn allows(&self) -> &Vec<T> {
&self.0.allow
Expand All @@ -421,7 +415,7 @@ impl<'a, T: ScopeObject> GlobalScope<'a, T> {
}
}

impl<'a, R: Runtime, T: ScopeObject> CommandArg<'a, R> for GlobalScope<'a, T> {
impl<'a, R: Runtime, T: ScopeObject> CommandArg<'a, R> for GlobalScope<T> {
/// Grabs the [`ResolvedScope`] from the [`CommandItem`] and returns the associated [`GlobalScope`].
fn from_command(command: CommandItem<'a, R>) -> Result<Self, InvokeError> {
command
Expand All @@ -437,6 +431,8 @@ impl<'a, R: Runtime, T: ScopeObject> CommandArg<'a, R> for GlobalScope<'a, T> {
.webview
.manager()
.runtime_authority
.lock()
.unwrap()
.scope_manager
.get_global_scope_typed(command.message.webview.app_handle(), plugin)
.map_err(InvokeError::from_error)
Expand Down Expand Up @@ -476,9 +472,9 @@ impl ScopeManager {
&self,
app: &AppHandle<R>,
plugin: &str,
) -> crate::Result<&ScopeValue<T>> {
match self.global_scope_cache.try_get() {
Some(cached) => Ok(cached),
) -> crate::Result<ScopeValue<T>> {
match self.global_scope_cache.try_get::<ScopeValue<T>>() {
Some(cached) => Ok(cached.clone()),
None => {
let mut allow: Vec<T> = Vec::new();
let mut deny: Vec<T> = Vec::new();
Expand All @@ -498,9 +494,12 @@ impl ScopeManager {
}
}

let scope = ScopeValue { allow, deny };
let _ = self.global_scope_cache.set(scope);
Ok(self.global_scope_cache.get())
let scope = ScopeValue {
allow: Arc::new(allow),
deny: Arc::new(deny),
};
self.global_scope_cache.set(scope.clone());
Ok(scope)
}
}
}
Expand All @@ -509,10 +508,10 @@ impl ScopeManager {
&self,
app: &AppHandle<R>,
key: &ScopeKey,
) -> crate::Result<&ScopeValue<T>> {
) -> crate::Result<ScopeValue<T>> {
let cache = self.command_cache.get(key).unwrap();
match cache.try_get() {
Some(cached) => Ok(cached),
match cache.try_get::<ScopeValue<T>>() {
Some(cached) => Ok(cached.clone()),
None => {
let resolved_scope = self
.command_scope
Expand All @@ -535,10 +534,13 @@ impl ScopeManager {
);
}

let value = ScopeValue { allow, deny };
let value = ScopeValue {
allow: Arc::new(allow),
deny: Arc::new(deny),
};

let _ = cache.set(value);
Ok(cache.get())
let _ = cache.set(value.clone());
Ok(value)
}
}
}
Expand Down
22 changes: 22 additions & 0 deletions core/tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,28 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
fn path(&self) -> &crate::path::PathResolver<R> {
self.state::<crate::path::PathResolver<R>>().inner()
}

/// Adds a capability to the app.
///
/// # Examples
/// ```
/// use tauri::Manager;
///
/// tauri::Builder::default()
/// .setup(|app| {
/// #[cfg(feature = "beta")]
/// app.add_capability(include_str!("../capabilities/beta.json"));
/// Ok(())
/// });
/// ```
fn add_capability(&self, capability: &'static str) -> Result<()> {
self
.manager()
.runtime_authority
.lock()
.unwrap()
.add_capability(capability.parse().expect("invalid capability"))
}
}

/// Prevent implementation details from leaking out of the [`Manager`] trait.
Expand Down
4 changes: 2 additions & 2 deletions core/tauri/src/manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ pub struct Asset {

#[default_runtime(crate::Wry, wry)]
pub struct AppManager<R: Runtime> {
pub runtime_authority: RuntimeAuthority,
pub runtime_authority: Mutex<RuntimeAuthority>,
pub window: window::WindowManager<R>,
pub webview: webview::WebviewManager<R>,
#[cfg(all(desktop, feature = "tray-icon"))]
Expand Down Expand Up @@ -245,7 +245,7 @@ impl<R: Runtime> AppManager<R> {
}

Self {
runtime_authority: context.runtime_authority,
runtime_authority: Mutex::new(context.runtime_authority),
window: window::WindowManager {
windows: Mutex::default(),
default_icon: context.default_window_icon,
Expand Down
4 changes: 3 additions & 1 deletion core/tauri/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,13 @@ impl<R: Runtime, C: DeserializeOwned> PluginApi<R, C> {
}

/// Gets the global scope defined on the permissions that are part of the app ACL.
pub fn scope<T: ScopeObject>(&self) -> crate::Result<&ScopeValue<T>> {
pub fn scope<T: ScopeObject>(&self) -> crate::Result<ScopeValue<T>> {
self
.handle
.manager
.runtime_authority
.lock()
.unwrap()
.scope_manager
.get_global_scope_typed(&self.handle, self.name)
}
Expand Down
24 changes: 15 additions & 9 deletions core/tauri/src/webview/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1118,6 +1118,8 @@ fn main() {
};
let resolved_acl = manager
.runtime_authority
.lock()
.unwrap()
.resolve_access(
&request.cmd,
message.webview.label(),
Expand All @@ -1142,15 +1144,19 @@ fn main() {
if request.cmd != crate::ipc::channel::FETCH_CHANNEL_DATA_COMMAND && invoke.acl.is_none() {
#[cfg(debug_assertions)]
{
invoke
.resolver
.reject(manager.runtime_authority.resolve_access_message(
plugin,
&command_name,
invoke.message.webview.window().label(),
invoke.message.webview.label(),
&acl_origin,
));
invoke.resolver.reject(
manager
.runtime_authority
.lock()
.unwrap()
.resolve_access_message(
plugin,
&command_name,
invoke.message.webview.window().label(),
invoke.message.webview.label(),
&acl_origin,
),
);
}
#[cfg(not(debug_assertions))]
invoke
Expand Down
Loading