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 {
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)]
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 {
@ -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::<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(
&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,

View File

@ -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<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.
///
/// See [`AppManifest`] for more information.

View File

@ -388,13 +388,21 @@ fn parse_permissions(paths: Vec<PathBuf>) -> Result<Vec<PermissionFile>, 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<String>,
/// The deny permissions generated from commands.
pub denied: Vec<String>,
}
/// 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:?}"));
}
}