use super::*;
use crate::graph::{AccessRequest, Action, AllowAll, Authorizer, Resource};
use crate::traversal::permission_check::Restrictions;
use std::collections::HashMap;
#[derive(Clone)]
pub(crate) struct ScriptAccess {
pub(crate) authorizer: Arc<dyn Authorizer>,
pub(crate) presets: Arc<HashMap<CompactString, ElementProperty>>,
pub(crate) max_value_depth: usize,
pub(crate) read_only: bool,
pub(crate) query_depth: usize,
}
impl Default for ScriptAccess {
fn default() -> Self {
Self {
authorizer: Arc::new(AllowAll),
presets: Arc::default(),
max_value_depth: 0,
read_only: false,
query_depth: 0,
}
}
}
impl ScriptAccess {
pub(crate) fn new(
authorizer: Arc<dyn Authorizer>,
options: &[(&str, ElementProperty)],
) -> Self {
Self {
authorizer,
max_value_depth: 0,
read_only: false,
query_depth: 0,
presets: Arc::new(
options
.iter()
.map(|(key, value)| (CompactString::new(key), value.clone()))
.collect(),
),
}
}
pub(crate) fn of_context(context: &NativeCallContext) -> Arc<ScriptAccess> {
context
.tag()
.and_then(|tag| {
tag.read_lock::<EngineAccess>()
.map(|access| access.0.clone())
})
.unwrap_or_default()
}
pub(crate) fn restrictions(&self, private_graph: bool) -> Restrictions {
Restrictions {
authorizer: self.authorizer.clone(),
private_graph,
preset_options: self.presets.clone(),
required: Vec::new(),
}
}
}
pub(crate) fn value_depth_of(context: &NativeCallContext) -> usize {
context
.tag()
.and_then(|tag| {
tag.read_lock::<EngineAccess>()
.map(|access| access.0.max_value_depth)
})
.filter(|depth| *depth > 0)
.unwrap_or(crate::value::depth::DEFAULT_MAX_VALUE_DEPTH)
}
#[derive(Clone)]
pub(crate) struct EngineAccess(pub(crate) Arc<ScriptAccess>);
pub(crate) fn install(
engine: &mut Engine,
authorizer: Arc<dyn Authorizer>,
options: &[(&str, ElementProperty)],
max_value_depth: usize,
) {
let mut access = ScriptAccess::new(authorizer.clone(), options);
access.max_value_depth = max_value_depth;
engine.set_default_tag(Dynamic::from(EngineAccess(Arc::new(access))));
engine.set_module_resolver(FileImports { authorizer });
}
pub(crate) struct FileImports {
authorizer: Arc<dyn Authorizer>,
}
impl rhai::ModuleResolver for FileImports {
fn resolve(
&self,
engine: &Engine,
source: Option<&str>,
path: &str,
pos: rhai::Position,
) -> Result<rhai::Shared<Module>, Box<EvalAltResult>> {
let file = std::path::Path::new(path).with_extension("rhai");
let restrictions = Restrictions {
authorizer: self.authorizer.clone(),
..Restrictions::default()
};
let resolved = restrictions
.authorize_file("import", Action::Execute, &file)
.map_err(|err| {
Box::new(EvalAltResult::ErrorRuntime(
Dynamic::from(crate::error::format_diagnostic(&err)),
pos,
))
})?;
#[cfg(any(not(target_family = "wasm"), not(target_os = "unknown")))]
{
let resolver = rhai::module_resolvers::FileModuleResolver::new();
if resolved == file {
resolver.resolve(engine, source, path, pos)
} else {
resolver.resolve(engine, source, &resolved.to_string_lossy(), pos)
}
}
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
{
let _ = (engine, source, resolved);
Err(Box::new(EvalAltResult::ErrorModuleNotFound(
path.to_string(),
pos,
)))
}
}
}
#[derive(Clone, Default)]
pub(crate) struct GraphOrigin {
access: Arc<ScriptAccess>,
host_graphs: Vec<Arc<dyn AnyGraph>>,
}
impl GraphOrigin {
pub(crate) fn source(&self, graph: Arc<dyn AnyGraph>) -> ScriptGraphTraversalSource {
let private = !self
.host_graphs
.iter()
.any(|host| super::plan::same_graph(host, &graph));
ScriptGraphSource::graph(graph).with_access(self.access.clone(), private)
}
}
impl ScriptGraphTraversalSource {
pub(crate) fn with_access(mut self, access: Arc<ScriptAccess>, private: bool) -> Self {
self.access = access;
self.private = private;
self
}
fn writes_into_host_graph(&self) -> bool {
self.steps.iter().any(|step| {
matches!(step, Step::Source(SourceStep::SideEffectGraph(_, target)) if !target.private)
})
}
pub(crate) fn restrictions(&self) -> Restrictions {
let into_host = self.writes_into_host_graph();
let mut restrictions = self.access.restrictions(self.private && !into_host);
if into_host {
restrictions.required.push((
"with_side_effect",
AccessRequest::new(Action::Create, Resource::data(None, None)),
));
}
restrictions
}
pub(crate) fn require(
&self,
operation: &str,
request: AccessRequest<'_>,
) -> Result<(), Box<EvalAltResult>> {
self.restrictions()
.authorize(operation, &request)
.map_err(script_error)
}
#[cfg(feature = "io")]
pub(crate) fn require_file(
&self,
operation: &str,
action: Action<'_>,
path: &str,
) -> Result<std::path::PathBuf, Box<EvalAltResult>> {
self.restrictions()
.authorize_file(operation, action, std::path::Path::new(path))
.map_err(script_error)
}
pub(crate) fn graph_origin(&self) -> GraphOrigin {
let mut host_graphs: Vec<Arc<dyn AnyGraph>> = self
.steps
.iter()
.filter_map(|step| match step {
Step::Source(SourceStep::SideEffectGraph(_, target)) if !target.private => {
Some(target.graph.clone() as Arc<dyn AnyGraph>)
}
_ => None,
})
.collect();
if !self.private
&& let Some(graph) = &self.g
{
host_graphs.push(graph.clone());
}
GraphOrigin {
access: self.access.clone(),
host_graphs,
}
}
}