mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-09-11 07:55:34 +03:00
initial draft for runtime authority
This commit is contained in:
parent
8467f138bb
commit
708cb9fa28
@ -489,18 +489,20 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
|
||||
.entry(member.clone())
|
||||
.or_insert_with(|| MemberResolution {
|
||||
member: member.clone(),
|
||||
capabilities: Default::default(),
|
||||
commands: Default::default(),
|
||||
});
|
||||
|
||||
for capability in &namespace.capabilities {
|
||||
let (plugin, capability) = manifests.find_capability(capability).unwrap_or_else(|| {
|
||||
panic!("could not find capability specification matching id {capability}")
|
||||
});
|
||||
let resolved_capability = member_resolution
|
||||
.capabilities
|
||||
.entry(capability.id.clone())
|
||||
.or_default();
|
||||
resolved_capability.features.extend(capability.features);
|
||||
member_resolution.commands.extend(
|
||||
capability
|
||||
.features
|
||||
.into_iter()
|
||||
.map(|f| format!("plugin:{plugin}|{f}"))
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{ffi::OsStr, str::FromStr};
|
||||
use std::{ffi::OsStr, fs::read_to_string, str::FromStr};
|
||||
|
||||
use base64::Engine;
|
||||
use proc_macro2::TokenStream;
|
||||
@ -15,6 +15,7 @@ use tauri_utils::config::{AppUrl, Config, PatternKind, WindowUrl};
|
||||
use tauri_utils::html::{
|
||||
inject_nonce_token, parse as parse_html, serialize_node as serialize_html_node,
|
||||
};
|
||||
use tauri_utils::namespace::NamespaceLockFile;
|
||||
|
||||
use crate::embedded_assets::{AssetOptions, CspHashes, EmbeddedAssets, EmbeddedAssetsError};
|
||||
|
||||
@ -421,6 +422,15 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
|
||||
}
|
||||
};
|
||||
|
||||
let lockfile_path = config_parent.join("tauri.namespace.lock");
|
||||
let lockfile: NamespaceLockFile = if lockfile_path.exists() {
|
||||
let lockfile = read_to_string(&lockfile_path)?;
|
||||
serde_json::from_str(&lockfile)?
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
let runtime_authority = runtime_authority_codegen(&root, lockfile);
|
||||
|
||||
Ok(quote!({
|
||||
#[allow(unused_mut, clippy::let_and_return)]
|
||||
let mut context = #root::Context::new(
|
||||
@ -431,12 +441,30 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
|
||||
#package_info,
|
||||
#info_plist,
|
||||
#pattern,
|
||||
#runtime_authority
|
||||
);
|
||||
#with_system_tray_icon_code
|
||||
context
|
||||
}))
|
||||
}
|
||||
|
||||
fn runtime_authority_codegen(root: &TokenStream, lockfile: NamespaceLockFile) -> TokenStream {
|
||||
let add_members = lockfile.resolution.iter().map(|r| {
|
||||
let member = &r.member;
|
||||
let commands = &r.commands;
|
||||
let resolution = quote!(#root::runtime_authority::MemberResolution {
|
||||
member: #member.into(),
|
||||
commands: vec![#(#commands)*.into()]
|
||||
});
|
||||
quote!(authority.add_member(#resolution);)
|
||||
});
|
||||
quote!({
|
||||
let mut authority = #root::runtime_authority::RuntimeAuthority::new();
|
||||
#(#add_members)*
|
||||
authority
|
||||
})
|
||||
}
|
||||
|
||||
fn ico_icon<P: AsRef<Path>>(
|
||||
root: &TokenStream,
|
||||
out_dir: &Path,
|
||||
|
@ -59,6 +59,12 @@ pub enum EmbeddedAssetsError {
|
||||
|
||||
#[error("version error: {0}")]
|
||||
Version(#[from] semver::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
Json(#[from] serde_json::Error),
|
||||
}
|
||||
|
||||
/// Represent a directory of assets that are compressed and embedded.
|
||||
|
@ -45,7 +45,7 @@ pub enum CodegenConfigError {
|
||||
ConfigError(#[from] ConfigError),
|
||||
}
|
||||
|
||||
/// Get the [`Config`] from the `TAURI_CONFIG` environmental variable, or read from the passed path.
|
||||
/// Get the [`Config`] from the passed path and merge it with the value from the `TAURI_CONFIG` environment variable.
|
||||
///
|
||||
/// If the passed path is relative, it should be relative to the current working directory of the
|
||||
/// compiling crate.
|
||||
@ -67,8 +67,8 @@ pub fn get_config(path: &Path) -> Result<(Config, PathBuf), CodegenConfigError>
|
||||
// it is impossible for the content of two separate configs to get mixed up. The chances are
|
||||
// already unlikely unless the developer goes out of their way to run the cli on a different
|
||||
// project than the target crate.
|
||||
let mut config =
|
||||
serde_json::from_value(tauri_utils::config::parse::read_from(parent.clone())?.0)?;
|
||||
let (config, config_path) = tauri_utils::config::parse::read_from(parent.clone())?;
|
||||
let mut config = serde_json::from_value(config)?;
|
||||
if let Ok(env) = std::env::var("TAURI_CONFIG") {
|
||||
let merge_config: serde_json::Value =
|
||||
serde_json::from_str(&env).map_err(CodegenConfigError::FormatInline)?;
|
||||
@ -84,5 +84,11 @@ pub fn get_config(path: &Path) -> Result<(Config, PathBuf), CodegenConfigError>
|
||||
// Reset working directory.
|
||||
std::env::set_current_dir(old_cwd).map_err(CodegenConfigError::CurrentDir)?;
|
||||
|
||||
Ok((config, parent))
|
||||
Ok((
|
||||
config,
|
||||
config_path
|
||||
.parent()
|
||||
.map(ToOwned::to_owned)
|
||||
.ok_or_else(|| CodegenConfigError::Parent(config_path))?,
|
||||
))
|
||||
}
|
||||
|
@ -6,28 +6,19 @@
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{config::Namespace, plugin::ManifestMap};
|
||||
|
||||
/// Resolved data associated with a member.
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||
pub struct MemberResolution {
|
||||
/// Member id.
|
||||
pub member: String,
|
||||
/// Resolved capabilities.
|
||||
pub capabilities: HashMap<String, ResolvedCapability>,
|
||||
}
|
||||
|
||||
/// A resolved capability.
|
||||
#[derive(Default, Deserialize, Serialize)]
|
||||
pub struct ResolvedCapability {
|
||||
/// List of features enabled.
|
||||
pub features: Vec<String>,
|
||||
/// List of commands enabled.
|
||||
pub commands: Vec<String>,
|
||||
}
|
||||
|
||||
/// Lock file of the namespaces configuration.
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||
pub struct NamespaceLockFile {
|
||||
/// Lock file version.
|
||||
pub version: u8,
|
||||
|
@ -118,7 +118,7 @@ impl Manifest {
|
||||
}
|
||||
|
||||
/// A collection mapping a plugin name to its manifest.
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||
pub struct ManifestMap(HashMap<String, Manifest>);
|
||||
|
||||
impl From<HashMap<String, Manifest>> for ManifestMap {
|
||||
|
@ -66,6 +66,7 @@ pub use cocoa;
|
||||
pub use embed_plist;
|
||||
/// The Tauri error enum.
|
||||
pub use error::Error;
|
||||
use runtime_authority::RuntimeAuthority;
|
||||
#[cfg(target_os = "ios")]
|
||||
#[doc(hidden)]
|
||||
pub use swift_rs;
|
||||
@ -85,6 +86,7 @@ mod hooks;
|
||||
mod manager;
|
||||
mod pattern;
|
||||
pub mod plugin;
|
||||
pub mod runtime_authority;
|
||||
mod vibrancy;
|
||||
pub mod window;
|
||||
use tauri_runtime as runtime;
|
||||
@ -389,6 +391,7 @@ pub struct Context<A: Assets> {
|
||||
pub(crate) package_info: PackageInfo,
|
||||
pub(crate) _info_plist: (),
|
||||
pub(crate) pattern: Pattern,
|
||||
pub(crate) runtime_authority: RuntimeAuthority,
|
||||
}
|
||||
|
||||
impl<A: Assets> fmt::Debug for Context<A> {
|
||||
@ -487,6 +490,7 @@ impl<A: Assets> Context<A> {
|
||||
package_info: PackageInfo,
|
||||
info_plist: (),
|
||||
pattern: Pattern,
|
||||
runtime_authority: RuntimeAuthority,
|
||||
) -> Self {
|
||||
Self {
|
||||
config,
|
||||
@ -498,6 +502,7 @@ impl<A: Assets> Context<A> {
|
||||
package_info,
|
||||
_info_plist: info_plist,
|
||||
pattern,
|
||||
runtime_authority,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,6 @@ use tauri_utils::{
|
||||
html::{SCRIPT_NONCE_TOKEN, STYLE_NONCE_TOKEN},
|
||||
};
|
||||
|
||||
use crate::app::{GlobalMenuEventListener, WindowMenuEvent};
|
||||
use crate::hooks::IpcJavascript;
|
||||
#[cfg(feature = "isolation")]
|
||||
use crate::hooks::IsolationJavascript;
|
||||
@ -51,6 +50,10 @@ use crate::{
|
||||
Context, EventLoopMessage, Icon, Invoke, Manager, Pattern, Runtime, Scopes, StateManager, Window,
|
||||
WindowEvent,
|
||||
};
|
||||
use crate::{
|
||||
app::{GlobalMenuEventListener, WindowMenuEvent},
|
||||
runtime_authority::RuntimeAuthority,
|
||||
};
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
||||
use crate::path::BaseDirectory;
|
||||
@ -234,6 +237,7 @@ pub struct InnerWindowManager<R: Runtime> {
|
||||
invoke_initialization_script: String,
|
||||
/// Application pattern.
|
||||
pub(crate) pattern: Pattern,
|
||||
pub(crate) runtime_authority: RuntimeAuthority,
|
||||
}
|
||||
|
||||
impl<R: Runtime> fmt::Debug for InnerWindowManager<R> {
|
||||
@ -334,6 +338,7 @@ impl<R: Runtime> WindowManager<R> {
|
||||
window_event_listeners: Arc::new(window_event_listeners),
|
||||
invoke_responder,
|
||||
invoke_initialization_script,
|
||||
runtime_authority: context.runtime_authority,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
30
core/tauri/src/runtime_authority.rs
Normal file
30
core/tauri/src/runtime_authority.rs
Normal file
@ -0,0 +1,30 @@
|
||||
//! Runtime authority.
|
||||
|
||||
pub use tauri_utils::namespace::MemberResolution;
|
||||
|
||||
/// The runtime authority verifies if a given IPC call is authorized.
|
||||
#[derive(Default)]
|
||||
pub struct RuntimeAuthority {
|
||||
members: Vec<MemberResolution>,
|
||||
}
|
||||
|
||||
impl RuntimeAuthority {
|
||||
/// Creates the default (empty) runtime authority.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Adds the given member resolution to this authority.
|
||||
pub fn add_member(&mut self, member: MemberResolution) {
|
||||
self.members.push(member);
|
||||
}
|
||||
|
||||
/// Determines if the given command is allowed for the member.
|
||||
pub fn is_allowed(&self, member: &str, command: &String) -> bool {
|
||||
if let Some(member) = self.members.iter().find(|m| m.member == member) {
|
||||
member.commands.contains(command)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
@ -1691,6 +1691,15 @@ impl<R: Runtime> Window<R> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if !manager
|
||||
.inner
|
||||
.runtime_authority
|
||||
.is_allowed(self.label(), &payload.cmd)
|
||||
{
|
||||
invoke.resolver.reject("Not allowed");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if payload.cmd.starts_with("plugin:") {
|
||||
if !is_local {
|
||||
let command = invoke.message.command.replace("plugin:", "");
|
||||
|
@ -3,10 +3,11 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
fn main() {
|
||||
tauri_build::build();
|
||||
|
||||
let mut codegen = tauri_build::CodegenContext::new();
|
||||
if !cfg!(feature = "custom-protocol") {
|
||||
codegen = codegen.dev();
|
||||
}
|
||||
codegen.build();
|
||||
tauri_build::build();
|
||||
}
|
||||
|
@ -48,13 +48,9 @@
|
||||
"resolution": [
|
||||
{
|
||||
"member": "main",
|
||||
"capabilities": {
|
||||
"allow-ping": {
|
||||
"features": [
|
||||
"ping"
|
||||
]
|
||||
}
|
||||
}
|
||||
"commands": [
|
||||
"plugin:sample|ping"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user