Skip to content

Commit

Permalink
refactor(core): allow referencing capabilities on the Tauri config fi…
Browse files Browse the repository at this point in the history
…le (#8797)

* refactor(core): capabilities must be referenced on the Tauri config file

* add all capabilities by default

* refactor(cli): reference all capabilities by default
  • Loading branch information
lucasfernog committed Feb 18, 2024
1 parent 0cb0a15 commit 83a68de
Show file tree
Hide file tree
Showing 14 changed files with 744 additions and 15 deletions.
7 changes: 7 additions & 0 deletions .changes/capabilities-tauri-conf.md
@@ -0,0 +1,7 @@
---
"tauri-build": patch:breaking
"tauri-utils": patch:enhance
"tauri-codegen": patch:enhance
---

Added a new configuration option `tauri.conf.json > app > security > capabilities` to reference existing capabilities and inline new ones. If it is empty, all capabilities are still included preserving the current behavior.
6 changes: 6 additions & 0 deletions .changes/update-app-template-capabilities-conf.md
@@ -0,0 +1,6 @@
---
"@tauri-apps/cli": patch:enhance
"tauri-cli": patch:enhance
---

Update app template following capabilities configuration change.
1 change: 1 addition & 0 deletions core/tauri-build/src/lib.rs
Expand Up @@ -567,6 +567,7 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
out_dir.join(PLUGIN_MANIFESTS_FILE_NAME),
serde_json::to_string(&plugin_manifests)?,
)?;

let capabilities = if let Some(pattern) = attributes.capabilities_path_pattern {
parse_capabilities(pattern)?
} else {
Expand Down
25 changes: 23 additions & 2 deletions core/tauri-codegen/src/context.rs
Expand Up @@ -15,7 +15,7 @@ use tauri_utils::acl::capability::Capability;
use tauri_utils::acl::plugin::Manifest;
use tauri_utils::acl::resolved::Resolved;
use tauri_utils::assets::AssetKey;
use tauri_utils::config::{Config, FrontendDist, PatternKind};
use tauri_utils::config::{CapabilityEntry, Config, FrontendDist, PatternKind};
use tauri_utils::html::{
inject_nonce_token, parse as parse_html, serialize_node as serialize_html_node,
};
Expand Down Expand Up @@ -381,14 +381,35 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
};

