Skip to main content

reifydb_engine/policy/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4//! Read policy is injected into the plan before execution; this module covers the cases injection cannot reach,
5//! where the engine has to actively gate the operation. Anything that mutates state or transitions a session
6//! goes through these enforce calls, and a "trusted" path that skips them runs no policy at all.
7
8use std::sync::Arc;
9
10use reifydb_core::{
11	interface::catalog::policy::{CallableOp, DataOp, PolicyTargetType, SessionOp},
12	value::column::{buffer::ColumnBuffer, columns::Columns},
13};
14use reifydb_evaluate::{
15	expression::{
16		compile::compile_expression,
17		context::{CompileContext, EvalContext},
18	},
19	stack::SymbolTable,
20};
21use reifydb_policy::{
22	enforce::{PolicyTarget, enforce_identity_policy, enforce_session_policy, enforce_write_policies},
23	evaluate::PolicyEvaluator as PolicyEvaluatorTrait,
24};
25use reifydb_rql::expression::Expression;
26use reifydb_transaction::transaction::Transaction;
27use reifydb_value::{Result, params::Params, value::identity::IdentityId};
28
29use crate::vm::services::Services;
30
31pub struct PolicyEvaluator<'a> {
32	services: &'a Arc<Services>,
33	symbols: &'a SymbolTable,
34}
35
36impl<'a> PolicyEvaluator<'a> {
37	pub fn new(services: &'a Arc<Services>, symbols: &'a SymbolTable) -> Self {
38		Self {
39			services,
40			symbols,
41		}
42	}
43
44	pub fn enforce_write_policies(
45		&self,
46		tx: &mut Transaction<'_>,
47		target_namespace: &str,
48		target_object: &str,
49		operation: DataOp,
50		row_columns: &Columns,
51		target_type: PolicyTargetType,
52	) -> Result<()> {
53		let target = PolicyTarget {
54			namespace: target_namespace,
55			object: target_object,
56			operation: operation.as_str(),
57			target_type,
58		};
59		enforce_write_policies(&self.services.catalog, tx, &target, row_columns, self)
60	}
61
62	pub fn enforce_session_policy(
63		&self,
64		tx: &mut Transaction<'_>,
65		session_type: SessionOp,
66		default_deny: bool,
67	) -> Result<()> {
68		enforce_session_policy(&self.services.catalog, tx, session_type.as_str(), default_deny, self)
69	}
70
71	pub fn enforce_identity_policy(
72		&self,
73		tx: &mut Transaction<'_>,
74		target_namespace: &str,
75		target_object: &str,
76		operation: CallableOp,
77		target_type: PolicyTargetType,
78	) -> Result<()> {
79		let target = PolicyTarget {
80			namespace: target_namespace,
81			object: target_object,
82			operation: operation.as_str(),
83			target_type,
84		};
85		enforce_identity_policy(&self.services.catalog, tx, &target, self)
86	}
87}
88
89impl PolicyEvaluatorTrait for PolicyEvaluator<'_> {
90	fn evaluate_condition(
91		&self,
92		expr: &Expression,
93		columns: &Columns,
94		row_count: usize,
95		identity: IdentityId,
96	) -> Result<bool> {
97		let compile_ctx = CompileContext {
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			routines: &self.services.routines,
106			runtime_context: &self.services.runtime_context,
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}