Skip to content

Commit 7213b9e

Browse files
feat(cli/add): add default permission to capabilities (#9124)
* feat(cli/add): add default permission to capabilities also cleanup `tauri add` command * license headers & clippy * print permission name * do not error out if default permission is not set --------- Co-authored-by: Lucas Nogueira <lucas@tauri.app>
1 parent acdd768 commit 7213b9e

File tree

11 files changed

+247
-189
lines changed

11 files changed

+247
-189
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"tauri-build": patch:enhance
3+
"tauri-utils": patch:enhance
4+
---
5+
6+
Fallback to an empty permission set if the plugin did not define its `default` permissions.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'tauri-cli': 'patch:feat'
3+
'@tauri-apps/cli': 'patch:feat'
4+
---
5+
6+
Add default permission for a plugin to capabilities when using `tauri add <plugin>`.

core/tauri-build/src/acl.rs

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,14 @@ fn capabilities_schema(acl_manifests: &BTreeMap<String, Manifest>) -> RootSchema
131131
permission_schemas.push(schema_from(key, set_id, Some(&set.description)));
132132
}
133133

134-
if let Some(default) = &manifest.default_permission {
135-
permission_schemas.push(schema_from(
136-
key,
137-
"default",
138-
Some(default.description.as_ref()),
139-
));
140-
}
134+
permission_schemas.push(schema_from(
135+
key,
136+
"default",
137+
manifest
138+
.default_permission
139+
.as_ref()
140+
.map(|d| d.description.as_ref()),
141+
));
141142

