logicaffeine_compile/semantics/
policy.rs1use crate::analysis::{PolicyCondition, PolicyRegistry};
4use crate::intern::{Interner, Symbol};
5use crate::interpreter::RuntimeValue;
6
7use super::compare::values_equal;
8
9pub fn evaluate_policy_condition(
11 registry: Option<&PolicyRegistry>,
12 interner: &Interner,
13 condition: &PolicyCondition,
14 subject: &RuntimeValue,
15 object: Option<&RuntimeValue>,
16) -> bool {
17 match condition {
18 PolicyCondition::FieldEquals { field, value, is_string_literal } => {
19 if let RuntimeValue::Struct(s) = subject {
20 let field_name = interner.resolve(*field);
21 if let Some(field_val) = s.fields.get(field_name) {
22 let expected = interner.resolve(*value);
23 match field_val {
24 RuntimeValue::Text(s) => s.as_str() == expected,
25 RuntimeValue::Int(n) => {
26 if *is_string_literal {
27 false
28 } else {
29 expected.parse::<i64>().map(|e| *n == e).unwrap_or(false)
30 }
31 }
32 RuntimeValue::Bool(b) => {
33 if *is_string_literal {
34 false
35 } else {
36 expected.parse::<bool>().map(|e| *b == e).unwrap_or(false)
37 }
38 }
39 _ => false,
40 }
41 } else {
42 false
43 }
44 } else {
45 false
46 }
47 }
48 PolicyCondition::FieldBool { field, value } => {
49 if let RuntimeValue::Struct(s) = subject {
50 let field_name = interner.resolve(*field);
51 if let Some(RuntimeValue::Bool(b)) = s.fields.get(field_name) {
52 *b == *value
53 } else {
54 false
55 }
56 } else {
57 false
58 }
59 }
60 PolicyCondition::Predicate { predicate, .. } => {
61 if let Some(registry) = registry {
62 if let RuntimeValue::Struct(s) = subject {
63 if let Some(subj_type_sym) = interner.lookup(&s.type_name) {
64 if let Some(preds) = registry.get_predicates(subj_type_sym) {
65 if let Some(pred) =
66 preds.iter().find(|p| p.predicate_name == *predicate)
67 {
68 return evaluate_policy_condition(
69 Some(registry),
70 interner,
71 &pred.condition,
72 subject,
73 object,
74 );
75 }
76 }
77 }
78 }
79 }
80 false
81 }
82 PolicyCondition::ObjectFieldEquals { subject: subj_field, object: obj_sym, field } => {
83 let obj = match object {
84 Some(o) => o,
85 None => return false,
86 };
87 if let (RuntimeValue::Struct(subj_s), RuntimeValue::Struct(obj_s)) = (subject, obj) {
88 let subj_field_name = interner.resolve(*subj_field);
89 let obj_field_name = interner.resolve(*field);
90 if let (Some(subj_val), Some(obj_val)) =
91 (subj_s.fields.get(subj_field_name), obj_s.fields.get(obj_field_name))
92 {
93 values_equal(subj_val, obj_val)
94 } else {
95 let _obj_sym_name = interner.resolve(*obj_sym);
96 false
97 }
98 } else {
99 false
100 }
101 }
102 PolicyCondition::SubjectFieldEqualsObjectField { subject_field, object: _, object_field } => {
103 let obj = match object {
104 Some(o) => o,
105 None => return false,
106 };
107 if let (RuntimeValue::Struct(subj_s), RuntimeValue::Struct(obj_s)) = (subject, obj) {
108 let subj_field_name = interner.resolve(*subject_field);
109 let obj_field_name = interner.resolve(*object_field);
110 if let (Some(a), Some(b)) =
111 (subj_s.fields.get(subj_field_name), obj_s.fields.get(obj_field_name))
112 {
113 values_equal(a, b)
114 } else {
115 false
116 }
117 } else {
118 false
119 }
120 }
121 PolicyCondition::Or(left, right) => {
122 evaluate_policy_condition(registry, interner, left, subject, object)
123 || evaluate_policy_condition(registry, interner, right, subject, object)
124 }
125 PolicyCondition::And(left, right) => {
126 evaluate_policy_condition(registry, interner, left, subject, object)
127 && evaluate_policy_condition(registry, interner, right, subject, object)
128 }
129 }
130}
131
132pub fn check_policy(
135 registry: &PolicyRegistry,
136 interner: &Interner,
137 subject: &RuntimeValue,
138 predicate: Symbol,
139 is_capability: bool,
140 object: Option<&RuntimeValue>,
141 source_text: &str,
142) -> Result<(), String> {
143 let subj_type_name = match subject {
144 RuntimeValue::Struct(s) => s.type_name.clone(),
145 _ => {
146 return Err(format!(
147 "Check subject must be a struct, got {}",
148 subject.type_name()
149 ));
150 }
151 };
152
153 let subj_type_sym = match interner.lookup(&subj_type_name) {
154 Some(sym) => sym,
155 None => {
156 return Err(format!("Unknown type '{}' in Check statement", subj_type_name));
157 }
158 };
159
160 let passed = if is_capability {
161 let caps = registry.get_capabilities(subj_type_sym);
162 let cap = caps.and_then(|caps| caps.iter().find(|c| c.action == predicate));
163 match cap {
164 Some(cap) => evaluate_policy_condition(
165 Some(registry),
166 interner,
167 &cap.condition,
168 subject,
169 object,
170 ),
171 None => {
172 let pred_name = interner.resolve(predicate);
173 return Err(format!(
174 "No capability '{}' defined for type '{}'",
175 pred_name, subj_type_name
176 ));
177 }
178 }
179 } else {
180 let preds = registry.get_predicates(subj_type_sym);
181 let pred_def = preds.and_then(|preds| preds.iter().find(|p| p.predicate_name == predicate));
182 match pred_def {
183 Some(pred) => evaluate_policy_condition(
184 Some(registry),
185 interner,
186 &pred.condition,
187 subject,
188 None,
189 ),
190 None => {
191 let pred_name = interner.resolve(predicate);
192 return Err(format!(
193 "No predicate '{}' defined for type '{}'",
194 pred_name, subj_type_name
195 ));
196 }
197 }
198 };
199
200 if !passed {
201 return Err(format!("Security Check Failed: {}", source_text));
202 }
203 Ok(())
204}