let capabilities_file_path = out_dir.join(CAPABILITIES_FILE_NAME);
let capabilities: BTreeMap<String, Capability> = if capabilities_file_path.exists() {
let mut capabilities_from_files: BTreeMap<String, Capability> = if capabilities_file_path.exists()
{
let capabilities_file =
std::fs::read_to_string(capabilities_file_path).expect("failed to read capabilities");
serde_json::from_str(&capabilities_file).expect("failed to parse capabilities")
} else {
Default::default()
};

let capabilities = if config.app.security.capabilities.is_empty() {
capabilities_from_files
} else {
let mut capabilities = BTreeMap::new();
for capability_entry in &config.app.security.capabilities {
match capability_entry {
CapabilityEntry::Inlined(capability) => {
capabilities.insert(capability.identifier.clone(), capability.clone());
}
CapabilityEntry::Reference(id) => {
let capability = capabilities_from_files
.remove(id)
.unwrap_or_else(|| panic!("capability with identifier {id} not found"));
capabilities.insert(id.clone(), capability);
}
}
}
capabilities
};

let resolved_acl = Resolved::resolve(acl, capabilities, target).expect("failed to resolve ACL");

Ok(quote!({
Expand Down
268 changes: 268 additions & 0 deletions core/tauri-config-schema/schema.json
Expand Up @@ -40,6 +40,7 @@
"enable": false,
"scope": []
},
"capabilities": [],
"dangerousDisableAssetCspModification": false,
"freezePrototype": false,
"pattern": {
Expand Down Expand Up @@ -158,6 +159,7 @@
"enable": false,
"scope": []
},
"capabilities": [],
"dangerousDisableAssetCspModification": false,
"freezePrototype": false,
"pattern": {
Expand Down Expand Up @@ -878,6 +880,14 @@
"$ref": "#/definitions/PatternKind"
}
]
},
"capabilities": {
"description": "List of capabilities that are enabled on the application.\n\nIf the list is empty, all capabilities are included.",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/CapabilityEntry"
}
}
},
"additionalProperties": false
Expand Down Expand Up @@ -1040,6 +1050,264 @@
}
]
},
"CapabilityEntry": {
"description": "A capability entry which can be either an inlined capability or a reference to a capability defined on its own file.",
"anyOf": [
{
"description": "An inlined capability.",
"allOf": [
{
"$ref": "#/definitions/Capability"
}
]
},
{
"description": "Reference to a capability identifier.",
"type": "string"
}
]
},
"Capability": {
"description": "a grouping and boundary mechanism developers can use to separate windows or plugins functionality from each other at runtime.\n\nIf a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create trust groups and reduce impact of vulnerabilities in certain plugins or windows. Windows can be added to a capability by exact name or glob patterns like *, admin-* or main-window.",
"type": "object",
"required": [
"identifier",
"permissions",
"windows"
],
"properties": {
"identifier": {
"description": "Identifier of the capability.",
"type": "string"
},
"description": {
"description": "Description of the capability.",
"default": "",
"type": "string"
},
"context": {
"description": "Execution context of the capability.\n\nAt runtime, Tauri filters the IPC command together with the context to determine whether it is allowed or not and its scope.",
"default": "local",
"allOf": [
{
"$ref": "#/definitions/CapabilityContext"
}
]
},
"windows": {
"description": "List of windows that uses this capability. Can be a glob pattern.",
"type": "array",
"items": {
"type": "string"
}
},
"permissions": {
"description": "List of permissions attached to this capability. Must include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionEntry"
}
},
"platforms": {
"description": "Target platforms this capability applies. By default all platforms applies.",
"default": [
"linux",
"macOS",
"windows",
"android",
"iOS"
],
"type": "array",
"items": {
"$ref": "#/definitions/Target"
}
}
}
},
"CapabilityContext": {
"description": "Context of the capability.",
"oneOf": [
{
"description": "Capability refers to local URL usage.",
"type": "string",
"enum": [
"local"
]
},
{
"description": "Capability refers to remote usage.",
"type": "object",
"required": [
"remote"
],
"properties": {
"remote": {
"type": "object",
"required": [
"domains"
],
"properties": {
"domains": {
"description": "Remote domains this capability refers to. Can use glob patterns.",
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
"additionalProperties": false
}
]
},
"PermissionEntry": {
"description": "An entry for a permission value in a [`Capability`] can be either a raw permission [`Identifier`] or an object that references a permission and extends its scope.",
"anyOf": [
{
"description": "Reference a permission or permission set by identifier.",
"allOf": [
{
"$ref": "#/definitions/Identifier"
}
]
},
{
"description": "Reference a permission or permission set by identifier and extends its scope.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"identifier": {
"description": "Identifier of the permission or permission set.",
"allOf": [
{
"$ref": "#/definitions/Identifier"
}
]
},
"allow": {
"description": "Data that defines what is allowed by the scope.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/Value"
}
},
"deny": {
"description": "Data that defines what is denied by the scope.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/Value"
}
}
}
}
]
},
"Identifier": {
"type": "string"
},
"Value": {
"description": "All supported ACL values.",
"anyOf": [
{
"description": "Represents a null JSON value.",
"type": "null"
},
{
"description": "Represents a [`bool`].",
"type": "boolean"
},
{
"description": "Represents a valid ACL [`Number`].",
"allOf": [
{
"$ref": "#/definitions/Number"
}
]
},
{
"description": "Represents a [`String`].",
"type": "string"
},
{
"description": "Represents a list of other [`Value`]s.",
"type": "array",
"items": {
"$ref": "#/definitions/Value"
}
},
{
"description": "Represents a map of [`String`] keys to [`Value`]s.",
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/Value"
}
}
]
},
"Number": {
"description": "A valid ACL number.",
"anyOf": [
{
"description": "Represents an [`i64`].",
"type": "integer",
"format": "int64"
},
{
"description": "Represents a [`f64`].",
"type": "number",
"format": "double"
}
]
},
"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"
]
}
]
},
"TrayIconConfig": {
"description": "Configuration for application tray icon.\n\nSee more: <https://tauri.app/v1/api/config#trayiconconfig>",
"type": "object",
Expand Down

0 comments on commit 83a68de

Please sign in to comment.