142143
for (permission_id, permission) in &manifest.permissions {
143144
permission_schemas.push(schema_from(
@@ -198,9 +199,14 @@ fn capabilities_schema(acl_manifests: &BTreeMap<String, Manifest>) -> RootSchema
198199
};
199200

200201
let mut permission_schemas = Vec::new();
201-
if let Some(default) = &manifest.default_permission {
202-
permission_schemas.push(schema_from(key, "default", Some(&default.description)));
203-
}
202+
permission_schemas.push(schema_from(
203+
key,
204+
"default",
205+
manifest
206+
.default_permission
207+
.as_ref()
208+
.map(|d| d.description.as_ref()),
209+
));
204210
for set in manifest.permission_sets.values() {
205211
permission_schemas.push(schema_from(key, &set.identifier, Some(&set.description)));
206212
}
@@ -471,12 +477,10 @@ pub fn validate_capabilities(
471477
let permission_exists = acl_manifests
472478
.get(key)
473479
.map(|manifest| {
474-
if permission_name == "default" {
475-
manifest.default_permission.is_some()
476-
} else {
477-
manifest.permissions.contains_key(permission_name)
478-
|| manifest.permission_sets.contains_key(permission_name)
479-
}
480+
// the default permission is always treated as valid, the CLI automatically adds it on the `tauri add` command
481+
permission_name == "default"
482+
|| manifest.permissions.contains_key(permission_name)
483+
|| manifest.permission_sets.contains_key(permission_name)
480484
})
481485
.unwrap_or(false);
482486

core/tauri-utils/src/acl/resolved.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -367,15 +367,8 @@ fn get_permissions<'a>(
367367
manifest
368368
.default_permission
369369
.as_ref()
370-
.ok_or_else(|| Error::UnknownPermission {
371-
key: if key == APP_ACL_KEY {
372-
"app manifest".to_string()
373-
} else {
374-
key.to_string()
375-
},
376-
permission: permission_name.to_string(),
377-
})
378-
.and_then(|default| get_permission_set_permissions(manifest, default))
370+
.map(|default| get_permission_set_permissions(manifest, default))
371+
.unwrap_or_else(|| Ok(Vec::new()))
379372
} else if let Some(set) = manifest.permission_sets.get(permission_name) {
380373
get_permission_set_permissions(manifest, set)
381374
} else if let Some(permission) = manifest.permissions.get(permission_name) {

tooling/cli/src/acl/permission/add.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,10 @@ fn capability_from_path<P: AsRef<Path>>(path: P) -> Option<TomlOrJson> {
8080
#[derive(Debug, Parser)]
8181
#[clap(about = "Add a permission to capabilities")]
8282
pub struct Options {
83-
/// Permission to remove.
84-
identifier: String,
83+
/// Permission to add.
84+
pub identifier: String,
8585
/// Capability to add the permission to.
86-
capability: Option<String>,
86+
pub capability: Option<String>,
8787
}
8888

8989
pub fn command(options: Options) -> Result<()> {
@@ -114,7 +114,10 @@ pub fn command(options: Options) -> Result<()> {
114114

115115
let mut capabilities = if capabilities.len() > 1 {
116116
let selections = prompts::multiselect(
117-
"Choose which capabilities to add the permission to:",
117+
&format!(
118+
"Choose which capabilities to add the permission `{}` to:",
119+
options.identifier
120+
),
118121
capabilities
119122
.iter()
120123
.map(|(c, p)| {

tooling/cli/src/acl/permission/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use clap::{Parser, Subcommand};
66

77
use crate::Result;
88

9-
mod add;
9+
pub mod add;
1010
mod ls;
1111
mod new;
1212
mod rm;

tooling/cli/src/add.rs

Lines changed: 76 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,67 @@
22
// SPDX-License-Identifier: Apache-2.0
33
// SPDX-License-Identifier: MIT
44

5-
use anyhow::Context;
65
use clap::Parser;
76
use colored::Colorize;
87
use regex::Regex;
98

109
use crate::{
10+
acl,
1111
helpers::{
1212
app_paths::{app_dir, tauri_dir},
13-
cross_command,
13+
cargo,
1414
npm::PackageManager,
1515
},
1616
Result,
1717
};
1818

1919
use std::{collections::HashMap, process::Command};
2020

21+
#[derive(Default)]
22+
struct PluginMetadata {
23+
desktop_only: bool,
24+
rust_only: bool,
25+
builder: bool,
26+
}
27+
28+
// known plugins with particular cases
29+
fn plugins() -> HashMap<&'static str, PluginMetadata> {
30+
let mut plugins: HashMap<&'static str, PluginMetadata> = HashMap::new();
31+
32+
// desktop-only
33+
for p in [
34+
"authenticator",
35+
"cli",
36+
"global-shortcut",
37+
"updater",
38+
"window-state",
39+
] {
40+
plugins.entry(p).or_default().desktop_only = true;
41+
}
42+
43+
// uses builder pattern
44+
for p in [
45+
"global-shortcut",
46+
"localhost",
47+
"log",
48+
"sql",
49+
"store",
50+
"stronghold",
51+
"updater",
52+
"window-state",
53+
] {
54+
plugins.entry(p).or_default().builder = true;
55+
}
56+
57+
// rust-only
58+
#[allow(clippy::single_element_loop)]
59+
for p in ["localhost"] {
60+
plugins.entry(p).or_default().rust_only = true;
61+
}
62+
63+
plugins
64+
}
65+
2166
#[derive(Debug, Parser)]
2267
#[clap(about = "Add a tauri plugin to the project")]
2368
pub struct Options {
@@ -45,43 +90,16 @@ pub fn command(options: Options) -> Result<()> {
4590

4691
let tauri_dir = tauri_dir();
4792

48-
let mut cargo = Command::new("cargo");
49-
cargo.current_dir(&tauri_dir).arg("add").arg(&crate_name);
50-
51-
if options.tag.is_some() || options.rev.is_some() || options.branch.is_some() {
52-
cargo
53-
.arg("--git")
54-
.arg("https://github.com/tauri-apps/plugins-workspace");
55-
}
56-
57-
if metadata.desktop_only {
58-
cargo
59-
.arg("--target")
60-
.arg(r#"cfg(not(any(target_os = "android", target_os = "ios")))"#);
61-
}
62-
63-
let npm_spec = match (options.tag, options.rev, options.branch) {
64-
(Some(tag), None, None) => {
65-
cargo.args(["--tag", &tag]);
66-
format!("tauri-apps/tauri-plugin-{plugin}#{tag}")
67-
}
68-
(None, Some(rev), None) => {
69-
cargo.args(["--rev", &rev]);
70-
format!("tauri-apps/tauri-plugin-{plugin}#{rev}")
71-
}
72-
(None, None, Some(branch)) => {
73-
cargo.args(["--branch", &branch]);
74-
format!("tauri-apps/tauri-plugin-{plugin}#{branch}")
75-
}
76-
(None, None, None) => npm_name,
77-
_ => anyhow::bail!("Only one of --tag, --rev and --branch can be specified"),
78-
};
79-
80-
log::info!("Installing Cargo dependency {crate_name}...");
81-
let status = cargo.status().context("failed to run `cargo add`")?;
82-
if !status.success() {
83-
anyhow::bail!("Failed to install Cargo dependency");
84-
}
93+
cargo::install_one(cargo::CargoInstallOptions {
94+
name: &crate_name,
95+
branch: options.branch.as_deref(),
96+
rev: options.rev.as_deref(),
97+
tag: options.tag.as_deref(),
98+
cwd: Some(&tauri_dir),
99+
target: metadata
100+
.desktop_only
101+
.then_some(r#"cfg(not(any(target_os = "android", target_os = "ios")))"#),
102+
})?;
85103

86104
if !metadata.rust_only {
87105
if let Some(manager) = std::panic::catch_unwind(app_dir)
@@ -90,26 +108,28 @@ pub fn command(options: Options) -> Result<()> {
90108
.map(PackageManager::from_project)
91109
.and_then(|managers| managers.into_iter().next())
92110
{
93-
let mut cmd = match manager {
94-
PackageManager::Npm => cross_command("npm"),
95-
PackageManager::Pnpm => cross_command("pnpm"),
96-
PackageManager::Yarn => cross_command("yarn"),
97-
PackageManager::YarnBerry => cross_command("yarn"),
98-
PackageManager::Bun => cross_command("bun"),
111+
let npm_spec = match (options.tag, options.rev, options.branch) {
112+
(Some(tag), None, None) => {
113+
format!("tauri-apps/tauri-plugin-{plugin}#{tag}")
114+
}
115+
(None, Some(rev), None) => {
116+
format!("tauri-apps/tauri-plugin-{plugin}#{rev}")
117+
}
118+
(None, None, Some(branch)) => {
119+
format!("tauri-apps/tauri-plugin-{plugin}#{branch}")
120+
}
121+
(None, None, None) => npm_name,
122+
_ => anyhow::bail!("Only one of --tag, --rev and --branch can be specified"),
99123
};
100-
101-
cmd.arg("add").arg(&npm_spec);
102-
103-
log::info!("Installing NPM dependency {npm_spec}...");
104-
let status = cmd
105-
.status()
106-
.with_context(|| format!("failed to run {manager}"))?;
107-
if !status.success() {
108-
anyhow::bail!("Failed to install NPM dependency");
109-
}
124+
manager.install(&[npm_spec])?;
110125
}
111126
}
112127

128+
let _ = acl::permission::add::command(acl::permission::add::Options {
129+
identifier: format!("{plugin}:default"),
130+
capability: None,
131+
});
132+
113133
// add plugin init code to main.rs or lib.rs
114134
let plugin_init_fn = if plugin == "stronghold" {
115135
"Builder::new(|pass| todo!()).build()"
@@ -119,6 +139,7 @@ pub fn command(options: Options) -> Result<()> {
119139
"init()"
120140
};
121141
let plugin_init = format!(".plugin(tauri_plugin_{plugin_snake_case}::{plugin_init_fn})");
142+
122143
let re = Regex::new(r"(tauri\s*::\s*Builder\s*::\s*default\(\))(\s*)")?;
123144
for file in [tauri_dir.join("src/main.rs"), tauri_dir.join("src/lib.rs")] {
124145
let contents = std::fs::read_to_string(&file)?;
@@ -143,7 +164,6 @@ pub fn command(options: Options) -> Result<()> {
143164
.arg("fmt")
144165
.current_dir(&tauri_dir)
145166
.status();
146-
147167
return Ok(());
148168
}
149169
}
@@ -176,48 +196,3 @@ pub fn command(options: Options) -> Result<()> {
176196

177197
Ok(())
178198
}
179-
180-
#[derive(Default)]
181-
struct PluginMetadata {
182-
desktop_only: bool,
183-
rust_only: bool,
184-
builder: bool,
185-
}
186-
187-
// known plugins with particular cases
188-
fn plugins() -> HashMap<&'static str, PluginMetadata> {
189-
let mut plugins: HashMap<&'static str, PluginMetadata> = HashMap::new();
190-
191-
// desktop-only
192-
for p in [
193-
"authenticator",
194-
"cli",
195-
"global-shortcut",
196-
"updater",
197-
"window-state",
198-
] {
199-
plugins.entry(p).or_default().desktop_only = true;
200-
}
201-
202-
// uses builder pattern
203-
for p in [
204-
"global-shortcut",
205-
"localhost",
206-
"log",
207-
"sql",
208-
"store",
209-
"stronghold",
210-
"updater",
211-
"window-state",
212-
] {
213-
plugins.entry(p).or_default().builder = true;
214-
}
215-
216-
// rust-only
217-
#[allow(clippy::single_element_loop)]
218-
for p in ["localhost"] {
219-
plugins.entry(p).or_default().rust_only = true;
220-
}
221-
222-
plugins
223-
}

0 commit comments

Comments
 (0)