mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-01-31 00:35:19 +01:00
Tauri ACL/Allowlist v2 Implementation and Plugin System Refactor (#8428)
* tauri-plugin concept * wip * move command module to its own directory * wip: new command traits and generated code * wip: whip * wip: static dispatch there is a man standing behind me * wip * re-add authority * fix build [skip ci] * parse plugin permissions * merge permission files [skip ci] * parse capabilities [skip ci] * resolve acl (untested) [skip ci] * split functionality, add some docs * remove command2 stuff * actually check runtime authority * small fixes [skip ci] * add function to auto generate basic permission for a command [skip ci] * retrieve command scope, implement CommandArg [skip ci] * fix tests [skip ci] * global scope * lint * license headers [skip ci] * skip canonicalize * separate scope type in example * remove inlinedpermission struct [skip ci] * permission file schema * capabilities schema * move items from tauri-plugin to tauri-utils this allows tauri-plugin to depend on tauri directly again which will be used by the runtime feature as a superset to existing plugin traits * enable schema and glob [skip ci] * fix glob [skip ci] * fix capability schema [skip ci] * enhance schema for permission set possible values [skip ci] * permission set can reference other sets [skip ci] * setup tests for resolving ACL * fixture for permission set [skip ci] * remote context test and small fix[skip ci] * ignore empty scope [skip ci] * code review [skip ci] * lint [skip ci] * runtime fixes * readd schema feature on tauri-config-schema [skip ci] * remove plugin example from workspace, it breaks workspace features resolution [skip ci] * scope as array, add test [skip ci] * accept new shapshot [skip ci] * core plugin permissions, default is now a set * license headers * fix on windows * update global api * glob is no longer optional on tauri-utils * add missing permissions on api example [skip ci] * remove ipc scope and dangerous remote access config * lint * fix asset scope usage * create out dir [skip ci] * reuse cargo_pkg_name [skip ci] * capability window glob pattern [skip ci] * add platforms for capability [skip ci] * per platform schema [skip ci] * lint [skip ci] * rename allowlist build mod [skip ci] * check restricted visibility * simplify capability target [skip ci] * hide codegen build behind tauri-build::try_run * optimize build scripts [skip ci] * fix tests * tests for RuntimeAuthority::resolve_access * remote domain glob pattern * lint --------- Co-authored-by: Chip Reed <chip@chip.sh> Co-authored-by: Lucas Nogueira <lucas@tauri.app> Co-authored-by: Lucas Nogueira <lucas@crabnebula.dev> Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
This commit is contained in:
@@ -10,9 +10,11 @@ members = [
|
||||
"core/tauri-build",
|
||||
"core/tauri-codegen",
|
||||
"core/tauri-config-schema",
|
||||
"core/tauri-plugin",
|
||||
|
||||
# integration tests
|
||||
"core/tests/restart",
|
||||
"core/tests/acl",
|
||||
]
|
||||
|
||||
exclude = [
|
||||
@@ -22,6 +24,7 @@ exclude = [
|
||||
"examples/web/core",
|
||||
"examples/file-associations/src-tauri",
|
||||
"examples/workspace",
|
||||
"examples/plugins/tauri-plugin-example",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
|
||||
@@ -39,6 +39,9 @@ walkdir = "2"
|
||||
tauri-winres = "0.1"
|
||||
semver = "1"
|
||||
dirs-next = "2"
|
||||
glob = "0.3"
|
||||
toml = "0.8"
|
||||
schemars = "0.8"
|
||||
|
||||
[target."cfg(target_os = \"macos\")".dependencies]
|
||||
swift-rs = { version = "1.0.6", features = [ "build" ] }
|
||||
|
||||
174
core/tauri-build/src/acl.rs
Normal file
174
core/tauri-build/src/acl.rs
Normal file
@@ -0,0 +1,174 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
fs::{copy, create_dir_all, File},
|
||||
io::{BufWriter, Write},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use schemars::{
|
||||
schema::{InstanceType, Metadata, RootSchema, Schema, SchemaObject, SubschemaValidation},
|
||||
schema_for,
|
||||
};
|
||||
use tauri_utils::{
|
||||
acl::{build::CapabilityFile, capability::Capability, plugin::Manifest},
|
||||
platform::Target,
|
||||
};
|
||||
|
||||
const CAPABILITIES_SCHEMA_FILE_NAME: &str = "schema.json";
|
||||
const CAPABILITIES_SCHEMA_FOLDER_NAME: &str = "schemas";
|
||||
|
||||
fn capabilities_schema(plugin_manifests: &BTreeMap<String, Manifest>) -> RootSchema {
|
||||
let mut schema = schema_for!(CapabilityFile);
|
||||
|
||||
fn schema_from(plugin: &str, id: &str, description: Option<&str>) -> Schema {
|
||||
Schema::Object(SchemaObject {
|
||||
metadata: Some(Box::new(Metadata {
|
||||
description: description
|
||||
.as_ref()
|
||||
.map(|d| format!("{plugin}:{id} -> {d}")),
|
||||
..Default::default()
|
||||
})),
|
||||
instance_type: Some(InstanceType::String.into()),
|
||||
enum_values: Some(vec![serde_json::Value::String(format!("{plugin}:{id}"))]),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
let mut permission_schemas = Vec::new();
|
||||
|
||||
for (plugin, manifest) in plugin_manifests {
|
||||
for (set_id, set) in &manifest.permission_sets {
|
||||
permission_schemas.push(schema_from(plugin, set_id, Some(&set.description)));
|
||||
}
|
||||
|
||||
if let Some(default) = &manifest.default_permission {
|
||||
permission_schemas.push(schema_from(
|
||||
plugin,
|
||||
"default",
|
||||
Some(default.description.as_ref()),
|
||||
));
|
||||
}
|
||||
|
||||
for (permission_id, permission) in &manifest.permissions {
|
||||
permission_schemas.push(schema_from(
|
||||
plugin,
|
||||
permission_id,
|
||||
permission.description.as_deref(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(Schema::Object(obj)) = schema.definitions.get_mut("Identifier") {
|
||||
obj.object = None;
|
||||
obj.instance_type = None;
|
||||
obj.metadata.as_mut().map(|metadata| {
|
||||
metadata
|
||||
.description
|
||||
.replace("Permission identifier".to_string());
|
||||
metadata
|
||||
});
|
||||
obj.subschemas.replace(Box::new(SubschemaValidation {
|
||||
one_of: Some(permission_schemas),
|
||||
..Default::default()
|
||||
}));
|
||||
}
|
||||
|
||||
schema
|
||||
}
|
||||
|
||||
pub fn generate_schema(
|
||||
plugin_manifests: &BTreeMap<String, Manifest>,
|
||||
target: Target,
|
||||
) -> Result<()> {
|
||||
let schema = capabilities_schema(plugin_manifests);
|
||||
let schema_str = serde_json::to_string_pretty(&schema).unwrap();
|
||||
let out_dir = PathBuf::from("capabilities").join(CAPABILITIES_SCHEMA_FOLDER_NAME);
|
||||
create_dir_all(&out_dir).context("unable to create schema output directory")?;
|
||||
|
||||
let schema_path = out_dir.join(format!("{target}-{CAPABILITIES_SCHEMA_FILE_NAME}"));
|
||||
let mut schema_file = BufWriter::new(File::create(&schema_path)?);
|
||||
write!(schema_file, "{schema_str}")?;
|
||||
|
||||
copy(
|
||||
schema_path,
|
||||
out_dir.join(format!(
|
||||
"{}-{CAPABILITIES_SCHEMA_FILE_NAME}",
|
||||
if target.is_desktop() {
|
||||
"desktop"
|
||||
} else {
|
||||
"mobile"
|
||||
}
|
||||
)),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_plugin_manifests() -> Result<BTreeMap<String, Manifest>> {
|
||||
let permission_map =
|
||||
tauri_utils::acl::build::read_permissions().context("failed to read plugin permissions")?;
|
||||
|
||||
let mut processed = BTreeMap::new();
|
||||
for (plugin_name, permission_files) in permission_map {
|
||||
processed.insert(plugin_name, Manifest::from_files(permission_files));
|
||||
}
|
||||
|
||||
Ok(processed)
|
||||
}
|
||||
|
||||
pub fn validate_capabilities(
|
||||
plugin_manifests: &BTreeMap<String, Manifest>,
|
||||
capabilities: &BTreeMap<String, Capability>,
|
||||
) -> Result<()> {
|
||||
let target = tauri_utils::platform::Target::from_triple(&std::env::var("TARGET").unwrap());
|
||||
|
||||
for capability in capabilities.values() {
|
||||
if !capability.platforms.contains(&target) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for permission in &capability.permissions {
|
||||
if let Some((plugin_name, permission_name)) = permission.get().split_once(':') {
|
||||
let permission_exists = plugin_manifests
|
||||
.get(plugin_name)
|
||||
.map(|manifest| {
|
||||
if permission_name == "default" {
|
||||
manifest.default_permission.is_some()
|
||||
} else {
|
||||
manifest.permissions.contains_key(permission_name)
|
||||
|| manifest.permission_sets.contains_key(permission_name)
|
||||
}
|
||||
})
|
||||
.unwrap_or(false);
|
||||
|
||||
if !permission_exists {
|
||||
let mut available_permissions = Vec::new();
|
||||
for (plugin, manifest) in plugin_manifests {
|
||||
if manifest.default_permission.is_some() {
|
||||
available_permissions.push(format!("{plugin}:default"));
|
||||
}
|
||||
for p in manifest.permissions.keys() {
|
||||
available_permissions.push(format!("{plugin}:{p}"));
|
||||
}
|
||||
for p in manifest.permission_sets.keys() {
|
||||
available_permissions.push(format!("{plugin}:{p}"));
|
||||
}
|
||||
}
|
||||
|
||||
anyhow::bail!(
|
||||
"Permission {} not found, expected one of {}",
|
||||
permission.get(),
|
||||
available_permissions.join(", ")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -78,20 +78,7 @@ impl CodegenContext {
|
||||
///
|
||||
/// Unless you are doing something special with this builder, you don't need to do anything with
|
||||
/// the returned output path.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If any parts of the codegen fail, this will panic with the related error message. This is
|
||||
/// typically desirable when running inside a build script; see [`Self::try_build`] for no panics.
|
||||
pub fn build(self) -> PathBuf {
|
||||
match self.try_build() {
|
||||
Ok(out) => out,
|
||||
Err(error) => panic!("Error found during Codegen::build: {error}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Non-panicking [`Self::build`]
|
||||
pub fn try_build(self) -> Result<PathBuf> {
|
||||
pub(crate) fn try_build(self) -> Result<PathBuf> {
|
||||
let (config, config_parent) = tauri_codegen::get_config(&self.config_path)?;
|
||||
|
||||
// rerun if changed
|
||||
|
||||
@@ -18,20 +18,23 @@ use cargo_toml::Manifest;
|
||||
use heck::AsShoutySnakeCase;
|
||||
|
||||
use tauri_utils::{
|
||||
acl::build::parse_capabilities,
|
||||
config::{BundleResources, Config, WebviewInstallMode},
|
||||
resources::{external_binaries, ResourcePaths},
|
||||
};
|
||||
|
||||
use std::{
|
||||
env::var_os,
|
||||
fs::read_to_string,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
mod allowlist;
|
||||
mod acl;
|
||||
#[cfg(feature = "codegen")]
|
||||
mod codegen;
|
||||
/// Tauri configuration functions.
|
||||
pub mod config;
|
||||
mod manifest;
|
||||
/// Mobile build functions.
|
||||
pub mod mobile;
|
||||
mod static_vcruntime;
|
||||
@@ -40,6 +43,9 @@ mod static_vcruntime;
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "codegen")))]
|
||||
pub use codegen::context::CodegenContext;
|
||||
|
||||
const PLUGIN_MANIFESTS_FILE_NAME: &str = "plugin-manifests.json";
|
||||
const CAPABILITIES_FILE_NAME: &str = "capabilities.json";
|
||||
|
||||
fn copy_file(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<()> {
|
||||
let from = from.as_ref();
|
||||
let to = to.as_ref();
|
||||
@@ -333,6 +339,9 @@ impl WindowsAttributes {
|
||||
pub struct Attributes {
|
||||
#[allow(dead_code)]
|
||||
windows_attributes: WindowsAttributes,
|
||||
capabilities_path_pattern: Option<&'static str>,
|
||||
#[cfg(feature = "codegen")]
|
||||
codegen: Option<codegen::context::CodegenContext>,
|
||||
}
|
||||
|
||||
impl Attributes {
|
||||
@@ -347,6 +356,21 @@ impl Attributes {
|
||||
self.windows_attributes = windows_attributes;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the glob pattern to be used to find the capabilities.
|
||||
#[must_use]
|
||||
pub fn capabilities_path_pattern(mut self, pattern: &'static str) -> Self {
|
||||
self.capabilities_path_pattern.replace(pattern);
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "codegen")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "codegen")))]
|
||||
#[must_use]
|
||||
pub fn codegen(mut self, codegen: codegen::context::CodegenContext) -> Self {
|
||||
self.codegen.replace(codegen);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Run all build time helpers for your Tauri Application.
|
||||
@@ -399,8 +423,11 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
|
||||
cfg_alias("desktop", !mobile);
|
||||
cfg_alias("mobile", mobile);
|
||||
|
||||
let target_triple = std::env::var("TARGET").unwrap();
|
||||
let target = tauri_utils::platform::Target::from_triple(&target_triple);
|
||||
|
||||
let mut config = serde_json::from_value(tauri_utils::config::parse::read_from(
|
||||
tauri_utils::platform::Target::from_triple(&std::env::var("TARGET").unwrap()),
|
||||
target,
|
||||
std::env::current_dir().unwrap(),
|
||||
)?)?;
|
||||
if let Ok(env) = std::env::var("TAURI_CONFIG") {
|
||||
@@ -441,13 +468,31 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
|
||||
Manifest::complete_from_path(&mut manifest, Path::new("Cargo.toml"))?;
|
||||
}
|
||||
|
||||
allowlist::check(&config, &mut manifest)?;
|
||||
let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
|
||||
|
||||
let target_triple = std::env::var("TARGET").unwrap();
|
||||
manifest::check(&config, &mut manifest)?;
|
||||
let plugin_manifests = acl::get_plugin_manifests()?;
|
||||
std::fs::write(
|
||||
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 {
|
||||
parse_capabilities("./capabilities/**/*")?
|
||||
};
|
||||
acl::generate_schema(&plugin_manifests, target)?;
|
||||
|
||||
acl::validate_capabilities(&plugin_manifests, &capabilities)?;
|
||||
|
||||
let capabilities_path = out_dir.join(CAPABILITIES_FILE_NAME);
|
||||
let capabilities_json = serde_json::to_string(&capabilities)?;
|
||||
if capabilities_json != read_to_string(&capabilities_path).unwrap_or_default() {
|
||||
std::fs::write(capabilities_path, capabilities_json)?;
|
||||
}
|
||||
|
||||
println!("cargo:rustc-env=TAURI_ENV_TARGET_TRIPLE={target_triple}");
|
||||
|
||||
let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
|
||||
// TODO: far from ideal, but there's no other way to get the target dir, see <https://github.com/rust-lang/cargo/issues/5457>
|
||||
let target_dir = out_dir
|
||||
.parent()
|
||||
@@ -611,6 +656,11 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "codegen")]
|
||||
if let Some(codegen) = attributes.codegen {
|
||||
codegen.try_build()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -211,7 +211,7 @@ mod tests {
|
||||
},
|
||||
),
|
||||
] {
|
||||
assert_eq!(super::features_diff(¤t, &expected), result);
|
||||
assert_eq!(crate::manifest::features_diff(¤t, &expected), result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{ffi::OsStr, str::FromStr};
|
||||
|
||||
@@ -10,6 +11,9 @@ use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
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::{AppUrl, Config, PatternKind, WindowUrl};
|
||||
use tauri_utils::html::{
|
||||
@@ -19,6 +23,9 @@ use tauri_utils::platform::Target;
|
||||
|
||||
use crate::embedded_assets::{AssetOptions, CspHashes, EmbeddedAssets, EmbeddedAssetsError};
|
||||
|
||||
const PLUGIN_MANIFESTS_FILE_NAME: &str = "plugin-manifests.json";
|
||||
const CAPABILITIES_FILE_NAME: &str = "capabilities.json";
|
||||
|
||||
/// Necessary data needed by [`context_codegen`] to generate code for a Tauri application context.
|
||||
pub struct ContextData {
|
||||
pub dev: bool,
|
||||
@@ -233,7 +240,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
|
||||
}
|
||||
};
|
||||
|
||||
let app_icon = if target == Target::Darwin && dev {
|
||||
let app_icon = if target == Target::MacOS && dev {
|
||||
let mut icon_path = find_icon(
|
||||
&config,
|
||||
&config_parent,
|
||||
@@ -297,7 +304,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
|
||||
};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
let info_plist = if target == Target::Darwin && dev {
|
||||
let info_plist = if target == Target::MacOS && dev {
|
||||
let info_plist_path = config_parent.join("Info.plist");
|
||||
let mut info_plist = if info_plist_path.exists() {
|
||||
plist::Value::from_file(&info_plist_path)
|
||||
@@ -373,6 +380,26 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
|
||||
}
|
||||
};
|
||||
|
||||
let acl_file_path = out_dir.join(PLUGIN_MANIFESTS_FILE_NAME);
|
||||
let acl: BTreeMap<String, Manifest> = if acl_file_path.exists() {
|
||||
let acl_file =
|
||||
std::fs::read_to_string(acl_file_path).expect("failed to read plugin manifest map");
|
||||
serde_json::from_str(&acl_file).expect("failed to parse plugin manifest map")
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
|
||||
let capabilities_file_path = out_dir.join(CAPABILITIES_FILE_NAME);
|
||||
let capabilities: 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 resolved_act = Resolved::resolve(acl, capabilities, target).expect("failed to resolve ACL");
|
||||
|
||||
Ok(quote!({
|
||||
#[allow(unused_mut, clippy::let_and_return)]
|
||||
let mut context = #root::Context::new(
|
||||
@@ -383,6 +410,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
|
||||
#package_info,
|
||||
#info_plist,
|
||||
#pattern,
|
||||
#resolved_act
|
||||
);
|
||||
#with_tray_icon_code
|
||||
context
|
||||
|
||||
@@ -5,7 +5,7 @@ edition = "2021"
|
||||
publish = false
|
||||
|
||||
[build-dependencies]
|
||||
tauri-utils = { features = ["schema"], path = "../tauri-utils" }
|
||||
tauri-utils = { features = [ "schema" ], path = "../tauri-utils" }
|
||||
schemars = { version = "0.8", features = ["url", "preserve_order"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
@@ -96,7 +96,6 @@
|
||||
"scope": []
|
||||
},
|
||||
"dangerousDisableAssetCspModification": false,
|
||||
"dangerousRemoteDomainIpcAccess": [],
|
||||
"freezePrototype": false
|
||||
},
|
||||
"windows": []
|
||||
@@ -254,7 +253,6 @@
|
||||
"scope": []
|
||||
},
|
||||
"dangerousDisableAssetCspModification": false,
|
||||
"dangerousRemoteDomainIpcAccess": [],
|
||||
"freezePrototype": false
|
||||
},
|
||||
"allOf": [
|
||||
@@ -2227,14 +2225,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"dangerousRemoteDomainIpcAccess": {
|
||||
"description": "Allow external domains to send command to Tauri.\n\nBy default, external domains do not have access to `window.__TAURI__`, which means they cannot communicate with the commands defined in Rust. This prevents attacks where an externally loaded malicious or compromised sites could start executing commands on the user's device.\n\nThis configuration allows a set of external domains to have access to the Tauri commands. When you configure a domain to be allowed to access the IPC, all subpaths are allowed. Subdomains are not allowed.\n\n**WARNING:** Only use this option if you either have internal checks against malicious external sites or you can trust the allowed external sites. You application might be vulnerable to dangerous Tauri command related attacks otherwise.",
|
||||
"default": [],
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/RemoteDomainAccessScope"
|
||||
}
|
||||
},
|
||||
"assetProtocol": {
|
||||
"description": "Custom protocol config.",
|
||||
"default": {
|
||||
@@ -2298,43 +2288,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"RemoteDomainAccessScope": {
|
||||
"description": "External command access definition.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"domain",
|
||||
"windows"
|
||||
],
|
||||
"properties": {
|
||||
"scheme": {
|
||||
"description": "The URL scheme to allow. By default, all schemas are allowed.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"domain": {
|
||||
"description": "The domain to allow.",
|
||||
"type": "string"
|
||||
},
|
||||
"windows": {
|
||||
"description": "The list of window labels this scope applies to.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"description": "The list of plugins that are allowed in this scope. The names should be without the `tauri-plugin-` prefix, for example `\"store\"` for `tauri-plugin-store`.",
|
||||
"default": [],
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"AssetProtocolConfig": {
|
||||
"description": "Config for the asset custom protocol.\n\nSee more: <https://tauri.app/v1/api/config#assetprotocolconfig>",
|
||||
"type": "object",
|
||||
|
||||
@@ -120,6 +120,7 @@ enum ArgumentCase {
|
||||
struct Invoke {
|
||||
message: Ident,
|
||||
resolver: Ident,
|
||||
acl: Ident,
|
||||
}
|
||||
|
||||
/// Create a new [`Wrapper`] from the function and the generated code parsed from the function.
|
||||
@@ -135,13 +136,14 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream {
|
||||
|
||||
// macros used with `pub use my_macro;` need to be exported with `#[macro_export]`
|
||||
let maybe_macro_export = match &function.vis {
|
||||
Visibility::Public(_) => quote!(#[macro_export]),
|
||||
_ => Default::default(),
|
||||
Visibility::Public(_) | Visibility::Restricted(_) => quote!(#[macro_export]),
|
||||
_ => TokenStream2::default(),
|
||||
};
|
||||
|
||||
let invoke = Invoke {
|
||||
message: format_ident!("__tauri_message__"),
|
||||
resolver: format_ident!("__tauri_resolver__"),
|
||||
acl: format_ident!("__tauri_acl__"),
|
||||
};
|
||||
|
||||
// Tauri currently doesn't support async commands that take a reference as input and don't return
|
||||
@@ -210,7 +212,11 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream {
|
||||
}
|
||||
};
|
||||
|
||||
let Invoke { message, resolver } = invoke;
|
||||
let Invoke {
|
||||
message,
|
||||
resolver,
|
||||
acl,
|
||||
} = invoke;
|
||||
|
||||
let root = attrs.root;
|
||||
|
||||
@@ -255,7 +261,7 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream {
|
||||
use #root::command::private::*;
|
||||
// prevent warnings when the body is a `compile_error!` or if the command has no arguments
|
||||
#[allow(unused_variables)]
|
||||
let #root::ipc::Invoke { message: #message, resolver: #resolver } = $invoke;
|
||||
let #root::ipc::Invoke { message: #message, resolver: #resolver, acl: #acl } = $invoke;
|
||||
|
||||
#maybe_span
|
||||
|
||||
@@ -280,8 +286,12 @@ fn body_async(
|
||||
invoke: &Invoke,
|
||||
attributes: &WrapperAttributes,
|
||||
) -> syn::Result<TokenStream2> {
|
||||
let Invoke { message, resolver } = invoke;
|
||||
parse_args(function, message, attributes).map(|args| {
|
||||
let Invoke {
|
||||
message,
|
||||
resolver,
|
||||
acl,
|
||||
} = invoke;
|
||||
parse_args(function, message, acl, attributes).map(|args| {
|
||||
#[cfg(feature = "tracing")]
|
||||
quote! {
|
||||
use tracing::Instrument;
|
||||
@@ -318,8 +328,12 @@ fn body_blocking(
|
||||
invoke: &Invoke,
|
||||
attributes: &WrapperAttributes,
|
||||
) -> syn::Result<TokenStream2> {
|
||||
let Invoke { message, resolver } = invoke;
|
||||
let args = parse_args(function, message, attributes)?;
|
||||
let Invoke {
|
||||
message,
|
||||
resolver,
|
||||
acl,
|
||||
} = invoke;
|
||||
let args = parse_args(function, message, acl, attributes)?;
|
||||
|
||||
// the body of a `match` to early return any argument that wasn't successful in parsing.
|
||||
let match_body = quote!({
|
||||
@@ -346,13 +360,14 @@ fn body_blocking(
|
||||
fn parse_args(
|
||||
function: &ItemFn,
|
||||
message: &Ident,
|
||||
acl: &Ident,
|
||||
attributes: &WrapperAttributes,
|
||||
) -> syn::Result<Vec<TokenStream2>> {
|
||||
function
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.map(|arg| parse_arg(&function.sig.ident, arg, message, attributes))
|
||||
.map(|arg| parse_arg(&function.sig.ident, arg, message, acl, attributes))
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -361,6 +376,7 @@ fn parse_arg(
|
||||
command: &Ident,
|
||||
arg: &FnArg,
|
||||
message: &Ident,
|
||||
acl: &Ident,
|
||||
attributes: &WrapperAttributes,
|
||||
) -> syn::Result<TokenStream2> {
|
||||
// we have no use for self arguments
|
||||
@@ -412,6 +428,7 @@ fn parse_arg(
|
||||
name: stringify!(#command),
|
||||
key: #key,
|
||||
message: &#message,
|
||||
acl: &#acl,
|
||||
}
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
use crate::context::ContextItems;
|
||||
use proc_macro::TokenStream;
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
use syn::parse_macro_input;
|
||||
|
||||
mod command;
|
||||
mod menu;
|
||||
@@ -87,7 +87,7 @@ pub fn generate_context(items: TokenStream) -> TokenStream {
|
||||
#[proc_macro_attribute]
|
||||
pub fn default_runtime(attributes: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let attributes = parse_macro_input!(attributes as runtime::Attributes);
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let input = parse_macro_input!(input as runtime::Input);
|
||||
runtime::default_runtime(attributes, input).into()
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,54 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::{parse_quote, DeriveInput, GenericParam, Ident, Token, Type, TypeParam};
|
||||
use syn::{
|
||||
parse_quote, DeriveInput, Error, GenericParam, Ident, ItemTrait, ItemType, Token, Type, TypeParam,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) enum Input {
|
||||
Derive(DeriveInput),
|
||||
Trait(ItemTrait),
|
||||
Type(ItemType),
|
||||
}
|
||||
|
||||
impl Parse for Input {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
input
|
||||
.parse::<DeriveInput>()
|
||||
.map(Self::Derive)
|
||||
.or_else(|_| input.parse().map(Self::Trait))
|
||||
.or_else(|_| input.parse().map(Self::Type))
|
||||
.map_err(|_| {
|
||||
Error::new(
|
||||
input.span(),
|
||||
"default_runtime only supports `struct`, `enum`, `type`, or `trait` definitions",
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Input {
|
||||
fn last_param_mut(&mut self) -> Option<&mut GenericParam> {
|
||||
match self {
|
||||
Input::Derive(d) => d.generics.params.last_mut(),
|
||||
Input::Trait(t) => t.generics.params.last_mut(),
|
||||
Input::Type(t) => t.generics.params.last_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Input {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
Input::Derive(d) => d.to_tokens(tokens),
|
||||
Input::Trait(t) => t.to_tokens(tokens),
|
||||
Input::Type(t) => t.to_tokens(tokens),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The default runtime type to enable when the provided feature is enabled.
|
||||
pub(crate) struct Attributes {
|
||||
@@ -24,13 +69,11 @@ impl Parse for Attributes {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn default_runtime(attributes: Attributes, input: DeriveInput) -> TokenStream {
|
||||
pub(crate) fn default_runtime(attributes: Attributes, input: Input) -> TokenStream {
|
||||
// create a new copy to manipulate for the wry feature flag
|
||||
let mut wry = input.clone();
|
||||
let wry_runtime = wry
|
||||
.generics
|
||||
.params
|
||||
.last_mut()
|
||||
.last_param_mut()
|
||||
.expect("default_runtime requires the item to have at least 1 generic parameter");
|
||||
|
||||
// set the default value of the last generic parameter to the provided runtime type
|
||||
|
||||
34
core/tauri-plugin/Cargo.toml
Normal file
34
core/tauri-plugin/Cargo.toml
Normal file
@@ -0,0 +1,34 @@
|
||||
[package]
|
||||
name = "tauri-plugin"
|
||||
version = "0.1.0"
|
||||
authors = { workspace = true }
|
||||
homepage = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
categories = { workspace = true }
|
||||
license = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
|
||||
[features]
|
||||
build = [
|
||||
"dep:serde",
|
||||
"dep:cargo_metadata",
|
||||
"dep:serde_json",
|
||||
"dep:glob",
|
||||
"dep:toml",
|
||||
]
|
||||
runtime = []
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1", optional = true }
|
||||
cargo_metadata = { version = "0.18", optional = true }
|
||||
tauri-utils = { version = "2.0.0-alpha.12", default-features = false, path = "../tauri-utils" }
|
||||
tauri = { version = "2.0.0-alpha.20", default-features = false, path = "../tauri" }
|
||||
serde_json = { version = "1", optional = true }
|
||||
glob = { version = "0.3", optional = true }
|
||||
toml = { version = "0.8", optional = true }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["build", "runtime"]
|
||||
rustc-args = ["--cfg", "docsrs"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
73
core/tauri-plugin/src/build/mod.rs
Normal file
73
core/tauri-plugin/src/build/mod.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use cargo_metadata::{Metadata, MetadataCommand};
|
||||
use tauri_utils::acl::{self, Error};
|
||||
|
||||
pub struct Builder<'a> {
|
||||
commands: &'a [&'static str],
|
||||
}
|
||||
|
||||
impl<'a> Builder<'a> {
|
||||
pub fn new(commands: &'a [&'static str]) -> Self {
|
||||
Self { commands }
|
||||
}
|
||||
|
||||
/// [`Self::try_build`] but will exit automatically if an error is found.
|
||||
pub fn build(self) {
|
||||
if let Err(error) = self.try_build() {
|
||||
println!("{}: {}", env!("CARGO_PKG_NAME"), error);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure this crate is properly configured to be a Tauri plugin.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Errors will occur if environmental variables expected to be set inside of [build scripts]
|
||||
/// are not found, or if the crate violates Tauri plugin conventions.
|
||||
pub fn try_build(self) -> Result<(), Error> {
|
||||
// convention: plugin names should not use underscores
|
||||
let name = build_var("CARGO_PKG_NAME")?;
|
||||
if name.contains('_') {
|
||||
return Err(Error::CrateName);
|
||||
}
|
||||
|
||||
// requirement: links MUST be set and MUST match the name
|
||||
let _links = build_var("CARGO_MANIFEST_LINKS")?;
|
||||
|
||||
let autogenerated = Path::new("permissions/autogenerated/");
|
||||
let commands_dir = &autogenerated.join("commands");
|
||||
|
||||
if !self.commands.is_empty() {
|
||||
acl::build::autogenerate_command_permissions(commands_dir, self.commands, "");
|
||||
}
|
||||
|
||||
let permissions = acl::build::define_permissions("./permissions/**/*.*", &name)?;
|
||||
acl::build::generate_schema(&permissions, "./permissions")?;
|
||||
|
||||
let metadata = find_metadata()?;
|
||||
println!("{metadata:#?}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Grab an env var that is expected to be set inside of build scripts.
|
||||
fn build_var(key: &'static str) -> Result<String, Error> {
|
||||
std::env::var(key).map_err(|_| Error::BuildVar(key))
|
||||
}
|
||||
|
||||
fn find_metadata() -> Result<Metadata, Error> {
|
||||
build_var("CARGO_MANIFEST_DIR").and_then(|dir| {
|
||||
MetadataCommand::new()
|
||||
.current_dir(dir)
|
||||
.no_deps()
|
||||
.exec()
|
||||
.map_err(Error::Metadata)
|
||||
})
|
||||
}
|
||||
25
core/tauri-plugin/src/lib.rs
Normal file
25
core/tauri-plugin/src/lib.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//! [](https://tauri.app)
|
||||
//!
|
||||
//! Interface for building Tauri plugins.
|
||||
|
||||
#![doc(
|
||||
html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png",
|
||||
html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png"
|
||||
)]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
#[cfg(feature = "build")]
|
||||
mod build;
|
||||
#[cfg(feature = "runtime")]
|
||||
mod runtime;
|
||||
|
||||
#[cfg(feature = "build")]
|
||||
#[cfg_attr(docsrs, doc(feature = "build"))]
|
||||
pub use build::*;
|
||||
#[cfg(feature = "runtime")]
|
||||
#[cfg_attr(docsrs, doc(feature = "runtime"))]
|
||||
pub use runtime::*;
|
||||
3
core/tauri-plugin/src/runtime.rs
Normal file
3
core/tauri-plugin/src/runtime.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
@@ -30,25 +30,26 @@ getrandom = { version = "0.2", optional = true, features = [ "std" ] }
|
||||
serialize-to-javascript = { version = "=0.1.1", optional = true }
|
||||
ctor = "0.2"
|
||||
json5 = { version = "0.4", optional = true }
|
||||
toml = { version = "0.8", optional = true }
|
||||
toml = { version = "0.8", features = ["parse"] }
|
||||
json-patch = "1.2"
|
||||
glob = { version = "0.3", optional = true }
|
||||
glob = "0.3"
|
||||
walkdir = { version = "2", optional = true }
|
||||
memchr = "2"
|
||||
semver = "1"
|
||||
infer = "0.15"
|
||||
dunce = "1"
|
||||
log = "0.4.20"
|
||||
cargo_metadata = { version = "0.18", optional = true }
|
||||
|
||||
[target."cfg(target_os = \"linux\")".dependencies]
|
||||
heck = "0.4"
|
||||
|
||||
[features]
|
||||
build = [ "proc-macro2", "quote" ]
|
||||
build = [ "proc-macro2", "quote", "cargo_metadata", "schema" ]
|
||||
compression = [ "brotli" ]
|
||||
schema = [ "schemars" ]
|
||||
isolation = [ "aes-gcm", "getrandom", "serialize-to-javascript" ]
|
||||
process-relaunch-dangerous-allow-symlink-macos = [ ]
|
||||
config-json5 = [ "json5" ]
|
||||
config-toml = [ "toml" ]
|
||||
resources = [ "glob", "walkdir" ]
|
||||
config-toml = []
|
||||
resources = [ "walkdir" ]
|
||||
|
||||
318
core/tauri-utils/src/acl/build.rs
Normal file
318
core/tauri-utils/src/acl/build.rs
Normal file
@@ -0,0 +1,318 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//! ACL items that are only useful inside of build script/codegen context.
|
||||
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
env::{current_dir, vars_os},
|
||||
fs::{create_dir_all, read_to_string, File},
|
||||
io::{BufWriter, Write},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use crate::acl::Error;
|
||||
use schemars::{
|
||||
schema::{InstanceType, Metadata, RootSchema, Schema, SchemaObject, SubschemaValidation},
|
||||
schema_for,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
use super::{capability::Capability, plugin::PermissionFile};
|
||||
|
||||
/// Cargo cfg key for permissions file paths
|
||||
pub const PERMISSION_FILES_PATH_KEY: &str = "PERMISSION_FILES_PATH";
|
||||
|
||||
/// Allowed permission file extensions
|
||||
pub const PERMISSION_FILE_EXTENSIONS: &[&str] = &["json", "toml"];
|
||||
|
||||
/// Known filename of a permission schema
|
||||
pub const PERMISSION_SCHEMA_FILE_NAME: &str = ".schema.json";
|
||||
|
||||
/// Allowed capability file extensions
|
||||
const CAPABILITY_FILE_EXTENSIONS: &[&str] = &["json", "toml"];
|
||||
|
||||
/// Known folder name of the capability schemas
|
||||
const CAPABILITIES_SCHEMA_FOLDER_NAME: &str = "schemas";
|
||||
|
||||
const CORE_PLUGIN_PERMISSIONS_TOKEN: &str = "__CORE_PLUGIN__";
|
||||
|
||||
/// Capability formats accepted in a capability file.
|
||||
#[derive(Deserialize, schemars::JsonSchema)]
|
||||
#[serde(untagged)]
|
||||
pub enum CapabilityFile {
|
||||
/// A single capability.
|
||||
Capability(Capability),
|
||||
/// A list of capabilities.
|
||||
List {
|
||||
/// The list of capabilities.
|
||||
capabilities: Vec<Capability>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Write the permissions to a temporary directory and pass it to the immediate consuming crate.
|
||||
pub fn define_permissions(pattern: &str, pkg_name: &str) -> Result<Vec<PermissionFile>, Error> {
|
||||
let permission_files = glob::glob(pattern)?
|
||||
.flatten()
|
||||
.flat_map(|p| p.canonicalize())
|
||||
// filter extension
|
||||
.filter(|p| {
|
||||
p.extension()
|
||||
.and_then(|e| e.to_str())
|
||||
.map(|e| PERMISSION_FILE_EXTENSIONS.contains(&e))
|
||||
.unwrap_or_default()
|
||||
})
|
||||
// filter schema file
|
||||
.filter(|p| {
|
||||
p.file_name()
|
||||
.map(|name| name != PERMISSION_SCHEMA_FILE_NAME)
|
||||
.unwrap_or(true)
|
||||
})
|
||||
.collect::<Vec<PathBuf>>();
|
||||
|
||||
for path in &permission_files {
|
||||
println!("cargo:rerun-if-changed={}", path.display());
|
||||
}
|
||||
|
||||
let permission_files_path = std::env::temp_dir().join(format!("{}-permission-files", pkg_name));
|
||||
std::fs::write(
|
||||
&permission_files_path,
|
||||
serde_json::to_string(&permission_files)?,
|
||||
)
|
||||
.map_err(Error::WriteFile)?;
|
||||
|
||||
if let Some(plugin_name) = pkg_name.strip_prefix("tauri:") {
|
||||
println!(
|
||||
"cargo:{plugin_name}{CORE_PLUGIN_PERMISSIONS_TOKEN}_{PERMISSION_FILES_PATH_KEY}={}",
|
||||
permission_files_path.display()
|
||||
);
|
||||
} else {
|
||||
println!(
|
||||
"cargo:{PERMISSION_FILES_PATH_KEY}={}",
|
||||
permission_files_path.display()
|
||||
);
|
||||
}
|
||||
|
||||
parse_permissions(permission_files)
|
||||
}
|
||||
|
||||
/// Parses all capability files with the given glob pattern.
|
||||
pub fn parse_capabilities(
|
||||
capabilities_path_pattern: &str,
|
||||
) -> Result<BTreeMap<String, Capability>, Error> {
|
||||
let mut capabilities_map = BTreeMap::new();
|
||||
|
||||
for path in glob::glob(capabilities_path_pattern)?
|
||||
.flatten() // filter extension
|
||||
.filter(|p| {
|
||||
p.extension()
|
||||
.and_then(|e| e.to_str())
|
||||
.map(|e| CAPABILITY_FILE_EXTENSIONS.contains(&e))
|
||||
.unwrap_or_default()
|
||||
})
|
||||
// filter schema files
|
||||
.filter(|p| p.parent().unwrap().file_name().unwrap() != CAPABILITIES_SCHEMA_FOLDER_NAME)
|
||||
{
|
||||
println!("cargo:rerun-if-changed={}", path.display());
|
||||
|
||||
let capability_file = std::fs::read_to_string(&path).map_err(Error::ReadFile)?;
|
||||
let ext = path.extension().unwrap().to_string_lossy().to_string();
|
||||
let capability: CapabilityFile = match ext.as_str() {
|
||||
"toml" => toml::from_str(&capability_file)?,
|
||||
"json" => serde_json::from_str(&capability_file)?,
|
||||
_ => return Err(Error::UnknownCapabilityFormat(ext)),
|
||||
};
|
||||
|
||||
match capability {
|
||||
CapabilityFile::Capability(capability) => {
|
||||
capabilities_map.insert(capability.identifier.clone(), capability);
|
||||
}
|
||||
CapabilityFile::List { capabilities } => {
|
||||
for capability in capabilities {
|
||||
capabilities_map.insert(capability.identifier.clone(), capability);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(capabilities_map)
|
||||
}
|
||||
|
||||
fn permissions_schema(permissions: &[PermissionFile]) -> RootSchema {
|
||||
let mut schema = schema_for!(PermissionFile);
|
||||
|
||||
fn schema_from(id: &str, description: Option<&str>) -> Schema {
|
||||
Schema::Object(SchemaObject {
|
||||
metadata: Some(Box::new(Metadata {
|
||||
description: description.map(|d| format!("{id} -> {d}")),
|
||||
..Default::default()
|
||||
})),
|
||||
instance_type: Some(InstanceType::String.into()),
|
||||
enum_values: Some(vec![serde_json::Value::String(id.into())]),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
let mut permission_schemas = Vec::new();
|
||||
for file in permissions {
|
||||
if let Some(permission) = &file.default {
|
||||
permission_schemas.push(schema_from("default", permission.description.as_deref()));
|
||||
}
|
||||
|
||||
permission_schemas.extend(
|
||||
file
|
||||
.set
|
||||
.iter()
|
||||
.map(|set| schema_from(&set.identifier, Some(set.description.as_str())))
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
permission_schemas.extend(
|
||||
file
|
||||
.permission
|
||||
.iter()
|
||||
.map(|permission| schema_from(&permission.identifier, permission.description.as_deref()))
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(Schema::Object(obj)) = schema.definitions.get_mut("PermissionSet") {
|
||||
if let Some(Schema::Object(permissions_prop_schema)) =
|
||||
obj.object().properties.get_mut("permissions")
|
||||
{
|
||||
permissions_prop_schema.array().items.replace(
|
||||
Schema::Object(SchemaObject {
|
||||
reference: Some("#/definitions/PermissionKind".into()),
|
||||
..Default::default()
|
||||
})
|
||||
.into(),
|
||||
);
|
||||
|
||||
schema.definitions.insert(
|
||||
"PermissionKind".into(),
|
||||
Schema::Object(SchemaObject {
|
||||
instance_type: Some(InstanceType::String.into()),
|
||||
subschemas: Some(Box::new(SubschemaValidation {
|
||||
one_of: Some(permission_schemas),
|
||||
..Default::default()
|
||||
})),
|
||||
..Default::default()
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
schema
|
||||
}
|
||||
|
||||
/// Generate and write a schema based on the format of a [`PermissionFile`].
|
||||
pub fn generate_schema<P: AsRef<Path>>(
|
||||
permissions: &[PermissionFile],
|
||||
out_dir: P,
|
||||
) -> Result<(), Error> {
|
||||
let schema = permissions_schema(permissions);
|
||||
let schema_str = serde_json::to_string_pretty(&schema).unwrap();
|
||||
|
||||
let out_dir = out_dir.as_ref();
|
||||
create_dir_all(out_dir).expect("unable to create schema output directory");
|
||||
|
||||
let mut schema_file = BufWriter::new(
|
||||
File::create(out_dir.join(PERMISSION_SCHEMA_FILE_NAME)).map_err(Error::CreateFile)?,
|
||||
);
|
||||
write!(schema_file, "{schema_str}").map_err(Error::WriteFile)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read all permissions listed from the defined cargo cfg key value.
|
||||
pub fn read_permissions() -> Result<HashMap<String, Vec<PermissionFile>>, Error> {
|
||||
let mut permissions_map = HashMap::new();
|
||||
|
||||
for (key, value) in vars_os() {
|
||||
let key = key.to_string_lossy();
|
||||
|
||||
if let Some(plugin_crate_name_var) = key
|
||||
.strip_prefix("DEP_")
|
||||
.and_then(|v| v.strip_suffix(&format!("_{PERMISSION_FILES_PATH_KEY}")))
|
||||
.map(|v| {
|
||||
v.strip_suffix(CORE_PLUGIN_PERMISSIONS_TOKEN)
|
||||
.and_then(|v| v.strip_prefix("TAURI_"))
|
||||
.unwrap_or(v)
|
||||
})
|
||||
{
|
||||
let permissions_path = PathBuf::from(value);
|
||||
let permissions_str = std::fs::read_to_string(&permissions_path).map_err(Error::ReadFile)?;
|
||||
let permissions: Vec<PathBuf> = serde_json::from_str(&permissions_str)?;
|
||||
let permissions = parse_permissions(permissions)?;
|
||||
|
||||
let plugin_crate_name = plugin_crate_name_var.to_lowercase().replace('_', "-");
|
||||
permissions_map.insert(
|
||||
plugin_crate_name
|
||||
.strip_prefix("tauri-plugin-")
|
||||
.map(|n| n.to_string())
|
||||
.unwrap_or(plugin_crate_name),
|
||||
permissions,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(permissions_map)
|
||||
}
|
||||
|
||||
fn parse_permissions(paths: Vec<PathBuf>) -> Result<Vec<PermissionFile>, Error> {
|
||||
let mut permissions = Vec::new();
|
||||
for path in paths {
|
||||
let permission_file = std::fs::read_to_string(&path).map_err(Error::ReadFile)?;
|
||||
let ext = path.extension().unwrap().to_string_lossy().to_string();
|
||||
let permission: PermissionFile = match ext.as_str() {
|
||||
"toml" => toml::from_str(&permission_file)?,
|
||||
"json" => serde_json::from_str(&permission_file)?,
|
||||
_ => return Err(Error::UnknownPermissionFormat(ext)),
|
||||
};
|
||||
permissions.push(permission);
|
||||
}
|
||||
Ok(permissions)
|
||||
}
|
||||
|
||||
/// Autogenerate permission files for a list of commands.
|
||||
pub fn autogenerate_command_permissions(path: &Path, commands: &[&str], license_header: &str) {
|
||||
if !path.exists() {
|
||||
create_dir_all(path).expect("unable to create autogenerated commands dir");
|
||||
}
|
||||
|
||||
let cwd = current_dir().unwrap();
|
||||
let components_len = path.strip_prefix(&cwd).unwrap_or(path).components().count();
|
||||
let schema_path = (1..components_len)
|
||||
.map(|_| "..")
|
||||
.collect::<PathBuf>()
|
||||
.join(PERMISSION_SCHEMA_FILE_NAME);
|
||||
|
||||
for command in commands {
|
||||
let slugified_command = command.replace('_', "-");
|
||||
let toml = format!(
|
||||
r###"{license_header}# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "{schema_path}"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-{slugified_command}"
|
||||
description = "Enables the {command} command without any pre-configured scope."
|
||||
commands.allow = ["{command}"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-{slugified_command}"
|
||||
description = "Denies the {command} command without any pre-configured scope."
|
||||
commands.deny = ["{command}"]
|
||||
"###,
|
||||
command = command,
|
||||
slugified_command = slugified_command,
|
||||
schema_path = schema_path.display().to_string().replace('\\', "\\\\")
|
||||
);
|
||||
|
||||
let out_path = path.join(format!("{command}.toml"));
|
||||
if toml != read_to_string(&out_path).unwrap_or_default() {
|
||||
std::fs::write(out_path, toml)
|
||||
.unwrap_or_else(|_| panic!("unable to autogenerate ${command}.toml"));
|
||||
}
|
||||
}
|
||||
}
|
||||
68
core/tauri-utils/src/acl/capability.rs
Normal file
68
core/tauri-utils/src/acl/capability.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//! End-user abstraction for selecting permissions a window has access to.
|
||||
|
||||
use crate::{acl::Identifier, platform::Target};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
||||
/// A set of direct capabilities grouped together under a new name.
|
||||
pub struct CapabilitySet {
|
||||
inner: Vec<Capability>,
|
||||
}
|
||||
|
||||
/// a grouping and boundary mechanism developers can use to separate windows or plugins functionality from each other at runtime.
|
||||
///
|
||||
/// If a window is not matching any capability then it has no access to the IPC layer at all.
|
||||
///
|
||||
/// This 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.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||
pub struct Capability {
|
||||
/// Identifier of the capability.
|
||||
pub identifier: String,
|
||||
/// Description of the capability.
|
||||
#[serde(default)]
|
||||
pub description: String,
|
||||
/// Execution context of the capability.
|
||||
///
|
||||
/// At runtime, Tauri filters the IPC command together with the context to determine wheter it is allowed or not and its scope.
|
||||
#[serde(default)]
|
||||
pub context: CapabilityContext,
|
||||
/// List of windows that uses this capability. Can be a glob pattern.
|
||||
pub windows: Vec<String>,
|
||||
/// 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<Identifier>,
|
||||
/// Target platforms this capability applies. By default all platforms applies.
|
||||
#[serde(default = "default_platforms")]
|
||||
pub platforms: Vec<Target>,
|
||||
}
|
||||
|
||||
fn default_platforms() -> Vec<Target> {
|
||||
vec![
|
||||
Target::Linux,
|
||||
Target::MacOS,
|
||||
Target::Windows,
|
||||
Target::Android,
|
||||
Target::Ios,
|
||||
]
|
||||
}
|
||||
|
||||
/// Context of the capability.
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord, Hash)]
|
||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum CapabilityContext {
|
||||
/// Capability refers to local URL usage.
|
||||
#[default]
|
||||
Local,
|
||||
/// Capability refers to remote usage.
|
||||
Remote {
|
||||
/// Remote domains this capability refers to. Can use glob patterns.
|
||||
domains: Vec<String>,
|
||||
},
|
||||
}
|
||||
262
core/tauri-utils/src/acl/identifier.rs
Normal file
262
core/tauri-utils/src/acl/identifier.rs
Normal file
@@ -0,0 +1,262 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//! Identifier for plugins.
|
||||
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::num::NonZeroU8;
|
||||
use thiserror::Error;
|
||||
|
||||
const IDENTIFIER_SEPARATOR: u8 = b':';
|
||||
const PLUGIN_PREFIX: &str = "tauri-plugin-";
|
||||
|
||||
// https://doc.rust-lang.org/cargo/reference/manifest.html#the-name-field
|
||||
const MAX_LEN_PREFIX: usize = 64 - PLUGIN_PREFIX.len();
|
||||
const MAX_LEN_BASE: usize = 64;
|
||||
const MAX_LEN_IDENTIFIER: usize = MAX_LEN_PREFIX + 1 + MAX_LEN_BASE;
|
||||
|
||||
/// Plugin identifier.
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||
pub struct Identifier {
|
||||
inner: String,
|
||||
separator: Option<NonZeroU8>,
|
||||
}
|
||||
|
||||
impl AsRef<str> for Identifier {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl Identifier {
|
||||
/// Get the identifier str.
|
||||
#[inline(always)]
|
||||
pub fn get(&self) -> &str {
|
||||
self.as_ref()
|
||||
}
|
||||
|
||||
/// Get the identifier without prefix.
|
||||
pub fn get_base(&self) -> &str {
|
||||
match self.separator_index() {
|
||||
None => self.get(),
|
||||
Some(i) => &self.inner[i + 1..],
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the prefix of the identifier.
|
||||
pub fn get_prefix(&self) -> Option<&str> {
|
||||
self.separator_index().map(|i| &self.inner[0..i])
|
||||
}
|
||||
|
||||
/// Set the identifier prefix.
|
||||
pub fn set_prefix(&mut self) -> Result<(), ParseIdentifierError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Get the identifier string and its separator.
|
||||
pub fn into_inner(self) -> (String, Option<NonZeroU8>) {
|
||||
(self.inner, self.separator)
|
||||
}
|
||||
|
||||
fn separator_index(&self) -> Option<usize> {
|
||||
self.separator.map(|i| i.get() as usize)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ValidByte {
|
||||
Separator,
|
||||
Byte(u8),
|
||||
}
|
||||
|
||||
impl ValidByte {
|
||||
fn lower_alpha(byte: u8) -> Option<Self> {
|
||||
byte.is_ascii_lowercase().then_some(Self::Byte(byte))
|
||||
}
|
||||
|
||||
fn lower_alpha_hyphen(byte: u8) -> Option<Self> {
|
||||
matches!(byte, b'a'..=b'z' | b'-').then_some(Self::Byte(byte))
|
||||
}
|
||||
|
||||
fn next(&self, next: u8) -> Option<ValidByte> {
|
||||
match (self, next) {
|
||||
(ValidByte::Byte(b'-'), IDENTIFIER_SEPARATOR) => None,
|
||||
(ValidByte::Separator, b'-') => None,
|
||||
|
||||
(_, IDENTIFIER_SEPARATOR) => Some(ValidByte::Separator),
|
||||
(ValidByte::Separator, next) => ValidByte::lower_alpha(next),
|
||||
(ValidByte::Byte(b'-'), next) => ValidByte::lower_alpha(next),
|
||||
(ValidByte::Byte(_), next) => ValidByte::lower_alpha_hyphen(next),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors that can happen when parsing an identifier.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ParseIdentifierError {
|
||||
/// Identifier start with the plugin prefix.
|
||||
#[error("identifiers cannot start with {}", PLUGIN_PREFIX)]
|
||||
StartsWithTauriPlugin,
|
||||
|
||||
/// Identifier empty.
|
||||
#[error("identifiers cannot be empty")]
|
||||
Empty,
|
||||
|
||||
/// Identifier is too long.
|
||||
#[error("identifiers cannot be longer than {}, found {0}", MAX_LEN_IDENTIFIER)]
|
||||
Humungous(usize),
|
||||
|
||||
/// Identifier is not in a valid format.
|
||||
#[error("identifiers can only include lowercase ASCII, hyphens which are not leading or trailing, and a single colon if using a prefix")]
|
||||
InvalidFormat,
|
||||
|
||||
/// Identifier has multiple separators.
|
||||
#[error(
|
||||
"identifiers can only include a single separator '{}'",
|
||||
IDENTIFIER_SEPARATOR
|
||||
)]
|
||||
MultipleSeparators,
|
||||
|
||||
/// Identifier has a trailing hyphen.
|
||||
#[error("identifiers cannot have a trailing hyphen")]
|
||||
TrailingHyphen,
|
||||
|
||||
/// Identifier has a prefix without a base.
|
||||
#[error("identifiers cannot have a prefix without a base")]
|
||||
PrefixWithoutBase,
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Identifier {
|
||||
type Error = ParseIdentifierError;
|
||||
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
if value.starts_with(PLUGIN_PREFIX) {
|
||||
return Err(Self::Error::StartsWithTauriPlugin);
|
||||
}
|
||||
|
||||
if value.is_empty() {
|
||||
return Err(Self::Error::Empty);
|
||||
}
|
||||
|
||||
let mut bytes = value.bytes();
|
||||
if bytes.len() > MAX_LEN_IDENTIFIER {
|
||||
return Err(Self::Error::Humungous(bytes.len()));
|
||||
}
|
||||
|
||||
// grab the first byte only before parsing the rest
|
||||
let mut prev = bytes
|
||||
.next()
|
||||
.and_then(ValidByte::lower_alpha)
|
||||
.ok_or(Self::Error::InvalidFormat)?;
|
||||
|
||||
let mut idx = 0;
|
||||
let mut seperator = None;
|
||||
for byte in bytes {
|
||||
idx += 1; // we already consumed first item
|
||||
match prev.next(byte) {
|
||||
None => return Err(Self::Error::InvalidFormat),
|
||||
Some(next @ ValidByte::Byte(_)) => prev = next,
|
||||
Some(ValidByte::Separator) => {
|
||||
if seperator.is_none() {
|
||||
// safe to unwrap because idx starts at 1 and cannot go over MAX_IDENTIFIER_LEN
|
||||
seperator = Some(idx.try_into().unwrap());
|
||||
prev = ValidByte::Separator
|
||||
} else {
|
||||
return Err(Self::Error::MultipleSeparators);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match prev {
|
||||
// empty base
|
||||
ValidByte::Separator => return Err(Self::Error::PrefixWithoutBase),
|
||||
|
||||
// trailing hyphen
|
||||
ValidByte::Byte(b'-') => return Err(Self::Error::TrailingHyphen),
|
||||
|
||||
_ => (),
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
inner: value,
|
||||
separator: seperator,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Identifier {
|
||||
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Self::try_from(String::deserialize(deserializer)?).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Identifier {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(self.get())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn ident(s: impl Into<String>) -> Result<Identifier, ParseIdentifierError> {
|
||||
Identifier::try_from(s.into())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn max_len_fits_in_u8() {
|
||||
assert!(MAX_LEN_IDENTIFIER < u8::MAX as usize)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format() {
|
||||
assert!(ident("prefix:base").is_ok());
|
||||
|
||||
// bad
|
||||
assert!(ident("tauri-plugin-prefix:base").is_err());
|
||||
|
||||
assert!(ident("-prefix-:-base-").is_err());
|
||||
assert!(ident("-prefix:base").is_err());
|
||||
assert!(ident("prefix-:base").is_err());
|
||||
assert!(ident("prefix:-base").is_err());
|
||||
assert!(ident("prefix:base-").is_err());
|
||||
|
||||
assert!(ident("pre--fix:base--sep").is_err());
|
||||
assert!(ident("prefix:base--sep").is_err());
|
||||
assert!(ident("pre--fix:base").is_err());
|
||||
|
||||
assert!(ident("prefix::base").is_err());
|
||||
assert!(ident(":base").is_err());
|
||||
assert!(ident("prefix:").is_err());
|
||||
assert!(ident(":prefix:base:").is_err());
|
||||
assert!(ident("base:").is_err());
|
||||
|
||||
assert!(ident("").is_err());
|
||||
assert!(ident("💩").is_err());
|
||||
|
||||
assert!(ident("a".repeat(MAX_LEN_IDENTIFIER + 1)).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn base() {
|
||||
assert_eq!(ident("prefix:base").unwrap().get_base(), "base");
|
||||
assert_eq!(ident("base").unwrap().get_base(), "base");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prefix() {
|
||||
assert_eq!(ident("prefix:base").unwrap().get_prefix(), Some("prefix"));
|
||||
assert_eq!(ident("base").unwrap().get_prefix(), None);
|
||||
}
|
||||
}
|
||||
220
core/tauri-utils/src/acl/mod.rs
Normal file
220
core/tauri-utils/src/acl/mod.rs
Normal file
@@ -0,0 +1,220 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//! Access Control List types.
|
||||
|
||||
use glob::Pattern;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::num::NonZeroU64;
|
||||
use thiserror::Error;
|
||||
|
||||
pub use self::{identifier::*, value::*};
|
||||
|
||||
#[cfg(feature = "build")]
|
||||
pub mod build;
|
||||
pub mod capability;
|
||||
pub mod identifier;
|
||||
pub mod plugin;
|
||||
pub mod resolved;
|
||||
pub mod value;
|
||||
|
||||
/// Possible errors while processing ACL files.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
/// Could not find an environmental variable that is set inside of build scripts.
|
||||
///
|
||||
/// Whatever generated this should be called inside of a build script.
|
||||
#[error("expected build script env var {0}, but it was not found - ensure this is called in a build script")]
|
||||
BuildVar(&'static str),
|
||||
|
||||
/// Plugin name doesn't follow Tauri standards
|
||||
#[error("plugin names cannot contain underscores")]
|
||||
CrateName,
|
||||
|
||||
/// The links field in the manifest **MUST** be set and match the name of the crate.
|
||||
#[error("package.links field in the Cargo manifest is not set, it should be set to the same as package.name")]
|
||||
LinksMissing,
|
||||
|
||||
/// The links field in the manifest **MUST** match the name of the crate.
|
||||
#[error(
|
||||
"package.links field in the Cargo manifest MUST be set to the same value as package.name"
|
||||
)]
|
||||
LinksName,
|
||||
|
||||
/// IO error while reading a file
|
||||
#[error("failed to read file: {0}")]
|
||||
ReadFile(std::io::Error),
|
||||
|
||||
/// IO error while writing a file
|
||||
#[error("failed to write file: {0}")]
|
||||
WriteFile(std::io::Error),
|
||||
|
||||
/// IO error while creating a file
|
||||
#[error("failed to create file: {0}")]
|
||||
CreateFile(std::io::Error),
|
||||
|
||||
/// [`cargo_metadata`] was not able to complete successfully
|
||||
#[cfg(feature = "build")]
|
||||
#[error("failed to execute: {0}")]
|
||||
Metadata(#[from] ::cargo_metadata::Error),
|
||||
|
||||
/// Invalid glob
|
||||
#[error("failed to run glob: {0}")]
|
||||
Glob(#[from] glob::PatternError),
|
||||
|
||||
/// Invalid TOML encountered
|
||||
#[error("failed to parse TOML: {0}")]
|
||||
Toml(#[from] toml::de::Error),
|
||||
|
||||
/// Invalid JSON encountered
|
||||
#[error("failed to parse JSON: {0}")]
|
||||
Json(#[from] serde_json::Error),
|
||||
|
||||
/// Invalid permissions file format
|
||||
#[error("unknown permission format {0}")]
|
||||
UnknownPermissionFormat(String),
|
||||
|
||||
/// Invalid capabilities file format
|
||||
#[error("unknown capability format {0}")]
|
||||
UnknownCapabilityFormat(String),
|
||||
|
||||
/// Permission referenced in set not found.
|
||||
#[error("permission {permission} not found from set {set}")]
|
||||
SetPermissionNotFound {
|
||||
/// Permission identifier.
|
||||
permission: String,
|
||||
/// Set identifier.
|
||||
set: String,
|
||||
},
|
||||
|
||||
/// Plugin has no default permission.
|
||||
#[error("plugin {plugin} has no default permission")]
|
||||
MissingDefaultPermission {
|
||||
/// Plugin name.
|
||||
plugin: String,
|
||||
},
|
||||
|
||||
/// Unknown plugin.
|
||||
#[error("unknown plugin {plugin}, expected one of {available}")]
|
||||
UnknownPlugin {
|
||||
/// Plugin name.
|
||||
plugin: String,
|
||||
/// Available plugins.
|
||||
available: String,
|
||||
},
|
||||
|
||||
/// Unknown permission.
|
||||
#[error("unknown permission {permission} for plugin {plugin}")]
|
||||
UnknownPermission {
|
||||
/// Plugin name.
|
||||
plugin: String,
|
||||
|
||||
/// Permission identifier.
|
||||
permission: String,
|
||||
},
|
||||
}
|
||||
|
||||
/// Allowed and denied commands inside a permission.
|
||||
///
|
||||
/// If two commands clash inside of `allow` and `deny`, it should be denied by default.
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||
pub struct Commands {
|
||||
/// Allowed command.
|
||||
#[serde(default)]
|
||||
pub allow: Vec<String>,
|
||||
|
||||
/// Denied command, which takes priority.
|
||||
#[serde(default)]
|
||||
pub deny: Vec<String>,
|
||||
}
|
||||
|
||||
/// A restriction of the command/endpoint functionality.
|
||||
///
|
||||
/// It can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command.
|
||||
///
|
||||
/// The scope is passed to the command and handled/enforced by the command itself.
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||
pub struct Scopes {
|
||||
/// Data that defines what is allowed by the scope.
|
||||
pub allow: Option<Vec<Value>>,
|
||||
/// Data that defines what is denied by the scope.
|
||||
pub deny: Option<Vec<Value>>,
|
||||
}
|
||||
|
||||
/// Descriptions of explicit privileges of commands.
|
||||
///
|
||||
/// It can enable commands to be accessible in the frontend of the application.
|
||||
///
|
||||
/// If the scope is defined it can be used to fine grain control the access of individual or multiple commands.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||
pub struct Permission {
|
||||
/// The version of the permission.
|
||||
pub version: Option<NonZeroU64>,
|
||||
|
||||
/// A unique identifier for the permission.
|
||||
pub identifier: String,
|
||||
|
||||
/// Human-readable description of what the permission does.
|
||||
pub description: Option<String>,
|
||||
|
||||
/// Allowed or denied commands when using this permission.
|
||||
#[serde(default)]
|
||||
pub commands: Commands,
|
||||
|
||||
/// Allowed or denied scoped when using this permission.
|
||||
#[serde(default)]
|
||||
pub scope: Scopes,
|
||||
}
|
||||
|
||||
/// A set of direct permissions grouped together under a new name.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||
pub struct PermissionSet {
|
||||
/// A unique identifier for the permission.
|
||||
pub identifier: String,
|
||||
|
||||
/// Human-readable description of what the permission does.
|
||||
pub description: String,
|
||||
|
||||
/// All permissions this set contains.
|
||||
pub permissions: Vec<String>,
|
||||
}
|
||||
|
||||
/// Execution context of an IPC call.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
|
||||
pub enum ExecutionContext {
|
||||
/// A local URL is used (the Tauri app URL).
|
||||
Local,
|
||||
/// Remote URL is tring to use the IPC.
|
||||
Remote {
|
||||
/// The domain trying to access the IPC (glob pattern).
|
||||
domain: Pattern,
|
||||
},
|
||||
}
|
||||
|
||||
#[cfg(feature = "build")]
|
||||
mod build_ {
|
||||
use super::*;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens, TokenStreamExt};
|
||||
|
||||
impl ToTokens for ExecutionContext {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let prefix = quote! { ::tauri::utils::acl::ExecutionContext };
|
||||
|
||||
tokens.append_all(match self {
|
||||
Self::Local => {
|
||||
quote! { #prefix::Local }
|
||||
}
|
||||
Self::Remote { domain } => {
|
||||
let domain = domain.as_str();
|
||||
quote! { #prefix::Remote { domain: #domain.parse().unwrap() } }
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
106
core/tauri-utils/src/acl/plugin.rs
Normal file
106
core/tauri-utils/src/acl/plugin.rs
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//! Plugin ACL types.
|
||||
|
||||
use std::{collections::HashMap, num::NonZeroU64};
|
||||
|
||||
use super::{Permission, PermissionSet};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// The default permission set of the plugin.
|
||||
///
|
||||
/// Works similarly to a permission with the "default" identifier.
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||
pub struct DefaultPermission {
|
||||
/// The version of the permission.
|
||||
pub version: Option<NonZeroU64>,
|
||||
|
||||
/// Human-readable description of what the permission does.
|
||||
pub description: Option<String>,
|
||||
|
||||
/// All permissions this set contains.
|
||||
pub permissions: Vec<String>,
|
||||
}
|
||||
|
||||
/// Permission file that can define a default permission, a set of permissions or a list of inlined permissions.
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||
pub struct PermissionFile {
|
||||
/// The default permission set for the plugin
|
||||
pub default: Option<DefaultPermission>,
|
||||
|
||||
/// A list of permissions sets defined
|
||||
#[serde(default)]
|
||||
pub set: Vec<PermissionSet>,
|
||||
|
||||
/// Test something!!
|
||||
pub test: Option<PermissionSet>,
|
||||
|
||||
/// A list of inlined permissions
|
||||
#[serde(default)]
|
||||
pub permission: Vec<Permission>,
|
||||
}
|
||||
|
||||
/// Plugin manifest.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Manifest {
|
||||
/// Default permission.
|
||||
pub default_permission: Option<PermissionSet>,
|
||||
/// Plugin permissions.
|
||||
pub permissions: HashMap<String, Permission>,
|
||||
/// Plugin permission sets.
|
||||
pub permission_sets: HashMap<String, PermissionSet>,
|
||||
}
|
||||
|
||||
impl Manifest {
|
||||
/// Creates a new manifest from a list of permission files.
|
||||
pub fn from_files(permission_files: Vec<PermissionFile>) -> Self {
|
||||
let mut manifest = Self {
|
||||
default_permission: None,
|
||||
permissions: HashMap::new(),
|
||||
permission_sets: HashMap::new(),
|
||||
};
|
||||
|
||||
for permission_file in permission_files {
|
||||
if let Some(default) = permission_file.default {
|
||||
manifest.default_permission.replace(PermissionSet {
|
||||
identifier: "default".into(),
|
||||
description: default
|
||||
.description
|
||||
.unwrap_or_else(|| "Default plugin permissions.".to_string()),
|
||||
permissions: default.permissions,
|
||||
});
|
||||
}
|
||||
|
||||
manifest.permissions.extend(
|
||||
permission_file
|
||||
.permission
|
||||
.into_iter()
|
||||
.map(|p| (p.identifier.clone(), p))
|
||||
.collect::<HashMap<_, _>>(),
|
||||
);
|
||||
|
||||
manifest.permission_sets.extend(
|
||||
permission_file
|
||||
.set
|
||||
.into_iter()
|
||||
.map(|set| {
|
||||
(
|
||||
set.identifier.clone(),
|
||||
PermissionSet {
|
||||
identifier: set.identifier,
|
||||
description: set.description,
|
||||
permissions: set.permissions,
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect::<HashMap<_, _>>(),
|
||||
);
|
||||
}
|
||||
|
||||
manifest
|
||||
}
|
||||
}
|
||||
397
core/tauri-utils/src/acl/resolved.rs
Normal file
397
core/tauri-utils/src/acl/resolved.rs
Normal file
@@ -0,0 +1,397 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//! Resolved ACL for runtime usage.
|
||||
|
||||
use std::{
|
||||
collections::{hash_map::DefaultHasher, BTreeMap, HashSet},
|
||||
hash::{Hash, Hasher},
|
||||
};
|
||||
|
||||
use glob::Pattern;
|
||||
|
||||
use crate::platform::Target;
|
||||
|
||||
use super::{
|
||||
capability::{Capability, CapabilityContext},
|
||||
plugin::Manifest,
|
||||
Error, ExecutionContext, Permission, PermissionSet, Value,
|
||||
};
|
||||
|
||||
/// A key for a scope, used to link a [`ResolvedCommand#structfield.scope`] to the store [`Resolved#structfield.scopes`].
|
||||
pub type ScopeKey = usize;
|
||||
|
||||
/// A resolved command permission.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ResolvedCommand {
|
||||
/// The list of window label patterns that is allowed to run this command.
|
||||
pub windows: Vec<glob::Pattern>,
|
||||
/// The reference of the scope that is associated with this command. See [`Resolved#structfield.scopes`].
|
||||
pub scope: Option<ScopeKey>,
|
||||
}
|
||||
|
||||
/// A resolved scope. Merges all scopes defined for a single command.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ResolvedScope {
|
||||
/// Allows something on the command.
|
||||
pub allow: Vec<Value>,
|
||||
/// Denies something on the command.
|
||||
pub deny: Vec<Value>,
|
||||
}
|
||||
|
||||
/// A command key for the map of allowed and denied commands.
|
||||
/// Takes into consideration the command name and the execution context.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
|
||||
pub struct CommandKey {
|
||||
/// The full command name.
|
||||
pub name: String,
|
||||
/// The context of the command.
|
||||
pub context: ExecutionContext,
|
||||
}
|
||||
|
||||
/// Resolved access control list.
|
||||
#[derive(Debug)]
|
||||
pub struct Resolved {
|
||||
/// The commands that are allowed. Map each command with its context to a [`ResolvedCommand`].
|
||||
pub allowed_commands: BTreeMap<CommandKey, ResolvedCommand>,
|
||||
/// The commands that are denied. Map each command with its context to a [`ResolvedCommand`].
|
||||
pub denied_commands: BTreeMap<CommandKey, ResolvedCommand>,
|
||||
/// The store of scopes referenced by a [`ResolvedCommand`].
|
||||
pub command_scope: BTreeMap<ScopeKey, ResolvedScope>,
|
||||
/// The global scope.
|
||||
pub global_scope: ResolvedScope,
|
||||
}
|
||||
|
||||
impl Resolved {
|
||||
/// Resolves the ACL for the given plugin permissions and app capabilities.
|
||||
pub fn resolve(
|
||||
acl: BTreeMap<String, Manifest>,
|
||||
capabilities: BTreeMap<String, Capability>,
|
||||
target: Target,
|
||||
) -> Result<Self, Error> {
|
||||
let mut allowed_commands = BTreeMap::new();
|
||||
let mut denied_commands = BTreeMap::new();
|
||||
|
||||
let mut current_scope_id = 0;
|
||||
let mut command_scopes = BTreeMap::new();
|
||||
let mut global_scope = Vec::new();
|
||||
|
||||
// resolve commands
|
||||
for capability in capabilities.values() {
|
||||
if !capability.platforms.contains(&target) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for permission_id in &capability.permissions {
|
||||
let permission_name = permission_id.get_base();
|
||||
|
||||
if let Some(plugin_name) = permission_id.get_prefix() {
|
||||
let permissions = get_permissions(plugin_name, permission_name, &acl)?;
|
||||
|
||||
for permission in permissions {
|
||||
if permission.commands.allow.is_empty() && permission.commands.deny.is_empty() {
|
||||
// global scope
|
||||
global_scope.push(permission.scope.clone());
|
||||
} else {
|
||||
let has_scope = permission.scope.allow.is_some() || permission.scope.deny.is_some();
|
||||
if has_scope {
|
||||
current_scope_id += 1;
|
||||
command_scopes.insert(current_scope_id, permission.scope.clone());
|
||||
}
|
||||
|
||||
let scope_id = if has_scope {
|
||||
Some(current_scope_id)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
for allowed_command in &permission.commands.allow {
|
||||
resolve_command(
|
||||
&mut allowed_commands,
|
||||
format!("plugin:{plugin_name}|{allowed_command}"),
|
||||
capability,
|
||||
scope_id,
|
||||
);
|
||||
}
|
||||
|
||||
for denied_command in &permission.commands.deny {
|
||||
resolve_command(
|
||||
&mut denied_commands,
|
||||
format!("plugin:{plugin_name}|{denied_command}"),
|
||||
capability,
|
||||
scope_id,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// resolve scopes
|
||||
let mut resolved_scopes = BTreeMap::new();
|
||||
|
||||
for allowed in allowed_commands.values_mut() {
|
||||
if !allowed.scope.is_empty() {
|
||||
allowed.scope.sort();
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
allowed.scope.hash(&mut hasher);
|
||||
let hash = hasher.finish() as usize;
|
||||
|
||||
allowed.resolved_scope_key.replace(hash);
|
||||
|
||||
let resolved_scope = ResolvedScope {
|
||||
allow: allowed
|
||||
.scope
|
||||
.iter()
|
||||
.flat_map(|s| command_scopes.get(s).unwrap().allow.clone())
|
||||
.flatten()
|
||||
.collect(),
|
||||
deny: allowed
|
||||
.scope
|
||||
.iter()
|
||||
.flat_map(|s| command_scopes.get(s).unwrap().deny.clone())
|
||||
.flatten()
|
||||
.collect(),
|
||||
};
|
||||
|
||||
resolved_scopes.insert(hash, resolved_scope);
|
||||
}
|
||||
}
|
||||
|
||||
let global_scope = ResolvedScope {
|
||||
allow: global_scope
|
||||
.iter_mut()
|
||||
.flat_map(|s| s.allow.take())
|
||||
.flatten()
|
||||
.collect(),
|
||||
deny: global_scope
|
||||
.iter_mut()
|
||||
.flat_map(|s| s.deny.take())
|
||||
.flatten()
|
||||
.collect(),
|
||||
};
|
||||
|
||||
let resolved = Self {
|
||||
allowed_commands: allowed_commands
|
||||
.into_iter()
|
||||
.map(|(key, cmd)| {
|
||||
Ok((
|
||||
key,
|
||||
ResolvedCommand {
|
||||
windows: parse_window_patterns(cmd.windows)?,
|
||||
scope: cmd.resolved_scope_key,
|
||||
},
|
||||
))
|
||||
})
|
||||
.collect::<Result<_, Error>>()?,
|
||||
denied_commands: denied_commands
|
||||
.into_iter()
|
||||
.map(|(key, cmd)| {
|
||||
Ok((
|
||||
key,
|
||||
ResolvedCommand {
|
||||
windows: parse_window_patterns(cmd.windows)?,
|
||||
scope: cmd.resolved_scope_key,
|
||||
},
|
||||
))
|
||||
})
|
||||
.collect::<Result<_, Error>>()?,
|
||||
command_scope: resolved_scopes,
|
||||
global_scope,
|
||||
};
|
||||
|
||||
Ok(resolved)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_window_patterns(windows: HashSet<String>) -> Result<Vec<glob::Pattern>, Error> {
|
||||
let mut patterns = Vec::new();
|
||||
for window in windows {
|
||||
patterns.push(glob::Pattern::new(&window)?);
|
||||
}
|
||||
Ok(patterns)
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct ResolvedCommandTemp {
|
||||
pub windows: HashSet<String>,
|
||||
pub scope: Vec<usize>,
|
||||
pub resolved_scope_key: Option<usize>,
|
||||
}
|
||||
|
||||
fn resolve_command(
|
||||
commands: &mut BTreeMap<CommandKey, ResolvedCommandTemp>,
|
||||
command: String,
|
||||
capability: &Capability,
|
||||
scope_id: Option<usize>,
|
||||
) {
|
||||
let contexts = match &capability.context {
|
||||
CapabilityContext::Local => {
|
||||
vec![ExecutionContext::Local]
|
||||
}
|
||||
CapabilityContext::Remote { domains } => domains
|
||||
.iter()
|
||||
.map(|domain| ExecutionContext::Remote {
|
||||
domain: Pattern::new(domain)
|
||||
.unwrap_or_else(|e| panic!("invalid glob pattern for remote domain {domain}: {e}")),
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
|
||||
for context in contexts {
|
||||
let resolved = commands
|
||||
.entry(CommandKey {
|
||||
name: command.clone(),
|
||||
context,
|
||||
})
|
||||
.or_default();
|
||||
|
||||
resolved.windows.extend(capability.windows.clone());
|
||||
if let Some(id) = scope_id {
|
||||
resolved.scope.push(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get the permissions from a permission set
|
||||
fn get_permission_set_permissions<'a>(
|
||||
manifest: &'a Manifest,
|
||||
set: &'a PermissionSet,
|
||||
) -> Result<Vec<&'a Permission>, Error> {
|
||||
let mut permissions = Vec::new();
|
||||
|
||||
for p in &set.permissions {
|
||||
if let Some(permission) = manifest.permissions.get(p) {
|
||||
permissions.push(permission);
|
||||
} else if let Some(permission_set) = manifest.permission_sets.get(p) {
|
||||
permissions.extend(get_permission_set_permissions(manifest, permission_set)?);
|
||||
} else {
|
||||
return Err(Error::SetPermissionNotFound {
|
||||
permission: p.to_string(),
|
||||
set: set.identifier.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(permissions)
|
||||
}
|
||||
|
||||
fn get_permissions<'a>(
|
||||
plugin_name: &'a str,
|
||||
permission_name: &'a str,
|
||||
acl: &'a BTreeMap<String, Manifest>,
|
||||
) -> Result<Vec<&'a Permission>, Error> {
|
||||
let manifest = acl.get(plugin_name).ok_or_else(|| Error::UnknownPlugin {
|
||||
plugin: plugin_name.to_string(),
|
||||
available: acl.keys().cloned().collect::<Vec<_>>().join(", "),
|
||||
})?;
|
||||
|
||||
if permission_name == "default" {
|
||||
manifest
|
||||
.default_permission
|
||||
.as_ref()
|
||||
.ok_or_else(|| Error::UnknownPermission {
|
||||
plugin: plugin_name.to_string(),
|
||||
permission: permission_name.to_string(),
|
||||
})
|
||||
.and_then(|default| get_permission_set_permissions(manifest, default))
|
||||
} else if let Some(set) = manifest.permission_sets.get(permission_name) {
|
||||
get_permission_set_permissions(manifest, set)
|
||||
} else if let Some(permission) = manifest.permissions.get(permission_name) {
|
||||
Ok(vec![permission])
|
||||
} else {
|
||||
Err(Error::UnknownPermission {
|
||||
plugin: plugin_name.to_string(),
|
||||
permission: permission_name.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "build")]
|
||||
mod build {
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens, TokenStreamExt};
|
||||
use std::convert::identity;
|
||||
|
||||
use super::*;
|
||||
use crate::tokens::*;
|
||||
|
||||
/// Write a `TokenStream` of the `$struct`'s fields to the `$tokens`.
|
||||
///
|
||||
/// All fields must represent a binding of the same name that implements `ToTokens`.
|
||||
macro_rules! literal_struct {
|
||||
($tokens:ident, $struct:ident, $($field:ident),+) => {
|
||||
$tokens.append_all(quote! {
|
||||
::tauri::utils::acl::resolved::$struct {
|
||||
$($field: #$field),+
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
impl ToTokens for CommandKey {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let name = str_lit(&self.name);
|
||||
let context = &self.context;
|
||||
literal_struct!(tokens, CommandKey, name, context)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ResolvedCommand {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let windows = vec_lit(&self.windows, |window| {
|
||||
let w = window.as_str();
|
||||
quote!(#w.parse().unwrap())
|
||||
});
|
||||
let scope = opt_lit(self.scope.as_ref());
|
||||
literal_struct!(tokens, ResolvedCommand, windows, scope)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ResolvedScope {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let allow = vec_lit(&self.allow, identity);
|
||||
let deny = vec_lit(&self.deny, identity);
|
||||
literal_struct!(tokens, ResolvedScope, allow, deny)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Resolved {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let allowed_commands = map_lit(
|
||||
quote! { ::std::collections::BTreeMap },
|
||||
&self.allowed_commands,
|
||||
identity,
|
||||
identity,
|
||||
);
|
||||
|
||||
let denied_commands = map_lit(
|
||||
quote! { ::std::collections::BTreeMap },
|
||||
&self.denied_commands,
|
||||
identity,
|
||||
identity,
|
||||
);
|
||||
|
||||
let command_scope = map_lit(
|
||||
quote! { ::std::collections::BTreeMap },
|
||||
&self.command_scope,
|
||||
identity,
|
||||
identity,
|
||||
);
|
||||
|
||||
let global_scope = &self.global_scope;
|
||||
|
||||
literal_struct!(
|
||||
tokens,
|
||||
Resolved,
|
||||
allowed_commands,
|
||||
denied_commands,
|
||||
command_scope,
|
||||
global_scope
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
160
core/tauri-utils/src/acl/value.rs
Normal file
160
core/tauri-utils/src/acl/value.rs
Normal file
@@ -0,0 +1,160 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//! A [`Value`] that is used instead of [`toml::Value`] or [`serde_json::Value`]
|
||||
//! to support both formats.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A valid ACL number.
|
||||
#[derive(Debug, Serialize, Deserialize, Copy, Clone, PartialOrd, PartialEq)]
|
||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||
#[serde(untagged)]
|
||||
pub enum Number {
|
||||
/// Represents an [`i64`].
|
||||
Int(i64),
|
||||
|
||||
/// Represents a [`f64`].
|
||||
Float(f64),
|
||||
}
|
||||
|
||||
impl From<i64> for Number {
|
||||
#[inline(always)]
|
||||
fn from(value: i64) -> Self {
|
||||
Self::Int(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Number {
|
||||
#[inline(always)]
|
||||
fn from(value: f64) -> Self {
|
||||
Self::Float(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// All supported ACL values.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialOrd, PartialEq)]
|
||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||
#[serde(untagged)]
|
||||
pub enum Value {
|
||||
/// Represents a [`bool`].
|
||||
Bool(bool),
|
||||
|
||||
/// Represents a valid ACL [`Number`].
|
||||
Number(Number),
|
||||
|
||||
/// Represents a [`String`].
|
||||
String(String),
|
||||
|
||||
/// Represents a list of other [`Value`]s.
|
||||
List(Vec<Value>),
|
||||
|
||||
/// Represents a map of [`String`] keys to [`Value`]s.
|
||||
Map(BTreeMap<String, Value>),
|
||||
}
|
||||
|
||||
impl Value {
|
||||
/// TODO: implement [`serde::Deserializer`] directly to avoid serializing then deserializing
|
||||
pub fn deserialize<T: DeserializeOwned + Debug>(&self) -> Option<T> {
|
||||
dbg!(serde_json::to_string(self))
|
||||
.ok()
|
||||
.and_then(|s| dbg!(serde_json::from_str(&s).ok()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Value {
|
||||
#[inline(always)]
|
||||
fn from(value: bool) -> Self {
|
||||
Self::Bool(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<Number>> From<T> for Value {
|
||||
#[inline(always)]
|
||||
fn from(value: T) -> Self {
|
||||
Self::Number(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Value {
|
||||
#[inline(always)]
|
||||
fn from(value: String) -> Self {
|
||||
Value::String(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<toml::Value> for Value {
|
||||
#[inline(always)]
|
||||
fn from(value: toml::Value) -> Self {
|
||||
use toml::Value as Toml;
|
||||
|
||||
match value {
|
||||
Toml::String(s) => s.into(),
|
||||
Toml::Integer(i) => i.into(),
|
||||
Toml::Float(f) => f.into(),
|
||||
Toml::Boolean(b) => b.into(),
|
||||
Toml::Datetime(d) => d.to_string().into(),
|
||||
Toml::Array(a) => Value::List(a.into_iter().map(Value::from).collect()),
|
||||
Toml::Table(t) => Value::Map(t.into_iter().map(|(k, v)| (k, v.into())).collect()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "build")]
|
||||
mod build {
|
||||
use std::convert::identity;
|
||||
|
||||
use crate::tokens::*;
|
||||
|
||||
use super::*;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens, TokenStreamExt};
|
||||
|
||||
impl ToTokens for Number {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let prefix = quote! { ::tauri::utils::acl::Number };
|
||||
|
||||
tokens.append_all(match self {
|
||||
Self::Int(i) => {
|
||||
quote! { #prefix::Int(#i) }
|
||||
}
|
||||
Self::Float(f) => {
|
||||
quote! { #prefix::Float (#f) }
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Value {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let prefix = quote! { ::tauri::utils::acl::Value };
|
||||
|
||||
tokens.append_all(match self {
|
||||
Value::Bool(bool) => quote! { #prefix::Bool(#bool) },
|
||||
Value::Number(number) => quote! { #prefix::Number(#number) },
|
||||
Value::String(str) => {
|
||||
let s = str_lit(str);
|
||||
quote! { #prefix::String(#s) }
|
||||
}
|
||||
Value::List(vec) => {
|
||||
let items = vec_lit(vec, identity);
|
||||
quote! { #prefix::List(#items) }
|
||||
}
|
||||
Value::Map(map) => {
|
||||
let map = map_lit(
|
||||
quote! { ::std::collections::BTreeMap },
|
||||
map,
|
||||
str_lit,
|
||||
identity,
|
||||
);
|
||||
quote! { #prefix::Map(#map) }
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1543,20 +1543,6 @@ pub struct SecurityConfig {
|
||||
/// Your application might be vulnerable to XSS attacks without this Tauri protection.
|
||||
#[serde(default, alias = "dangerous-disable-asset-csp-modification")]
|
||||
pub dangerous_disable_asset_csp_modification: DisabledCspModificationKind,
|
||||
/// Allow external domains to send command to Tauri.
|
||||
///
|
||||
/// By default, external domains do not have access to `window.__TAURI__`, which means they cannot
|
||||
/// communicate with the commands defined in Rust. This prevents attacks where an externally
|
||||
/// loaded malicious or compromised sites could start executing commands on the user's device.
|
||||
///
|
||||
/// This configuration allows a set of external domains to have access to the Tauri commands.
|
||||
/// When you configure a domain to be allowed to access the IPC, all subpaths are allowed. Subdomains are not allowed.
|
||||
///
|
||||
/// **WARNING:** Only use this option if you either have internal checks against malicious
|
||||
/// external sites or you can trust the allowed external sites. You application might be
|
||||
/// vulnerable to dangerous Tauri command related attacks otherwise.
|
||||
#[serde(default, alias = "dangerous-remote-domain-ipc-access")]
|
||||
pub dangerous_remote_domain_ipc_access: Vec<RemoteDomainAccessScope>,
|
||||
/// Custom protocol config.
|
||||
#[serde(default, alias = "asset-protocol")]
|
||||
pub asset_protocol: AssetProtocolConfig,
|
||||
@@ -2140,149 +2126,11 @@ fn default_build() -> BuildConfig {
|
||||
/// application using tauri while only parsing it once (in the build script).
|
||||
#[cfg(feature = "build")]
|
||||
mod build {
|
||||
use std::{convert::identity, path::Path};
|
||||
|
||||
use super::*;
|
||||
use crate::tokens::*;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens, TokenStreamExt};
|
||||
|
||||
use super::*;
|
||||
|
||||
use serde_json::Value as JsonValue;
|
||||
|
||||
/// Create a `String` constructor `TokenStream`.
|
||||
///
|
||||
/// e.g. `"Hello World" -> String::from("Hello World").
|
||||
/// This takes a `&String` to reduce casting all the `&String` -> `&str` manually.
|
||||
fn str_lit(s: impl AsRef<str>) -> TokenStream {
|
||||
let s = s.as_ref();
|
||||
quote! { #s.into() }
|
||||
}
|
||||
|
||||
/// Create an `Option` constructor `TokenStream`.
|
||||
fn opt_lit(item: Option<&impl ToTokens>) -> TokenStream {
|
||||
match item {
|
||||
None => quote! { ::core::option::Option::None },
|
||||
Some(item) => quote! { ::core::option::Option::Some(#item) },
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function to combine an `opt_lit` with `str_lit`.
|
||||
fn opt_str_lit(item: Option<impl AsRef<str>>) -> TokenStream {
|
||||
opt_lit(item.map(str_lit).as_ref())
|
||||
}
|
||||
|
||||
/// Helper function to combine an `opt_lit` with a list of `str_lit`
|
||||
fn opt_vec_str_lit(item: Option<impl IntoIterator<Item = impl AsRef<str>>>) -> TokenStream {
|
||||
opt_lit(item.map(|list| vec_lit(list, str_lit)).as_ref())
|
||||
}
|
||||
|
||||
/// Create a `Vec` constructor, mapping items with a function that spits out `TokenStream`s.
|
||||
fn vec_lit<Raw, Tokens>(
|
||||
list: impl IntoIterator<Item = Raw>,
|
||||
map: impl Fn(Raw) -> Tokens,
|
||||
) -> TokenStream
|
||||
where
|
||||
Tokens: ToTokens,
|
||||
{
|
||||
let items = list.into_iter().map(map);
|
||||
quote! { vec![#(#items),*] }
|
||||
}
|
||||
|
||||
/// Create a `PathBuf` constructor `TokenStream`.
|
||||
///
|
||||
/// e.g. `"Hello World" -> String::from("Hello World").
|
||||
fn path_buf_lit(s: impl AsRef<Path>) -> TokenStream {
|
||||
let s = s.as_ref().to_string_lossy().into_owned();
|
||||
quote! { ::std::path::PathBuf::from(#s) }
|
||||
}
|
||||
|
||||
/// Creates a `Url` constructor `TokenStream`.
|
||||
fn url_lit(url: &Url) -> TokenStream {
|
||||
let url = url.as_str();
|
||||
quote! { #url.parse().unwrap() }
|
||||
}
|
||||
|
||||
/// Create a map constructor, mapping keys and values with other `TokenStream`s.
|
||||
///
|
||||
/// This function is pretty generic because the types of keys AND values get transformed.
|
||||
fn map_lit<Map, Key, Value, TokenStreamKey, TokenStreamValue, FuncKey, FuncValue>(
|
||||
map_type: TokenStream,
|
||||
map: Map,
|
||||
map_key: FuncKey,
|
||||
map_value: FuncValue,
|
||||
) -> TokenStream
|
||||
where
|
||||
<Map as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
Map: IntoIterator<Item = (Key, Value)>,
|
||||
TokenStreamKey: ToTokens,
|
||||
TokenStreamValue: ToTokens,
|
||||
FuncKey: Fn(Key) -> TokenStreamKey,
|
||||
FuncValue: Fn(Value) -> TokenStreamValue,
|
||||
{
|
||||
let ident = quote::format_ident!("map");
|
||||
let map = map.into_iter();
|
||||
|
||||
if map.len() > 0 {
|
||||
let items = map.map(|(key, value)| {
|
||||
let key = map_key(key);
|
||||
let value = map_value(value);
|
||||
quote! { #ident.insert(#key, #value); }
|
||||
});
|
||||
|
||||
quote! {{
|
||||
let mut #ident = #map_type::new();
|
||||
#(#items)*
|
||||
#ident
|
||||
}}
|
||||
} else {
|
||||
quote! { #map_type::new() }
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `serde_json::Value` variant `TokenStream` for a number
|
||||
fn json_value_number_lit(num: &serde_json::Number) -> TokenStream {
|
||||
// See https://docs.rs/serde_json/1/serde_json/struct.Number.html for guarantees
|
||||
let prefix = quote! { ::serde_json::Value };
|
||||
if num.is_u64() {
|
||||
// guaranteed u64
|
||||
let num = num.as_u64().unwrap();
|
||||
quote! { #prefix::Number(#num.into()) }
|
||||
} else if num.is_i64() {
|
||||
// guaranteed i64
|
||||
let num = num.as_i64().unwrap();
|
||||
quote! { #prefix::Number(#num.into()) }
|
||||
} else if num.is_f64() {
|
||||
// guaranteed f64
|
||||
let num = num.as_f64().unwrap();
|
||||
quote! { #prefix::Number(::serde_json::Number::from_f64(#num).unwrap(/* safe to unwrap, guaranteed f64 */)) }
|
||||
} else {
|
||||
// invalid number
|
||||
quote! { #prefix::Null }
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `serde_json::Value` constructor `TokenStream`
|
||||
fn json_value_lit(jv: &JsonValue) -> TokenStream {
|
||||
let prefix = quote! { ::serde_json::Value };
|
||||
|
||||
match jv {
|
||||
JsonValue::Null => quote! { #prefix::Null },
|
||||
JsonValue::Bool(bool) => quote! { #prefix::Bool(#bool) },
|
||||
JsonValue::Number(number) => json_value_number_lit(number),
|
||||
JsonValue::String(str) => {
|
||||
let s = str_lit(str);
|
||||
quote! { #prefix::String(#s) }
|
||||
}
|
||||
JsonValue::Array(vec) => {
|
||||
let items = vec.iter().map(json_value_lit);
|
||||
quote! { #prefix::Array(vec![#(#items),*]) }
|
||||
}
|
||||
JsonValue::Object(map) => {
|
||||
let map = map_lit(quote! { ::serde_json::Map }, map, str_lit, json_value_lit);
|
||||
quote! { #prefix::Object(#map) }
|
||||
}
|
||||
}
|
||||
}
|
||||
use std::convert::identity;
|
||||
|
||||
/// Write a `TokenStream` of the `$struct`'s fields to the `$tokens`.
|
||||
///
|
||||
@@ -2749,8 +2597,6 @@ mod build {
|
||||
let dev_csp = opt_lit(self.dev_csp.as_ref());
|
||||
let freeze_prototype = self.freeze_prototype;
|
||||
let dangerous_disable_asset_csp_modification = &self.dangerous_disable_asset_csp_modification;
|
||||
let dangerous_remote_domain_ipc_access =
|
||||
vec_lit(&self.dangerous_remote_domain_ipc_access, identity);
|
||||
let asset_protocol = &self.asset_protocol;
|
||||
|
||||
literal_struct!(
|
||||
@@ -2760,7 +2606,6 @@ mod build {
|
||||
dev_csp,
|
||||
freeze_prototype,
|
||||
dangerous_disable_asset_csp_modification,
|
||||
dangerous_remote_domain_ipc_access,
|
||||
asset_protocol
|
||||
);
|
||||
}
|
||||
@@ -2921,7 +2766,6 @@ mod test {
|
||||
dev_csp: None,
|
||||
freeze_prototype: false,
|
||||
dangerous_disable_asset_csp_modification: DisabledCspModificationKind::Flag(false),
|
||||
dangerous_remote_domain_ipc_access: Vec::new(),
|
||||
asset_protocol: AssetProtocolConfig::default(),
|
||||
},
|
||||
tray_icon: None,
|
||||
|
||||
@@ -51,21 +51,21 @@ impl ConfigFormat {
|
||||
fn into_platform_file_name(self, target: Target) -> &'static str {
|
||||
match self {
|
||||
Self::Json => match target {
|
||||
Target::Darwin => "tauri.macos.conf.json",
|
||||
Target::MacOS => "tauri.macos.conf.json",
|
||||
Target::Windows => "tauri.windows.conf.json",
|
||||
Target::Linux => "tauri.linux.conf.json",
|
||||
Target::Android => "tauri.android.conf.json",
|
||||
Target::Ios => "tauri.ios.conf.json",
|
||||
},
|
||||
Self::Json5 => match target {
|
||||
Target::Darwin => "tauri.macos.conf.json5",
|
||||
Target::MacOS => "tauri.macos.conf.json5",
|
||||
Target::Windows => "tauri.windows.conf.json5",
|
||||
Target::Linux => "tauri.linux.conf.json5",
|
||||
Target::Android => "tauri.android.conf.json5",
|
||||
Target::Ios => "tauri.ios.conf.json5",
|
||||
},
|
||||
Self::Toml => match target {
|
||||
Target::Darwin => "Tauri.macos.toml",
|
||||
Target::MacOS => "Tauri.macos.toml",
|
||||
Target::Windows => "Tauri.windows.toml",
|
||||
Target::Linux => "Tauri.linux.toml",
|
||||
Target::Android => "Tauri.android.toml",
|
||||
|
||||
@@ -24,6 +24,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use log::warn;
|
||||
|
||||
pub mod acl;
|
||||
pub mod assets;
|
||||
pub mod config;
|
||||
pub mod html;
|
||||
@@ -33,6 +34,8 @@ pub mod platform;
|
||||
/// Prepare application resources and sidecars.
|
||||
#[cfg(feature = "resources")]
|
||||
pub mod resources;
|
||||
#[cfg(feature = "build")]
|
||||
pub mod tokens;
|
||||
|
||||
/// Application pattern.
|
||||
pub mod pattern;
|
||||
|
||||
@@ -4,17 +4,25 @@
|
||||
|
||||
//! Platform helper functions.
|
||||
|
||||
use std::path::{PathBuf, MAIN_SEPARATOR};
|
||||
use std::{
|
||||
fmt::Display,
|
||||
path::{PathBuf, MAIN_SEPARATOR},
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{Env, PackageInfo};
|
||||
|
||||
mod starting_binary;
|
||||
|
||||
/// Platform target.
|
||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||
#[derive(PartialEq, Eq, Copy, Debug, Clone, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum Target {
|
||||
/// MacOS.
|
||||
Darwin,
|
||||
#[serde(rename = "macOS")]
|
||||
MacOS,
|
||||
/// Windows.
|
||||
Windows,
|
||||
/// Linux.
|
||||
@@ -22,14 +30,31 @@ pub enum Target {
|
||||
/// Android.
|
||||
Android,
|
||||
/// iOS.
|
||||
#[serde(rename = "iOS")]
|
||||
Ios,
|
||||
}
|
||||
|
||||
impl Display for Target {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
Self::MacOS => "macOS",
|
||||
Self::Windows => "windows",
|
||||
Self::Linux => "linux",
|
||||
Self::Android => "android",
|
||||
Self::Ios => "iOS",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Target {
|
||||
/// Parses the target from the given target triple.
|
||||
pub fn from_triple(target: &str) -> Self {
|
||||
if target.contains("darwin") {
|
||||
Self::Darwin
|
||||
Self::MacOS
|
||||
} else if target.contains("windows") {
|
||||
Self::Windows
|
||||
} else if target.contains("android") {
|
||||
@@ -44,9 +69,13 @@ impl Target {
|
||||
/// Gets the current build target.
|
||||
pub fn current() -> Self {
|
||||
if cfg!(target_os = "macos") {
|
||||
Self::Darwin
|
||||
Self::MacOS
|
||||
} else if cfg!(target_os = "windows") {
|
||||
Self::Windows
|
||||
} else if cfg!(target_os = "ios") {
|
||||
Self::Ios
|
||||
} else if cfg!(target_os = "android") {
|
||||
Self::Android
|
||||
} else {
|
||||
Self::Linux
|
||||
}
|
||||
|
||||
147
core/tauri-utils/src/tokens.rs
Normal file
147
core/tauri-utils/src/tokens.rs
Normal file
@@ -0,0 +1,147 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//! Utilities to implement [`ToTokens`] for a type.
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens};
|
||||
use serde_json::Value as JsonValue;
|
||||
use url::Url;
|
||||
|
||||
/// Create a `String` constructor `TokenStream`.
|
||||
///
|
||||
/// e.g. `"Hello World" -> String::from("Hello World").
|
||||
/// This takes a `&String` to reduce casting all the `&String` -> `&str` manually.
|
||||
pub fn str_lit(s: impl AsRef<str>) -> TokenStream {
|
||||
let s = s.as_ref();
|
||||
quote! { #s.into() }
|
||||
}
|
||||
|
||||
/// Create an `Option` constructor `TokenStream`.
|
||||
pub fn opt_lit(item: Option<&impl ToTokens>) -> TokenStream {
|
||||
match item {
|
||||
None => quote! { ::core::option::Option::None },
|
||||
Some(item) => quote! { ::core::option::Option::Some(#item) },
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function to combine an `opt_lit` with `str_lit`.
|
||||
pub fn opt_str_lit(item: Option<impl AsRef<str>>) -> TokenStream {
|
||||
opt_lit(item.map(str_lit).as_ref())
|
||||
}
|
||||
|
||||
/// Helper function to combine an `opt_lit` with a list of `str_lit`
|
||||
pub fn opt_vec_str_lit(item: Option<impl IntoIterator<Item = impl AsRef<str>>>) -> TokenStream {
|
||||
opt_lit(item.map(|list| vec_lit(list, str_lit)).as_ref())
|
||||
}
|
||||
|
||||
/// Create a `Vec` constructor, mapping items with a function that spits out `TokenStream`s.
|
||||
pub fn vec_lit<Raw, Tokens>(
|
||||
list: impl IntoIterator<Item = Raw>,
|
||||
map: impl Fn(Raw) -> Tokens,
|
||||
) -> TokenStream
|
||||
where
|
||||
Tokens: ToTokens,
|
||||
{
|
||||
let items = list.into_iter().map(map);
|
||||
quote! { vec![#(#items),*] }
|
||||
}
|
||||
|
||||
/// Create a `PathBuf` constructor `TokenStream`.
|
||||
///
|
||||
/// e.g. `"Hello World" -> String::from("Hello World").
|
||||
pub fn path_buf_lit(s: impl AsRef<Path>) -> TokenStream {
|
||||
let s = s.as_ref().to_string_lossy().into_owned();
|
||||
quote! { ::std::path::PathBuf::from(#s) }
|
||||
}
|
||||
|
||||
/// Creates a `Url` constructor `TokenStream`.
|
||||
pub fn url_lit(url: &Url) -> TokenStream {
|
||||
let url = url.as_str();
|
||||
quote! { #url.parse().unwrap() }
|
||||
}
|
||||
|
||||
/// Create a map constructor, mapping keys and values with other `TokenStream`s.
|
||||
///
|
||||
/// This function is pretty generic because the types of keys AND values get transformed.
|
||||
pub fn map_lit<Map, Key, Value, TokenStreamKey, TokenStreamValue, FuncKey, FuncValue>(
|
||||
map_type: TokenStream,
|
||||
map: Map,
|
||||
map_key: FuncKey,
|
||||
map_value: FuncValue,
|
||||
) -> TokenStream
|
||||
where
|
||||
<Map as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||
Map: IntoIterator<Item = (Key, Value)>,
|
||||
TokenStreamKey: ToTokens,
|
||||
TokenStreamValue: ToTokens,
|
||||
FuncKey: Fn(Key) -> TokenStreamKey,
|
||||
FuncValue: Fn(Value) -> TokenStreamValue,
|
||||
{
|
||||
let ident = quote::format_ident!("map");
|
||||
let map = map.into_iter();
|
||||
|
||||
if map.len() > 0 {
|
||||
let items = map.map(|(key, value)| {
|
||||
let key = map_key(key);
|
||||
let value = map_value(value);
|
||||
quote! { #ident.insert(#key, #value); }
|
||||
});
|
||||
|
||||
quote! {{
|
||||
let mut #ident = #map_type::new();
|
||||
#(#items)*
|
||||
#ident
|
||||
}}
|
||||
} else {
|
||||
quote! { #map_type::new() }
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `serde_json::Value` variant `TokenStream` for a number
|
||||
pub fn json_value_number_lit(num: &serde_json::Number) -> TokenStream {
|
||||
// See https://docs.rs/serde_json/1/serde_json/struct.Number.html for guarantees
|
||||
let prefix = quote! { ::serde_json::Value };
|
||||
if num.is_u64() {
|
||||
// guaranteed u64
|
||||
let num = num.as_u64().unwrap();
|
||||
quote! { #prefix::Number(#num.into()) }
|
||||
} else if num.is_i64() {
|
||||
// guaranteed i64
|
||||
let num = num.as_i64().unwrap();
|
||||
quote! { #prefix::Number(#num.into()) }
|
||||
} else if num.is_f64() {
|
||||
// guaranteed f64
|
||||
let num = num.as_f64().unwrap();
|
||||
quote! { #prefix::Number(::serde_json::Number::from_f64(#num).unwrap(/* safe to unwrap, guaranteed f64 */)) }
|
||||
} else {
|
||||
// invalid number
|
||||
quote! { #prefix::Null }
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `serde_json::Value` constructor `TokenStream`
|
||||
pub fn json_value_lit(jv: &JsonValue) -> TokenStream {
|
||||
let prefix = quote! { ::serde_json::Value };
|
||||
|
||||
match jv {
|
||||
JsonValue::Null => quote! { #prefix::Null },
|
||||
JsonValue::Bool(bool) => quote! { #prefix::Bool(#bool) },
|
||||
JsonValue::Number(number) => json_value_number_lit(number),
|
||||
JsonValue::String(str) => {
|
||||
let s = str_lit(str);
|
||||
quote! { #prefix::String(#s) }
|
||||
}
|
||||
JsonValue::Array(vec) => {
|
||||
let items = vec.iter().map(json_value_lit);
|
||||
quote! { #prefix::Array(vec![#(#items),*]) }
|
||||
}
|
||||
JsonValue::Object(map) => {
|
||||
let map = map_lit(quote! { ::serde_json::Map }, map, str_lit, json_value_lit);
|
||||
quote! { #prefix::Object(#map) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,7 @@ png = { version = "0.17", optional = true }
|
||||
ico = { version = "0.3.0", optional = true }
|
||||
http-range = { version = "0.1.5", optional = true }
|
||||
tracing = { version = "0.1", optional = true }
|
||||
static_assertions = "1"
|
||||
|
||||
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"windows\", target_os = \"macos\"))".dependencies]
|
||||
muda = { version = "0.11", default-features = false, features = [ "serde" ] }
|
||||
@@ -110,6 +111,7 @@ swift-rs = "1.0.6"
|
||||
[build-dependencies]
|
||||
heck = "0.4"
|
||||
tauri-build = { path = "../tauri-build/", version = "2.0.0-alpha.14" }
|
||||
tauri-utils = { path = "../tauri-utils/", version = "2.0.0-alpha.13", features = [ "build" ] }
|
||||
|
||||
[dev-dependencies]
|
||||
proptest = "1.4.0"
|
||||
|
||||
@@ -15,6 +15,146 @@ use std::{
|
||||
};
|
||||
|
||||
static CHECKED_FEATURES: OnceLock<Mutex<Vec<String>>> = OnceLock::new();
|
||||
const PLUGINS: &[(&str, &[(&str, bool)])] = &[
|
||||
// (plugin_name, &[(command, enabled-by_default)])
|
||||
(
|
||||
"path",
|
||||
&[
|
||||
("resolve_directory", true),
|
||||
("resolve", true),
|
||||
("normalize", true),
|
||||
("join", true),
|
||||
("dirname", true),
|
||||
("extname", true),
|
||||
("basename", true),
|
||||
("is_absolute", true),
|
||||
],
|
||||
),
|
||||
(
|
||||
"event",
|
||||
&[("listen", true), ("unlisten", true), ("emit", true)],
|
||||
),
|
||||
(
|
||||
"window",
|
||||
&[
|
||||
("create", false),
|
||||
// getters
|
||||
("scale_factor", true),
|
||||
("inner_position", true),
|
||||
("outer_position", true),
|
||||
("inner_size", true),
|
||||
("outer_size", true),
|
||||
("is_fullscreen", true),
|
||||
("is_minimized", true),
|
||||
("is_maximized", true),
|
||||
("is_focused", true),
|
||||
("is_decorated", true),
|
||||
("is_resizable", true),
|
||||
("is_maximizable", true),
|
||||
("is_minimizable", true),
|
||||
("is_closable", true),
|
||||
("is_visible", true),
|
||||
("title", true),
|
||||
("current_monitor", true),
|
||||
("primary_monitor", true),
|
||||
("available_monitors", true),
|
||||
("theme", true),
|
||||
// setters
|
||||
("center", false),
|
||||
("request_user_attention", false),
|
||||
("set_resizable", false),
|
||||
("set_maximizable", false),
|
||||
("set_minimizable", false),
|
||||
("set_closable", false),
|
||||
("set_title", false),
|
||||
("maximize", false),
|
||||
("unmaximize", false),
|
||||
("minimize", false),
|
||||
("unminimize", false),
|
||||
("show", false),
|
||||
("hide", false),
|
||||
("close", false),
|
||||
("set_decorations", false),
|
||||
("set_shadow", false),
|
||||
("set_effects", false),
|
||||
("set_always_on_top", false),
|
||||
("set_always_on_bottom", false),
|
||||
("set_content_protected", false),
|
||||
("set_size", false),
|
||||
("set_min_size", false),
|
||||
("set_max_size", false),
|
||||
("set_position", false),
|
||||
("set_fullscreen", false),
|
||||
("set_focus", false),
|
||||
("set_skip_taskbar", false),
|
||||
("set_cursor_grab", false),
|
||||
("set_cursor_visible", false),
|
||||
("set_cursor_icon", false),
|
||||
("set_cursor_position", false),
|
||||
("set_ignore_cursor_events", false),
|
||||
("start_dragging", false),
|
||||
("set_progress_bar", false),
|
||||
("print", false),
|
||||
("set_icon", false),
|
||||
("toggle_maximize", false),
|
||||
// internal
|
||||
("internal_toggle_maximize", true),
|
||||
("internal_toggle_devtools", true),
|
||||
],
|
||||
),
|
||||
(
|
||||
"app",
|
||||
&[
|
||||
("version", true),
|
||||
("name", true),
|
||||
("tauri_version", true),
|
||||
("app_show", false),
|
||||
("app_hide", false),
|
||||
],
|
||||
),
|
||||
("resources", &[("close", true)]),
|
||||
(
|
||||
"menu",
|
||||
&[
|
||||
("new", false),
|
||||
("append", false),
|
||||
("prepend", false),
|
||||
("insert", false),
|
||||
("remove", false),
|
||||
("remove_at", false),
|
||||
("items", false),
|
||||
("get", false),
|
||||
("popup", false),
|
||||
("create_default", false),
|
||||
("set_as_app_menu", false),
|
||||
("set_as_window_menu", false),
|
||||
("text", false),
|
||||
("set_text", false),
|
||||
("is_enabled", false),
|
||||
("set_enabled", false),
|
||||
("set_accelerator", false),
|
||||
("set_as_windows_menu_for_nsapp", false),
|
||||
("set_as_help_menu_for_nsapp", false),
|
||||
("is_checked", false),
|
||||
("set_checked", false),
|
||||
("set_icon", false),
|
||||
],
|
||||
),
|
||||
(
|
||||
"tray",
|
||||
&[
|
||||
("new", false),
|
||||
("set_icon", false),
|
||||
("set_menu", false),
|
||||
("set_tooltip", false),
|
||||
("set_title", false),
|
||||
("set_visible", false),
|
||||
("set_temp_dir_path", false),
|
||||
("set_icon_as_template", false),
|
||||
("set_show_menu_on_left_click", false),
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
// checks if the given Cargo feature is enabled.
|
||||
fn has_feature(feature: &str) -> bool {
|
||||
@@ -134,6 +274,58 @@ fn main() {
|
||||
println!("cargo:ios_library_path={}", lib_path.display());
|
||||
}
|
||||
}
|
||||
|
||||
define_permissions();
|
||||
}
|
||||
|
||||
fn define_permissions() {
|
||||
let license_header = r#"# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
"#;
|
||||
|
||||
for (plugin, commands) in PLUGINS {
|
||||
let autogenerated = PathBuf::from(format!("permissions/{plugin}/autogenerated/"));
|
||||
let commands_dir = autogenerated.join("commands");
|
||||
|
||||
tauri_utils::acl::build::autogenerate_command_permissions(
|
||||
&commands_dir,
|
||||
&commands.iter().map(|(cmd, _)| *cmd).collect::<Vec<_>>(),
|
||||
license_header,
|
||||
);
|
||||
let default_permissions = commands
|
||||
.iter()
|
||||
.filter(|(_cmd, default)| *default)
|
||||
.map(|(cmd, _)| {
|
||||
let slugified_command = cmd.replace('_', "-");
|
||||
format!("\"allow-{}\"", slugified_command)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
let default_toml = format!(
|
||||
r###"{license_header}# Automatically generated - DO NOT EDIT!
|
||||
|
||||
[default]
|
||||
description = "Default permissions for the plugin."
|
||||
permissions = [{default_permissions}]
|
||||
"###,
|
||||
);
|
||||
|
||||
let out_path = autogenerated.join("default.toml");
|
||||
if default_toml != read_to_string(&out_path).unwrap_or_default() {
|
||||
std::fs::write(out_path, default_toml)
|
||||
.unwrap_or_else(|_| panic!("unable to autogenerate default permissions"));
|
||||
}
|
||||
|
||||
let permissions = tauri_utils::acl::build::define_permissions(
|
||||
&format!("./permissions/{plugin}/**/*.toml"),
|
||||
&format!("tauri:{plugin}"),
|
||||
)
|
||||
.unwrap_or_else(|e| panic!("failed to define permissions for {plugin}: {e}"));
|
||||
tauri_utils::acl::build::generate_schema(&permissions, format!("./permissions/{plugin}"))
|
||||
.unwrap_or_else(|e| panic!("failed to generate schema for {plugin}: {e}"));
|
||||
}
|
||||
}
|
||||
|
||||
fn add_manifest() {
|
||||
|
||||
1
core/tauri/permissions/.gitignore
vendored
Normal file
1
core/tauri/permissions/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.schema.json
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-app-hide"
|
||||
description = "Enables the app_hide command without any pre-configured scope."
|
||||
commands.allow = ["app_hide"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-app-hide"
|
||||
description = "Denies the app_hide command without any pre-configured scope."
|
||||
commands.deny = ["app_hide"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-app-show"
|
||||
description = "Enables the app_show command without any pre-configured scope."
|
||||
commands.allow = ["app_show"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-app-show"
|
||||
description = "Denies the app_show command without any pre-configured scope."
|
||||
commands.deny = ["app_show"]
|
||||
16
core/tauri/permissions/app/autogenerated/commands/name.toml
Normal file
16
core/tauri/permissions/app/autogenerated/commands/name.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-name"
|
||||
description = "Enables the name command without any pre-configured scope."
|
||||
commands.allow = ["name"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-name"
|
||||
description = "Denies the name command without any pre-configured scope."
|
||||
commands.deny = ["name"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-tauri-version"
|
||||
description = "Enables the tauri_version command without any pre-configured scope."
|
||||
commands.allow = ["tauri_version"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-tauri-version"
|
||||
description = "Denies the tauri_version command without any pre-configured scope."
|
||||
commands.deny = ["tauri_version"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-version"
|
||||
description = "Enables the version command without any pre-configured scope."
|
||||
commands.allow = ["version"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-version"
|
||||
description = "Denies the version command without any pre-configured scope."
|
||||
commands.deny = ["version"]
|
||||
8
core/tauri/permissions/app/autogenerated/default.toml
Normal file
8
core/tauri/permissions/app/autogenerated/default.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
[default]
|
||||
description = "Default permissions for the plugin."
|
||||
permissions = ["allow-version", "allow-name", "allow-tauri-version"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-emit"
|
||||
description = "Enables the emit command without any pre-configured scope."
|
||||
commands.allow = ["emit"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-emit"
|
||||
description = "Denies the emit command without any pre-configured scope."
|
||||
commands.deny = ["emit"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-listen"
|
||||
description = "Enables the listen command without any pre-configured scope."
|
||||
commands.allow = ["listen"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-listen"
|
||||
description = "Denies the listen command without any pre-configured scope."
|
||||
commands.deny = ["listen"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-unlisten"
|
||||
description = "Enables the unlisten command without any pre-configured scope."
|
||||
commands.allow = ["unlisten"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-unlisten"
|
||||
description = "Denies the unlisten command without any pre-configured scope."
|
||||
commands.deny = ["unlisten"]
|
||||
8
core/tauri/permissions/event/autogenerated/default.toml
Normal file
8
core/tauri/permissions/event/autogenerated/default.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
[default]
|
||||
description = "Default permissions for the plugin."
|
||||
permissions = ["allow-listen", "allow-unlisten", "allow-emit"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-append"
|
||||
description = "Enables the append command without any pre-configured scope."
|
||||
commands.allow = ["append"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-append"
|
||||
description = "Denies the append command without any pre-configured scope."
|
||||
commands.deny = ["append"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-create-default"
|
||||
description = "Enables the create_default command without any pre-configured scope."
|
||||
commands.allow = ["create_default"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-create-default"
|
||||
description = "Denies the create_default command without any pre-configured scope."
|
||||
commands.deny = ["create_default"]
|
||||
16
core/tauri/permissions/menu/autogenerated/commands/get.toml
Normal file
16
core/tauri/permissions/menu/autogenerated/commands/get.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-get"
|
||||
description = "Enables the get command without any pre-configured scope."
|
||||
commands.allow = ["get"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-get"
|
||||
description = "Denies the get command without any pre-configured scope."
|
||||
commands.deny = ["get"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-insert"
|
||||
description = "Enables the insert command without any pre-configured scope."
|
||||
commands.allow = ["insert"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-insert"
|
||||
description = "Denies the insert command without any pre-configured scope."
|
||||
commands.deny = ["insert"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-is-checked"
|
||||
description = "Enables the is_checked command without any pre-configured scope."
|
||||
commands.allow = ["is_checked"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-is-checked"
|
||||
description = "Denies the is_checked command without any pre-configured scope."
|
||||
commands.deny = ["is_checked"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-is-enabled"
|
||||
description = "Enables the is_enabled command without any pre-configured scope."
|
||||
commands.allow = ["is_enabled"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-is-enabled"
|
||||
description = "Denies the is_enabled command without any pre-configured scope."
|
||||
commands.deny = ["is_enabled"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-items"
|
||||
description = "Enables the items command without any pre-configured scope."
|
||||
commands.allow = ["items"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-items"
|
||||
description = "Denies the items command without any pre-configured scope."
|
||||
commands.deny = ["items"]
|
||||
16
core/tauri/permissions/menu/autogenerated/commands/new.toml
Normal file
16
core/tauri/permissions/menu/autogenerated/commands/new.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-new"
|
||||
description = "Enables the new command without any pre-configured scope."
|
||||
commands.allow = ["new"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-new"
|
||||
description = "Denies the new command without any pre-configured scope."
|
||||
commands.deny = ["new"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-popup"
|
||||
description = "Enables the popup command without any pre-configured scope."
|
||||
commands.allow = ["popup"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-popup"
|
||||
description = "Denies the popup command without any pre-configured scope."
|
||||
commands.deny = ["popup"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-prepend"
|
||||
description = "Enables the prepend command without any pre-configured scope."
|
||||
commands.allow = ["prepend"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-prepend"
|
||||
description = "Denies the prepend command without any pre-configured scope."
|
||||
commands.deny = ["prepend"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-remove"
|
||||
description = "Enables the remove command without any pre-configured scope."
|
||||
commands.allow = ["remove"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-remove"
|
||||
description = "Denies the remove command without any pre-configured scope."
|
||||
commands.deny = ["remove"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-remove-at"
|
||||
description = "Enables the remove_at command without any pre-configured scope."
|
||||
commands.allow = ["remove_at"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-remove-at"
|
||||
description = "Denies the remove_at command without any pre-configured scope."
|
||||
commands.deny = ["remove_at"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-set-accelerator"
|
||||
description = "Enables the set_accelerator command without any pre-configured scope."
|
||||
commands.allow = ["set_accelerator"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-set-accelerator"
|
||||
description = "Denies the set_accelerator command without any pre-configured scope."
|
||||
commands.deny = ["set_accelerator"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-set-as-app-menu"
|
||||
description = "Enables the set_as_app_menu command without any pre-configured scope."
|
||||
commands.allow = ["set_as_app_menu"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-set-as-app-menu"
|
||||
description = "Denies the set_as_app_menu command without any pre-configured scope."
|
||||
commands.deny = ["set_as_app_menu"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-set-as-help-menu-for-nsapp"
|
||||
description = "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope."
|
||||
commands.allow = ["set_as_help_menu_for_nsapp"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-set-as-help-menu-for-nsapp"
|
||||
description = "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope."
|
||||
commands.deny = ["set_as_help_menu_for_nsapp"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-set-as-window-menu"
|
||||
description = "Enables the set_as_window_menu command without any pre-configured scope."
|
||||
commands.allow = ["set_as_window_menu"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-set-as-window-menu"
|
||||
description = "Denies the set_as_window_menu command without any pre-configured scope."
|
||||
commands.deny = ["set_as_window_menu"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-set-as-windows-menu-for-nsapp"
|
||||
description = "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope."
|
||||
commands.allow = ["set_as_windows_menu_for_nsapp"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-set-as-windows-menu-for-nsapp"
|
||||
description = "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope."
|
||||
commands.deny = ["set_as_windows_menu_for_nsapp"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-set-checked"
|
||||
description = "Enables the set_checked command without any pre-configured scope."
|
||||
commands.allow = ["set_checked"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-set-checked"
|
||||
description = "Denies the set_checked command without any pre-configured scope."
|
||||
commands.deny = ["set_checked"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-set-enabled"
|
||||
description = "Enables the set_enabled command without any pre-configured scope."
|
||||
commands.allow = ["set_enabled"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-set-enabled"
|
||||
description = "Denies the set_enabled command without any pre-configured scope."
|
||||
commands.deny = ["set_enabled"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-set-icon"
|
||||
description = "Enables the set_icon command without any pre-configured scope."
|
||||
commands.allow = ["set_icon"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-set-icon"
|
||||
description = "Denies the set_icon command without any pre-configured scope."
|
||||
commands.deny = ["set_icon"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-set-text"
|
||||
description = "Enables the set_text command without any pre-configured scope."
|
||||
commands.allow = ["set_text"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-set-text"
|
||||
description = "Denies the set_text command without any pre-configured scope."
|
||||
commands.deny = ["set_text"]
|
||||
16
core/tauri/permissions/menu/autogenerated/commands/text.toml
Normal file
16
core/tauri/permissions/menu/autogenerated/commands/text.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-text"
|
||||
description = "Enables the text command without any pre-configured scope."
|
||||
commands.allow = ["text"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-text"
|
||||
description = "Denies the text command without any pre-configured scope."
|
||||
commands.deny = ["text"]
|
||||
8
core/tauri/permissions/menu/autogenerated/default.toml
Normal file
8
core/tauri/permissions/menu/autogenerated/default.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
[default]
|
||||
description = "Default permissions for the plugin."
|
||||
permissions = []
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-basename"
|
||||
description = "Enables the basename command without any pre-configured scope."
|
||||
commands.allow = ["basename"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-basename"
|
||||
description = "Denies the basename command without any pre-configured scope."
|
||||
commands.deny = ["basename"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-dirname"
|
||||
description = "Enables the dirname command without any pre-configured scope."
|
||||
commands.allow = ["dirname"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-dirname"
|
||||
description = "Denies the dirname command without any pre-configured scope."
|
||||
commands.deny = ["dirname"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-extname"
|
||||
description = "Enables the extname command without any pre-configured scope."
|
||||
commands.allow = ["extname"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-extname"
|
||||
description = "Denies the extname command without any pre-configured scope."
|
||||
commands.deny = ["extname"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-is-absolute"
|
||||
description = "Enables the is_absolute command without any pre-configured scope."
|
||||
commands.allow = ["is_absolute"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-is-absolute"
|
||||
description = "Denies the is_absolute command without any pre-configured scope."
|
||||
commands.deny = ["is_absolute"]
|
||||
16
core/tauri/permissions/path/autogenerated/commands/join.toml
Normal file
16
core/tauri/permissions/path/autogenerated/commands/join.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-join"
|
||||
description = "Enables the join command without any pre-configured scope."
|
||||
commands.allow = ["join"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-join"
|
||||
description = "Denies the join command without any pre-configured scope."
|
||||
commands.deny = ["join"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-normalize"
|
||||
description = "Enables the normalize command without any pre-configured scope."
|
||||
commands.allow = ["normalize"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-normalize"
|
||||
description = "Denies the normalize command without any pre-configured scope."
|
||||
commands.deny = ["normalize"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-resolve"
|
||||
description = "Enables the resolve command without any pre-configured scope."
|
||||
commands.allow = ["resolve"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-resolve"
|
||||
description = "Denies the resolve command without any pre-configured scope."
|
||||
commands.deny = ["resolve"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-resolve-directory"
|
||||
description = "Enables the resolve_directory command without any pre-configured scope."
|
||||
commands.allow = ["resolve_directory"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-resolve-directory"
|
||||
description = "Denies the resolve_directory command without any pre-configured scope."
|
||||
commands.deny = ["resolve_directory"]
|
||||
8
core/tauri/permissions/path/autogenerated/default.toml
Normal file
8
core/tauri/permissions/path/autogenerated/default.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
[default]
|
||||
description = "Default permissions for the plugin."
|
||||
permissions = ["allow-resolve-directory", "allow-resolve", "allow-normalize", "allow-join", "allow-dirname", "allow-extname", "allow-basename", "allow-is-absolute"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-close"
|
||||
description = "Enables the close command without any pre-configured scope."
|
||||
commands.allow = ["close"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-close"
|
||||
description = "Denies the close command without any pre-configured scope."
|
||||
commands.deny = ["close"]
|
||||
@@ -0,0 +1,8 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
[default]
|
||||
description = "Default permissions for the plugin."
|
||||
permissions = ["allow-close"]
|
||||
16
core/tauri/permissions/tray/autogenerated/commands/new.toml
Normal file
16
core/tauri/permissions/tray/autogenerated/commands/new.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-new"
|
||||
description = "Enables the new command without any pre-configured scope."
|
||||
commands.allow = ["new"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-new"
|
||||
description = "Denies the new command without any pre-configured scope."
|
||||
commands.deny = ["new"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-set-icon"
|
||||
description = "Enables the set_icon command without any pre-configured scope."
|
||||
commands.allow = ["set_icon"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-set-icon"
|
||||
description = "Denies the set_icon command without any pre-configured scope."
|
||||
commands.deny = ["set_icon"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-set-icon-as-template"
|
||||
description = "Enables the set_icon_as_template command without any pre-configured scope."
|
||||
commands.allow = ["set_icon_as_template"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-set-icon-as-template"
|
||||
description = "Denies the set_icon_as_template command without any pre-configured scope."
|
||||
commands.deny = ["set_icon_as_template"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-set-menu"
|
||||
description = "Enables the set_menu command without any pre-configured scope."
|
||||
commands.allow = ["set_menu"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-set-menu"
|
||||
description = "Denies the set_menu command without any pre-configured scope."
|
||||
commands.deny = ["set_menu"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-set-show-menu-on-left-click"
|
||||
description = "Enables the set_show_menu_on_left_click command without any pre-configured scope."
|
||||
commands.allow = ["set_show_menu_on_left_click"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-set-show-menu-on-left-click"
|
||||
description = "Denies the set_show_menu_on_left_click command without any pre-configured scope."
|
||||
commands.deny = ["set_show_menu_on_left_click"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-set-temp-dir-path"
|
||||
description = "Enables the set_temp_dir_path command without any pre-configured scope."
|
||||
commands.allow = ["set_temp_dir_path"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-set-temp-dir-path"
|
||||
description = "Denies the set_temp_dir_path command without any pre-configured scope."
|
||||
commands.deny = ["set_temp_dir_path"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-set-title"
|
||||
description = "Enables the set_title command without any pre-configured scope."
|
||||
commands.allow = ["set_title"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-set-title"
|
||||
description = "Denies the set_title command without any pre-configured scope."
|
||||
commands.deny = ["set_title"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-set-tooltip"
|
||||
description = "Enables the set_tooltip command without any pre-configured scope."
|
||||
commands.allow = ["set_tooltip"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-set-tooltip"
|
||||
description = "Denies the set_tooltip command without any pre-configured scope."
|
||||
commands.deny = ["set_tooltip"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-set-visible"
|
||||
description = "Enables the set_visible command without any pre-configured scope."
|
||||
commands.allow = ["set_visible"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-set-visible"
|
||||
description = "Denies the set_visible command without any pre-configured scope."
|
||||
commands.deny = ["set_visible"]
|
||||
8
core/tauri/permissions/tray/autogenerated/default.toml
Normal file
8
core/tauri/permissions/tray/autogenerated/default.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
[default]
|
||||
description = "Default permissions for the plugin."
|
||||
permissions = []
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-available-monitors"
|
||||
description = "Enables the available_monitors command without any pre-configured scope."
|
||||
commands.allow = ["available_monitors"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-available-monitors"
|
||||
description = "Denies the available_monitors command without any pre-configured scope."
|
||||
commands.deny = ["available_monitors"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-center"
|
||||
description = "Enables the center command without any pre-configured scope."
|
||||
commands.allow = ["center"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-center"
|
||||
description = "Denies the center command without any pre-configured scope."
|
||||
commands.deny = ["center"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-close"
|
||||
description = "Enables the close command without any pre-configured scope."
|
||||
commands.allow = ["close"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-close"
|
||||
description = "Denies the close command without any pre-configured scope."
|
||||
commands.deny = ["close"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-create"
|
||||
description = "Enables the create command without any pre-configured scope."
|
||||
commands.allow = ["create"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-create"
|
||||
description = "Denies the create command without any pre-configured scope."
|
||||
commands.deny = ["create"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-current-monitor"
|
||||
description = "Enables the current_monitor command without any pre-configured scope."
|
||||
commands.allow = ["current_monitor"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-current-monitor"
|
||||
description = "Denies the current_monitor command without any pre-configured scope."
|
||||
commands.deny = ["current_monitor"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-hide"
|
||||
description = "Enables the hide command without any pre-configured scope."
|
||||
commands.allow = ["hide"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-hide"
|
||||
description = "Denies the hide command without any pre-configured scope."
|
||||
commands.deny = ["hide"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-inner-position"
|
||||
description = "Enables the inner_position command without any pre-configured scope."
|
||||
commands.allow = ["inner_position"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-inner-position"
|
||||
description = "Denies the inner_position command without any pre-configured scope."
|
||||
commands.deny = ["inner_position"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-inner-size"
|
||||
description = "Enables the inner_size command without any pre-configured scope."
|
||||
commands.allow = ["inner_size"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-inner-size"
|
||||
description = "Denies the inner_size command without any pre-configured scope."
|
||||
commands.deny = ["inner_size"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-internal-toggle-devtools"
|
||||
description = "Enables the internal_toggle_devtools command without any pre-configured scope."
|
||||
commands.allow = ["internal_toggle_devtools"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-internal-toggle-devtools"
|
||||
description = "Denies the internal_toggle_devtools command without any pre-configured scope."
|
||||
commands.deny = ["internal_toggle_devtools"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-internal-toggle-maximize"
|
||||
description = "Enables the internal_toggle_maximize command without any pre-configured scope."
|
||||
commands.allow = ["internal_toggle_maximize"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-internal-toggle-maximize"
|
||||
description = "Denies the internal_toggle_maximize command without any pre-configured scope."
|
||||
commands.deny = ["internal_toggle_maximize"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-is-closable"
|
||||
description = "Enables the is_closable command without any pre-configured scope."
|
||||
commands.allow = ["is_closable"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-is-closable"
|
||||
description = "Denies the is_closable command without any pre-configured scope."
|
||||
commands.deny = ["is_closable"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-is-decorated"
|
||||
description = "Enables the is_decorated command without any pre-configured scope."
|
||||
commands.allow = ["is_decorated"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-is-decorated"
|
||||
description = "Denies the is_decorated command without any pre-configured scope."
|
||||
commands.deny = ["is_decorated"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-is-focused"
|
||||
description = "Enables the is_focused command without any pre-configured scope."
|
||||
commands.allow = ["is_focused"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-is-focused"
|
||||
description = "Denies the is_focused command without any pre-configured scope."
|
||||
commands.deny = ["is_focused"]
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../../.schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-is-fullscreen"
|
||||
description = "Enables the is_fullscreen command without any pre-configured scope."
|
||||
commands.allow = ["is_fullscreen"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-is-fullscreen"
|
||||
description = "Denies the is_fullscreen command without any pre-configured scope."
|
||||
commands.deny = ["is_fullscreen"]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user