Skip to main content

reifydb_engine/policy/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4//! Engine-side policy enforcement. Wraps `reifydb-policy`'s evaluators with the call-sites the VM needs: write
5//! policy at commit boundaries, identity policy at session start, callable policy when invoking a routine. Read
6//! policy is injected into the plan before execution; this module handles the cases where injection isn't enough
7//! and the engine has to actively gate an operation.
8//!
9//! Anything that mutates state or transitions a session goes through these enforce calls. Bypassing them - even
10//! for a "trusted" code path inside the engine - means the matching policy never runs.
11
12use std::sync::Arc;
13
14use reifydb_core::{
15	interface::catalog::policy::{CallableOp, DataOp, PolicyTargetType, SessionOp},
16	value::column::{buffer::ColumnBuffer, columns::Columns},
17};
18use reifydb_policy::{
19	enforce::{PolicyTarget, enforce_identity_policy, enforce_session_policy, enforce_write_policies},
20	evaluate::PolicyEvaluator as PolicyEvaluatorTrait,
21};
22use reifydb_rql::expression::Expression;
23use reifydb_transaction::transaction::Transaction;
24use reifydb_type::{Result, params::Params, value::identity::IdentityId};
25
26use crate::{
27	expression::{
28		compile::compile_expression,
29		context::{CompileContext, EvalContext},
30	},
31	vm::{services::Services, stack::SymbolTable},
32};
33
34pub struct PolicyEvaluator<'a> {
35	services: &'a Arc<Services>,
36	symbols: &'a SymbolTable,
37}
38
39impl<'a> PolicyEvaluator<'a> {
40	pub fn new(services: &'a Arc<Services>, symbols: &'a SymbolTable) -> Self {
41		Self {
42			services,
43			symbols,
44		}
45	}
46
47	pub fn enforce_write_policies(
48		&self,
49		tx: &mut Transaction<'_>,
50		target_namespace: &str,
51		target_shape: &str,
52		operation: DataOp,
53		row_columns: &Columns,
54		target_type: PolicyTargetType,
55	) -> Result<()> {
56		let target = PolicyTarget {
57			namespace: target_namespace,
58			shape: target_shape,
59			operation: operation.as_str(),
60			target_type,
61		};
62		enforce_write_policies(&self.services.catalog, tx, &target, row_columns, self)
63	}
64
65	pub fn enforce_session_policy(
66		&self,
67		tx: &mut Transaction<'_>,
68		session_type: SessionOp,
69		default_deny: bool,
70	) -> Result<()> {
71		enforce_session_policy(&self.services.catalog, tx, session_type.as_str(), default_deny, self)
72	}
73
74	pub fn enforce_identity_policy(
75		&self,
76		tx: &mut Transaction<'_>,
77		target_namespace: &str,
78		target_shape: &str,
79		operation: CallableOp,
80		target_type: PolicyTargetType,
81	) -> Result<()> {
82		let target = PolicyTarget {
83			namespace: target_namespace,
84			shape: target_shape,
85			operation: operation.as_str(),
86			target_type,
87		};
88		enforce_identity_policy(&self.services.catalog, tx, &target, self)
89	}
90}
91
92impl PolicyEvaluatorTrait for PolicyEvaluator<'_> {
93	fn evaluate_condition(
94		&self,
95		expr: &Expression,
96		columns: &Columns,
97		row_count: usize,
98		identity: IdentityId,
99	) -> Result<bool> {
100		let compile_ctx = CompileContext {
101			symbols: self.symbols,
102		};
103		let compiled = compile_expression(&compile_ctx, expr)?;
104
105		let base = EvalContext {
106			params: &Params::None,
107			symbols: self.symbols,
108			routines: &self.services.routines,
109			runtime_context: &self.services.runtime_context,
110			arena: None,
111			identity,
112			is_aggregate_context: false,
113			columns: Columns::empty(),
114			row_count: 1,
115			target: None,
116			take: None,
117		};
118		let eval_ctx = base.with_eval(columns.clone(), row_count);
119
120		let result = compiled.execute(&eval_ctx)?;
121
122		let denied = match result.data() {
123			ColumnBuffer::Bool(container) => {
124				(0..row_count).any(|i| !container.is_defined(i) || !container.data().get(i))
125			}
126			ColumnBuffer::Option {
127				inner,
128				bitvec,
129			} => match inner.as_ref() {
130				ColumnBuffer::Bool(container) => (0..row_count).any(|i| {
131					let defined = i < bitvec.len() && bitvec.get(i);
132					let valid = defined && container.is_defined(i);
133					!(valid && container.data().get(i))
134				}),
135				_ => true,
136			},
137			_ => true,
138		};
139
140		Ok(!denied)
141	}
142}