Skip to content

Commit

Permalink
core plugin permissions, default is now a set
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog committed Jan 17, 2024
1 parent 5218e32 commit 3edd138
Show file tree
Hide file tree
Showing 127 changed files with 1,740 additions and 90 deletions.
2 changes: 1 addition & 1 deletion core/tauri-build/src/acl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ fn capabilities_schema(plugin_manifests: &BTreeMap<String, Manifest>) -> RootSch
permission_schemas.push(schema_from(
plugin,
"default",
default.description.as_deref(),
Some(default.description.as_ref()),
));
}

Expand Down
54 changes: 7 additions & 47 deletions core/tauri-plugin/src/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

use std::{
env::current_dir,
fs::create_dir_all,
path::{Path, PathBuf},
};
use std::path::Path;

use cargo_metadata::{Metadata, MetadataCommand};
use tauri_utils::acl::{self, Error};
Expand Down Expand Up @@ -48,11 +44,14 @@ impl<'a> Builder<'a> {
let commands_dir = &autogenerated.join("commands");

if !self.commands.is_empty() {
autogenerate_command_permissions(commands_dir, self.commands);
acl::build::autogenerate_command_permissions(commands_dir, self.commands);
}

let permissions = acl::build::define_permissions("./permissions/**/*.*")?;
acl::build::generate_schema(&permissions)?;
let permissions = acl::build::define_permissions(
"./permissions/**/*.*",
&std::env::var("CARGO_PKG_NAME").unwrap(),
)?;
acl::build::generate_schema(&permissions, "./permissions")?;

let metadata = find_metadata()?;
println!("{metadata:#?}");
Expand All @@ -61,45 +60,6 @@ impl<'a> Builder<'a> {
}
}

fn autogenerate_command_permissions(path: &Path, commands: &[&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(acl::build::PERMISSION_SCHEMA_FILE_NAME);

for command in commands {
let slugified_command = command.replace('_', "-");
let toml = format!(
r###"# 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()
);

std::fs::write(path.join(format!("{command}.toml")), toml)
.unwrap_or_else(|_| panic!("unable to autogenerate ${command}.toml"));
}
}

/// 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))
Expand Down
86 changes: 71 additions & 15 deletions core/tauri-utils/src/acl/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
use std::{
collections::{BTreeMap, HashMap},
env::vars_os,
fs::File,
env::{current_dir, vars_os},
fs::{create_dir_all, File},
io::{BufWriter, Write},
path::PathBuf,
path::{Path, PathBuf},
};

use crate::acl::Error;
Expand All @@ -36,6 +36,8 @@ const CAPABILITY_FILE_EXTENSIONS: &[&str] = &["json", "toml"];
/// Known filename of a capability schema
const CAPABILITIES_SCHEMA_FILE_NAME: &str = ".schema.json";

const CORE_PLUGIN_PERMISSIONS_TOKEN: &str = "__CORE_PLUGIN__";

/// Capability formats accepted in a capability file.
#[derive(Deserialize, schemars::JsonSchema)]
#[serde(untagged)]
Expand All @@ -50,7 +52,7 @@ pub enum CapabilityFile {
}

/// Write the permissions to a temporary directory and pass it to the immediate consuming crate.
pub fn define_permissions(pattern: &str) -> Result<Vec<PermissionFile>, Error> {
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())
Expand All @@ -73,19 +75,24 @@ pub fn define_permissions(pattern: &str) -> Result<Vec<PermissionFile>, Error> {
println!("cargo:rerun-if-changed={}", path.display());
}

let permission_files_path = std::env::temp_dir().join(format!(
"{}-permission-files",
std::env::var("CARGO_PKG_NAME").unwrap()
));
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)?;
println!(
"cargo:{PERMISSION_FILES_PATH_KEY}={}",
permission_files_path.display()
);

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)
}
Expand Down Expand Up @@ -204,12 +211,16 @@ fn permissions_schema(permissions: &[PermissionFile]) -> RootSchema {
}

/// Generate and write a schema based on the format of a [`PermissionFile`].
pub fn generate_schema(permissions: &[PermissionFile]) -> Result<(), Error> {
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_path = PathBuf::from("permissions").join(PERMISSION_SCHEMA_FILE_NAME);

let mut schema_file = BufWriter::new(File::create(out_path).map_err(Error::CreateFile)?);
let mut schema_file = BufWriter::new(
File::create(out_dir.as_ref().join(PERMISSION_SCHEMA_FILE_NAME)).map_err(Error::CreateFile)?,
);
write!(schema_file, "{schema_str}").map_err(Error::WriteFile)?;
Ok(())
}
Expand All @@ -224,6 +235,11 @@ pub fn read_permissions() -> Result<HashMap<String, Vec<PermissionFile>>, Error>
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)?;
Expand Down Expand Up @@ -258,3 +274,43 @@ fn parse_permissions(paths: Vec<PathBuf>) -> Result<Vec<PermissionFile>, Error>
}
Ok(permissions)
}

/// Autogenerate permission files for a list of commands.
pub fn autogenerate_command_permissions(path: &Path, commands: &[&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###"# 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()
);

std::fs::write(path.join(format!("{command}.toml")), toml)
.unwrap_or_else(|_| panic!("unable to autogenerate ${command}.toml"));
}
}
27 changes: 11 additions & 16 deletions core/tauri-utils/src/acl/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
use std::{collections::HashMap, num::NonZeroU64};

use super::{Commands, Permission, PermissionSet, Scopes};
use super::{Permission, PermissionSet};
use serde::{Deserialize, Serialize};

/// The default permission of the plugin.
/// The default permission set of the plugin.
///
/// Works similarly to a permission with the "default" identifier.
#[derive(Debug, Deserialize)]
Expand All @@ -21,13 +21,8 @@ pub struct DefaultPermission {
/// 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,
/// 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.
Expand All @@ -50,10 +45,10 @@ pub struct PermissionFile {
}

/// Plugin manifest.
#[derive(Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize)]
pub struct Manifest {
/// Default permission.
pub default_permission: Option<Permission>,
pub default_permission: Option<PermissionSet>,
/// Plugin permissions.
pub permissions: HashMap<String, Permission>,
/// Plugin permission sets.
Expand All @@ -71,12 +66,12 @@ impl Manifest {

for permission_file in permission_files {
if let Some(default) = permission_file.default {
manifest.default_permission.replace(Permission {
manifest.default_permission.replace(PermissionSet {
identifier: "default".into(),
version: default.version,
description: default.description,
commands: default.commands,
scope: default.scope,
description: default
.description
.unwrap_or_else(|| "Default plugin permissions.".to_string()),
permissions: default.permissions,
});
}

Expand Down
11 changes: 7 additions & 4 deletions core/tauri-utils/src/acl/resolved.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,11 +272,14 @@ fn get_permissions<'a>(
})?;

if permission_name == "default" {
Ok(vec![manifest.default_permission.as_ref().ok_or_else(
|| Error::MissingDefaultPermission {
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) {
Expand Down
1 change: 1 addition & 0 deletions core/tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -111,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"
Expand Down
Loading

0 comments on commit 3edd138

Please sign in to comment.