Skip to content

Commit ec13c28

Browse files
author
Devendra
committed
feat(acl): automatically remove outdated autogenerated permission files
1 parent 19f14df commit ec13c28

File tree

5 files changed

+564
-28
lines changed

5 files changed

+564
-28
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'tauri-utils': 'patch'
3+
'tauri-cli': 'patch'
4+
---
5+
6+
Automatically remove outdated autogenerated permission files.

FETCH_HEAD

Whitespace-only changes.

crates/tauri-utils/src/acl/build.rs

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
//! ACL items that are only useful inside of build script/codegen context.
66
77
use std::{
8-
collections::{BTreeMap, HashMap, HashSet},
8+
collections::{BTreeMap, HashMap},
99
env, fs,
1010
path::{Path, PathBuf},
1111
};
@@ -205,15 +205,15 @@ pub fn parse_capabilities(pattern: &str) -> Result<BTreeMap<String, Capability>,
205205
let mut capabilities_map = BTreeMap::new();
206206

207207
for path in glob::glob(pattern)?
208-
.flatten()
209-
// filter extension
208+
.flatten() // filter extension
210209
.filter(|p| {
211210
p.extension()
212211
.and_then(|e| e.to_str())
213212
.map(|e| CAPABILITY_FILE_EXTENSIONS.contains(&e))
214213
.unwrap_or_default()
215214
})
216215
// filter schema files
216+
// TODO: remove this before stable
217217
.filter(|p| p.parent().unwrap().file_name().unwrap() != CAPABILITIES_SCHEMA_FOLDER_NAME)
218218
{
219219
match CapabilityFile::load(&path)? {
@@ -244,15 +244,13 @@ pub fn parse_capabilities(pattern: &str) -> Result<BTreeMap<String, Capability>,
244244
}
245245

246246
/// Permissions that are generated from commands using [`autogenerate_command_permissions`].
247-
/// Autogenerated permission sets (allow + deny)
248247
pub struct AutogeneratedPermissions {
249-
/// List of automatically allowed commands
250-
pub allowed: Vec<String>,
251-
/// List of automatically denied commands
252-
pub denied: Vec<String>,
248+
/// The allow permissions generated from commands.
249+
pub allowed: Vec<String>,
250+
/// The deny permissions generated from commands.
251+
pub denied: Vec<String>,
253252
}
254253

255-
256254
/// Autogenerate permission files for a list of commands.
257255
pub fn autogenerate_command_permissions(
258256
path: &Path,
@@ -272,7 +270,6 @@ pub fn autogenerate_command_permissions(
272270
.collect::<PathBuf>()
273271
.join(PERMISSION_SCHEMAS_FOLDER_NAME)
274272
.join(PERMISSION_SCHEMA_FILE_NAME);
275-
276273
format!(
277274
"\n\"$schema\" = \"{}\"\n",
278275
dunce::simplified(&schema_path)
@@ -289,15 +286,15 @@ pub fn autogenerate_command_permissions(
289286
denied: Vec::new(),
290287
};
291288

292-
// -------------------------------------------------
293-
// Generate permission files
294-
// -------------------------------------------------
289+
let mut generated_files = Vec::new();
290+
295291
for command in commands {
296292
let slugified_command = command.replace('_', "-");
297293

298294
let toml = format!(
299295
r###"{license_header}# Automatically generated - DO NOT EDIT!
300296
{schema_entry}
297+
301298
[[permission]]
302299
identifier = "allow-{slugified_command}"
303300
description = "Enables the {command} command without any pre-configured scope."
@@ -314,6 +311,8 @@ commands.deny = ["{command}"]
314311
write_if_changed(&out_path, toml)
315312
.unwrap_or_else(|_| panic!("unable to autogenerate {out_path:?}"));
316313

314+
generated_files.push(out_path);
315+
317316
autogenerated
318317
.allowed
319318
.push(format!("allow-{slugified_command}"));
@@ -322,20 +321,18 @@ commands.deny = ["{command}"]
322321
.push(format!("deny-{slugified_command}"));
323322
}
324323

325-
// -------------------------------------------------
326-
// 🔥 CLEANUP BLOCK (final version)
327-
// -------------------------------------------------
328-
let expected: HashSet<String> = commands.iter().map(|cmd| format!("{}.toml", cmd)).collect();
324+
use std::collections::HashSet;
325+
326+
let expected_files: HashSet<PathBuf> = generated_files.into_iter().collect();
329327

330328
if let Ok(entries) = fs::read_dir(path) {
331329
for entry in entries.flatten() {
332-
if entry.file_type().map(|ft| ft.is_file()).unwrap_or(false) {
333-
let name = entry.file_name().to_string_lossy().to_string();
330+
if let Ok(file_type) = entry.file_type() {
331+
if file_type.is_file() {
332+
let file_path = entry.path();
334333

335-
if !expected.contains(&name) {
336-
let p = entry.path();
337-
if let Err(e) = fs::remove_file(&p) {
338-
eprintln!("[cleanup] Failed to remove outdated file {:?}: {}", p, e);
334+
if !expected_files.contains(&file_path) {
335+
let _ = fs::remove_file(&file_path);
339336
}
340337
}
341338
}
@@ -408,6 +405,10 @@ pub fn generate_docs(
408405
Ok(())
409406
}
410407

408+
// TODO: We have way too many duplicated code around getting the config files, e.g.
409+
// - crates/tauri-codegen/src/lib.rs (`get_config`)
410+
// - crates/tauri-build/src/lib.rs (`try_build`)
411+
// - crates/tauri-cli/src/helpers/config.rs (`get_internal`)
411412
/// Generate allowed commands file for the `generate_handler` macro to remove never allowed commands
412413
pub fn generate_allowed_commands(
413414
out_dir: &Path,
@@ -428,9 +429,12 @@ pub fn generate_allowed_commands(
428429
return Ok(());
429430
}
430431

432+
// It's safe to `unwrap` here since we have checked if the result is ok above
431433
let config_directory = PathBuf::from(remove_unused_commands_env_var.unwrap());
432434
let capabilities_path = config_directory.join("capabilities");
433-
435+
// Cargo re-builds if the variable points to an empty path,
436+
// so we check for exists here
437+
// see https://github.com/rust-lang/cargo/issues/4213
434438
if capabilities_path.exists() {
435439
println!("cargo:rerun-if-changed={}", capabilities_path.display());
436440
}
@@ -450,11 +454,13 @@ pub fn generate_allowed_commands(
450454

451455
println!("cargo:rerun-if-env-changed=TAURI_CONFIG");
452456

457+
// Set working directory to where `tauri.config.json` is, so that relative paths in it are parsed correctly.
453458
let old_cwd = std::env::current_dir()?;
454-
std::env::set_current_dir(&config_directory)?;
459+
std::env::set_current_dir(config_directory)?;
455460

456461
let config: Config = serde_json::from_value(config)?;
457462

463+
// Reset working directory.
458464
std::env::set_current_dir(old_cwd)?;
459465

460466
let acl: BTreeMap<String, crate::acl::manifest::Manifest> = permissions_map
@@ -477,18 +483,15 @@ pub fn generate_allowed_commands(
477483
glob::Pattern::escape(&capabilities_path.to_string_lossy())
478484
))?
479485
};
480-
481486
let capabilities = crate::acl::get_capabilities(&config, capabilities_from_files, None)?;
482487

483488
let permission_entries = capabilities
484489
.into_iter()
485490
.flat_map(|(_, capabilities)| capabilities.permissions);
486-
487491
let mut allowed_commands = AllowedCommands {
488492
has_app_acl: has_app_manifest(&acl),
489493
..Default::default()
490494
};
491-
492495
for permission_entry in permission_entries {
493496
let Ok(permissions) =
494497
crate::acl::resolved::get_permissions(permission_entry.identifier(), &acl)

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"example:api:dev": "pnpm run --filter \"api\" tauri dev"
2626
},
2727
"devDependencies": {
28+
"@changesets/cli": "^2.29.7",
2829
"prettier": "^3.6.2"
2930
},
3031
"minimumReleaseAge": 4320,

0 commit comments

Comments
 (0)