Skip to main content

reifydb_engine/policy/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use std::sync::Arc;
5
6use reifydb_core::{
7	interface::catalog::policy::{CallableOp, DataOp, PolicyTargetType, SessionOp},
8	value::column::{columns::Columns, data::ColumnData},
9};
10use reifydb_policy::{
11	enforce::{PolicyTarget, enforce_identity_policy, enforce_session_policy, enforce_write_policies},
12	evaluate::PolicyEvaluator as PolicyEvaluatorTrait,
13};
14use reifydb_rql::expression::Expression;
15use reifydb_transaction::transaction::Transaction;
16use reifydb_type::{Result, params::Params, value::identity::IdentityId};
17
18use crate::{
19	expression::{
20		compile::compile_expression,
21		context::{CompileContext, EvalContext},
22	},
23	vm::{services::Services, stack::SymbolTable},
24};
25
26/// Engine-side implementation of the policy evaluator trait.
27///
28/// Holds references to `Services` (for functions/clock) and `SymbolTable`
29/// (for variable resolution), and compiles+evaluates RQL expressions.
30pub struct PolicyEvaluator<'a> {
31	services: &'a Arc<Services>,
32	symbols: &'a SymbolTable,
33}
34
35impl<'a> PolicyEvaluator<'a> {
36	pub fn new(services: &'a Arc<Services>, symbols: &'a SymbolTable) -> Self {
37		Self {
38			services,
39			symbols,
40		}
41	}
42
43	pub fn enforce_write_policies(
44		&self,
45		tx: &mut Transaction<'_>,
46		target_namespace: &str,
47		target_shape: &str,
48		operation: DataOp,
49		row_columns: &Columns,
50		target_type: PolicyTargetType,
51	) -> Result<()> {
52		let target = PolicyTarget {
53			namespace: target_namespace,
54			shape: target_shape,
55			operation: operation.as_str(),
56			target_type,
57		};
58		enforce_write_policies(&self.services.catalog, tx, &target, row_columns, self)
59	}
60
61	pub fn enforce_session_policy(
62		&self,
63		tx: &mut Transaction<'_>,
64		session_type: SessionOp,
65		default_deny: bool,
66	) -> Result<()> {
67		enforce_session_policy(&self.services.catalog, tx, session_type.as_str(), default_deny, self)
68	}
69
70	pub fn enforce_identity_policy(
71		&self,
72		tx: &mut Transaction<'_>,
73		target_namespace: &str,
74		target_shape: &str,
75		operation: CallableOp,
76		target_type: PolicyTargetType,
77	) -> Result<()> {
78		let target = PolicyTarget {
79			namespace: target_namespace,
80			shape: target_shape,
81			operation: operation.as_str(),
82			target_type,
83		};
84		enforce_identity_policy(&self.services.catalog, tx, &target, self)
85	}
86}
87
88impl PolicyEvaluatorTrait for PolicyEvaluator<'_> {
89	fn evaluate_condition(
90		&self,
91		expr: &Expression,
92		columns: &Columns,
93		row_count: usize,
94		identity: IdentityId,
95	) -> Result<bool> {
96		let compile_ctx = CompileContext {
97			functions: &self.services.functions,
98			symbols: self.symbols,
99		};
100		let compiled = compile_expression(&compile_ctx, expr)?;
101
102		let base = EvalContext {
103			params: &Params::None,
104			symbols: self.symbols,
105			functions: &self.services.functions,
106			runtime_context: &self.services.runtime_context,
107			arena: None,
108			identity,
109			is_aggregate_context: false,
110			columns: Columns::empty(),
111			row_count: 1,
112			target: None,
113			take: None,
114		};
115		let eval_ctx = base.with_eval(columns.clone(), row_count);
116
117		let result = compiled.execute(&eval_ctx)?;
118
119		let denied = match result.data() {
120			ColumnData::Bool(container) => {
121				(0..row_count).any(|i| !container.is_defined(i) || !container.data().get(i))
122			}
123			ColumnData::Option {
124				inner,
125				bitvec,
126			} => match inner.as_ref() {
127				ColumnData::Bool(container) => (0..row_count).any(|i| {
128					let defined = i < bitvec.len() && bitvec.get(i);
129					let valid = defined && container.is_defined(i);
130					!(valid && container.data().get(i))
131				}),
132				_ => true,
133			},
134			_ => true,
135		};
136
137		Ok(!denied)
138	}
139}