mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-05-31 13:39:36 +02:00
feat: update to tauri beta, add permissions (#862)
Co-authored-by: Lucas Nogueira <lucas@tauri.app> Co-authored-by: Lucas Nogueira <lucas@crabnebula.dev>
This commit is contained in:
+82
-48
@@ -4,11 +4,10 @@
|
||||
|
||||
use crate::open::Program;
|
||||
use crate::process::Command;
|
||||
use crate::{Manager, Runtime};
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use tauri::ipc::ScopeObject;
|
||||
use tauri::Manager;
|
||||
|
||||
/// Allowed representation of `Execute` command arguments.
|
||||
#[derive(Debug, Clone, serde::Deserialize)]
|
||||
@@ -55,19 +54,12 @@ impl From<Vec<String>> for ExecuteArgs {
|
||||
}
|
||||
}
|
||||
|
||||
/// Shell scope configuration.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ScopeConfig {
|
||||
/// The validation regex that `shell > open` paths must match against.
|
||||
pub open: Option<Regex>,
|
||||
|
||||
/// All allowed commands, using their unique command name as the keys.
|
||||
pub scopes: HashMap<String, ScopeAllowedCommand>,
|
||||
}
|
||||
|
||||
/// A configured scoped shell command.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ScopeAllowedCommand {
|
||||
/// Name of the command (key).
|
||||
pub name: String,
|
||||
|
||||
/// The shell command to be called.
|
||||
pub command: std::path::PathBuf,
|
||||
|
||||
@@ -78,6 +70,47 @@ pub struct ScopeAllowedCommand {
|
||||
pub sidecar: bool,
|
||||
}
|
||||
|
||||
impl ScopeObject for ScopeAllowedCommand {
|
||||
type Error = crate::Error;
|
||||
fn deserialize<R: tauri::Runtime>(
|
||||
app: &tauri::AppHandle<R>,
|
||||
raw: tauri::utils::acl::Value,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let scope = serde_json::from_value::<crate::scope_entry::Entry>(raw.into())?;
|
||||
|
||||
let args = match scope.args.clone() {
|
||||
crate::scope_entry::ShellAllowedArgs::Flag(true) => None,
|
||||
crate::scope_entry::ShellAllowedArgs::Flag(false) => Some(Vec::new()),
|
||||
crate::scope_entry::ShellAllowedArgs::List(list) => {
|
||||
let list = list.into_iter().map(|arg| match arg {
|
||||
crate::scope_entry::ShellAllowedArg::Fixed(fixed) => {
|
||||
crate::scope::ScopeAllowedArg::Fixed(fixed)
|
||||
}
|
||||
crate::scope_entry::ShellAllowedArg::Var { validator } => {
|
||||
let validator = Regex::new(&validator)
|
||||
.unwrap_or_else(|e| panic!("invalid regex {validator}: {e}"));
|
||||
crate::scope::ScopeAllowedArg::Var { validator }
|
||||
}
|
||||
});
|
||||
Some(list.collect())
|
||||
}
|
||||
};
|
||||
|
||||
let command = if let Ok(path) = app.path().parse(&scope.command) {
|
||||
path
|
||||
} else {
|
||||
scope.command.clone()
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
name: scope.name,
|
||||
command,
|
||||
args,
|
||||
sidecar: scope.sidecar,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A configured argument to a scoped shell command.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ScopeAllowedArg {
|
||||
@@ -98,9 +131,18 @@ impl ScopeAllowedArg {
|
||||
}
|
||||
}
|
||||
|
||||
/// Scope for filesystem access.
|
||||
/// Scope for the open command
|
||||
pub struct OpenScope {
|
||||
/// The validation regex that `shell > open` paths must match against.
|
||||
pub open: Option<Regex>,
|
||||
}
|
||||
|
||||
/// Scope for shell process spawning.
|
||||
#[derive(Clone)]
|
||||
pub struct Scope(ScopeConfig);
|
||||
pub struct ShellScope<'a> {
|
||||
/// All allowed commands, using their unique command name as the keys.
|
||||
pub scopes: Vec<&'a ScopeAllowedCommand>,
|
||||
}
|
||||
|
||||
/// All errors that can happen while validating a scoped command.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
@@ -147,17 +189,33 @@ pub enum Error {
|
||||
Io(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
/// Creates a new shell scope.
|
||||
pub(crate) fn new<R: Runtime, M: Manager<R>>(manager: &M, mut scope: ScopeConfig) -> Self {
|
||||
for cmd in scope.scopes.values_mut() {
|
||||
if let Ok(path) = manager.path().parse(&cmd.command) {
|
||||
cmd.command = path;
|
||||
impl OpenScope {
|
||||
/// Open a path in the default (or specified) browser.
|
||||
///
|
||||
/// The path is validated against the `plugins > shell > open` validation regex, which
|
||||
/// defaults to `^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+`.
|
||||
pub fn open(&self, path: &str, with: Option<Program>) -> Result<(), Error> {
|
||||
// ensure we pass validation if the configuration has one
|
||||
if let Some(regex) = &self.open {
|
||||
if !regex.is_match(path) {
|
||||
return Err(Error::Validation {
|
||||
index: 0,
|
||||
validation: regex.as_str().into(),
|
||||
});
|
||||
}
|
||||
}
|
||||
Self(scope)
|
||||
}
|
||||
|
||||
// The prevention of argument escaping is handled by the usage of std::process::Command::arg by
|
||||
// the `open` dependency. This behavior should be re-confirmed during upgrades of `open`.
|
||||
match with.map(Program::name) {
|
||||
Some(program) => ::open::with_detached(path, program),
|
||||
None => ::open::that_detached(path),
|
||||
}
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ShellScope<'a> {
|
||||
/// Validates argument inputs and creates a Tauri sidecar [`Command`].
|
||||
pub fn prepare_sidecar(
|
||||
&self,
|
||||
@@ -180,7 +238,7 @@ impl Scope {
|
||||
args: ExecuteArgs,
|
||||
sidecar: Option<&str>,
|
||||
) -> Result<Command, Error> {
|
||||
let command = match self.0.scopes.get(command_name) {
|
||||
let command = match self.scopes.iter().find(|s| s.name == command_name) {
|
||||
Some(command) => command,
|
||||
None => return Err(Error::NotFound(command_name.into())),
|
||||
};
|
||||
@@ -245,28 +303,4 @@ impl Scope {
|
||||
|
||||
Ok(command.args(args))
|
||||
}
|
||||
|
||||
/// Open a path in the default (or specified) browser.
|
||||
///
|
||||
/// The path is validated against the `plugins > shell > open` validation regex, which
|
||||
/// defaults to `^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+`.
|
||||
pub fn open(&self, path: &str, with: Option<Program>) -> Result<(), Error> {
|
||||
// ensure we pass validation if the configuration has one
|
||||
if let Some(regex) = &self.0.open {
|
||||
if !regex.is_match(path) {
|
||||
return Err(Error::Validation {
|
||||
index: 0,
|
||||
validation: regex.as_str().into(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// The prevention of argument escaping is handled by the usage of std::process::Command::arg by
|
||||
// the `open` dependency. This behavior should be re-confirmed during upgrades of `open`.
|
||||
match with.map(Program::name) {
|
||||
Some(program) => ::open::with_detached(path, program),
|
||||
None => ::open::that_detached(path),
|
||||
}
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user