1use std::collections::HashSet;
2
3use hashbrown::HashMap;
4
5use opcua_types::{
6 AttributeId, ContentFilter, ContentFilterElementResult, ContentFilterResult, ElementOperand,
7 EventFilter, EventFilterResult, FilterOperator, LiteralOperand, NodeClass, NodeId,
8 NumericRange, ObjectTypeId, Operand, QualifiedName, RelativePath, SimpleAttributeOperand,
9 StatusCode, UAString,
10};
11
12use crate::TypeTree;
13
14#[derive(Debug, Clone)]
15pub struct ParsedAttributeOperand {
17 pub node_id: NodeId,
19 pub alias: UAString,
21 pub browse_path: RelativePath,
23 pub attribute_id: AttributeId,
25 pub index_range: NumericRange,
27}
28
29#[derive(Debug, Clone)]
30pub struct ParsedSimpleAttributeOperand {
32 pub type_definition_id: NodeId,
34 pub browse_path: Vec<QualifiedName>,
36 pub attribute_id: AttributeId,
38 pub index_range: NumericRange,
40}
41
42#[derive(Debug, Clone)]
43pub enum ParsedOperand {
45 ElementOperand(ElementOperand),
47 LiteralOperand(LiteralOperand),
49 AttributeOperand(ParsedAttributeOperand),
51 SimpleAttributeOperand(ParsedSimpleAttributeOperand),
53}
54
55impl ParsedOperand {
56 pub(crate) fn parse(
57 operand: Operand,
58 num_elements: usize,
59 type_tree: &dyn TypeTree,
60 allow_attribute_operands: bool,
61 ) -> Result<Self, StatusCode> {
62 match operand {
63 Operand::ElementOperand(e) => {
64 if e.index as usize >= num_elements {
65 Err(StatusCode::BadFilterOperandInvalid)
66 } else {
67 Ok(Self::ElementOperand(e))
68 }
69 }
70 Operand::LiteralOperand(o) => Ok(Self::LiteralOperand(o)),
71 Operand::AttributeOperand(o) => {
72 if !allow_attribute_operands {
73 return Err(StatusCode::BadFilterOperandInvalid);
74 }
75 let attribute_id = AttributeId::from_u32(o.attribute_id)
76 .map_err(|_| StatusCode::BadAttributeIdInvalid)?;
77 Ok(Self::AttributeOperand(ParsedAttributeOperand {
78 node_id: o.node_id,
79 attribute_id,
80 alias: o.alias,
81 browse_path: o.browse_path,
82 index_range: o.index_range,
83 }))
84 }
85 Operand::SimpleAttributeOperand(o) => Ok(Self::SimpleAttributeOperand(
86 validate_select_clause(o, type_tree)?,
87 )),
88 }
89 }
90}
91
92#[derive(Debug, Clone)]
93pub struct ParsedEventFilter {
95 pub(super) content_filter: ParsedContentFilter,
96 pub(super) select_clauses: Vec<ParsedSimpleAttributeOperand>,
97}
98
99impl ParsedEventFilter {
100 pub fn new(
104 raw: EventFilter,
105 type_tree: &dyn TypeTree,
106 ) -> (EventFilterResult, Result<Self, StatusCode>) {
107 validate(raw, type_tree)
108 }
109}
110
111#[derive(Debug, Clone)]
112pub struct ParsedContentFilter {
114 pub(super) elements: Vec<ParsedContentFilterElement>,
115}
116
117impl ParsedContentFilter {
118 pub fn empty() -> Self {
120 Self {
121 elements: Vec::new(),
122 }
123 }
124
125 pub fn parse(
134 filter: ContentFilter,
135 type_tree: &dyn TypeTree,
136 allow_attribute_operands: bool,
137 banned_operators: &[FilterOperator],
138 ) -> (ContentFilterResult, Result<ParsedContentFilter, StatusCode>) {
139 validate_where_clause(
140 filter,
141 type_tree,
142 allow_attribute_operands,
143 banned_operators,
144 )
145 }
146}
147
148#[derive(Debug, Clone)]
149pub struct ParsedContentFilterElement {
151 pub(super) operator: FilterOperator,
152 pub(super) operands: Vec<ParsedOperand>,
153}
154
155fn validate(
157 event_filter: EventFilter,
158 type_tree: &dyn TypeTree,
159) -> (EventFilterResult, Result<ParsedEventFilter, StatusCode>) {
160 let num_select_clauses = event_filter
161 .select_clauses
162 .as_ref()
163 .map(|r| r.len())
164 .unwrap_or_default();
165 let mut select_clause_results = Vec::with_capacity(num_select_clauses);
166 let mut final_select_clauses = Vec::with_capacity(num_select_clauses);
167 for clause in event_filter.select_clauses.into_iter().flatten() {
168 match validate_select_clause(clause, type_tree) {
169 Ok(result) => {
170 select_clause_results.push(StatusCode::Good);
171 final_select_clauses.push(result);
172 }
173 Err(e) => select_clause_results.push(e),
174 }
175 }
176 let (where_clause_result, parsed_where_clause) = validate_where_clause(
177 event_filter.where_clause,
178 type_tree,
179 false,
180 &[FilterOperator::InView, FilterOperator::RelatedTo],
181 );
182
183 (
184 EventFilterResult {
185 select_clause_results: if select_clause_results.is_empty() {
186 None
187 } else {
188 Some(select_clause_results)
189 },
190 select_clause_diagnostic_infos: None,
191 where_clause_result,
192 },
193 parsed_where_clause.map(|f| ParsedEventFilter {
194 content_filter: f,
195 select_clauses: final_select_clauses,
196 }),
197 )
198}
199
200fn validate_select_clause(
201 clause: SimpleAttributeOperand,
202 type_tree: &dyn TypeTree,
203) -> Result<ParsedSimpleAttributeOperand, StatusCode> {
204 let Some(path) = clause.browse_path else {
205 return Err(StatusCode::BadNodeIdUnknown);
206 };
207
208 let Ok(attribute_id) = AttributeId::from_u32(clause.attribute_id) else {
209 return Err(StatusCode::BadAttributeIdInvalid);
210 };
211
212 if clause.type_definition_id == (0, ObjectTypeId::BaseEventType as u32) {
216 if attribute_id != AttributeId::NodeId && attribute_id != AttributeId::Value {
218 return Err(StatusCode::BadAttributeIdInvalid);
219 }
220 return Ok(ParsedSimpleAttributeOperand {
224 type_definition_id: clause.type_definition_id,
225 browse_path: path,
226 attribute_id,
227 index_range: clause.index_range,
228 });
229 }
230
231 let Some(node) = type_tree.find_type_prop_by_browse_path(&clause.type_definition_id, &path)
232 else {
233 return Err(StatusCode::BadNodeIdUnknown);
234 };
235
236 let is_valid = match node.node_class {
245 NodeClass::Object => attribute_id == AttributeId::NodeId,
246 NodeClass::Variable => attribute_id == AttributeId::Value,
247 _ => false,
248 };
249
250 if !is_valid {
251 Err(StatusCode::BadAttributeIdInvalid)
252 } else {
253 Ok(ParsedSimpleAttributeOperand {
254 type_definition_id: clause.type_definition_id,
255 browse_path: path,
256 attribute_id,
257 index_range: clause.index_range,
258 })
259 }
260}
261
262fn validate_where_clause(
263 where_clause: ContentFilter,
264 type_tree: &dyn TypeTree,
265 allow_attribute_operand: bool,
266 banned_operators: &[FilterOperator],
267) -> (ContentFilterResult, Result<ParsedContentFilter, StatusCode>) {
268 let Some(elements) = where_clause.elements else {
281 return (
282 ContentFilterResult {
283 element_results: None,
284 element_diagnostic_infos: None,
285 },
286 Ok(ParsedContentFilter::empty()),
287 );
288 };
289
290 let mut operand_refs: HashMap<usize, Vec<usize>> = HashMap::new();
291 let num_elements = elements.len();
292 let element_result_pairs: Vec<(
293 ContentFilterElementResult,
294 Option<ParsedContentFilterElement>,
295 )> = elements
296 .into_iter()
297 .enumerate()
298 .map(|(element_idx, e)| {
299 let Some(filter_operands) = e.filter_operands else {
300 return (
301 ContentFilterElementResult {
302 status_code: StatusCode::BadFilterOperandCountMismatch,
303 operand_status_codes: None,
304 operand_diagnostic_infos: None,
305 },
306 None,
307 );
308 };
309 let num_filter_operands = filter_operands.len();
310
311 let operand_count_mismatch = match e.filter_operator {
312 FilterOperator::Equals => filter_operands.len() != 2,
313 FilterOperator::IsNull => filter_operands.len() != 1,
314 FilterOperator::GreaterThan => filter_operands.len() != 2,
315 FilterOperator::LessThan => filter_operands.len() != 2,
316 FilterOperator::GreaterThanOrEqual => filter_operands.len() != 2,
317 FilterOperator::LessThanOrEqual => filter_operands.len() != 2,
318 FilterOperator::Like => filter_operands.len() != 2,
319 FilterOperator::Not => filter_operands.len() != 1,
320 FilterOperator::Between => filter_operands.len() != 3,
321 FilterOperator::InList => filter_operands.len() < 2, FilterOperator::And => filter_operands.len() != 2,
323 FilterOperator::Or => filter_operands.len() != 2,
324 FilterOperator::Cast => filter_operands.len() != 2,
325 FilterOperator::BitwiseAnd => filter_operands.len() != 2,
326 FilterOperator::BitwiseOr => filter_operands.len() != 2,
327 FilterOperator::InView => filter_operands.len() != 1,
328 FilterOperator::OfType => filter_operands.len() != 1,
329 FilterOperator::RelatedTo => filter_operands.len() != 6,
330 };
331
332 if banned_operators.contains(&e.filter_operator) {
333 return (
334 ContentFilterElementResult {
335 status_code: StatusCode::BadFilterOperatorUnsupported,
336 operand_status_codes: None,
337 operand_diagnostic_infos: None,
338 },
339 None,
340 );
341 }
342
343 let mut valid_operands = Vec::with_capacity(filter_operands.len());
344 let mut operand_status_codes = Vec::with_capacity(filter_operands.len());
345
346 let operand_results: Vec<_> = filter_operands
347 .into_iter()
348 .map(|e| {
349 let operand = <Operand>::try_from(e.clone())?;
350 ParsedOperand::parse(operand, num_elements, type_tree, allow_attribute_operand)
351 })
352 .collect();
353
354 for res in operand_results {
355 match res {
356 Ok(op) => {
357 operand_status_codes.push(StatusCode::Good);
358 if let ParsedOperand::ElementOperand(e) = &op {
359 operand_refs
360 .entry(element_idx)
361 .or_default()
362 .push(e.index as usize);
363 }
364 valid_operands.push(op);
365 }
366 Err(e) => operand_status_codes.push(e),
367 }
368 }
369 let operator_invalid = valid_operands.len() != num_filter_operands;
370
371 let status_code = if operand_count_mismatch {
373 StatusCode::BadFilterOperandCountMismatch
374 } else if operator_invalid {
375 StatusCode::BadFilterOperandInvalid
376 } else {
377 StatusCode::Good
378 };
379
380 let res = if status_code.is_good() {
381 Some(ParsedContentFilterElement {
382 operator: e.filter_operator,
383 operands: valid_operands,
384 })
385 } else {
386 None
387 };
388
389 (
390 ContentFilterElementResult {
391 status_code,
392 operand_status_codes: Some(operand_status_codes),
393 operand_diagnostic_infos: None,
394 },
395 res,
396 )
397 })
398 .collect();
399
400 let mut is_valid = true;
401 let mut valid_elements = Vec::with_capacity(num_elements);
402 let mut element_results = Vec::with_capacity(num_elements);
403 for (result, element) in element_result_pairs {
404 if let Some(element) = element {
405 valid_elements.push(element);
406 } else {
407 is_valid = false;
408 }
409 element_results.push(result);
410 }
411
412 let mut path = HashSet::new();
414 match has_cycles(&operand_refs, 0, &mut path) {
415 Ok(()) => (),
416 Err(()) => is_valid = false,
417 }
418
419 (
420 ContentFilterResult {
421 element_results: Some(element_results),
422 element_diagnostic_infos: None,
423 },
424 if is_valid {
425 Ok(ParsedContentFilter {
426 elements: valid_elements,
427 })
428 } else {
429 Err(StatusCode::BadEventFilterInvalid)
430 },
431 )
432}
433
434fn has_cycles(
435 children: &HashMap<usize, Vec<usize>>,
436 id: usize,
437 path: &mut HashSet<usize>,
438) -> Result<(), ()> {
439 let Some(child_refs) = children.get(&id) else {
440 return Ok(());
441 };
442 if !path.insert(id) {
443 return Err(());
444 }
445
446 for child in child_refs {
447 has_cycles(children, *child, path)?;
448 }
449
450 path.remove(&id);
451
452 Ok(())
453}
454
455#[cfg(test)]
456mod tests {
457 use crate::{events::validation::validate_where_clause, DefaultTypeTree};
458 use opcua_types::{
459 AttributeId, ContentFilter, ContentFilterElement, ContentFilterResult, FilterOperator,
460 NodeClass, NodeId, ObjectTypeId, Operand, SimpleAttributeOperand, StatusCode,
461 };
462
463 #[test]
464 fn test_validate_empty_where_clause() {
465 let type_tree = DefaultTypeTree::new();
466 let where_clause = ContentFilter { elements: None };
468 let (result, filter) = validate_where_clause(where_clause, &type_tree, false, &[]);
469 assert_eq!(
470 result,
471 ContentFilterResult {
472 element_results: None,
473 element_diagnostic_infos: None,
474 }
475 );
476 assert!(filter.is_ok());
477 }
478
479 #[test]
480 fn test_validate_operator_len() {
481 let type_tree = DefaultTypeTree::new();
482
483 let where_clause = ContentFilter {
486 elements: Some(vec![
487 ContentFilterElement::from((FilterOperator::Equals, vec![Operand::literal(10)])),
488 ContentFilterElement::from((FilterOperator::IsNull, vec![])),
489 ContentFilterElement::from((
490 FilterOperator::GreaterThan,
491 vec![Operand::literal(10)],
492 )),
493 ContentFilterElement::from((FilterOperator::LessThan, vec![Operand::literal(10)])),
494 ContentFilterElement::from((
495 FilterOperator::GreaterThanOrEqual,
496 vec![Operand::literal(10)],
497 )),
498 ContentFilterElement::from((
499 FilterOperator::LessThanOrEqual,
500 vec![Operand::literal(10)],
501 )),
502 ContentFilterElement::from((FilterOperator::Like, vec![Operand::literal(10)])),
503 ContentFilterElement::from((FilterOperator::Not, vec![])),
504 ContentFilterElement::from((
505 FilterOperator::Between,
506 vec![Operand::literal(10), Operand::literal(20)],
507 )),
508 ContentFilterElement::from((FilterOperator::InList, vec![Operand::literal(10)])),
509 ContentFilterElement::from((FilterOperator::And, vec![Operand::literal(10)])),
510 ContentFilterElement::from((FilterOperator::Or, vec![Operand::literal(10)])),
511 ContentFilterElement::from((FilterOperator::Cast, vec![Operand::literal(10)])),
512 ContentFilterElement::from((
513 FilterOperator::BitwiseAnd,
514 vec![Operand::literal(10)],
515 )),
516 ContentFilterElement::from((FilterOperator::BitwiseOr, vec![Operand::literal(10)])),
517 ContentFilterElement::from((FilterOperator::Like, vec![Operand::literal(10)])),
518 ]),
519 };
520 let (result, filter) = validate_where_clause(where_clause, &type_tree, false, &[]);
522 result
523 .element_results
524 .unwrap()
525 .iter()
526 .for_each(|e| assert_eq!(e.status_code, StatusCode::BadFilterOperandCountMismatch));
527 assert_eq!(filter.unwrap_err(), StatusCode::BadEventFilterInvalid);
528 }
529
530 #[test]
531 fn test_validate_bad_filter_operand() {
532 let type_tree = DefaultTypeTree::new();
533
534 use opcua_types::{ContentFilterElement, ExtensionObject};
536 let bad_operator = ExtensionObject::null();
537 let where_clause = ContentFilter {
538 elements: Some(vec![ContentFilterElement {
539 filter_operator: FilterOperator::IsNull,
540 filter_operands: Some(vec![bad_operator]),
541 }]),
542 };
543 let (result, filter) = validate_where_clause(where_clause, &type_tree, false, &[]);
544 let element_results = result.element_results.unwrap();
545 assert_eq!(element_results.len(), 1);
546 assert_eq!(
547 element_results[0].status_code,
548 StatusCode::BadFilterOperandInvalid
549 );
550 let err = filter.unwrap_err();
551 assert_eq!(err, StatusCode::BadEventFilterInvalid);
552 }
553
554 #[test]
555 fn test_validate_select_operands() {
556 let mut type_tree = DefaultTypeTree::new();
557
558 type_tree.add_type_node(
559 &NodeId::new(1, "event"),
560 &ObjectTypeId::BaseEventType.into(),
561 NodeClass::ObjectType,
562 );
563 type_tree.add_type_property(
564 &NodeId::new(1, "prop"),
565 &NodeId::new(1, "event"),
566 &[&"Prop".into()],
567 NodeClass::Variable,
568 );
569
570 let where_clause = ContentFilter {
572 elements: Some(vec![
573 ContentFilterElement::from((
574 FilterOperator::IsNull,
575 vec![Operand::SimpleAttributeOperand(SimpleAttributeOperand {
576 type_definition_id: NodeId::new(1, "event"),
577 browse_path: Some(vec!["Prop".into()]),
578 attribute_id: AttributeId::Value as u32,
579 index_range: Default::default(),
580 })],
581 )),
582 ContentFilterElement::from((
583 FilterOperator::IsNull,
584 vec![Operand::SimpleAttributeOperand(SimpleAttributeOperand {
585 type_definition_id: NodeId::new(1, "event"),
586 browse_path: Some(vec!["Prop2".into()]),
587 attribute_id: AttributeId::Value as u32,
588 index_range: Default::default(),
589 })],
590 )),
591 ]),
592 };
593
594 let (result, filter) = validate_where_clause(where_clause, &type_tree, false, &[]);
595 let element_results = result.element_results.unwrap();
596 assert_eq!(element_results.len(), 2);
597 assert_eq!(element_results[0].status_code, StatusCode::Good);
598 assert_eq!(
599 element_results[1].status_code,
600 StatusCode::BadFilterOperandInvalid
601 );
602 let status_codes = element_results[1].operand_status_codes.as_ref().unwrap();
603 assert_eq!(status_codes.len(), 1);
604 assert_eq!(status_codes[0], StatusCode::BadNodeIdUnknown);
605 assert_eq!(filter.unwrap_err(), StatusCode::BadEventFilterInvalid);
606 }
607
608 #[test]
609 fn test_validate_circular_filter() {
610 let type_tree = DefaultTypeTree::new();
611
612 let where_clause = ContentFilter {
613 elements: Some(vec![
614 ContentFilterElement::from((
615 FilterOperator::And,
616 vec![Operand::element(1), Operand::element(2)],
617 )),
618 ContentFilterElement::from((FilterOperator::IsNull, vec![Operand::literal(10)])),
619 ContentFilterElement::from((
620 FilterOperator::Or,
621 vec![Operand::element(1), Operand::element(3)],
622 )),
623 ContentFilterElement::from((FilterOperator::Not, vec![Operand::element(0)])),
624 ]),
625 };
626
627 let (_result, filter) = validate_where_clause(where_clause, &type_tree, false, &[]);
628 assert_eq!(filter.unwrap_err(), StatusCode::BadEventFilterInvalid);
629 }
630}