Skip to content

Commit d7f56fe

Browse files
authored
feat(acl): allow a permission to apply to a subset of target platforms (#9014)
* feat(acl): allow a permission to apply to a subset of target platforms * fix cli
1 parent d7d03c7 commit d7f56fe

File tree

20 files changed

+358
-11
lines changed

20 files changed

+358
-11
lines changed

.changes/permission-platforms.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"tauri": patch:feat
3+
"tauri-utils": patch:feat
4+
---
5+
6+
Allow defining a permission that only applies to a set of target platforms via the `platforms` configuration option.

core/tauri-config-schema/schema.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1123,7 +1123,7 @@
11231123
}
11241124
},
11251125
"platforms": {
1126-
"description": "Target platforms this capability applies. By default all platforms applies.",
1126+
"description": "Target platforms this capability applies. By default all platforms are affected by this capability.",
11271127
"default": [
11281128
"linux",
11291129
"macOS",

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ pub struct Capability {
7474
pub webviews: Vec<String>,
7575
/// List of permissions attached to this capability. Must include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`.
7676
pub permissions: Vec<PermissionEntry>,
77-
/// Target platforms this capability applies. By default all platforms applies.
77+
/// Target platforms this capability applies. By default all platforms are affected by this capability.
7878
#[serde(default = "default_platforms", skip_serializing_if = "Vec::is_empty")]
7979
pub platforms: Vec<Target>,
8080
}

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

+20-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use serde::{Deserialize, Serialize};
99
use std::num::NonZeroU64;
1010
use thiserror::Error;
1111

12+
use crate::platform::Target;
13+
1214
pub use self::{identifier::*, value::*};
1315

1416
/// Known filename of the permission schema JSON file
@@ -172,6 +174,20 @@ pub struct Permission {
172174
/// Allowed or denied scoped when using this permission.
173175
#[serde(default, skip_serializing_if = "Scopes::is_empty")]
174176
pub scope: Scopes,
177+
178+
/// Target platforms this permission applies. By default all platforms are affected by this permission.
179+
#[serde(default = "default_platforms", skip_serializing_if = "Vec::is_empty")]
180+
pub platforms: Vec<Target>,
181+
}
182+
183+
fn default_platforms() -> Vec<Target> {
184+
vec![
185+
Target::Linux,
186+
Target::MacOS,
187+
Target::Windows,
188+
Target::Android,
189+
Target::Ios,
190+
]
175191
}
176192

177193
/// A set of direct permissions grouped together under a new name.
@@ -252,14 +268,17 @@ mod build_ {
252268
let description = opt_str_lit(self.description.as_ref());
253269
let commands = &self.commands;
254270
let scope = &self.scope;
271+
let platforms = vec_lit(&self.platforms, identity);
272+
255273
literal_struct!(
256274
tokens,
257275
::tauri::utils::acl::Permission,
258276
version,
259277
identifier,
260278
description,
261279
commands,
262-
scope
280+
scope,
281+
platforms
263282
)
264283
}
265284
}

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

+6-1
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ impl Resolved {
112112
with_resolved_permissions(
113113
capability,
114114
acl,
115+
target,
115116
|ResolvedPermission {
116117
key,
117118
permission_name,
@@ -273,6 +274,7 @@ struct ResolvedPermission<'a> {
273274
fn with_resolved_permissions<F: FnMut(ResolvedPermission<'_>)>(
274275
capability: &Capability,
275276
acl: &BTreeMap<String, Manifest>,
277+
target: Target,
276278
mut f: F,
277279
) -> Result<(), Error> {
278280
for permission_entry in &capability.permissions {
@@ -281,7 +283,10 @@ fn with_resolved_permissions<F: FnMut(ResolvedPermission<'_>)>(
281283

282284
let key = permission_id.get_prefix().unwrap_or(APP_ACL_KEY);
283285

284-
let permissions = get_permissions(key, permission_name, acl)?;
286+
let permissions = get_permissions(key, permission_name, acl)?
287+
.into_iter()
288+
.filter(|p| p.platforms.contains(&target))
289+
.collect::<Vec<_>>();
285290

286291
let mut resolved_scope = Scopes::default();
287292
let mut commands = Commands::default();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
identifier = "run-app"
2+
description = "app capability"
3+
windows = ["main"]
4+
permissions = [
5+
"os:allow-apt-linux",
6+
"os:allow-library-folder-macos",
7+
"os:deny-webview-folder-windows",
8+
"os:open-browser",
9+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
["os"]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[[permission]]
2+
identifier = "allow-apt-linux"
3+
platforms = ["linux"]
4+
description = "Allows spawning the apt command on Linux"
5+
commands.allow = ["spawn"]
6+
[[permission.scope.allow]]
7+
command = "apt"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
[[permission]]
3+
identifier = "allow-library-folder-macos"
4+
platforms = ["macOS"]
5+
description = "Allows access to the $HOME/Library folder on maOS"
6+
[[permission.scope.allow]]
7+
path = "$HOME/Library/**"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[[permission]]
2+
identifier = "allow-servo-linux"
3+
platforms = ["linux"]
4+
description = "Allows starting servo on Linux"
5+
commands.allow = ["spawn"]
6+
[[permission.scope.allow]]
7+
command = "servo"
8+
9+
[[permission]]
10+
identifier = "allow-edge-windows"
11+
platforms = ["windows"]
12+
description = "Allows starting edge on Windows"
13+
commands.allow = ["spawn"]
14+
[[permission.scope.allow]]
15+
command = "edge"
16+
17+
[[permission]]
18+
identifier = "allow-safari-macos"
19+
platforms = ["macOS"]
20+
description = "Allows starting safari on macOS"
21+
commands.allow = ["spawn"]
22+
[[permission.scope.allow]]
23+
command = "safari"
24+
25+
[[set]]
26+
identifier = "open-browser"
27+
description = "allows opening a URL on the platform browser"
28+
permissions = ["allow-servo-linux", "allow-edge-windows", "allow-safari-macos"]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[[permission]]
2+
identifier = "deny-webview-folder-windows"
3+
platforms = ["windows"]
4+
description = "Denies access to the webview folder on Windows"
5+
[[permission.scope.deny]]
6+
path = "$APP/EBWebView/**"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
---
2+
source: core/tests/acl/src/lib.rs
3+
expression: resolved
4+
---
5+
Resolved {
6+
allowed_commands: {
7+
CommandKey {
8+
name: "plugin:os|spawn",
9+
context: Local,
10+
}: ResolvedCommand {
11+
windows: [
12+
Pattern {
13+
original: "main",
14+
tokens: [
15+
Char(
16+
'm',
17+
),
18+
Char(
19+
'a',
20+
),
21+
Char(
22+
'i',
23+
),
24+
Char(
25+
'n',
26+
),
27+
],
28+
is_recursive: false,
29+
},
30+
],
31+
webviews: [],
32+
scope: Some(
33+
8031926490300119127,
34+
),
35+
},
36+
},
37+
denied_commands: {},
38+
command_scope: {
39+
8031926490300119127: ResolvedScope {
40+
allow: [
41+
Map(
42+
{
43+
"command": String(
44+
"apt",
45+
),
46+
},
47+
),
48+
Map(
49+
{
50+
"command": String(
51+
"servo",
52+
),
53+
},
54+
),
55+
],
56+
deny: [],
57+
},
58+
},
59+
global_scope: {
60+
"os": ResolvedScope {
61+
allow: [],
62+
deny: [],
63+
},
64+
},
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
---
2+
source: core/tests/acl/src/lib.rs
3+
expression: resolved
4+
---
5+
Resolved {
6+
allowed_commands: {
7+
CommandKey {
8+
name: "plugin:os|spawn",
9+
context: Local,
10+
}: ResolvedCommand {
11+
windows: [
12+
Pattern {
13+
original: "main",
14+
tokens: [
15+
Char(
16+
'm',
17+
),
18+
Char(
19+
'a',
20+
),
21+
Char(
22+
'i',
23+
),
24+
Char(
25+
'n',
26+
),
27+
],
28+
is_recursive: false,
29+
},
30+
],
31+
webviews: [],
32+
scope: Some(
33+
7912899488978770657,
34+
),
35+
},
36+
},
37+
denied_commands: {},
38+
command_scope: {
39+
7912899488978770657: ResolvedScope {
40+
allow: [
41+
Map(
42+
{
43+
"command": String(
44+
"safari",
45+
),
46+
},
47+
),
48+
],
49+
deny: [],
50+
},
51+
},
52+
global_scope: {
53+
"os": ResolvedScope {
54+
allow: [
55+
Map(
56+
{
57+
"path": String(
58+
"$HOME/Library/**",
59+
),
60+
},
61+
),
62+
],
63+
deny: [],
64+
},
65+
},
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
---
2+
source: core/tests/acl/src/lib.rs
3+
expression: resolved
4+
---
5+
Resolved {
6+
allowed_commands: {
7+
CommandKey {
8+
name: "plugin:os|spawn",
9+
context: Local,
10+
}: ResolvedCommand {
11+
windows: [
12+
Pattern {
13+
original: "main",
14+
tokens: [
15+
Char(
16+
'm',
17+
),
18+
Char(
19+
'a',
20+
),
21+
Char(
22+
'i',
23+
),
24+
Char(
25+
'n',
26+
),
27+
],
28+
is_recursive: false,
29+
},
30+
],
31+
webviews: [],
32+
scope: Some(
33+
7912899488978770657,
34+
),
35+
},
36+
},
37+
denied_commands: {},
38+
command_scope: {
39+
7912899488978770657: ResolvedScope {
40+
allow: [
41+
Map(
42+
{
43+
"command": String(
44+
"edge",
45+
),
46+
},
47+
),
48+
],
49+
deny: [],
50+
},
51+
},
52+
global_scope: {
53+
"os": ResolvedScope {
54+
allow: [],
55+
deny: [
56+
Map(
57+
{
58+
"path": String(
59+
"$APP/EBWebView/**",
60+
),
61+
},
62+
),
63+
],
64+
},
65+
},
66+
}

core/tests/acl/src/lib.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,21 @@ mod tests {
4141

4242
#[test]
4343
fn resolve_acl() {
44-
let mut settings = insta::Settings::clone_current();
45-
settings.set_snapshot_path("../fixtures/snapshots");
46-
let _guard = settings.bind_to_scope();
47-
4844
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
4945
let fixtures_path = manifest_dir.join("fixtures").join("capabilities");
5046
for fixture_path in read_dir(fixtures_path).expect("failed to read fixtures") {
5147
let fixture_entry = fixture_path.expect("failed to read fixture entry");
48+
49+
let mut settings = insta::Settings::clone_current();
50+
settings.set_snapshot_path(
51+
if fixture_entry.path().file_name().unwrap() == "platform-specific-permissions" {
52+
Path::new("../fixtures/snapshots").join(Target::current().to_string())
53+
} else {
54+
Path::new("../fixtures/snapshots").to_path_buf()
55+
},
56+
);
57+
let _guard = settings.bind_to_scope();
58+
5259
let fixture_plugins_str = read_to_string(fixture_entry.path().join("required-plugins.json"))
5360
.expect("failed to read fixture required-plugins.json file");
5461
let fixture_plugins: Vec<String> = serde_json::from_str(&fixture_plugins_str)

0 commit comments

Comments
 (0)