mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-10-26 18:12:23 +03:00
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
This commit is contained in:
parent
d7d03c7197
commit
d7f56fef85
6
.changes/permission-platforms.md
Normal file
6
.changes/permission-platforms.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"tauri": patch:feat
|
||||||
|
"tauri-utils": patch:feat
|
||||||
|
---
|
||||||
|
|
||||||
|
Allow defining a permission that only applies to a set of target platforms via the `platforms` configuration option.
|
@ -1123,7 +1123,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"platforms": {
|
"platforms": {
|
||||||
"description": "Target platforms this capability applies. By default all platforms applies.",
|
"description": "Target platforms this capability applies. By default all platforms are affected by this capability.",
|
||||||
"default": [
|
"default": [
|
||||||
"linux",
|
"linux",
|
||||||
"macOS",
|
"macOS",
|
||||||
|
@ -74,7 +74,7 @@ pub struct Capability {
|
|||||||
pub webviews: Vec<String>,
|
pub webviews: Vec<String>,
|
||||||
/// List of permissions attached to this capability. Must include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`.
|
/// List of permissions attached to this capability. Must include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`.
|
||||||
pub permissions: Vec<PermissionEntry>,
|
pub permissions: Vec<PermissionEntry>,
|
||||||
/// Target platforms this capability applies. By default all platforms applies.
|
/// Target platforms this capability applies. By default all platforms are affected by this capability.
|
||||||
#[serde(default = "default_platforms", skip_serializing_if = "Vec::is_empty")]
|
#[serde(default = "default_platforms", skip_serializing_if = "Vec::is_empty")]
|
||||||
pub platforms: Vec<Target>,
|
pub platforms: Vec<Target>,
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::num::NonZeroU64;
|
use std::num::NonZeroU64;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::platform::Target;
|
||||||
|
|
||||||
pub use self::{identifier::*, value::*};
|
pub use self::{identifier::*, value::*};
|
||||||
|
|
||||||
/// Known filename of the permission schema JSON file
|
/// Known filename of the permission schema JSON file
|
||||||
@ -172,6 +174,20 @@ pub struct Permission {
|
|||||||
/// Allowed or denied scoped when using this permission.
|
/// Allowed or denied scoped when using this permission.
|
||||||
#[serde(default, skip_serializing_if = "Scopes::is_empty")]
|
#[serde(default, skip_serializing_if = "Scopes::is_empty")]
|
||||||
pub scope: Scopes,
|
pub scope: Scopes,
|
||||||
|
|
||||||
|
/// Target platforms this permission applies. By default all platforms are affected by this permission.
|
||||||
|
#[serde(default = "default_platforms", skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub platforms: Vec<Target>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_platforms() -> Vec<Target> {
|
||||||
|
vec![
|
||||||
|
Target::Linux,
|
||||||
|
Target::MacOS,
|
||||||
|
Target::Windows,
|
||||||
|
Target::Android,
|
||||||
|
Target::Ios,
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A set of direct permissions grouped together under a new name.
|
/// A set of direct permissions grouped together under a new name.
|
||||||
@ -252,6 +268,8 @@ mod build_ {
|
|||||||
let description = opt_str_lit(self.description.as_ref());
|
let description = opt_str_lit(self.description.as_ref());
|
||||||
let commands = &self.commands;
|
let commands = &self.commands;
|
||||||
let scope = &self.scope;
|
let scope = &self.scope;
|
||||||
|
let platforms = vec_lit(&self.platforms, identity);
|
||||||
|
|
||||||
literal_struct!(
|
literal_struct!(
|
||||||
tokens,
|
tokens,
|
||||||
::tauri::utils::acl::Permission,
|
::tauri::utils::acl::Permission,
|
||||||
@ -259,7 +277,8 @@ mod build_ {
|
|||||||
identifier,
|
identifier,
|
||||||
description,
|
description,
|
||||||
commands,
|
commands,
|
||||||
scope
|
scope,
|
||||||
|
platforms
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,6 +112,7 @@ impl Resolved {
|
|||||||
with_resolved_permissions(
|
with_resolved_permissions(
|
||||||
capability,
|
capability,
|
||||||
acl,
|
acl,
|
||||||
|
target,
|
||||||
|ResolvedPermission {
|
|ResolvedPermission {
|
||||||
key,
|
key,
|
||||||
permission_name,
|
permission_name,
|
||||||
@ -273,6 +274,7 @@ struct ResolvedPermission<'a> {
|
|||||||
fn with_resolved_permissions<F: FnMut(ResolvedPermission<'_>)>(
|
fn with_resolved_permissions<F: FnMut(ResolvedPermission<'_>)>(
|
||||||
capability: &Capability,
|
capability: &Capability,
|
||||||
acl: &BTreeMap<String, Manifest>,
|
acl: &BTreeMap<String, Manifest>,
|
||||||
|
target: Target,
|
||||||
mut f: F,
|
mut f: F,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
for permission_entry in &capability.permissions {
|
for permission_entry in &capability.permissions {
|
||||||
@ -281,7 +283,10 @@ fn with_resolved_permissions<F: FnMut(ResolvedPermission<'_>)>(
|
|||||||
|
|
||||||
let key = permission_id.get_prefix().unwrap_or(APP_ACL_KEY);
|
let key = permission_id.get_prefix().unwrap_or(APP_ACL_KEY);
|
||||||
|
|
||||||
let permissions = get_permissions(key, permission_name, acl)?;
|
let permissions = get_permissions(key, permission_name, acl)?
|
||||||
|
.into_iter()
|
||||||
|
.filter(|p| p.platforms.contains(&target))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut resolved_scope = Scopes::default();
|
let mut resolved_scope = Scopes::default();
|
||||||
let mut commands = Commands::default();
|
let mut commands = Commands::default();
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
identifier = "run-app"
|
||||||
|
description = "app capability"
|
||||||
|
windows = ["main"]
|
||||||
|
permissions = [
|
||||||
|
"os:allow-apt-linux",
|
||||||
|
"os:allow-library-folder-macos",
|
||||||
|
"os:deny-webview-folder-windows",
|
||||||
|
"os:open-browser",
|
||||||
|
]
|
@ -0,0 +1 @@
|
|||||||
|
["os"]
|
7
core/tests/acl/fixtures/plugins/os/linux.toml
Normal file
7
core/tests/acl/fixtures/plugins/os/linux.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[[permission]]
|
||||||
|
identifier = "allow-apt-linux"
|
||||||
|
platforms = ["linux"]
|
||||||
|
description = "Allows spawning the apt command on Linux"
|
||||||
|
commands.allow = ["spawn"]
|
||||||
|
[[permission.scope.allow]]
|
||||||
|
command = "apt"
|
7
core/tests/acl/fixtures/plugins/os/macos.toml
Normal file
7
core/tests/acl/fixtures/plugins/os/macos.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "allow-library-folder-macos"
|
||||||
|
platforms = ["macOS"]
|
||||||
|
description = "Allows access to the $HOME/Library folder on maOS"
|
||||||
|
[[permission.scope.allow]]
|
||||||
|
path = "$HOME/Library/**"
|
28
core/tests/acl/fixtures/plugins/os/open-browser.toml
Normal file
28
core/tests/acl/fixtures/plugins/os/open-browser.toml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
[[permission]]
|
||||||
|
identifier = "allow-servo-linux"
|
||||||
|
platforms = ["linux"]
|
||||||
|
description = "Allows starting servo on Linux"
|
||||||
|
commands.allow = ["spawn"]
|
||||||
|
[[permission.scope.allow]]
|
||||||
|
command = "servo"
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "allow-edge-windows"
|
||||||
|
platforms = ["windows"]
|
||||||
|
description = "Allows starting edge on Windows"
|
||||||
|
commands.allow = ["spawn"]
|
||||||
|
[[permission.scope.allow]]
|
||||||
|
command = "edge"
|
||||||
|
|
||||||
|
[[permission]]
|
||||||
|
identifier = "allow-safari-macos"
|
||||||
|
platforms = ["macOS"]
|
||||||
|
description = "Allows starting safari on macOS"
|
||||||
|
commands.allow = ["spawn"]
|
||||||
|
[[permission.scope.allow]]
|
||||||
|
command = "safari"
|
||||||
|
|
||||||
|
[[set]]
|
||||||
|
identifier = "open-browser"
|
||||||
|
description = "allows opening a URL on the platform browser"
|
||||||
|
permissions = ["allow-servo-linux", "allow-edge-windows", "allow-safari-macos"]
|
6
core/tests/acl/fixtures/plugins/os/windows.toml
Normal file
6
core/tests/acl/fixtures/plugins/os/windows.toml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[[permission]]
|
||||||
|
identifier = "deny-webview-folder-windows"
|
||||||
|
platforms = ["windows"]
|
||||||
|
description = "Denies access to the webview folder on Windows"
|
||||||
|
[[permission.scope.deny]]
|
||||||
|
path = "$APP/EBWebView/**"
|
@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
source: core/tests/acl/src/lib.rs
|
||||||
|
expression: resolved
|
||||||
|
---
|
||||||
|
Resolved {
|
||||||
|
allowed_commands: {
|
||||||
|
CommandKey {
|
||||||
|
name: "plugin:os|spawn",
|
||||||
|
context: Local,
|
||||||
|
}: ResolvedCommand {
|
||||||
|
windows: [
|
||||||
|
Pattern {
|
||||||
|
original: "main",
|
||||||
|
tokens: [
|
||||||
|
Char(
|
||||||
|
'm',
|
||||||
|
),
|
||||||
|
Char(
|
||||||
|
'a',
|
||||||
|
),
|
||||||
|
Char(
|
||||||
|
'i',
|
||||||
|
),
|
||||||
|
Char(
|
||||||
|
'n',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
is_recursive: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
webviews: [],
|
||||||
|
scope: Some(
|
||||||
|
8031926490300119127,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
denied_commands: {},
|
||||||
|
command_scope: {
|
||||||
|
8031926490300119127: ResolvedScope {
|
||||||
|
allow: [
|
||||||
|
Map(
|
||||||
|
{
|
||||||
|
"command": String(
|
||||||
|
"apt",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Map(
|
||||||
|
{
|
||||||
|
"command": String(
|
||||||
|
"servo",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
deny: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
global_scope: {
|
||||||
|
"os": ResolvedScope {
|
||||||
|
allow: [],
|
||||||
|
deny: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
---
|
||||||
|
source: core/tests/acl/src/lib.rs
|
||||||
|
expression: resolved
|
||||||
|
---
|
||||||
|
Resolved {
|
||||||
|
allowed_commands: {
|
||||||
|
CommandKey {
|
||||||
|
name: "plugin:os|spawn",
|
||||||
|
context: Local,
|
||||||
|
}: ResolvedCommand {
|
||||||
|
windows: [
|
||||||
|
Pattern {
|
||||||
|
original: "main",
|
||||||
|
tokens: [
|
||||||
|
Char(
|
||||||
|
'm',
|
||||||
|
),
|
||||||
|
Char(
|
||||||
|
'a',
|
||||||
|
),
|
||||||
|
Char(
|
||||||
|
'i',
|
||||||
|
),
|
||||||
|
Char(
|
||||||
|
'n',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
is_recursive: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
webviews: [],
|
||||||
|
scope: Some(
|
||||||
|
7912899488978770657,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
denied_commands: {},
|
||||||
|
command_scope: {
|
||||||
|
7912899488978770657: ResolvedScope {
|
||||||
|
allow: [
|
||||||
|
Map(
|
||||||
|
{
|
||||||
|
"command": String(
|
||||||
|
"safari",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
deny: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
global_scope: {
|
||||||
|
"os": ResolvedScope {
|
||||||
|
allow: [
|
||||||
|
Map(
|
||||||
|
{
|
||||||
|
"path": String(
|
||||||
|
"$HOME/Library/**",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
deny: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
---
|
||||||
|
source: core/tests/acl/src/lib.rs
|
||||||
|
expression: resolved
|
||||||
|
---
|
||||||
|
Resolved {
|
||||||
|
allowed_commands: {
|
||||||
|
CommandKey {
|
||||||
|
name: "plugin:os|spawn",
|
||||||
|
context: Local,
|
||||||
|
}: ResolvedCommand {
|
||||||
|
windows: [
|
||||||
|
Pattern {
|
||||||
|
original: "main",
|
||||||
|
tokens: [
|
||||||
|
Char(
|
||||||
|
'm',
|
||||||
|
),
|
||||||
|
Char(
|
||||||
|
'a',
|
||||||
|
),
|
||||||
|
Char(
|
||||||
|
'i',
|
||||||
|
),
|
||||||
|
Char(
|
||||||
|
'n',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
is_recursive: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
webviews: [],
|
||||||
|
scope: Some(
|
||||||
|
7912899488978770657,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
denied_commands: {},
|
||||||
|
command_scope: {
|
||||||
|
7912899488978770657: ResolvedScope {
|
||||||
|
allow: [
|
||||||
|
Map(
|
||||||
|
{
|
||||||
|
"command": String(
|
||||||
|
"edge",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
deny: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
global_scope: {
|
||||||
|
"os": ResolvedScope {
|
||||||
|
allow: [],
|
||||||
|
deny: [
|
||||||
|
Map(
|
||||||
|
{
|
||||||
|
"path": String(
|
||||||
|
"$APP/EBWebView/**",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
@ -41,14 +41,21 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn resolve_acl() {
|
fn resolve_acl() {
|
||||||
let mut settings = insta::Settings::clone_current();
|
|
||||||
settings.set_snapshot_path("../fixtures/snapshots");
|
|
||||||
let _guard = settings.bind_to_scope();
|
|
||||||
|
|
||||||
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
|
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||||
let fixtures_path = manifest_dir.join("fixtures").join("capabilities");
|
let fixtures_path = manifest_dir.join("fixtures").join("capabilities");
|
||||||
for fixture_path in read_dir(fixtures_path).expect("failed to read fixtures") {
|
for fixture_path in read_dir(fixtures_path).expect("failed to read fixtures") {
|
||||||
let fixture_entry = fixture_path.expect("failed to read fixture entry");
|
let fixture_entry = fixture_path.expect("failed to read fixture entry");
|
||||||
|
|
||||||
|
let mut settings = insta::Settings::clone_current();
|
||||||
|
settings.set_snapshot_path(
|
||||||
|
if fixture_entry.path().file_name().unwrap() == "platform-specific-permissions" {
|
||||||
|
Path::new("../fixtures/snapshots").join(Target::current().to_string())
|
||||||
|
} else {
|
||||||
|
Path::new("../fixtures/snapshots").to_path_buf()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let _guard = settings.bind_to_scope();
|
||||||
|
|
||||||
let fixture_plugins_str = read_to_string(fixture_entry.path().join("required-plugins.json"))
|
let fixture_plugins_str = read_to_string(fixture_entry.path().join("required-plugins.json"))
|
||||||
.expect("failed to read fixture required-plugins.json file");
|
.expect("failed to read fixture required-plugins.json file");
|
||||||
let fixture_plugins: Vec<String> = serde_json::from_str(&fixture_plugins_str)
|
let fixture_plugins: Vec<String> = serde_json::from_str(&fixture_plugins_str)
|
||||||
|
@ -136,6 +136,20 @@
|
|||||||
"$ref": "#/definitions/Scopes"
|
"$ref": "#/definitions/Scopes"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"platforms": {
|
||||||
|
"description": "Target platforms this permission applies. By default all platforms are affected by this permission.",
|
||||||
|
"default": [
|
||||||
|
"linux",
|
||||||
|
"macOS",
|
||||||
|
"windows",
|
||||||
|
"android",
|
||||||
|
"iOS"
|
||||||
|
],
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Target"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -241,6 +255,46 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"Target": {
|
||||||
|
"description": "Platform target.",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"description": "MacOS.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"macOS"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Windows.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"windows"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Linux.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Android.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"android"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "iOS.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"iOS"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"PermissionKind": {
|
"PermissionKind": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
|
@ -5,5 +5,5 @@ version = 1
|
|||||||
identifier = "deny-home-dir-config"
|
identifier = "deny-home-dir-config"
|
||||||
description = "Denies read access to the complete $HOME folder."
|
description = "Denies read access to the complete $HOME folder."
|
||||||
|
|
||||||
[[scope.deny]]
|
[[permission.scope.deny]]
|
||||||
path = "$HOME/.config"
|
path = "$HOME/.config"
|
||||||
|
@ -6,5 +6,5 @@ identifier = "allow-home-dir"
|
|||||||
description = "Allows read access to the complete $HOME folder."
|
description = "Allows read access to the complete $HOME folder."
|
||||||
commands.allow = ["readDirectory", "readFile"]
|
commands.allow = ["readDirectory", "readFile"]
|
||||||
|
|
||||||
[[scope.allow]]
|
[[permission.scope.allow]]
|
||||||
path = "$HOME/**"
|
path = "$HOME/**"
|
||||||
|
@ -1123,7 +1123,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"platforms": {
|
"platforms": {
|
||||||
"description": "Target platforms this capability applies. By default all platforms applies.",
|
"description": "Target platforms this capability applies. By default all platforms are affected by this capability.",
|
||||||
"default": [
|
"default": [
|
||||||
"linux",
|
"linux",
|
||||||
"macOS",
|
"macOS",
|
||||||
|
@ -63,6 +63,7 @@ pub fn command(options: Options) -> Result<()> {
|
|||||||
description,
|
description,
|
||||||
commands: Commands { allow, deny },
|
commands: Commands { allow, deny },
|
||||||
scope: Default::default(),
|
scope: Default::default(),
|
||||||
|
platforms: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let path = match options.out {
|
let path = match options.out {
|
||||||
|
Loading…
Reference in New Issue
Block a user