Skip to main content

reifydb_engine/policy/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use std::sync::Arc;
5
6use arrow_array::RecordBatch;
7use reifydb_core::{
8	expression::Expression,
9	interface::catalog::policy::{CallableOp, DataOp, PolicyTargetType, SessionOp},
10	value::batch::empty_batch,
11};
12use reifydb_evaluate::{expression::context::EvalContext, lower::LoweredExpr, stack::SymbolTable};
13use reifydb_policy::{
14	enforce::{
15		CompiledWritePolicies, PolicyTarget, compile_write_policies, enforce_identity_policy,
16		enforce_session_policy, enforce_write_policies,
17	},
18	evaluate::PolicyEvaluator as PolicyEvaluatorTrait,
19};
20use reifydb_transaction::transaction::Transaction;
21use reifydb_value::{
22	Result,
23	params::Params,
24	value::{
25		column_view::{ColumnView, ViewData},
26		identity::IdentityId,
27	},
28};
29
30use crate::vm::services::Services;
31
32pub struct PolicyEvaluator<'a> {
33	services: &'a Arc<Services>,
34	symbols: &'a SymbolTable,
35}
36
37impl<'a> PolicyEvaluator<'a> {
38	pub fn new(services: &'a Arc<Services>, symbols: &'a SymbolTable) -> Self {
39		Self {
40			services,
41			symbols,
42		}
43	}
44
45	pub fn enforce_write_policies(
46		&self,
47		tx: &mut Transaction<'_>,
48		target_namespace: &str,
49		target_object: &str,
50		operation: DataOp,
51		row_columns: &RecordBatch,
52		target_type: PolicyTargetType,
53	) -> Result<()> {
54		let target = PolicyTarget {
55			namespace: target_namespace,
56			object: target_object,
57			operation: operation.as_str(),
58			target_type,
59		};
60		enforce_write_policies(&self.services.catalog, tx, &target, row_columns, self)
61	}
62
63	pub fn compile_write_policies(
64		&self,
65		tx: &mut Transaction<'_>,
66		target_namespace: &str,
67		target_object: &str,
68		operation: DataOp,
69		target_type: PolicyTargetType,
70	) -> Result<Option<CompiledWritePolicies>> {
71		let target = PolicyTarget {
72			namespace: target_namespace,
73			object: target_object,
74			operation: operation.as_str(),
75			target_type,
76		};
77		compile_write_policies(&self.services.catalog, tx, &target)
78	}
79
80	pub fn enforce_compiled(
81		&self,
82		compiled: &CompiledWritePolicies,
83		identity: IdentityId,
84		row_columns: &RecordBatch,
85	) -> Result<()> {
86		compiled.enforce(row_columns, identity, self)
87	}
88
89	pub fn enforce_compiled_row_dependent(
90		&self,
91		compiled: &CompiledWritePolicies,
92		identity: IdentityId,
93		row_columns: &RecordBatch,
94	) -> Result<()> {
95		compiled.enforce_row_dependent(row_columns, identity, self)
96	}
97
98	pub fn enforce_session_policy(
99		&self,
100		tx: &mut Transaction<'_>,
101		session_type: SessionOp,
102		default_deny: bool,
103	) -> Result<()> {
104		enforce_session_policy(&self.services.catalog, tx, session_type.as_str(), default_deny, self)
105	}
106
107	pub fn enforce_identity_policy(
108		&self,
109		tx: &mut Transaction<'_>,
110		target_namespace: &str,
111		target_object: &str,
112		operation: CallableOp,
113		target_type: PolicyTargetType,
114	) -> Result<()> {
115		let target = PolicyTarget {
116			namespace: target_namespace,
117			object: target_object,
118			operation: operation.as_str(),
119			target_type,
120		};
121		enforce_identity_policy(&self.services.catalog, tx, &target, self)
122	}
123}
124
125impl PolicyEvaluatorTrait for PolicyEvaluator<'_> {
126	fn evaluate_condition(
127		&self,
128		expr: &Expression,
129		batch: &RecordBatch,
130		row_count: usize,
131		identity: IdentityId,
132	) -> Result<bool> {
133		let lowered = LoweredExpr::new(expr.clone(), "policy");
134
135		let base = EvalContext {
136			params: &Params::None,
137			symbols: self.symbols,
138			routines: &self.services.routines,
139			runtime_context: &self.services.runtime_context,
140			identity,
141			batch: empty_batch(),
142			row_count: 1,
143			target: None,
144			take: None,
145		};
146		let eval_ctx = base.with_eval(batch.clone(), row_count);
147
148		let result = lowered.evaluate(&eval_ctx)?;
149		let view = ColumnView::try_from(&result)?;
150
151		let denied = match &view.data {
152			ViewData::Bool(container) => {
153				(0..row_count).any(|i| !(view.is_defined(i) && container.value(i)))
154			}
155			_ => true,
156		};
157
158		Ok(!denied)
159	}
160}