feat: Add option to generate default permissions for inlined plugins (#10559)

* feat: Add option to allow all commands by default

* option to use a list of permissions, move logic to tauri-build

* fix plugin

* add utils change file

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
This commit is contained in:
Norbiros 2024-08-10 20:34:49 +02:00 committed by GitHub
parent 213c0b1b8e
commit 0bb7b0f352
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 103 additions and 6 deletions

View File

@ -0,0 +1,5 @@
---
"tauri-build": patch:feat
---
Added `InlinedPlugin::default_permission` to autogenerate the default permission of an inlined plugin.

View File

@ -0,0 +1,5 @@
---
"tauri-utils": patch:enhance
---
Return autogenerated permissions from `autogenerate_command_permissions`.

View File

@ -42,6 +42,19 @@ const ACL_MANIFESTS_FILE_NAME: &str = "acl-manifests.json";
pub struct InlinedPlugin { pub struct InlinedPlugin {
commands: &'static [&'static str], commands: &'static [&'static str],
permissions_path_pattern: Option<&'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)]
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 { impl InlinedPlugin {
@ -65,6 +78,14 @@ impl InlinedPlugin {
self.permissions_path_pattern.replace(pattern); self.permissions_path_pattern.replace(pattern);
self 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. /// Tauri application permission manifest.
@ -337,12 +358,35 @@ pub fn inline_plugins(
let mut permission_files = if plugin.commands.is_empty() { let mut permission_files = if plugin.commands.is_empty() {
Vec::new() Vec::new()
} else { } else {
tauri_utils::acl::build::autogenerate_command_permissions( let autogenerated = tauri_utils::acl::build::autogenerate_command_permissions(
&plugin_out_dir, &plugin_out_dir,
plugin.commands, plugin.commands,
"", "",
false, 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::<Vec<String>>()
.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( tauri_utils::acl::build::define_permissions(
&plugin_out_dir.join("*").to_string_lossy(), &plugin_out_dir.join("*").to_string_lossy(),
name, name,
@ -384,6 +428,12 @@ pub fn inline_plugins(
Ok(acl_manifests) 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( pub fn app_manifest_permissions(
out_dir: &Path, out_dir: &Path,
manifest: AppManifest, manifest: AppManifest,

View File

@ -373,6 +373,17 @@ impl Attributes {
self 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<I>(mut self, plugins: I) -> Self
where
I: IntoIterator<Item = (&'static str, InlinedPlugin)>,
{
self.inlined_plugins.extend(plugins);
self
}
/// Sets the application manifest for the Access Control List. /// Sets the application manifest for the Access Control List.
/// ///
/// See [`AppManifest`] for more information. /// See [`AppManifest`] for more information.

View File

@ -388,13 +388,21 @@ fn parse_permissions(paths: Vec<PathBuf>) -> Result<Vec<PermissionFile>, Error>
Ok(permissions) Ok(permissions)
} }
/// Permissions that are generated from commands using [`autogenerate_command_permissions`].
pub struct AutogeneratedPermissions {
/// The allow permissions generated from commands.
pub allowed: Vec<String>,
/// The deny permissions generated from commands.
pub denied: Vec<String>,
}
/// Autogenerate permission files for a list of commands. /// Autogenerate permission files for a list of commands.
pub fn autogenerate_command_permissions( pub fn autogenerate_command_permissions(
path: &Path, path: &Path,
commands: &[&str], commands: &[&str],
license_header: &str, license_header: &str,
schema_ref: bool, schema_ref: bool,
) { ) -> AutogeneratedPermissions {
if !path.exists() { if !path.exists() {
create_dir_all(path).expect("unable to create autogenerated commands dir"); create_dir_all(path).expect("unable to create autogenerated commands dir");
} }
@ -418,8 +426,14 @@ pub fn autogenerate_command_permissions(
"".to_string() "".to_string()
}; };
let mut autogenerated = AutogeneratedPermissions {
allowed: Vec::new(),
denied: Vec::new(),
};
for command in commands { for command in commands {
let slugified_command = command.replace('_', "-"); let slugified_command = command.replace('_', "-");
let toml = format!( let toml = format!(
r###"{license_header}# Automatically generated - DO NOT EDIT! r###"{license_header}# Automatically generated - DO NOT EDIT!
{schema_entry} {schema_entry}
@ -436,9 +450,21 @@ commands.deny = ["{command}"]
); );
let out_path = path.join(format!("{command}.toml")); let out_path = path.join(format!("{command}.toml"));
if toml != read_to_string(&out_path).unwrap_or_default() { write_if_changed(&toml, &out_path);
std::fs::write(out_path, toml)
.unwrap_or_else(|_| panic!("unable to autogenerate ${command}.toml")); 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:?}"));
} }
} }