Skip to content

Commit

Permalink
permission file schema
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog committed Jan 15, 2024
1 parent 609fcfb commit eba00d6
Show file tree
Hide file tree
Showing 15 changed files with 175 additions and 57 deletions.
49 changes: 24 additions & 25 deletions core/tauri-build/src/acl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,31 +67,30 @@ pub(crate) fn get_plugin_manifests() -> Result<HashMap<String, Manifest>> {
});
}

if let Some(permissions) = permission_file.permission {
manifest.permissions.extend(
permissions
.into_iter()
.map(|p| (p.identifier.clone(), p))
.collect::<HashMap<_, _>>(),
);
}
if let Some(sets) = permission_file.set {
manifest.permission_sets.extend(
sets
.into_iter()
.map(|set| {
(
set.identifier.clone(),
PermissionSet {
identifier: set.identifier,
description: set.description,
permissions: set.permissions,
},
)
})
.collect::<HashMap<_, _>>(),
);
}
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<_, _>>(),
);
}

processed.insert(plugin_name, manifest);
Expand Down
19 changes: 15 additions & 4 deletions core/tauri-plugin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,29 @@ edition = { workspace = true }
rust-version = { workspace = true }

[features]
build = ["dep:serde", "dep:cargo_metadata", "dep:thiserror", "dep:serde_json", "dep:glob", "dep:toml"]
build = [
"dep:serde",
"dep:cargo_metadata",
"dep:thiserror",
"dep:serde_json",
"dep:glob",
"dep:toml",
"dep:schemars",
]

[dependencies]
serde = { version = "1", optional = true }
thiserror = { 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-utils = { version = "2.0.0-alpha.12", default-features = false, path = "../tauri-utils", features = [
"schema",
] }
serde_json = { version = "1", optional = true }
glob = { version = "0.3", optional = true }
toml = { version = "0.8", optional = true }
schemars = { version = "0.8", optional = true }

[package.metadata.docs.rs]
features = ["build", "runtime"]
rustc-args = [ "--cfg", "docsrs" ]
rustdoc-args = [ "--cfg", "docsrs" ]
rustc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs"]
50 changes: 44 additions & 6 deletions core/tauri-plugin/src/build/acl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,24 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

use std::{collections::HashMap, env::vars_os, num::NonZeroU64, path::PathBuf};
use std::{
collections::HashMap,
env::vars_os,
fs::File,
io::{BufWriter, Write},
num::NonZeroU64,
path::PathBuf,
};

use super::Error;
use serde::Deserialize;
use tauri_utils::acl::{Commands, Permission, Scopes};

const PERMISSION_FILES_PATH_KEY: &str = "PERMISSION_FILES_PATH";
const PERMISSION_FILE_EXTENSIONS: &[&str] = &["json", "toml"];
pub(crate) const PERMISSION_SCHEMA_FILE_NAME: &str = ".schema.json";

#[derive(Debug, Deserialize)]
#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct PermissionSet {
/// A unique identifier for the permission.
pub identifier: String,
Expand All @@ -22,7 +31,10 @@ pub struct PermissionSet {
pub permissions: Vec<String>,
}

#[derive(Debug, Deserialize)]
/// The default permission of the plugin.
///
/// Works similarly to a permission with the "default" identifier.
#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct DefaultPermission {
/// The version of the permission.
pub version: Option<NonZeroU64>,
Expand All @@ -39,17 +51,33 @@ pub struct DefaultPermission {
pub scope: Scopes,
}

#[derive(Debug, Deserialize)]
/// Permission file that can define a default permission, a set of permissions or a list of inlined permissions.
#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct PermissionFile {
pub default: Option<DefaultPermission>,
pub set: Option<Vec<PermissionSet>>,
pub permission: Option<Vec<Permission>>,
#[serde(default)]
pub set: Vec<PermissionSet>,
#[serde(default)]
pub permission: Vec<Permission>,
}

pub(crate) fn define_permissions(pattern: &str) -> Result<(), 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 {
Expand All @@ -73,6 +101,16 @@ pub(crate) fn define_permissions(pattern: &str) -> Result<(), Error> {
Ok(())
}

pub(crate) fn generate_schema() -> Result<(), Error> {
let schema = schemars::schema_for!(PermissionFile);
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)?);
write!(schema_file, "{schema_str}").map_err(Error::WriteFile)?;
Ok(())
}

pub fn read_permissions() -> Result<HashMap<String, Vec<PermissionFile>>, Error> {
let mut permissions_map = HashMap::new();

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

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

use cargo_metadata::{Metadata, MetadataCommand};
use thiserror::Error;
Expand Down Expand Up @@ -31,6 +35,9 @@ pub enum Error {
#[error("failed to write file: {0}")]
WriteFile(std::io::Error),

#[error("failed to create file: {0}")]
CreateFile(std::io::Error),

#[error("failed to execute: {0}")]
Metadata(#[from] cargo_metadata::Error),

Expand Down Expand Up @@ -88,6 +95,7 @@ impl<'a> Builder<'a> {
}

acl::define_permissions("./permissions/**/*.*")?;
acl::generate_schema()?;

let metadata = find_metadata()?;
println!("{metadata:#?}");
Expand All @@ -101,26 +109,33 @@ fn autogenerate_command_permissions(path: &Path, commands: &[&str]) {
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::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 = "This enables the {command} command without any pre-configured scope."
commands.allow = [
"{command}"
]
commands.allow = ["{command}"]
[[permission]]
identifier = "deny-{slugified_command}"
description = "This denies the {command} command without any pre-configured scope."
commands.deny = [
"{command}"
]"###,
commands.deny = ["{command}"]
"###,
command = command,
slugified_command = slugified_command
slugified_command = slugified_command,
schema_path = schema_path.display()
);

std::fs::write(path.join(format!("{command}.toml")), toml)
Expand Down
4 changes: 4 additions & 0 deletions core/tauri-utils/src/acl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub mod value;
///
/// 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)]
Expand All @@ -35,6 +36,7 @@ pub struct Commands {
///
/// 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<Value>,
Expand All @@ -48,6 +50,7 @@ pub struct Scopes {
///
/// 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>,
Expand All @@ -69,6 +72,7 @@ pub struct Permission {

/// 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,
Expand Down
2 changes: 2 additions & 0 deletions core/tauri-utils/src/acl/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ 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`].
Expand All @@ -38,6 +39,7 @@ impl From<f64> for Number {

/// 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`].
Expand Down
44 changes: 44 additions & 0 deletions examples/api/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.schema.json
Loading

0 comments on commit eba00d6

Please sign in to comment.