/
githubmirror
/
tauri
Обзор
Документация
Войти
/
githubmirror
/
tauri
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
dev
crates/tauri-build/src/acl.rs
443 строки
14 KB
Tony
chore: fix some typos (#14334)
20 окт 2025, 17:51
Не верифицирован
20 окт 2025, 17:51
752c923
Код
Авторство
О чём код?
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT use std::{ collections::{BTreeMap, HashMap}, env, fs, path::{Path, PathBuf}, }; use anyhow::{Context, Result}; use tauri_utils::{ acl::{ capability::Capability, manifest::{Manifest, PermissionFile}, schema::CAPABILITIES_SCHEMA_FOLDER_PATH, ACL_MANIFESTS_FILE_NAME, APP_ACL_KEY, CAPABILITIES_FILE_NAME, }, platform::Target, write_if_changed, }; use crate::Attributes; /// Definition of a plugin that is part of the Tauri application instead of having its own crate. /// /// By default it generates a plugin manifest that parses permissions from the `permissions/$plugin-name` directory. /// To change the glob pattern that is used to find permissions, use [`Self::permissions_path_pattern`]. /// /// To autogenerate permissions for each of the plugin commands, see [`Self::commands`]. #[derive(Debug, Default, Clone)] pub struct InlinedPlugin { commands: &'static [&'static str], permissions_path_pattern: Option<&'static str>, default: Option<DefaultPermissionRule>, } /// Variants of a generated default permission that can be used on an [`InlinedPlugin`]. #[derive(Debug, Clone)] pub enum DefaultPermissionRule { /// Allow all commands from [`InlinedPlugin::commands`]. AllowAllCommands, /// Allow the given list of permissions. /// /// Note that the list refers to permissions instead of command names, /// so for example a command called `execute` would need to be allowed as `allow-execute`. Allow(Vec<String>), } impl InlinedPlugin { pub fn new() -> Self { Self::default() } /// Define a list of commands that gets permissions autogenerated in the format of `allow-$command` and `deny-$command` /// where $command is the command name in snake_case. pub fn commands(mut self, commands: &'static [&'static str]) -> Self { self.commands = commands; self } /// Sets a glob pattern that is used to find the permissions of this inlined plugin. /// /// **Note:** You must emit [rerun-if-changed] instructions for the plugin permissions directory. /// /// By default it is `./permissions/$plugin-name/**/*` pub fn permissions_path_pattern(mut self, pattern: &'static str) -> Self { self.permissions_path_pattern.replace(pattern); self } /// Creates a default permission for the plugin using the given rule. /// /// Alternatively you can pull a permission in the filesystem in the permissions directory, see [`Self::permissions_path_pattern`]. pub fn default_permission(mut self, default: DefaultPermissionRule) -> Self { self.default.replace(default); self } } /// Tauri application permission manifest. /// /// By default it generates a manifest that parses permissions from the `permissions` directory. /// To change the glob pattern that is used to find permissions, use [`Self::permissions_path_pattern`]. /// /// To autogenerate permissions for each of the app commands, see [`Self::commands`]. #[derive(Debug, Default, Clone, Copy)] pub struct AppManifest { commands: &'static [&'static str], permissions_path_pattern: Option<&'static str>, } impl AppManifest { pub fn new() -> Self { Self::default() } /// Define a list of commands that gets permissions autogenerated in the format of `allow-$command` and `deny-$command` /// where $command is the command name in snake_case. pub fn commands(mut self, commands: &'static [&'static str]) -> Self { self.commands = commands; self } /// Sets a glob pattern that is used to find the permissions of the app. /// /// **Note:** You must emit [rerun-if-changed] instructions for the permissions directory. /// /// By default it is `./permissions/**/*` ignoring any [`InlinedPlugin`]. pub fn permissions_path_pattern(mut self, pattern: &'static str) -> Self { self.permissions_path_pattern.replace(pattern); self } } /// Saves capabilities in a file inside the project, mainly to be read by tauri-cli. fn save_capabilities(capabilities: &BTreeMap<String, Capability>) -> Result<PathBuf> { let dir = Path::new(CAPABILITIES_SCHEMA_FOLDER_PATH); fs::create_dir_all(dir)?; let path = dir.join(CAPABILITIES_FILE_NAME); let json = serde_json::to_string(&capabilities)?; write_if_changed(&path, json)?; Ok(path) } /// Saves ACL manifests in a file inside the project, mainly to be read by tauri-cli. fn save_acl_manifests(acl_manifests: &BTreeMap<String, Manifest>) -> Result<PathBuf> { let dir = Path::new(CAPABILITIES_SCHEMA_FOLDER_PATH); fs::create_dir_all(dir)?; let path = dir.join(ACL_MANIFESTS_FILE_NAME); let json = serde_json::to_string(&acl_manifests)?; write_if_changed(&path, json)?; Ok(path) } /// Read plugin permissions and scope schema from env vars fn read_plugins_manifests() -> Result<BTreeMap<String, Manifest>> { use tauri_utils::acl; let permission_map = acl::build::read_permissions().context("failed to read plugin permissions")?; let mut global_scope_map = acl::build::read_global_scope_schemas().context("failed to read global scope schemas")?; let mut manifests = BTreeMap::new(); for (plugin_name, permission_files) in permission_map { let global_scope_schema = global_scope_map.remove(&plugin_name); let manifest = Manifest::new(permission_files, global_scope_schema); manifests.insert(plugin_name, manifest); } Ok(manifests) } struct InlinedPluginsAcl { manifests: BTreeMap<String, Manifest>, permission_files: BTreeMap<String, Vec<PermissionFile>>, } fn inline_plugins( out_dir: &Path, inlined_plugins: HashMap<&'static str, InlinedPlugin>, ) -> Result<InlinedPluginsAcl> { let mut acl_manifests = BTreeMap::new(); let mut permission_files_map = BTreeMap::new(); for (name, plugin) in inlined_plugins { let plugin_out_dir = out_dir.join("plugins").join(name); fs::create_dir_all(&plugin_out_dir)?; let mut permission_files = if plugin.commands.is_empty() { Vec::new() } else { let autogenerated = tauri_utils::acl::build::autogenerate_command_permissions( &plugin_out_dir, plugin.commands, "", false, ); let default_permissions = plugin.default.map(|default| match default { DefaultPermissionRule::AllowAllCommands => autogenerated.allowed, DefaultPermissionRule::Allow(permissions) => permissions, }); if let Some(default_permissions) = default_permissions { let default_permissions = default_permissions .iter() .map(|p| format!("\"{p}\"")) .collect::<Vec<String>>() .join(","); let default_permission = format!( r###"# Automatically generated - DO NOT EDIT! [default] permissions = [{default_permissions}] "### ); let default_permission_path = plugin_out_dir.join("default.toml"); write_if_changed(&default_permission_path, default_permission) .unwrap_or_else(|_| panic!("unable to autogenerate {default_permission_path:?}")); } tauri_utils::acl::build::define_permissions( &PathBuf::from(glob::Pattern::escape(&plugin_out_dir.to_string_lossy())) .join("*") .to_string_lossy(), name, &plugin_out_dir, |_| true, )? }; if let Some(pattern) = plugin.permissions_path_pattern { permission_files.extend(tauri_utils::acl::build::define_permissions( pattern, name, &plugin_out_dir, |_| true, )?); } else { let default_permissions_path = Path::new("permissions").join(name); if default_permissions_path.exists() { println!( "cargo:rerun-if-changed={}", default_permissions_path.display() ); } permission_files.extend(tauri_utils::acl::build::define_permissions( &PathBuf::from(glob::Pattern::escape( &default_permissions_path.to_string_lossy(), )) .join("**") .join("*") .to_string_lossy(), name, &plugin_out_dir, |_| true, )?); } permission_files_map.insert(name.into(), permission_files.clone()); let manifest = tauri_utils::acl::manifest::Manifest::new(permission_files, None); acl_manifests.insert(name.into(), manifest); } Ok(InlinedPluginsAcl { manifests: acl_manifests, permission_files: permission_files_map, }) } #[derive(Debug)] struct AppManifestAcl { manifest: Manifest, permission_files: Vec<PermissionFile>, } fn app_manifest_permissions( out_dir: &Path, manifest: AppManifest, inlined_plugins: &HashMap<&'static str, InlinedPlugin>, ) -> Result<AppManifestAcl> { let app_out_dir = out_dir.join("app-manifest"); fs::create_dir_all(&app_out_dir)?; let pkg_name = "__app__"; let mut permission_files = if manifest.commands.is_empty() { Vec::new() } else { let autogenerated_path = Path::new("./permissions/autogenerated"); tauri_utils::acl::build::autogenerate_command_permissions( autogenerated_path, manifest.commands, "", false, ); tauri_utils::acl::build::define_permissions( &autogenerated_path.join("*").to_string_lossy(), pkg_name, &app_out_dir, |_| true, )? }; if let Some(pattern) = manifest.permissions_path_pattern { permission_files.extend(tauri_utils::acl::build::define_permissions( pattern, pkg_name, &app_out_dir, |_| true, )?); } else { let default_permissions_path = Path::new("permissions"); if default_permissions_path.exists() { println!( "cargo:rerun-if-changed={}", default_permissions_path.display() ); } let permissions_root = env::current_dir()?.join("permissions"); let inlined_plugins_permissions: Vec<_> = inlined_plugins .keys() .map(|name| permissions_root.join(name)) .flat_map(|p| p.canonicalize()) .collect(); permission_files.extend(tauri_utils::acl::build::define_permissions( &default_permissions_path .join("**") .join("*") .to_string_lossy(), pkg_name, &app_out_dir, // filter out directories containing inlined plugins |p| { !inlined_plugins_permissions .iter() .any(|inlined_path| p.starts_with(inlined_path)) }, )?); } Ok(AppManifestAcl { permission_files: permission_files.clone(), manifest: tauri_utils::acl::manifest::Manifest::new(permission_files, None), }) } fn validate_capabilities( acl_manifests: &BTreeMap<String, Manifest>, capabilities: &BTreeMap<String, Capability>, ) -> Result<()> { let target = tauri_utils::platform::Target::from_triple(&std::env::var("TARGET").unwrap()); for capability in capabilities.values() { if !capability .platforms .as_ref() .map(|platforms| platforms.contains(&target)) .unwrap_or(true) { continue; } for permission_entry in &capability.permissions { let permission_id = permission_entry.identifier(); let key = permission_id.get_prefix().unwrap_or(APP_ACL_KEY); let permission_name = permission_id.get_base(); let permission_exists = acl_manifests .get(key) .map(|manifest| { // the default permission is always treated as valid, the CLI automatically adds it on the `tauri add` command permission_name == "default" || manifest.permissions.contains_key(permission_name) || manifest.permission_sets.contains_key(permission_name) }) .unwrap_or(false); if !permission_exists { let mut available_permissions = Vec::new(); for (key, manifest) in acl_manifests { let prefix = if key == APP_ACL_KEY { "".to_string() } else { format!("{key}:") }; if manifest.default_permission.is_some() { available_permissions.push(format!("{prefix}default")); } for p in manifest.permissions.keys() { available_permissions.push(format!("{prefix}{p}")); } for p in manifest.permission_sets.keys() { available_permissions.push(format!("{prefix}{p}")); } } anyhow::bail!( "Permission {} not found, expected one of {}", permission_id.get(), available_permissions.join(", ") ); } } } Ok(()) } pub fn build(out_dir: &Path, target: Target, attributes: &Attributes) -> super::Result<()> { let mut acl_manifests = read_plugins_manifests()?; let app_acl = app_manifest_permissions( out_dir, attributes.app_manifest, &attributes.inlined_plugins, )?; let has_app_manifest = app_acl.manifest.default_permission.is_some() || !app_acl.manifest.permission_sets.is_empty() || !app_acl.manifest.permissions.is_empty(); if has_app_manifest { acl_manifests.insert(APP_ACL_KEY.into(), app_acl.manifest); } let inline_plugins_acl = inline_plugins(out_dir, attributes.inlined_plugins.clone())?; acl_manifests.extend(inline_plugins_acl.manifests); let acl_manifests_path = save_acl_manifests(&acl_manifests)?; fs::copy(acl_manifests_path, out_dir.join(ACL_MANIFESTS_FILE_NAME))?; tauri_utils::acl::schema::generate_capability_schema(&acl_manifests, target)?; let capabilities = if let Some(pattern) = attributes.capabilities_path_pattern { tauri_utils::acl::build::parse_capabilities(pattern)? } else { println!("cargo:rerun-if-changed=capabilities"); tauri_utils::acl::build::parse_capabilities("./capabilities/**/*")? }; validate_capabilities(&acl_manifests, &capabilities)?; let capabilities_path = save_capabilities(&capabilities)?; fs::copy(capabilities_path, out_dir.join(CAPABILITIES_FILE_NAME))?; let mut permissions_map = inline_plugins_acl.permission_files; if has_app_manifest { permissions_map.insert(APP_ACL_KEY.to_string(), app_acl.permission_files); } tauri_utils::acl::build::generate_allowed_commands(out_dir, Some(capabilities), permissions_map)?; Ok(()) }