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::{buffer::ColumnBuffer, columns::Columns},
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			symbols: self.symbols,
98		};
99		let compiled = compile_expression(&compile_ctx, expr)?;
100
101		let base = EvalContext {
102			params: &Params::None,
103			symbols: self.symbols,
104			routines: &self.services.routines,
105			runtime_context: &self.services.runtime_context,
106			arena: None,
107			identity,
108			is_aggregate_context: false,
109			columns: Columns::empty(),
110			row_count: 1,
111			target: None,
112			take: None,
113		};
114		let eval_ctx = base.with_eval(columns.clone(), row_count);
115
116		let result = compiled.execute(&eval_ctx)?;
117
118		let denied = match result.data() {
119			ColumnBuffer::Bool(container) => {
120				(0..row_count).any(|i| !container.is_defined(i) || !container.data().get(i))
121			}
122			ColumnBuffer::Option {
123				inner,
124				bitvec,
125			} => match inner.as_ref() {
126				ColumnBuffer::Bool(container) => (0..row_count).any(|i| {
127					let defined = i < bitvec.len() && bitvec.get(i);
128					let valid = defined && container.is_defined(i);
129					!(valid && container.data().get(i))
130				}),
131				_ => true,
132			},
133			_ => true,
134		};
135
136		Ok(!denied)
137	}
138}