diff --git a/.changes/allow-commands-by-default.md b/.changes/allow-commands-by-default.md new file mode 100644 index 000000000..6f4114514 --- /dev/null +++ b/.changes/allow-commands-by-default.md @@ -0,0 +1,5 @@ +--- +"tauri-build": patch:feat +--- + +Added `InlinedPlugin::default_permission` to autogenerate the default permission of an inlined plugin. diff --git a/.changes/utils-autogenerated-command-return-list.md b/.changes/utils-autogenerated-command-return-list.md new file mode 100644 index 000000000..86286c773 --- /dev/null +++ b/.changes/utils-autogenerated-command-return-list.md @@ -0,0 +1,5 @@ +--- +"tauri-utils": patch:enhance +--- + +Return autogenerated permissions from `autogenerate_command_permissions`. diff --git a/core/tauri-build/src/acl.rs b/core/tauri-build/src/acl.rs index 219b542d5..2f916f1ae 100644 --- a/core/tauri-build/src/acl.rs +++ b/core/tauri-build/src/acl.rs @@ -42,6 +42,19 @@ const ACL_MANIFESTS_FILE_NAME: &str = "acl-manifests.json"; pub struct InlinedPlugin { commands: &'static [&'static str], permissions_path_pattern: Option<&'static str>, + default: Option, +} + +/// Variants of a generated default permission that can be used on an [`InlinedPlugin`]. +#[derive(Debug)] +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), } impl InlinedPlugin { @@ -65,6 +78,14 @@ impl InlinedPlugin { 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. @@ -337,12 +358,35 @@ pub fn inline_plugins( let mut permission_files = if plugin.commands.is_empty() { Vec::new() } else { - tauri_utils::acl::build::autogenerate_command_permissions( + 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_permission_toml = format!( + r###"# Automatically generated - DO NOT EDIT! +[default] +permissions = [{default_permissions}] +"###, + default_permissions = default_permissions + .iter() + .map(|p| format!("\"{p}\"")) + .collect::>() + .join(",") + ); + + let default_permission_toml_path = plugin_out_dir.join("default.toml"); + + write_if_changed(&default_permission_toml, &default_permission_toml_path); + } + tauri_utils::acl::build::define_permissions( &plugin_out_dir.join("*").to_string_lossy(), name, @@ -384,6 +428,12 @@ pub fn inline_plugins( Ok(acl_manifests) } +fn write_if_changed(content: &str, path: &Path) { + if content != read_to_string(path).unwrap_or_default() { + std::fs::write(path, content).unwrap_or_else(|_| panic!("unable to autogenerate {path:?}")); + } +} + pub fn app_manifest_permissions( out_dir: &Path, manifest: AppManifest, diff --git a/core/tauri-build/src/lib.rs b/core/tauri-build/src/lib.rs index b29c7baa9..1646a0f7e 100644 --- a/core/tauri-build/src/lib.rs +++ b/core/tauri-build/src/lib.rs @@ -373,6 +373,17 @@ impl Attributes { self } + /// Adds the given list of plugins to the list of inlined plugins (a plugin that is part of your application). + /// + /// See [`InlinedPlugin`] for more information. + pub fn plugins(mut self, plugins: I) -> Self + where + I: IntoIterator, + { + self.inlined_plugins.extend(plugins); + self + } + /// Sets the application manifest for the Access Control List. /// /// See [`AppManifest`] for more information. diff --git a/core/tauri-utils/src/acl/build.rs b/core/tauri-utils/src/acl/build.rs index 637dfc3be..3e1b686c3 100644 --- a/core/tauri-utils/src/acl/build.rs +++ b/core/tauri-utils/src/acl/build.rs @@ -388,13 +388,21 @@ fn parse_permissions(paths: Vec) -> Result, Error> Ok(permissions) } +/// Permissions that are generated from commands using [`autogenerate_command_permissions`]. +pub struct AutogeneratedPermissions { + /// The allow permissions generated from commands. + pub allowed: Vec, + /// The deny permissions generated from commands. + pub denied: Vec, +} + /// Autogenerate permission files for a list of commands. pub fn autogenerate_command_permissions( path: &Path, commands: &[&str], license_header: &str, schema_ref: bool, -) { +) -> AutogeneratedPermissions { if !path.exists() { create_dir_all(path).expect("unable to create autogenerated commands dir"); } @@ -418,8 +426,14 @@ pub fn autogenerate_command_permissions( "".to_string() }; + let mut autogenerated = AutogeneratedPermissions { + allowed: Vec::new(), + denied: Vec::new(), + }; + for command in commands { let slugified_command = command.replace('_', "-"); + let toml = format!( r###"{license_header}# Automatically generated - DO NOT EDIT! {schema_entry} @@ -436,9 +450,21 @@ commands.deny = ["{command}"] ); let out_path = path.join(format!("{command}.toml")); - if toml != read_to_string(&out_path).unwrap_or_default() { - std::fs::write(out_path, toml) - .unwrap_or_else(|_| panic!("unable to autogenerate ${command}.toml")); - } + write_if_changed(&toml, &out_path); + + autogenerated + .allowed + .push(format!("allow-{slugified_command}")); + autogenerated + .denied + .push(format!("deny-{slugified_command}")); + } + + autogenerated +} + +fn write_if_changed(content: &str, path: &Path) { + if content != read_to_string(path).unwrap_or_default() { + std::fs::write(path, content).unwrap_or_else(|_| panic!("unable to autogenerate {path:?}")); } }