kanidmd_lib/
filter.rs

1//! [`Filter`]s are one of the three foundational concepts of the design in kanidm.
2//! They are used in nearly every aspect of the server to provide searching of
3//! datasets and assertion of entry properties.
4//!
5//! A filter is a logical statement of properties that an [`Entry`] and its
6//! AVAs must uphold to be considered true.
7//!
8//! [`Filter`]: struct.Filter.html
9//! [`Entry`]: ../entry/struct.Entry.html
10
11use std::cmp::{Ordering, PartialOrd};
12use std::collections::BTreeSet;
13use std::fmt;
14use std::hash::Hash;
15use std::iter;
16use std::num::NonZeroU8;
17use std::str::FromStr;
18use std::sync::Arc;
19
20use concread::arcache::{ARCache, ARCacheReadTxn};
21use hashbrown::HashMap;
22#[cfg(test)]
23use hashbrown::HashSet;
24use kanidm_proto::constants::ATTR_UUID;
25use kanidm_proto::internal::{Filter as ProtoFilter, OperationError, SchemaError};
26use kanidm_proto::scim_v1::client::{AttrPath as ScimAttrPath, ScimFilter};
27use ldap3_proto::proto::{LdapFilter, LdapSubstringFilter};
28use serde::Deserialize;
29use uuid::Uuid;
30
31use crate::be::{IdxKey, IdxKeyRef, IdxKeyToRef, IdxMeta, IdxSlope};
32use crate::idm::ldap::ldap_attr_filter_map;
33use crate::prelude::*;
34use crate::schema::SchemaTransaction;
35use crate::value::{IndexType, PartialValue};
36
37pub type ResolveFilterCache =
38    ARCache<(IdentityId, Arc<Filter<FilterValid>>), Arc<Filter<FilterValidResolved>>>;
39
40pub type ResolveFilterCacheReadTxn<'a> = ARCacheReadTxn<
41    'a,
42    (IdentityId, Arc<Filter<FilterValid>>),
43    Arc<Filter<FilterValidResolved>>,
44    (),
45>;
46
47// Default filter is safe, ignores all hidden types!
48
49// This is &Value so we can lazy const then clone, but perhaps we can reconsider
50// later if this should just take Value.
51pub fn f_eq(a: Attribute, v: PartialValue) -> FC {
52    FC::Eq(a, v)
53}
54
55pub fn f_sub(a: Attribute, v: PartialValue) -> FC {
56    FC::Cnt(a, v)
57}
58
59pub fn f_pres(a: Attribute) -> FC {
60    FC::Pres(a)
61}
62
63pub fn f_lt(a: Attribute, v: PartialValue) -> FC {
64    FC::LessThan(a, v)
65}
66
67pub fn f_or(vs: Vec<FC>) -> FC {
68    FC::Or(vs)
69}
70
71pub fn f_and(vs: Vec<FC>) -> FC {
72    FC::And(vs)
73}
74
75pub fn f_inc(vs: Vec<FC>) -> FC {
76    FC::Inclusion(vs)
77}
78
79pub fn f_andnot(fc: FC) -> FC {
80    FC::AndNot(Box::new(fc))
81}
82
83pub fn f_self() -> FC {
84    FC::SelfUuid
85}
86
87pub fn f_invalid(a: Attribute) -> FC {
88    FC::Invalid(a)
89}
90
91pub fn f_id(uuid: &str) -> FC {
92    let uf = Uuid::parse_str(uuid)
93        .ok()
94        .map(|u| FC::Eq(Attribute::Uuid, PartialValue::Uuid(u)));
95    let spnf = PartialValue::new_spn_s(uuid).map(|spn| FC::Eq(Attribute::Spn, spn));
96    let nf = FC::Eq(Attribute::Name, PartialValue::new_iname(uuid));
97    let f: Vec<_> = iter::once(uf)
98        .chain(iter::once(spnf))
99        .flatten()
100        .chain(iter::once(nf))
101        .collect();
102    FC::Or(f)
103}
104
105pub fn f_spn_name(id: &str) -> FC {
106    let spnf = PartialValue::new_spn_s(id).map(|spn| FC::Eq(Attribute::Spn, spn));
107    let nf = FC::Eq(Attribute::Name, PartialValue::new_iname(id));
108    let f: Vec<_> = iter::once(spnf).flatten().chain(iter::once(nf)).collect();
109    FC::Or(f)
110}
111
112/// This is the short-form for tests and internal filters that can then
113/// be transformed into a filter for the server to use.
114#[derive(Clone, Debug, Deserialize)]
115pub enum FC {
116    Eq(Attribute, PartialValue),
117    Cnt(Attribute, PartialValue),
118    Pres(Attribute),
119    LessThan(Attribute, PartialValue),
120    Or(Vec<FC>),
121    And(Vec<FC>),
122    Inclusion(Vec<FC>),
123    AndNot(Box<FC>),
124    SelfUuid,
125    Invalid(Attribute),
126    // Not(Box<FC>),
127}
128
129/// This is the filters internal representation
130#[derive(Clone, Hash, PartialEq, PartialOrd, Ord, Eq)]
131enum FilterComp {
132    // This is attr - value
133    Eq(Attribute, PartialValue),
134    Cnt(Attribute, PartialValue),
135    Stw(Attribute, PartialValue),
136    Enw(Attribute, PartialValue),
137    Pres(Attribute),
138    LessThan(Attribute, PartialValue),
139    Or(Vec<FilterComp>),
140    And(Vec<FilterComp>),
141    Inclusion(Vec<FilterComp>),
142    AndNot(Box<FilterComp>),
143    SelfUuid,
144    Invalid(Attribute),
145    // Does this mean we can add a true not to the type now?
146    // Not(Box<FilterComp>),
147}
148
149impl fmt::Debug for FilterComp {
150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151        match self {
152            FilterComp::Eq(attr, pv) => {
153                write!(f, "{} eq {:?}", attr, pv)
154            }
155            FilterComp::Cnt(attr, pv) => {
156                write!(f, "{} cnt {:?}", attr, pv)
157            }
158            FilterComp::Stw(attr, pv) => {
159                write!(f, "{} stw {:?}", attr, pv)
160            }
161            FilterComp::Enw(attr, pv) => {
162                write!(f, "{} enw {:?}", attr, pv)
163            }
164            FilterComp::Pres(attr) => {
165                write!(f, "{} pres", attr)
166            }
167            FilterComp::LessThan(attr, pv) => {
168                write!(f, "{} lt {:?}", attr, pv)
169            }
170            FilterComp::And(list) => {
171                write!(f, "(")?;
172                for (i, fc) in list.iter().enumerate() {
173                    write!(f, "{:?}", fc)?;
174                    if i != list.len() - 1 {
175                        write!(f, " and ")?;
176                    }
177                }
178                write!(f, ")")
179            }
180            FilterComp::Or(list) => {
181                write!(f, "(")?;
182                for (i, fc) in list.iter().enumerate() {
183                    write!(f, "{:?}", fc)?;
184                    if i != list.len() - 1 {
185                        write!(f, " or ")?;
186                    }
187                }
188                write!(f, ")")
189            }
190            FilterComp::Inclusion(list) => {
191                write!(f, "(")?;
192                for (i, fc) in list.iter().enumerate() {
193                    write!(f, "{:?}", fc)?;
194                    if i != list.len() - 1 {
195                        write!(f, " inc ")?;
196                    }
197                }
198                write!(f, ")")
199            }
200            FilterComp::AndNot(inner) => {
201                write!(f, "not ( {:?} )", inner)
202            }
203            FilterComp::SelfUuid => {
204                write!(f, "uuid eq self")
205            }
206            FilterComp::Invalid(attr) => {
207                write!(f, "invalid ( {:?} )", attr)
208            }
209        }
210    }
211}
212
213/// This is the fully resolved internal representation. Note the lack of Not and selfUUID
214/// because these are resolved into And(Pres(class), AndNot(term)) and Eq(uuid, ...) respectively.
215/// Importantly, we make this accessible to Entry so that it can then match on filters
216/// internally.
217///
218/// Each filter that has been resolved also has been enriched with metadata about its
219/// index, and index slope. For the purpose of this module, consider slope as a "weight"
220/// where small value - faster index, larger value - slower index. This metadata is extremely
221/// important for the query optimiser to make decisions about how to re-arrange queries
222/// correctly.
223#[derive(Clone, Eq)]
224pub enum FilterResolved {
225    // This is attr - value - indexed slope factor
226    Eq(Attribute, PartialValue, Option<NonZeroU8>),
227    Cnt(Attribute, PartialValue, Option<NonZeroU8>),
228    Stw(Attribute, PartialValue, Option<NonZeroU8>),
229    Enw(Attribute, PartialValue, Option<NonZeroU8>),
230    Pres(Attribute, Option<NonZeroU8>),
231    LessThan(Attribute, PartialValue, Option<NonZeroU8>),
232    Or(Vec<FilterResolved>, Option<NonZeroU8>),
233    And(Vec<FilterResolved>, Option<NonZeroU8>),
234    Invalid(Attribute),
235    // All terms must have 1 or more items, or the inclusion is false!
236    Inclusion(Vec<FilterResolved>, Option<NonZeroU8>),
237    AndNot(Box<FilterResolved>, Option<NonZeroU8>),
238}
239
240impl fmt::Debug for FilterResolved {
241    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242        match self {
243            FilterResolved::Eq(attr, pv, idx) => {
244                write!(
245                    f,
246                    "(s{} {} eq {:?})",
247                    idx.unwrap_or(NonZeroU8::MAX),
248                    attr,
249                    pv
250                )
251            }
252            FilterResolved::Cnt(attr, pv, idx) => {
253                write!(
254                    f,
255                    "(s{} {} cnt {:?})",
256                    idx.unwrap_or(NonZeroU8::MAX),
257                    attr,
258                    pv
259                )
260            }
261            FilterResolved::Stw(attr, pv, idx) => {
262                write!(
263                    f,
264                    "(s{} {} stw {:?})",
265                    idx.unwrap_or(NonZeroU8::MAX),
266                    attr,
267                    pv
268                )
269            }
270            FilterResolved::Enw(attr, pv, idx) => {
271                write!(
272                    f,
273                    "(s{} {} enw {:?})",
274                    idx.unwrap_or(NonZeroU8::MAX),
275                    attr,
276                    pv
277                )
278            }
279            FilterResolved::Pres(attr, idx) => {
280                write!(f, "(s{} {} pres)", idx.unwrap_or(NonZeroU8::MAX), attr)
281            }
282            FilterResolved::LessThan(attr, pv, idx) => {
283                write!(
284                    f,
285                    "(s{} {} lt {:?})",
286                    idx.unwrap_or(NonZeroU8::MAX),
287                    attr,
288                    pv
289                )
290            }
291            FilterResolved::And(list, idx) => {
292                write!(f, "(s{} ", idx.unwrap_or(NonZeroU8::MAX))?;
293                for (i, fc) in list.iter().enumerate() {
294                    write!(f, "{:?}", fc)?;
295                    if i != list.len() - 1 {
296                        write!(f, " and ")?;
297                    }
298                }
299                write!(f, ")")
300            }
301            FilterResolved::Or(list, idx) => {
302                write!(f, "(s{} ", idx.unwrap_or(NonZeroU8::MAX))?;
303                for (i, fc) in list.iter().enumerate() {
304                    write!(f, "{:?}", fc)?;
305                    if i != list.len() - 1 {
306                        write!(f, " or ")?;
307                    }
308                }
309                write!(f, ")")
310            }
311            FilterResolved::Inclusion(list, idx) => {
312                write!(f, "(s{} ", idx.unwrap_or(NonZeroU8::MAX))?;
313                for (i, fc) in list.iter().enumerate() {
314                    write!(f, "{:?}", fc)?;
315                    if i != list.len() - 1 {
316                        write!(f, " inc ")?;
317                    }
318                }
319                write!(f, ")")
320            }
321            FilterResolved::AndNot(inner, idx) => {
322                write!(f, "not (s{} {:?})", idx.unwrap_or(NonZeroU8::MAX), inner)
323            }
324            FilterResolved::Invalid(attr) => {
325                write!(f, "{} inv", attr)
326            }
327        }
328    }
329}
330
331#[derive(Debug, Clone, PartialEq, Eq)]
332/// A filter before it's gone through schema validation
333pub struct FilterInvalid {
334    inner: FilterComp,
335}
336
337#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
338/// A filter after it's gone through schema validation
339pub struct FilterValid {
340    inner: FilterComp,
341}
342
343#[derive(Clone, PartialEq, Eq)]
344pub struct FilterValidResolved {
345    inner: FilterResolved,
346}
347
348#[derive(Debug)]
349pub enum FilterPlan {
350    Invalid,
351    EqIndexed(Attribute, String),
352    EqUnindexed(Attribute),
353    EqCorrupt(Attribute),
354    SubIndexed(Attribute, String),
355    SubUnindexed(Attribute),
356    SubCorrupt(Attribute),
357    PresIndexed(Attribute),
358    PresUnindexed(Attribute),
359    PresCorrupt(Attribute),
360    LessThanUnindexed(Attribute),
361    OrUnindexed(Vec<FilterPlan>),
362    OrIndexed(Vec<FilterPlan>),
363    OrPartial(Vec<FilterPlan>),
364    OrPartialThreshold(Vec<FilterPlan>),
365    AndEmptyCand(Vec<FilterPlan>),
366    AndIndexed(Vec<FilterPlan>),
367    AndUnindexed(Vec<FilterPlan>),
368    AndPartial(Vec<FilterPlan>),
369    AndPartialThreshold(Vec<FilterPlan>),
370    AndNot(Box<FilterPlan>),
371    InclusionInvalid(Vec<FilterPlan>),
372    InclusionIndexed(Vec<FilterPlan>),
373}
374
375// This difference in this is that we want to show unindexed elements more prominently
376// in the execution.
377
378fn fmt_filterplan_set(f: &mut fmt::Formatter<'_>, name: &str, plan: &[FilterPlan]) -> fmt::Result {
379    write!(f, "{name}(")?;
380    for item in plan {
381        write!(f, "{}, ", item)?;
382    }
383    write!(f, ")")
384}
385
386impl fmt::Display for FilterPlan {
387    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388        match self {
389            Self::Invalid => write!(f, "Invalid"),
390            Self::EqIndexed(attr, _) => write!(f, "EqIndexed({attr})"),
391            Self::EqCorrupt(attr) => write!(f, "EqCorrupt({attr})"),
392            Self::EqUnindexed(attr) => write!(f, "EqUnindexed({attr})"),
393
394            Self::SubIndexed(attr, _) => write!(f, "SubIndexed({attr})"),
395            Self::SubCorrupt(attr) => write!(f, "SubCorrupt({attr})"),
396            Self::SubUnindexed(attr) => write!(f, "SubUnindexed({attr})"),
397
398            Self::PresIndexed(attr) => write!(f, "PresIndexed({attr})"),
399            Self::PresCorrupt(attr) => write!(f, "PresCorrupt({attr})"),
400            Self::PresUnindexed(attr) => write!(f, "PresUnindexed({attr})"),
401
402            Self::LessThanUnindexed(attr) => write!(f, "LessThanUnindexed({attr})"),
403
404            Self::OrUnindexed(plan) => fmt_filterplan_set(f, "OrUnindexed", plan),
405            Self::OrIndexed(plan) => write!(f, "OrIndexed(len={})", plan.len()),
406            Self::OrPartial(plan) => fmt_filterplan_set(f, "OrPartial", plan),
407            Self::OrPartialThreshold(plan) => fmt_filterplan_set(f, "OrPartialThreshold", plan),
408
409            Self::AndEmptyCand(plan) => write!(f, "AndEmptyCand(len={})", plan.len()),
410            Self::AndUnindexed(plan) => fmt_filterplan_set(f, "AndUnindexed", plan),
411            Self::AndIndexed(plan) => write!(f, "AndIndexed(len={})", plan.len()),
412            Self::AndPartial(plan) => fmt_filterplan_set(f, "AndPartial", plan),
413            Self::AndPartialThreshold(plan) => fmt_filterplan_set(f, "AndPartialThreshold", plan),
414
415            Self::AndNot(plan) => write!(f, "AndNot({plan})"),
416
417            Self::InclusionInvalid(plan) => fmt_filterplan_set(f, "InclusionInvalid", plan),
418            Self::InclusionIndexed(plan) => write!(f, "InclusionIndexed(len={})", plan.len()),
419        }
420    }
421}
422
423/// A `Filter` is a logical set of assertions about the state of an [`Entry`] and
424/// it's avas. `Filter`s are built from a set of possible assertions.
425///
426/// * `Pres`ence. An ava of that attribute's name exists, with any value on the [`Entry`].
427/// * `Eq`uality. An ava of the attribute exists and contains this matching value.
428/// * `Sub`string. An ava of the attribute exists and has a substring containing the requested value.
429/// * `Or`. Contains multiple filters and asserts at least one is true.
430/// * `And`. Contains multiple filters and asserts all of them are true.
431/// * `AndNot`. This is different to a "logical not" operation. This asserts that a condition is not
432///   true in the current candidate set. A search of `AndNot` alone will yield not results, but an
433///   `AndNot` in an `And` query will assert that a condition can not hold.
434///
435/// `Filter`s for security reasons are validated by the schema to assert all requested attributes
436/// are valid and exist in the schema so that they can have their indexes correctly used. This avoids
437/// a denial of service attack that may lead to full-table scans.
438///
439/// This `Filter` validation state is in the `STATE` attribute and will be either `FilterInvalid`
440/// or `FilterValid`. The `Filter` must be checked by the schema to move to `FilterValid`. This
441/// helps to prevent errors at compile time to assert `Filters` are securely checked
442///
443/// [`Entry`]: ../entry/struct.Entry.html
444#[derive(Clone, Hash, Ord, Eq, PartialOrd, PartialEq)]
445pub struct Filter<STATE> {
446    state: STATE,
447}
448
449impl fmt::Debug for Filter<FilterValidResolved> {
450    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
451        writeln!(f, "Filter(Valid) {:?}", self.state.inner)
452    }
453}
454
455impl fmt::Debug for Filter<FilterValid> {
456    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
457        writeln!(f, "Filter(Valid) {:?}", self.state.inner)
458    }
459}
460
461impl fmt::Debug for Filter<FilterInvalid> {
462    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
463        writeln!(f, "Filter(Invalid) {:?}", self.state.inner)
464    }
465}
466
467impl Filter<FilterValidResolved> {
468    // Does this need mut self? Aren't we returning
469    // a new copied filter?
470
471    #[cfg(test)]
472    fn optimise(&self) -> Self {
473        // Apply optimisations to the filter
474        // An easy way would be imple partialOrd
475        // then do sort on the or/and/not
476        // as the general conditions we want
477        // to optimise on are in those ...
478        //
479        // The other big one is folding redundant
480        // terms down.
481        //
482        // If an or/not/and condition has no items, remove it
483        //
484        // If its the root item?
485
486        Filter {
487            state: FilterValidResolved {
488                inner: self.state.inner.optimise(),
489            },
490        }
491    }
492
493    // It's not possible to invalid a resolved filter, because we don't know
494    // what the origin of the Self or Not keywords were.
495    //
496    // Saying this, we have entry -> filter_from_attrs. Should that return
497    // a valid or validResolved? If it does valid resolved we need invalidate
498    // so we can down cast and then re-validate and re-resolve ...
499
500    // Allow the entry crate to read the internals of the filter.
501    // more likely, we should move entry matching HERE ...
502    pub fn to_inner(&self) -> &FilterResolved {
503        &self.state.inner
504    }
505}
506
507impl Filter<FilterValid> {
508    pub fn invalidate(self) -> Filter<FilterInvalid> {
509        // Just move the state.
510        Filter {
511            state: FilterInvalid {
512                inner: self.state.inner,
513            },
514        }
515    }
516
517    pub fn resolve(
518        &self,
519        ev: &Identity,
520        idxmeta: Option<&IdxMeta>,
521        mut rsv_cache: Option<&mut ResolveFilterCacheReadTxn<'_>>,
522    ) -> Result<Filter<FilterValidResolved>, OperationError> {
523        // Given a filter, resolve Not and SelfUuid to real terms.
524        //
525        // The benefit of moving optimisation to this step is from various inputs, we can
526        // get to a resolved + optimised filter, and then we can cache those outputs in many
527        // cases! The exception is *large* filters, especially from the memberof plugin. We
528        // want to skip these because they can really jam up the server.
529
530        // Don't cache anything unless we have valid indexing metadata.
531        let cacheable = idxmeta.is_some() && FilterResolved::resolve_cacheable(&self.state.inner);
532
533        let cache_key = if cacheable {
534            // do we have a cache?
535            if let Some(rcache) = rsv_cache.as_mut() {
536                // construct the key. For now it's expensive because we have to clone, but ... eh.
537                let cache_key = (ev.get_event_origin_id(), Arc::new(self.clone()));
538                if let Some(f) = rcache.get(&cache_key) {
539                    // Got it? Shortcut and return!
540                    trace!("shortcut: a resolved filter already exists.");
541                    return Ok(f.as_ref().clone());
542                };
543                // Not in cache? Set the cache_key.
544                Some(cache_key)
545            } else {
546                None
547            }
548        } else {
549            // Not cacheable, lets just bail.
550            None
551        };
552
553        // If we got here, nothing in cache - resolve it the hard way.
554
555        let resolved_filt = Filter {
556            state: FilterValidResolved {
557                inner: match idxmeta {
558                    Some(idx) => {
559                        FilterResolved::resolve_idx(self.state.inner.clone(), ev, &idx.idxkeys)
560                    }
561                    None => FilterResolved::resolve_no_idx(self.state.inner.clone(), ev),
562                }
563                .map(|f| {
564                    match idxmeta {
565                        // Do a proper optimise if we have idxmeta.
566                        Some(_) => f.optimise(),
567                        // Only do this if we don't have idxmeta.
568                        None => f.fast_optimise(),
569                    }
570                })
571                .ok_or(OperationError::FilterUuidResolution)?,
572            },
573        };
574
575        // Now it's computed, inject it. Remember, we won't have a cache_key here
576        // if cacheable == false.
577        if let Some(cache_key) = cache_key {
578            if let Some(rcache) = rsv_cache.as_mut() {
579                trace!(?resolved_filt, "inserting filter to resolved cache");
580                rcache.insert(cache_key, Arc::new(resolved_filt.clone()));
581            }
582        }
583
584        Ok(resolved_filt)
585    }
586
587    pub fn get_attr_set(&self) -> BTreeSet<Attribute> {
588        // Recurse through the filter getting an attribute set.
589        let mut r_set = BTreeSet::new();
590        self.state.inner.get_attr_set(&mut r_set);
591        r_set
592    }
593
594    /*
595     * CORRECTNESS: This is a transform on the "immutable" filtervalid type.
596     * We know this is correct because internally we can assert that the hidden
597     * and recycled types *must* be valid.
598     */
599
600    pub fn into_ignore_hidden(self) -> Self {
601        // Destructure the former filter, and surround it with an ignore_hidden.
602        Filter {
603            state: FilterValid {
604                inner: FilterComp::new_ignore_hidden(self.state.inner),
605            },
606        }
607    }
608
609    pub fn into_recycled(self) -> Self {
610        // Destructure the former filter and surround it with a recycled only query
611        Filter {
612            state: FilterValid {
613                inner: FilterComp::new_recycled(self.state.inner),
614            },
615        }
616    }
617}
618
619impl Filter<FilterInvalid> {
620    pub fn new(inner: FC) -> Self {
621        let fc = FilterComp::new(inner);
622        Filter {
623            state: FilterInvalid { inner: fc },
624        }
625    }
626
627    pub fn new_ignore_hidden(inner: FC) -> Self {
628        let fc = FilterComp::new(inner);
629        Filter {
630            state: FilterInvalid {
631                inner: FilterComp::new_ignore_hidden(fc),
632            },
633        }
634    }
635
636    pub fn new_recycled(inner: FC) -> Self {
637        // Create a filter that searches recycled items only.
638        let fc = FilterComp::new(inner);
639        Filter {
640            state: FilterInvalid {
641                inner: FilterComp::new_recycled(fc),
642            },
643        }
644    }
645
646    pub fn join_parts_and(a: Self, b: Self) -> Self {
647        // I regret this function so much, but then again ...
648        Filter {
649            state: FilterInvalid {
650                inner: FilterComp::And(vec![a.state.inner, b.state.inner]),
651            },
652        }
653    }
654
655    /// ⚠️  - Bypass the schema state machine and force the filter to be considered valid.
656    /// This is a TEST ONLY method and will never be exposed in production.
657    #[cfg(test)]
658    pub fn into_valid_resolved(self) -> Filter<FilterValidResolved> {
659        // There is a good reason this function only exists in tests ...
660        //
661        // YOLO.
662        // tl;dr - panic if there is a Self term because we don't have the QS
663        // to resolve the uuid. Perhaps in the future we can provide a uuid
664        // to this for the resolving to make it safer and test case usable.
665
666        // First we make a fake idx meta, which is meant to be "just enough" to make
667        // some core test idxs faster. This is never used in production, it's JUST for
668        // test case speedups.
669        let idxmeta = vec![
670            (Attribute::Uuid, IndexType::Equality),
671            (Attribute::Uuid, IndexType::Presence),
672            (Attribute::Name, IndexType::Equality),
673            (Attribute::Name, IndexType::SubString),
674            (Attribute::Name, IndexType::Presence),
675            (Attribute::Class, IndexType::Equality),
676            (Attribute::Class, IndexType::Presence),
677            (Attribute::Member, IndexType::Equality),
678            (Attribute::Member, IndexType::Presence),
679            (Attribute::MemberOf, IndexType::Equality),
680            (Attribute::MemberOf, IndexType::Presence),
681            (Attribute::DirectMemberOf, IndexType::Equality),
682            (Attribute::DirectMemberOf, IndexType::Presence),
683        ];
684
685        let idxmeta_ref = idxmeta.iter().map(|(attr, itype)| (attr, itype)).collect();
686
687        Filter {
688            state: FilterValidResolved {
689                inner: FilterResolved::from_invalid(self.state.inner, &idxmeta_ref),
690            },
691        }
692    }
693
694    /// ⚠️  - Bypass the schema state machine and force the filter to be considered valid.
695    /// This is a TEST ONLY method and will never be exposed in production.
696    #[cfg(test)]
697    pub fn into_valid(self) -> Filter<FilterValid> {
698        // There is a good reason this function only exists in tests ...
699        //
700        // YOLO.
701        // tl;dr - blindly accept that this filter and it's ava's MUST have
702        // been normalised and exist in schema. If they don't things may subtly
703        // break, fail, or explode. As subtle as an explosion can be.
704        Filter {
705            state: FilterValid {
706                inner: self.state.inner,
707            },
708        }
709    }
710
711    pub fn validate(
712        &self,
713        schema: &dyn SchemaTransaction,
714    ) -> Result<Filter<FilterValid>, SchemaError> {
715        // TODO: Add a schema validation cache that can return pre-validated filters.
716
717        Ok(Filter {
718            state: FilterValid {
719                inner: self.state.inner.validate(schema)?,
720            },
721        })
722    }
723
724    // This has to have two versions to account for ro/rw traits, because RS can't
725    // monomorphise on the trait to call clone_value. An option is to make a fn that
726    // takes "clone_value(t, a, v) instead, but that may have a similar issue.
727    #[instrument(name = "filter::from_ro", level = "trace", skip_all)]
728    pub fn from_ro(
729        ev: &Identity,
730        f: &ProtoFilter,
731        qs: &mut QueryServerReadTransaction,
732    ) -> Result<Self, OperationError> {
733        let depth = DEFAULT_LIMIT_FILTER_DEPTH_MAX as usize;
734        let mut elems = ev.limits().filter_max_elements;
735        Ok(Filter {
736            state: FilterInvalid {
737                inner: FilterComp::from_ro(f, qs, depth, &mut elems)?,
738            },
739        })
740    }
741
742    #[instrument(name = "filter::from_rw", level = "trace", skip_all)]
743    pub fn from_rw(
744        ev: &Identity,
745        f: &ProtoFilter,
746        qs: &mut QueryServerWriteTransaction,
747    ) -> Result<Self, OperationError> {
748        let depth = DEFAULT_LIMIT_FILTER_DEPTH_MAX as usize;
749        let mut elems = ev.limits().filter_max_elements;
750        Ok(Filter {
751            state: FilterInvalid {
752                inner: FilterComp::from_rw(f, qs, depth, &mut elems)?,
753            },
754        })
755    }
756
757    #[instrument(name = "filter::from_ldap_ro", level = "trace", skip_all)]
758    pub fn from_ldap_ro(
759        ev: &Identity,
760        f: &LdapFilter,
761        qs: &mut QueryServerReadTransaction,
762    ) -> Result<Self, OperationError> {
763        let depth = DEFAULT_LIMIT_FILTER_DEPTH_MAX as usize;
764        let mut elems = ev.limits().filter_max_elements;
765        Ok(Filter {
766            state: FilterInvalid {
767                inner: FilterComp::from_ldap_ro(f, qs, depth, &mut elems)?,
768            },
769        })
770    }
771
772    #[instrument(name = "filter::from_scim_ro", level = "trace", skip_all)]
773    pub fn from_scim_ro(
774        ev: &Identity,
775        f: &ScimFilter,
776        qs: &mut QueryServerReadTransaction,
777    ) -> Result<Self, OperationError> {
778        let depth = DEFAULT_LIMIT_FILTER_DEPTH_MAX as usize;
779        let mut elems = ev.limits().filter_max_elements;
780        Ok(Filter {
781            state: FilterInvalid {
782                inner: FilterComp::from_scim_ro(f, qs, depth, &mut elems)?,
783            },
784        })
785    }
786}
787
788impl FromStr for Filter<FilterInvalid> {
789    type Err = OperationError;
790    fn from_str(s: &str) -> Result<Self, Self::Err> {
791        let f: FC = serde_json::from_str(s).map_err(|_| OperationError::FilterParseError)?;
792        Ok(Filter {
793            state: FilterInvalid {
794                inner: FilterComp::new(f),
795            },
796        })
797    }
798}
799
800impl FilterComp {
801    fn new(fc: FC) -> Self {
802        match fc {
803            FC::Eq(a, v) => FilterComp::Eq(a, v),
804            FC::Cnt(a, v) => FilterComp::Cnt(a, v),
805            FC::Pres(a) => FilterComp::Pres(a),
806            FC::LessThan(a, v) => FilterComp::LessThan(a, v),
807            FC::Or(v) => FilterComp::Or(v.into_iter().map(FilterComp::new).collect()),
808            FC::And(v) => FilterComp::And(v.into_iter().map(FilterComp::new).collect()),
809            FC::Inclusion(v) => FilterComp::Inclusion(v.into_iter().map(FilterComp::new).collect()),
810            FC::AndNot(b) => FilterComp::AndNot(Box::new(FilterComp::new(*b))),
811            FC::SelfUuid => FilterComp::SelfUuid,
812            FC::Invalid(a) => FilterComp::Invalid(a),
813        }
814    }
815
816    fn new_ignore_hidden(fc: FilterComp) -> Self {
817        FilterComp::And(vec![
818            FilterComp::AndNot(Box::new(FilterComp::Or(vec![
819                FilterComp::Eq(Attribute::Class, EntryClass::Tombstone.into()),
820                FilterComp::Eq(Attribute::Class, EntryClass::Recycled.into()),
821            ]))),
822            fc,
823        ])
824    }
825
826    fn new_recycled(fc: FilterComp) -> Self {
827        FilterComp::And(vec![
828            FilterComp::Eq(Attribute::Class, EntryClass::Recycled.into()),
829            fc,
830        ])
831    }
832
833    fn get_attr_set(&self, r_set: &mut BTreeSet<Attribute>) {
834        match self {
835            FilterComp::Eq(attr, _)
836            | FilterComp::Cnt(attr, _)
837            | FilterComp::Stw(attr, _)
838            | FilterComp::Enw(attr, _)
839            | FilterComp::Pres(attr)
840            | FilterComp::LessThan(attr, _)
841            | FilterComp::Invalid(attr) => {
842                r_set.insert(attr.clone());
843            }
844            FilterComp::Or(vs) => vs.iter().for_each(|f| f.get_attr_set(r_set)),
845            FilterComp::And(vs) => vs.iter().for_each(|f| f.get_attr_set(r_set)),
846            FilterComp::Inclusion(vs) => vs.iter().for_each(|f| f.get_attr_set(r_set)),
847            FilterComp::AndNot(f) => f.get_attr_set(r_set),
848            FilterComp::SelfUuid => {
849                r_set.insert(Attribute::Uuid);
850            }
851        }
852    }
853
854    fn validate(&self, schema: &dyn SchemaTransaction) -> Result<FilterComp, SchemaError> {
855        // Optimisation is done at another stage.
856
857        // This probably needs some rework
858
859        // Getting this each recursion could be slow. Maybe
860        // we need an inner function that passes the reference?
861        let schema_attributes = schema.get_attributes();
862        // We used to check the attr_name by normalising it (lowercasing)
863        // but should we? I think we actually should just call a special
864        // handler on schema to fix it up.
865
866        match self {
867            FilterComp::Eq(attr, value) => {
868                // Check the requested attribute exists, and this PV type matches something
869                // that can be queried of the attribute.
870                match schema_attributes.get(attr) {
871                    Some(schema_a) => {
872                        schema_a
873                            .validate_partialvalue(attr, value)
874                            // Okay, it worked, transform to a filter component
875                            .map(|_| FilterComp::Eq(attr.clone(), value.clone()))
876                        // On error, pass the error back out.
877                    }
878                    None => Err(SchemaError::InvalidAttribute(attr.to_string())),
879                }
880            }
881            FilterComp::Cnt(attr, value) => {
882                match schema_attributes.get(attr) {
883                    Some(schema_a) => {
884                        schema_a
885                            .validate_partialvalue(attr, value)
886                            // Okay, it worked, transform to a filter component
887                            .map(|_| FilterComp::Cnt(attr.clone(), value.clone()))
888                        // On error, pass the error back out.
889                    }
890                    None => Err(SchemaError::InvalidAttribute(attr.to_string())),
891                }
892            }
893            FilterComp::Stw(attr, value) => {
894                match schema_attributes.get(attr) {
895                    Some(schema_a) => {
896                        schema_a
897                            .validate_partialvalue(attr, value)
898                            // Okay, it worked, transform to a filter component
899                            .map(|_| FilterComp::Stw(attr.clone(), value.clone()))
900                        // On error, pass the error back out.
901                    }
902                    None => Err(SchemaError::InvalidAttribute(attr.to_string())),
903                }
904            }
905            FilterComp::Enw(attr, value) => {
906                match schema_attributes.get(attr) {
907                    Some(schema_a) => {
908                        schema_a
909                            .validate_partialvalue(attr, value)
910                            // Okay, it worked, transform to a filter component
911                            .map(|_| FilterComp::Enw(attr.clone(), value.clone()))
912                        // On error, pass the error back out.
913                    }
914                    None => Err(SchemaError::InvalidAttribute(attr.to_string())),
915                }
916            }
917            FilterComp::Pres(attr) => {
918                match schema_attributes.get(attr) {
919                    Some(_attr_name) => {
920                        // Return our valid data
921                        Ok(FilterComp::Pres(attr.clone()))
922                    }
923                    None => Err(SchemaError::InvalidAttribute(attr.to_string())),
924                }
925            }
926            FilterComp::LessThan(attr, value) => {
927                match schema_attributes.get(attr) {
928                    Some(schema_a) => {
929                        schema_a
930                            .validate_partialvalue(attr, value)
931                            // Okay, it worked, transform to a filter component
932                            .map(|_| FilterComp::LessThan(attr.clone(), value.clone()))
933                        // On error, pass the error back out.
934                    }
935                    None => Err(SchemaError::InvalidAttribute(attr.to_string())),
936                }
937            }
938            FilterComp::Or(filters) => {
939                // * If all filters are okay, return Ok(Filter::Or())
940                // * Any filter is invalid, return the error.
941                // * An empty "or" is a valid filter in mathematical terms, but we throw an
942                //   error to warn the user because it's super unlikely they want that
943                if filters.is_empty() {
944                    return Err(SchemaError::EmptyFilter);
945                };
946                let x: Result<Vec<_>, _> = filters
947                    .iter()
948                    .map(|filter| filter.validate(schema))
949                    .collect();
950                // Now put the valid filters into the Filter
951                x.map(FilterComp::Or)
952            }
953            FilterComp::And(filters) => {
954                // * If all filters are okay, return Ok(Filter::Or())
955                // * Any filter is invalid, return the error.
956                // * An empty "and" is a valid filter in mathematical terms, but we throw an
957                //   error to warn the user because it's super unlikely they want that
958                if filters.is_empty() {
959                    return Err(SchemaError::EmptyFilter);
960                };
961                let x: Result<Vec<_>, _> = filters
962                    .iter()
963                    .map(|filter| filter.validate(schema))
964                    .collect();
965                // Now put the valid filters into the Filter
966                x.map(FilterComp::And)
967            }
968            FilterComp::Inclusion(filters) => {
969                if filters.is_empty() {
970                    return Err(SchemaError::EmptyFilter);
971                };
972                let x: Result<Vec<_>, _> = filters
973                    .iter()
974                    .map(|filter| filter.validate(schema))
975                    .collect();
976                // Now put the valid filters into the Filter
977                x.map(FilterComp::Inclusion)
978            }
979            FilterComp::AndNot(filter) => {
980                // Just validate the inner
981                filter
982                    .validate(schema)
983                    .map(|r_filter| FilterComp::AndNot(Box::new(r_filter)))
984            }
985            FilterComp::SelfUuid => {
986                // Pretty hard to mess this one up ;)
987                Ok(FilterComp::SelfUuid)
988            }
989            FilterComp::Invalid(attr) => {
990                // FilterComp may be invalid but Invalid is still a valid value.
991                // we continue the evaluation so OR queries can still succeed
992                Ok(FilterComp::Invalid(attr.clone()))
993            }
994        }
995    }
996
997    fn from_ro(
998        f: &ProtoFilter,
999        qs: &mut QueryServerReadTransaction,
1000        depth: usize,
1001        elems: &mut usize,
1002    ) -> Result<Self, OperationError> {
1003        let ndepth = depth.checked_sub(1).ok_or(OperationError::ResourceLimit)?;
1004        Ok(match f {
1005            ProtoFilter::Eq(a, v) => {
1006                let nk = Attribute::from(a.as_str());
1007                let v = qs.clone_partialvalue(&nk, v)?;
1008                FilterComp::Eq(nk, v)
1009            }
1010            ProtoFilter::Cnt(a, v) => {
1011                let nk = Attribute::from(a.as_str());
1012                let v = qs.clone_partialvalue(&nk, v)?;
1013                FilterComp::Cnt(nk, v)
1014            }
1015            ProtoFilter::Pres(a) => {
1016                let nk = Attribute::from(a.as_str());
1017                FilterComp::Pres(nk)
1018            }
1019            ProtoFilter::Or(l) => {
1020                *elems = (*elems)
1021                    .checked_sub(l.len())
1022                    .ok_or(OperationError::ResourceLimit)?;
1023                FilterComp::Or(
1024                    l.iter()
1025                        .map(|f| Self::from_ro(f, qs, ndepth, elems))
1026                        .collect::<Result<Vec<_>, _>>()?,
1027                )
1028            }
1029            ProtoFilter::And(l) => {
1030                *elems = (*elems)
1031                    .checked_sub(l.len())
1032                    .ok_or(OperationError::ResourceLimit)?;
1033                FilterComp::And(
1034                    l.iter()
1035                        .map(|f| Self::from_ro(f, qs, ndepth, elems))
1036                        .collect::<Result<Vec<_>, _>>()?,
1037                )
1038            }
1039            ProtoFilter::AndNot(l) => {
1040                *elems = (*elems)
1041                    .checked_sub(1)
1042                    .ok_or(OperationError::ResourceLimit)?;
1043                FilterComp::AndNot(Box::new(Self::from_ro(l, qs, ndepth, elems)?))
1044            }
1045            ProtoFilter::SelfUuid => FilterComp::SelfUuid,
1046        })
1047    }
1048
1049    fn from_rw(
1050        f: &ProtoFilter,
1051        qs: &mut QueryServerWriteTransaction,
1052        depth: usize,
1053        elems: &mut usize,
1054    ) -> Result<Self, OperationError> {
1055        let ndepth = depth.checked_sub(1).ok_or(OperationError::ResourceLimit)?;
1056        Ok(match f {
1057            ProtoFilter::Eq(a, v) => {
1058                let nk = Attribute::from(a.as_str());
1059                let v = qs.clone_partialvalue(&nk, v)?;
1060                FilterComp::Eq(nk, v)
1061            }
1062            ProtoFilter::Cnt(a, v) => {
1063                let nk = Attribute::from(a.as_str());
1064                let v = qs.clone_partialvalue(&nk, v)?;
1065                FilterComp::Cnt(nk, v)
1066            }
1067            ProtoFilter::Pres(a) => {
1068                let nk = Attribute::from(a.as_str());
1069                FilterComp::Pres(nk)
1070            }
1071            ProtoFilter::Or(l) => {
1072                *elems = (*elems)
1073                    .checked_sub(l.len())
1074                    .ok_or(OperationError::ResourceLimit)?;
1075                FilterComp::Or(
1076                    l.iter()
1077                        .map(|f| Self::from_rw(f, qs, ndepth, elems))
1078                        .collect::<Result<Vec<_>, _>>()?,
1079                )
1080            }
1081            ProtoFilter::And(l) => {
1082                *elems = (*elems)
1083                    .checked_sub(l.len())
1084                    .ok_or(OperationError::ResourceLimit)?;
1085                FilterComp::And(
1086                    l.iter()
1087                        .map(|f| Self::from_rw(f, qs, ndepth, elems))
1088                        .collect::<Result<Vec<_>, _>>()?,
1089                )
1090            }
1091            ProtoFilter::AndNot(l) => {
1092                *elems = (*elems)
1093                    .checked_sub(1)
1094                    .ok_or(OperationError::ResourceLimit)?;
1095
1096                FilterComp::AndNot(Box::new(Self::from_rw(l, qs, ndepth, elems)?))
1097            }
1098            ProtoFilter::SelfUuid => FilterComp::SelfUuid,
1099        })
1100    }
1101
1102    fn from_ldap_ro(
1103        f: &LdapFilter,
1104        qs: &mut QueryServerReadTransaction,
1105        depth: usize,
1106        elems: &mut usize,
1107    ) -> Result<Self, OperationError> {
1108        let ndepth = depth.checked_sub(1).ok_or(OperationError::ResourceLimit)?;
1109        *elems = (*elems)
1110            .checked_sub(1)
1111            .ok_or(OperationError::ResourceLimit)?;
1112        Ok(match f {
1113            LdapFilter::And(l) => FilterComp::And(
1114                l.iter()
1115                    .map(|f| Self::from_ldap_ro(f, qs, ndepth, elems))
1116                    .collect::<Result<Vec<_>, _>>()?,
1117            ),
1118            LdapFilter::Or(l) => FilterComp::Or(
1119                l.iter()
1120                    .map(|f| Self::from_ldap_ro(f, qs, ndepth, elems))
1121                    .collect::<Result<Vec<_>, _>>()?,
1122            ),
1123            LdapFilter::Not(l) => {
1124                FilterComp::AndNot(Box::new(Self::from_ldap_ro(l, qs, ndepth, elems)?))
1125            }
1126            LdapFilter::Equality(a, v) => {
1127                let a = ldap_attr_filter_map(a);
1128                let pv = qs.clone_partialvalue(&a, v);
1129
1130                match pv {
1131                    Ok(pv) => FilterComp::Eq(a, pv),
1132                    Err(_) if a == Attribute::Spn => FilterComp::Invalid(a),
1133                    Err(err) => return Err(err),
1134                }
1135            }
1136            LdapFilter::Present(a) => FilterComp::Pres(ldap_attr_filter_map(a)),
1137            LdapFilter::Substring(
1138                a,
1139                LdapSubstringFilter {
1140                    initial,
1141                    any,
1142                    final_,
1143                },
1144            ) => {
1145                let a = ldap_attr_filter_map(a);
1146
1147                let mut terms = Vec::with_capacity(any.len() + 2);
1148                if let Some(ini) = initial {
1149                    let v = qs.clone_partialvalue(&a, ini)?;
1150                    terms.push(FilterComp::Stw(a.clone(), v));
1151                }
1152
1153                for term in any.iter() {
1154                    let v = qs.clone_partialvalue(&a, term)?;
1155                    terms.push(FilterComp::Cnt(a.clone(), v));
1156                }
1157
1158                if let Some(fin) = final_ {
1159                    let v = qs.clone_partialvalue(&a, fin)?;
1160                    terms.push(FilterComp::Enw(a.clone(), v));
1161                }
1162
1163                FilterComp::And(terms)
1164            }
1165            LdapFilter::GreaterOrEqual(_, _) => {
1166                admin_error!("Unsupported filter operation - greater or equal");
1167                return Err(OperationError::FilterGeneration);
1168            }
1169            LdapFilter::LessOrEqual(_, _) => {
1170                admin_error!("Unsupported filter operation - less or equal");
1171                return Err(OperationError::FilterGeneration);
1172            }
1173            LdapFilter::Approx(_, _) => {
1174                admin_error!("Unsupported filter operation - approximate");
1175                return Err(OperationError::FilterGeneration);
1176            }
1177            LdapFilter::Extensible(_) => {
1178                admin_error!("Unsupported filter operation - extensible");
1179                return Err(OperationError::FilterGeneration);
1180            }
1181        })
1182    }
1183
1184    fn from_scim_ro(
1185        f: &ScimFilter,
1186        qs: &mut QueryServerReadTransaction,
1187        depth: usize,
1188        elems: &mut usize,
1189    ) -> Result<Self, OperationError> {
1190        let ndepth = depth.checked_sub(1).ok_or(OperationError::ResourceLimit)?;
1191        *elems = (*elems)
1192            .checked_sub(1)
1193            .ok_or(OperationError::ResourceLimit)?;
1194        Ok(match f {
1195            ScimFilter::Present(ScimAttrPath { a, s: None }) => FilterComp::Pres(a.clone()),
1196            ScimFilter::Equal(ScimAttrPath { a, s: None }, json_value) => {
1197                let pv = qs.resolve_scim_json_get(a, json_value)?;
1198                FilterComp::Eq(a.clone(), pv)
1199            }
1200
1201            ScimFilter::Contains(ScimAttrPath { a, s: None }, json_value) => {
1202                let pv = qs.resolve_scim_json_get(a, json_value)?;
1203                FilterComp::Cnt(a.clone(), pv)
1204            }
1205            ScimFilter::StartsWith(ScimAttrPath { a, s: None }, json_value) => {
1206                let pv = qs.resolve_scim_json_get(a, json_value)?;
1207                FilterComp::Stw(a.clone(), pv)
1208            }
1209            ScimFilter::EndsWith(ScimAttrPath { a, s: None }, json_value) => {
1210                let pv = qs.resolve_scim_json_get(a, json_value)?;
1211                FilterComp::Enw(a.clone(), pv)
1212            }
1213            ScimFilter::Greater(ScimAttrPath { a, s: None }, json_value) => {
1214                let pv = qs.resolve_scim_json_get(a, json_value)?;
1215                // Greater is equivalent to "not equal or less than".
1216                FilterComp::And(vec![
1217                    FilterComp::Pres(a.clone()),
1218                    FilterComp::AndNot(Box::new(FilterComp::Or(vec![
1219                        FilterComp::LessThan(a.clone(), pv.clone()),
1220                        FilterComp::Eq(a.clone(), pv),
1221                    ]))),
1222                ])
1223            }
1224            ScimFilter::Less(ScimAttrPath { a, s: None }, json_value) => {
1225                let pv = qs.resolve_scim_json_get(a, json_value)?;
1226                FilterComp::LessThan(a.clone(), pv)
1227            }
1228            ScimFilter::GreaterOrEqual(ScimAttrPath { a, s: None }, json_value) => {
1229                let pv = qs.resolve_scim_json_get(a, json_value)?;
1230                // Greater or equal is equivalent to "not less than".
1231                FilterComp::And(vec![
1232                    FilterComp::Pres(a.clone()),
1233                    FilterComp::AndNot(Box::new(FilterComp::LessThan(a.clone(), pv.clone()))),
1234                ])
1235            }
1236            ScimFilter::LessOrEqual(ScimAttrPath { a, s: None }, json_value) => {
1237                let pv = qs.resolve_scim_json_get(a, json_value)?;
1238                FilterComp::Or(vec![
1239                    FilterComp::LessThan(a.clone(), pv.clone()),
1240                    FilterComp::Eq(a.clone(), pv),
1241                ])
1242            }
1243            ScimFilter::Not(f) => {
1244                let f = Self::from_scim_ro(f, qs, ndepth, elems)?;
1245                FilterComp::AndNot(Box::new(f))
1246            }
1247            ScimFilter::Or(left, right) => {
1248                let left = Self::from_scim_ro(left, qs, ndepth, elems)?;
1249                let right = Self::from_scim_ro(right, qs, ndepth, elems)?;
1250                FilterComp::Or(vec![left, right])
1251            }
1252            ScimFilter::And(left, right) => {
1253                let left = Self::from_scim_ro(left, qs, ndepth, elems)?;
1254                let right = Self::from_scim_ro(right, qs, ndepth, elems)?;
1255                FilterComp::And(vec![left, right])
1256            }
1257            ScimFilter::NotEqual(ScimAttrPath { s: None, .. }, _) => {
1258                error!("Unsupported filter operation - not-equal");
1259                return Err(OperationError::FilterGeneration);
1260            }
1261            ScimFilter::Present(ScimAttrPath { s: Some(_), .. })
1262            | ScimFilter::Equal(ScimAttrPath { s: Some(_), .. }, _)
1263            | ScimFilter::NotEqual(ScimAttrPath { s: Some(_), .. }, _)
1264            | ScimFilter::Contains(ScimAttrPath { s: Some(_), .. }, _)
1265            | ScimFilter::StartsWith(ScimAttrPath { s: Some(_), .. }, _)
1266            | ScimFilter::EndsWith(ScimAttrPath { s: Some(_), .. }, _)
1267            | ScimFilter::Greater(ScimAttrPath { s: Some(_), .. }, _)
1268            | ScimFilter::Less(ScimAttrPath { s: Some(_), .. }, _)
1269            | ScimFilter::GreaterOrEqual(ScimAttrPath { s: Some(_), .. }, _)
1270            | ScimFilter::LessOrEqual(ScimAttrPath { s: Some(_), .. }, _) => {
1271                error!("Unsupported filter operation - sub-attribute");
1272                return Err(OperationError::FilterGeneration);
1273            }
1274            ScimFilter::Complex(..) => {
1275                error!("Unsupported filter operation - complex");
1276                return Err(OperationError::FilterGeneration);
1277            }
1278        })
1279    }
1280}
1281
1282/* We only configure partial eq if cfg test on the invalid/valid types */
1283/*
1284impl PartialEq for Filter<FilterInvalid> {
1285    fn eq(&self, rhs: &Filter<FilterInvalid>) -> bool {
1286        self.state.inner == rhs.state.inner
1287    }
1288}
1289
1290impl PartialEq for Filter<FilterValid> {
1291    fn eq(&self, rhs: &Filter<FilterValid>) -> bool {
1292        self.state.inner == rhs.state.inner
1293    }
1294}
1295
1296impl PartialEq for Filter<FilterValidResolved> {
1297    fn eq(&self, rhs: &Filter<FilterValidResolved>) -> bool {
1298        self.state.inner == rhs.state.inner
1299    }
1300}
1301*/
1302
1303/*
1304 * Only needed in tests, in run time order only matters on the inner for
1305 * optimisation.
1306 */
1307#[cfg(test)]
1308impl PartialOrd for Filter<FilterValidResolved> {
1309    fn partial_cmp(&self, rhs: &Filter<FilterValidResolved>) -> Option<Ordering> {
1310        self.state.inner.partial_cmp(&rhs.state.inner)
1311    }
1312}
1313
1314impl PartialEq for FilterResolved {
1315    fn eq(&self, rhs: &FilterResolved) -> bool {
1316        match (self, rhs) {
1317            (FilterResolved::Eq(a1, v1, _), FilterResolved::Eq(a2, v2, _)) => a1 == a2 && v1 == v2,
1318            (FilterResolved::Cnt(a1, v1, _), FilterResolved::Cnt(a2, v2, _)) => {
1319                a1 == a2 && v1 == v2
1320            }
1321            (FilterResolved::Pres(a1, _), FilterResolved::Pres(a2, _)) => a1 == a2,
1322            (FilterResolved::LessThan(a1, v1, _), FilterResolved::LessThan(a2, v2, _)) => {
1323                a1 == a2 && v1 == v2
1324            }
1325            (FilterResolved::And(vs1, _), FilterResolved::And(vs2, _)) => vs1 == vs2,
1326            (FilterResolved::Or(vs1, _), FilterResolved::Or(vs2, _)) => vs1 == vs2,
1327            (FilterResolved::Inclusion(vs1, _), FilterResolved::Inclusion(vs2, _)) => vs1 == vs2,
1328            (FilterResolved::AndNot(f1, _), FilterResolved::AndNot(f2, _)) => f1 == f2,
1329            (_, _) => false,
1330        }
1331    }
1332}
1333
1334impl PartialOrd for FilterResolved {
1335    fn partial_cmp(&self, rhs: &FilterResolved) -> Option<Ordering> {
1336        Some(self.cmp(rhs))
1337    }
1338}
1339
1340impl Ord for FilterResolved {
1341    /// Ordering of filters for optimisation and subsequent dead term elimination.
1342    fn cmp(&self, rhs: &FilterResolved) -> Ordering {
1343        let left_slopey = self.get_slopeyness_factor();
1344        let right_slopey = rhs.get_slopeyness_factor();
1345
1346        let r = match (left_slopey, right_slopey) {
1347            (Some(sfl), Some(sfr)) => sfl.cmp(&sfr),
1348            (Some(_), None) => Ordering::Less,
1349            (None, Some(_)) => Ordering::Greater,
1350            (None, None) => Ordering::Equal,
1351        };
1352
1353        // If they are equal, compare other elements for determining the order. This is what
1354        // allows dead term elimination to occur.
1355        //
1356        // In almost all cases, we'll miss this step though as slopes will vary distinctly.
1357        //
1358        // We do NOT need to check for indexed vs unindexed here, we already did it in the slope check!
1359        if r == Ordering::Equal {
1360            match (self, rhs) {
1361                (FilterResolved::Eq(a1, v1, _), FilterResolved::Eq(a2, v2, _))
1362                | (FilterResolved::Cnt(a1, v1, _), FilterResolved::Cnt(a2, v2, _))
1363                | (FilterResolved::LessThan(a1, v1, _), FilterResolved::LessThan(a2, v2, _)) => {
1364                    match a1.cmp(a2) {
1365                        Ordering::Equal => v1.cmp(v2),
1366                        o => o,
1367                    }
1368                }
1369                (FilterResolved::Pres(a1, _), FilterResolved::Pres(a2, _)) => a1.cmp(a2),
1370                // Now sort these into the generally "best" order.
1371                (FilterResolved::Eq(_, _, _), _) => Ordering::Less,
1372                (_, FilterResolved::Eq(_, _, _)) => Ordering::Greater,
1373                (FilterResolved::Pres(_, _), _) => Ordering::Less,
1374                (_, FilterResolved::Pres(_, _)) => Ordering::Greater,
1375                (FilterResolved::LessThan(_, _, _), _) => Ordering::Less,
1376                (_, FilterResolved::LessThan(_, _, _)) => Ordering::Greater,
1377                (FilterResolved::Cnt(_, _, _), _) => Ordering::Less,
1378                (_, FilterResolved::Cnt(_, _, _)) => Ordering::Greater,
1379                // They can't be re-arranged, they don't move!
1380                (_, _) => Ordering::Equal,
1381            }
1382        } else {
1383            r
1384        }
1385    }
1386}
1387
1388impl FilterResolved {
1389    /// ⚠️  - Process a filter without verifying with schema.
1390    /// This is a TEST ONLY method and will never be exposed in production.
1391    #[cfg(test)]
1392    fn from_invalid(fc: FilterComp, idxmeta: &HashSet<(&Attribute, &IndexType)>) -> Self {
1393        match fc {
1394            FilterComp::Eq(a, v) => {
1395                let idx = idxmeta.contains(&(&a, &IndexType::Equality));
1396                let idx = NonZeroU8::new(idx as u8);
1397                FilterResolved::Eq(a, v, idx)
1398            }
1399            FilterComp::SelfUuid => panic!("Not possible to resolve SelfUuid in from_invalid!"),
1400            FilterComp::Invalid(attr) => FilterResolved::Invalid(attr),
1401            FilterComp::Cnt(a, v) => {
1402                let idx = idxmeta.contains(&(&a, &IndexType::SubString));
1403                let idx = NonZeroU8::new(idx as u8);
1404                FilterResolved::Cnt(a, v, idx)
1405            }
1406            FilterComp::Stw(a, v) => {
1407                let idx = idxmeta.contains(&(&a, &IndexType::SubString));
1408                let idx = NonZeroU8::new(idx as u8);
1409                FilterResolved::Stw(a, v, idx)
1410            }
1411            FilterComp::Enw(a, v) => {
1412                let idx = idxmeta.contains(&(&a, &IndexType::SubString));
1413                let idx = NonZeroU8::new(idx as u8);
1414                FilterResolved::Enw(a, v, idx)
1415            }
1416            FilterComp::Pres(a) => {
1417                let idx = idxmeta.contains(&(&a, &IndexType::Presence));
1418                FilterResolved::Pres(a, NonZeroU8::new(idx as u8))
1419            }
1420            FilterComp::LessThan(a, v) => {
1421                // let idx = idxmeta.contains(&(&a, &IndexType::ORDERING));
1422                // TODO: For now, don't emit ordering indexes.
1423                FilterResolved::LessThan(a, v, None)
1424            }
1425            FilterComp::Or(vs) => FilterResolved::Or(
1426                vs.into_iter()
1427                    .map(|v| FilterResolved::from_invalid(v, idxmeta))
1428                    .collect(),
1429                None,
1430            ),
1431            FilterComp::And(vs) => FilterResolved::And(
1432                vs.into_iter()
1433                    .map(|v| FilterResolved::from_invalid(v, idxmeta))
1434                    .collect(),
1435                None,
1436            ),
1437            FilterComp::Inclusion(vs) => FilterResolved::Inclusion(
1438                vs.into_iter()
1439                    .map(|v| FilterResolved::from_invalid(v, idxmeta))
1440                    .collect(),
1441                None,
1442            ),
1443            FilterComp::AndNot(f) => {
1444                // TODO: pattern match box here. (AndNot(box f)).
1445                // We have to clone f into our space here because pattern matching can
1446                // not today remove the box, and we need f in our ownership. Since
1447                // AndNot currently is a rare request, cloning is not the worst thing
1448                // here ...
1449                FilterResolved::AndNot(
1450                    Box::new(FilterResolved::from_invalid((*f).clone(), idxmeta)),
1451                    None,
1452                )
1453            }
1454        }
1455    }
1456
1457    fn resolve_cacheable(fc: &FilterComp) -> bool {
1458        match fc {
1459            FilterComp::Or(vs) | FilterComp::And(vs) | FilterComp::Inclusion(vs) => {
1460                if vs.len() < 8 {
1461                    vs.iter().all(FilterResolved::resolve_cacheable)
1462                } else {
1463                    // Too lorge.
1464                    false
1465                }
1466            }
1467            FilterComp::AndNot(f) => FilterResolved::resolve_cacheable(f.as_ref()),
1468            FilterComp::Eq(..)
1469            | FilterComp::SelfUuid
1470            | FilterComp::Cnt(..)
1471            | FilterComp::Stw(..)
1472            | FilterComp::Enw(..)
1473            | FilterComp::Pres(_)
1474            | FilterComp::Invalid(_)
1475            | FilterComp::LessThan(..) => true,
1476        }
1477    }
1478
1479    fn resolve_idx(
1480        fc: FilterComp,
1481        ev: &Identity,
1482        idxmeta: &HashMap<IdxKey, IdxSlope>,
1483    ) -> Option<Self> {
1484        match fc {
1485            FilterComp::Eq(a, v) => {
1486                let idxkref = IdxKeyRef::new(&a, &IndexType::Equality);
1487                let idx = idxmeta
1488                    .get(&idxkref as &dyn IdxKeyToRef)
1489                    .copied()
1490                    .and_then(NonZeroU8::new);
1491                Some(FilterResolved::Eq(a, v, idx))
1492            }
1493            FilterComp::SelfUuid => ev.get_uuid().map(|uuid| {
1494                let idxkref = IdxKeyRef::new(Attribute::Uuid.as_ref(), &IndexType::Equality);
1495                let idx = idxmeta
1496                    .get(&idxkref as &dyn IdxKeyToRef)
1497                    .copied()
1498                    .and_then(NonZeroU8::new);
1499                FilterResolved::Eq(Attribute::Uuid, PartialValue::Uuid(uuid), idx)
1500            }),
1501            FilterComp::Cnt(a, v) => {
1502                let idxkref = IdxKeyRef::new(&a, &IndexType::SubString);
1503                let idx = idxmeta
1504                    .get(&idxkref as &dyn IdxKeyToRef)
1505                    .copied()
1506                    .and_then(NonZeroU8::new);
1507                Some(FilterResolved::Cnt(a, v, idx))
1508            }
1509            FilterComp::Stw(a, v) => {
1510                let idxkref = IdxKeyRef::new(&a, &IndexType::SubString);
1511                let idx = idxmeta
1512                    .get(&idxkref as &dyn IdxKeyToRef)
1513                    .copied()
1514                    .and_then(NonZeroU8::new);
1515                Some(FilterResolved::Stw(a, v, idx))
1516            }
1517            FilterComp::Enw(a, v) => {
1518                let idxkref = IdxKeyRef::new(&a, &IndexType::SubString);
1519                let idx = idxmeta
1520                    .get(&idxkref as &dyn IdxKeyToRef)
1521                    .copied()
1522                    .and_then(NonZeroU8::new);
1523                Some(FilterResolved::Enw(a, v, idx))
1524            }
1525            FilterComp::Pres(a) => {
1526                let idxkref = IdxKeyRef::new(&a, &IndexType::Presence);
1527                let idx = idxmeta
1528                    .get(&idxkref as &dyn IdxKeyToRef)
1529                    .copied()
1530                    .and_then(NonZeroU8::new);
1531                Some(FilterResolved::Pres(a, idx))
1532            }
1533            FilterComp::LessThan(a, v) => {
1534                // let idx = idxmeta.contains(&(&a, &IndexType::SubString));
1535                Some(FilterResolved::LessThan(a, v, None))
1536            }
1537            // We set the compound filters slope factor to "None" here, because when we do
1538            // optimise we'll actually fill in the correct slope factors after we sort those
1539            // inner terms in a more optimal way.
1540            FilterComp::Or(vs) => {
1541                let fi: Option<Vec<_>> = vs
1542                    .into_iter()
1543                    .map(|f| FilterResolved::resolve_idx(f, ev, idxmeta))
1544                    .collect();
1545                fi.map(|fi| FilterResolved::Or(fi, None))
1546            }
1547            FilterComp::And(vs) => {
1548                let fi: Option<Vec<_>> = vs
1549                    .into_iter()
1550                    .map(|f| FilterResolved::resolve_idx(f, ev, idxmeta))
1551                    .collect();
1552                fi.map(|fi| FilterResolved::And(fi, None))
1553            }
1554            FilterComp::Inclusion(vs) => {
1555                let fi: Option<Vec<_>> = vs
1556                    .into_iter()
1557                    .map(|f| FilterResolved::resolve_idx(f, ev, idxmeta))
1558                    .collect();
1559                fi.map(|fi| FilterResolved::Inclusion(fi, None))
1560            }
1561            FilterComp::AndNot(f) => {
1562                // TODO: pattern match box here. (AndNot(box f)).
1563                // We have to clone f into our space here because pattern matching can
1564                // not today remove the box, and we need f in our ownership. Since
1565                // AndNot currently is a rare request, cloning is not the worst thing
1566                // here ...
1567                FilterResolved::resolve_idx((*f).clone(), ev, idxmeta)
1568                    .map(|fi| FilterResolved::AndNot(Box::new(fi), None))
1569            }
1570            FilterComp::Invalid(attr) => Some(FilterResolved::Invalid(attr)),
1571        }
1572    }
1573
1574    fn resolve_no_idx(fc: FilterComp, ev: &Identity) -> Option<Self> {
1575        // ⚠️  ⚠️  ⚠️  ⚠️
1576        // Remember, this function means we have NO INDEX METADATA so we can only
1577        // assign slopes to values we can GUARANTEE will EXIST.
1578        match fc {
1579            FilterComp::Eq(a, v) => {
1580                // Since we have no index data, we manually configure a reasonable
1581                // slope and indicate the presence of some expected basic
1582                // indexes.
1583                let idx = matches!(a.as_str(), ATTR_NAME | ATTR_UUID);
1584                let idx = NonZeroU8::new(idx as u8);
1585                Some(FilterResolved::Eq(a, v, idx))
1586            }
1587            FilterComp::SelfUuid => ev.get_uuid().map(|uuid| {
1588                FilterResolved::Eq(
1589                    Attribute::Uuid,
1590                    PartialValue::Uuid(uuid),
1591                    NonZeroU8::new(true as u8),
1592                )
1593            }),
1594            FilterComp::Cnt(a, v) => Some(FilterResolved::Cnt(a, v, None)),
1595            FilterComp::Stw(a, v) => Some(FilterResolved::Stw(a, v, None)),
1596            FilterComp::Enw(a, v) => Some(FilterResolved::Enw(a, v, None)),
1597            FilterComp::Pres(a) => Some(FilterResolved::Pres(a, None)),
1598            FilterComp::LessThan(a, v) => Some(FilterResolved::LessThan(a, v, None)),
1599            FilterComp::Or(vs) => {
1600                let fi: Option<Vec<_>> = vs
1601                    .into_iter()
1602                    .map(|f| FilterResolved::resolve_no_idx(f, ev))
1603                    .collect();
1604                fi.map(|fi| FilterResolved::Or(fi, None))
1605            }
1606            FilterComp::And(vs) => {
1607                let fi: Option<Vec<_>> = vs
1608                    .into_iter()
1609                    .map(|f| FilterResolved::resolve_no_idx(f, ev))
1610                    .collect();
1611                fi.map(|fi| FilterResolved::And(fi, None))
1612            }
1613            FilterComp::Inclusion(vs) => {
1614                let fi: Option<Vec<_>> = vs
1615                    .into_iter()
1616                    .map(|f| FilterResolved::resolve_no_idx(f, ev))
1617                    .collect();
1618                fi.map(|fi| FilterResolved::Inclusion(fi, None))
1619            }
1620            FilterComp::AndNot(f) => {
1621                // TODO: pattern match box here. (AndNot(box f)).
1622                // We have to clone f into our space here because pattern matching can
1623                // not today remove the box, and we need f in our ownership. Since
1624                // AndNot currently is a rare request, cloning is not the worst thing
1625                // here ...
1626                FilterResolved::resolve_no_idx((*f).clone(), ev)
1627                    .map(|fi| FilterResolved::AndNot(Box::new(fi), None))
1628            }
1629            FilterComp::Invalid(attr) => Some(FilterResolved::Invalid(attr)),
1630        }
1631    }
1632
1633    // This is an optimise that only attempts to optimise the outer terms.
1634    fn fast_optimise(self) -> Self {
1635        match self {
1636            FilterResolved::Inclusion(mut f_list, _) => {
1637                f_list.sort_unstable();
1638                f_list.dedup();
1639                let sf = f_list.last().and_then(|f| f.get_slopeyness_factor());
1640                FilterResolved::Inclusion(f_list, sf)
1641            }
1642            FilterResolved::And(mut f_list, _) => {
1643                f_list.sort_unstable();
1644                f_list.dedup();
1645                let sf = f_list.first().and_then(|f| f.get_slopeyness_factor());
1646                FilterResolved::And(f_list, sf)
1647            }
1648            v => v,
1649        }
1650    }
1651
1652    fn optimise(&self) -> Self {
1653        // Most optimisations only matter around or/and terms.
1654        match self {
1655            FilterResolved::Inclusion(f_list, _) => {
1656                // first, optimise all our inner elements
1657                let (f_list_inc, mut f_list_new): (Vec<_>, Vec<_>) = f_list
1658                    .iter()
1659                    .map(|f_ref| f_ref.optimise())
1660                    .partition(|f| matches!(f, FilterResolved::Inclusion(_, _)));
1661
1662                f_list_inc.into_iter().for_each(|fc| {
1663                    if let FilterResolved::Inclusion(mut l, _) = fc {
1664                        f_list_new.append(&mut l)
1665                    }
1666                });
1667                // finally, optimise this list by sorting.
1668                f_list_new.sort_unstable();
1669                f_list_new.dedup();
1670                // Inclusions are similar to or, so what's our worst case?
1671                let sf = f_list_new.last().and_then(|f| f.get_slopeyness_factor());
1672                FilterResolved::Inclusion(f_list_new, sf)
1673            }
1674            FilterResolved::And(f_list, _) => {
1675                // first, optimise all our inner elements
1676                let (f_list_and, mut f_list_new): (Vec<_>, Vec<_>) = f_list
1677                    .iter()
1678                    .map(|f_ref| f_ref.optimise())
1679                    .partition(|f| matches!(f, FilterResolved::And(_, _)));
1680
1681                // now, iterate over this list - for each "and" term, fold
1682                // it's elements to this level.
1683                // This is one of the most important improvements because it means
1684                // that we can compare terms such that:
1685                //
1686                // (&(class=*)(&(uid=foo)))
1687                // if we did not and fold, this would remain as is. However, by and
1688                // folding, we can optimise to:
1689                // (&(uid=foo)(class=*))
1690                // Which will be faster when indexed as the uid=foo will trigger
1691                // shortcutting
1692
1693                f_list_and.into_iter().for_each(|fc| {
1694                    if let FilterResolved::And(mut l, _) = fc {
1695                        f_list_new.append(&mut l)
1696                    }
1697                });
1698
1699                // If the f_list_and only has one element, pop it and return.
1700                if f_list_new.len() == 1 {
1701                    f_list_new.remove(0)
1702                } else {
1703                    // finally, optimise this list by sorting.
1704                    f_list_new.sort_unstable();
1705                    f_list_new.dedup();
1706                    // Which ever element as the head is first must be the min SF
1707                    // so we use this in our And to represent the "best possible" value
1708                    // of how indexes will perform.
1709                    let sf = f_list_new.first().and_then(|f| f.get_slopeyness_factor());
1710                    //
1711                    // return!
1712                    FilterResolved::And(f_list_new, sf)
1713                }
1714            }
1715            FilterResolved::Or(f_list, _) => {
1716                let (f_list_or, mut f_list_new): (Vec<_>, Vec<_>) = f_list
1717                    .iter()
1718                    // Optimise all inner items.
1719                    .map(|f_ref| f_ref.optimise())
1720                    // Split out inner-or terms to fold into this term.
1721                    .partition(|f| matches!(f, FilterResolved::Or(_, _)));
1722
1723                // Append the inner terms.
1724                f_list_or.into_iter().for_each(|fc| {
1725                    if let FilterResolved::Or(mut l, _) = fc {
1726                        f_list_new.append(&mut l)
1727                    }
1728                });
1729
1730                // If the f_list_or only has one element, pop it and return.
1731                if f_list_new.len() == 1 {
1732                    f_list_new.remove(0)
1733                } else {
1734                    // sort, but reverse so that sub-optimal elements are earlier
1735                    // to promote fast-failure.
1736                    // We have to allow this on clippy, which attempts to suggest:
1737                    //   f_list_new.sort_unstable_by_key(|&b| Reverse(b))
1738                    // However, because these are references, it causes a lifetime
1739                    // issue, and fails to compile.
1740                    #[allow(clippy::unnecessary_sort_by)]
1741                    f_list_new.sort_unstable_by(|a, b| b.cmp(a));
1742                    f_list_new.dedup();
1743                    // The *last* element is the most here, and our worst case factor.
1744                    let sf = f_list_new.last().and_then(|f| f.get_slopeyness_factor());
1745                    FilterResolved::Or(f_list_new, sf)
1746                }
1747            }
1748            f => f.clone(),
1749        }
1750    }
1751
1752    pub fn is_andnot(&self) -> bool {
1753        matches!(self, FilterResolved::AndNot(_, _))
1754    }
1755
1756    #[inline(always)]
1757    fn get_slopeyness_factor(&self) -> Option<NonZeroU8> {
1758        match self {
1759            FilterResolved::Eq(_, _, sf)
1760            | FilterResolved::Cnt(_, _, sf)
1761            | FilterResolved::Stw(_, _, sf)
1762            | FilterResolved::Enw(_, _, sf)
1763            | FilterResolved::Pres(_, sf)
1764            | FilterResolved::LessThan(_, _, sf)
1765            | FilterResolved::Or(_, sf)
1766            | FilterResolved::And(_, sf)
1767            | FilterResolved::Inclusion(_, sf)
1768            | FilterResolved::AndNot(_, sf) => *sf,
1769            // We hard code 1 because there is no slope for an invlid filter
1770            FilterResolved::Invalid(_) => NonZeroU8::new(1),
1771        }
1772    }
1773}
1774
1775#[cfg(test)]
1776mod tests {
1777    use std::cmp::{Ordering, PartialOrd};
1778    use std::collections::BTreeSet;
1779    use std::time::Duration;
1780
1781    use kanidm_proto::internal::Filter as ProtoFilter;
1782    use ldap3_proto::simple::LdapFilter;
1783
1784    use crate::event::{CreateEvent, DeleteEvent};
1785    use crate::filter::{Filter, FilterInvalid, DEFAULT_LIMIT_FILTER_DEPTH_MAX};
1786    use crate::prelude::*;
1787
1788    #[test]
1789    fn test_filter_simple() {
1790        // Test construction.
1791        let _filt: Filter<FilterInvalid> = filter!(f_eq(Attribute::Class, EntryClass::User.into()));
1792
1793        // AFTER
1794        let _complex_filt: Filter<FilterInvalid> = filter!(f_and!([
1795            f_or!([
1796                f_eq(Attribute::UserId, PartialValue::new_iutf8("test_a")),
1797                f_eq(Attribute::UserId, PartialValue::new_iutf8("test_b")),
1798            ]),
1799            f_sub(Attribute::Class, EntryClass::User.into()),
1800        ]));
1801    }
1802
1803    macro_rules! filter_optimise_assert {
1804        (
1805            $init:expr,
1806            $expect:expr
1807        ) => {{
1808            #[allow(unused_imports)]
1809            use crate::filter::{f_and, f_andnot, f_eq, f_or, f_pres, f_sub};
1810            use crate::filter::{Filter, FilterInvalid};
1811            let f_init: Filter<FilterInvalid> = Filter::new($init);
1812            let f_expect: Filter<FilterInvalid> = Filter::new($expect);
1813            // Create a resolved filter, via the most unsafe means possible!
1814            let f_init_r = f_init.into_valid_resolved();
1815            let f_init_o = f_init_r.optimise();
1816            let f_init_e = f_expect.into_valid_resolved();
1817            debug!("--");
1818            debug!("init   --> {:?}", f_init_r);
1819            debug!("opt    --> {:?}", f_init_o);
1820            debug!("expect --> {:?}", f_init_e);
1821            assert_eq!(f_init_o, f_init_e);
1822        }};
1823    }
1824
1825    #[test]
1826    fn test_filter_optimise() {
1827        sketching::test_init();
1828        // Given sets of "optimisable" filters, optimise them.
1829        filter_optimise_assert!(
1830            f_and(vec![f_and(vec![f_eq(
1831                Attribute::Class,
1832                EntryClass::TestClass.into()
1833            )])]),
1834            f_eq(Attribute::Class, EntryClass::TestClass.into())
1835        );
1836
1837        filter_optimise_assert!(
1838            f_or(vec![f_or(vec![f_eq(
1839                Attribute::Class,
1840                EntryClass::TestClass.into()
1841            )])]),
1842            f_eq(Attribute::Class, EntryClass::TestClass.into())
1843        );
1844
1845        filter_optimise_assert!(
1846            f_and(vec![f_or(vec![f_and(vec![f_eq(
1847                Attribute::Class,
1848                EntryClass::TestClass.to_partialvalue()
1849            )])])]),
1850            f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue())
1851        );
1852
1853        // Later this can test duplicate filter detection.
1854        filter_optimise_assert!(
1855            f_and(vec![
1856                f_and(vec![f_eq(
1857                    Attribute::Class,
1858                    EntryClass::TestClass.to_partialvalue()
1859                )]),
1860                f_sub(Attribute::Class, PartialValue::new_class("te")),
1861                f_pres(Attribute::Class),
1862                f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue())
1863            ]),
1864            f_and(vec![
1865                f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue()),
1866                f_pres(Attribute::Class),
1867                f_sub(Attribute::Class, PartialValue::new_class("te")),
1868            ])
1869        );
1870
1871        // Test dedup removes only the correct element despite padding.
1872        filter_optimise_assert!(
1873            f_and(vec![
1874                f_and(vec![
1875                    f_eq(Attribute::Class, PartialValue::new_class("foo")),
1876                    f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue()),
1877                    f_eq(Attribute::Uid, PartialValue::new_class("bar")),
1878                ]),
1879                f_sub(Attribute::Class, PartialValue::new_class("te")),
1880                f_pres(Attribute::Class),
1881                f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue())
1882            ]),
1883            f_and(vec![
1884                f_eq(Attribute::Class, PartialValue::new_class("foo")),
1885                f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue()),
1886                f_pres(Attribute::Class),
1887                f_eq(Attribute::Uid, PartialValue::new_class("bar")),
1888                f_sub(Attribute::Class, PartialValue::new_class("te")),
1889            ])
1890        );
1891
1892        filter_optimise_assert!(
1893            f_or(vec![
1894                f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue()),
1895                f_pres(Attribute::Class),
1896                f_sub(Attribute::Class, PartialValue::new_class("te")),
1897                f_or(vec![f_eq(
1898                    Attribute::Class,
1899                    EntryClass::TestClass.to_partialvalue()
1900                )]),
1901            ]),
1902            f_or(vec![
1903                f_sub(Attribute::Class, PartialValue::new_class("te")),
1904                f_pres(Attribute::Class),
1905                f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue())
1906            ])
1907        );
1908
1909        // Test dedup doesn't affect nested items incorrectly.
1910        filter_optimise_assert!(
1911            f_or(vec![
1912                f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue()),
1913                f_and(vec![
1914                    f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue()),
1915                    f_eq(Attribute::Term, EntryClass::TestClass.to_partialvalue()),
1916                    f_or(vec![f_eq(
1917                        Attribute::Class,
1918                        EntryClass::TestClass.to_partialvalue()
1919                    )])
1920                ]),
1921            ]),
1922            f_or(vec![
1923                f_and(vec![
1924                    f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue()),
1925                    f_eq(Attribute::Term, EntryClass::TestClass.to_partialvalue())
1926                ]),
1927                f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue()),
1928            ])
1929        );
1930    }
1931
1932    #[test]
1933    fn test_filter_eq() {
1934        let f_t1a = filter!(f_pres(Attribute::UserId));
1935        let f_t1b = filter!(f_pres(Attribute::UserId));
1936        let f_t1c = filter!(f_pres(Attribute::NonExist));
1937
1938        assert_eq!(f_t1a, f_t1b);
1939        assert!(f_t1a != f_t1c);
1940        assert!(f_t1b != f_t1c);
1941
1942        let f_t2a = filter!(f_and!([f_pres(Attribute::UserId)]));
1943        let f_t2b = filter!(f_and!([f_pres(Attribute::UserId)]));
1944        let f_t2c = filter!(f_and!([f_pres(Attribute::NonExist)]));
1945        assert_eq!(f_t2a, f_t2b);
1946        assert!(f_t2a != f_t2c);
1947        assert!(f_t2b != f_t2c);
1948
1949        assert!(f_t2c != f_t1a);
1950        assert!(f_t2c != f_t1c);
1951    }
1952
1953    #[test]
1954    fn test_filter_ord() {
1955        // Test that we uphold the rules of partialOrd
1956        // Basic equality
1957        // Test the two major paths here (str vs list)
1958        let f_t1a = filter_resolved!(f_pres(Attribute::UserId));
1959        let f_t1b = filter_resolved!(f_pres(Attribute::UserId));
1960
1961        assert_eq!(f_t1a.partial_cmp(&f_t1b), Some(Ordering::Equal));
1962        assert_eq!(f_t1b.partial_cmp(&f_t1a), Some(Ordering::Equal));
1963
1964        let f_t2a = filter_resolved!(f_and!([]));
1965        let f_t2b = filter_resolved!(f_and!([]));
1966        assert_eq!(f_t2a.partial_cmp(&f_t2b), Some(Ordering::Equal));
1967        assert_eq!(f_t2b.partial_cmp(&f_t2a), Some(Ordering::Equal));
1968
1969        // antisymmetry: if a < b then !(a > b), as well as a > b implying !(a < b); and
1970        // These are unindexed so we have to check them this way.
1971        let f_t3b = filter_resolved!(f_eq(Attribute::UserId, PartialValue::new_iutf8("")));
1972        assert_eq!(f_t1a.partial_cmp(&f_t3b), Some(Ordering::Greater));
1973        assert_eq!(f_t3b.partial_cmp(&f_t1a), Some(Ordering::Less));
1974
1975        // transitivity: a < b and b < c implies a < c. The same must hold for both == and >.
1976        let f_t4b = filter_resolved!(f_sub(Attribute::UserId, PartialValue::new_iutf8("")));
1977        assert_eq!(f_t1a.partial_cmp(&f_t4b), Some(Ordering::Less));
1978        assert_eq!(f_t3b.partial_cmp(&f_t4b), Some(Ordering::Less));
1979
1980        assert_eq!(f_t4b.partial_cmp(&f_t1a), Some(Ordering::Greater));
1981        assert_eq!(f_t4b.partial_cmp(&f_t3b), Some(Ordering::Greater));
1982    }
1983
1984    #[test]
1985    fn test_filter_clone() {
1986        // Test that cloning filters yields the same result regardless of
1987        // complexity.
1988        let f_t1a = filter_resolved!(f_pres(Attribute::UserId));
1989        let f_t1b = f_t1a.clone();
1990        let f_t1c = filter_resolved!(f_pres(Attribute::NonExist));
1991
1992        assert_eq!(f_t1a, f_t1b);
1993        assert!(f_t1a != f_t1c);
1994
1995        let f_t2a = filter_resolved!(f_and!([f_pres(Attribute::UserId)]));
1996        let f_t2b = f_t2a.clone();
1997        let f_t2c = filter_resolved!(f_and!([f_pres(Attribute::NonExist)]));
1998
1999        assert_eq!(f_t2a, f_t2b);
2000        assert!(f_t2a != f_t2c);
2001    }
2002
2003    #[test]
2004    fn test_lessthan_entry_filter() {
2005        let e = entry_init!(
2006            (Attribute::UserId, Value::new_iutf8("william")),
2007            (
2008                Attribute::Uuid,
2009                Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
2010            ),
2011            (Attribute::GidNumber, Value::Uint32(1000))
2012        )
2013        .into_sealed_new();
2014
2015        let f_t1a = filter_resolved!(f_lt(Attribute::GidNumber, PartialValue::new_uint32(500)));
2016        assert!(!e.entry_match_no_index(&f_t1a));
2017
2018        let f_t1b = filter_resolved!(f_lt(Attribute::GidNumber, PartialValue::new_uint32(1000)));
2019        assert!(!e.entry_match_no_index(&f_t1b));
2020
2021        let f_t1c = filter_resolved!(f_lt(Attribute::GidNumber, PartialValue::new_uint32(1001)));
2022        assert!(e.entry_match_no_index(&f_t1c));
2023    }
2024
2025    #[test]
2026    fn test_or_entry_filter() {
2027        let e = entry_init!(
2028            (Attribute::UserId, Value::new_iutf8("william")),
2029            (
2030                Attribute::Uuid,
2031                Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
2032            ),
2033            (Attribute::GidNumber, Value::Uint32(1000))
2034        )
2035        .into_sealed_new();
2036
2037        let f_t1a = filter_resolved!(f_or!([
2038            f_eq(Attribute::UserId, PartialValue::new_iutf8("william")),
2039            f_eq(Attribute::GidNumber, PartialValue::Uint32(1000)),
2040        ]));
2041        assert!(e.entry_match_no_index(&f_t1a));
2042
2043        let f_t2a = filter_resolved!(f_or!([
2044            f_eq(Attribute::UserId, PartialValue::new_iutf8("william")),
2045            f_eq(Attribute::GidNumber, PartialValue::Uint32(1000)),
2046        ]));
2047        assert!(e.entry_match_no_index(&f_t2a));
2048
2049        let f_t3a = filter_resolved!(f_or!([
2050            f_eq(Attribute::UserId, PartialValue::new_iutf8("alice")),
2051            f_eq(Attribute::GidNumber, PartialValue::Uint32(1000)),
2052        ]));
2053        assert!(e.entry_match_no_index(&f_t3a));
2054
2055        let f_t4a = filter_resolved!(f_or!([
2056            f_eq(Attribute::UserId, PartialValue::new_iutf8("alice")),
2057            f_eq(Attribute::GidNumber, PartialValue::Uint32(1001)),
2058        ]));
2059        assert!(!e.entry_match_no_index(&f_t4a));
2060    }
2061
2062    #[test]
2063    fn test_and_entry_filter() {
2064        let e = entry_init!(
2065            (Attribute::UserId, Value::new_iutf8("william")),
2066            (
2067                Attribute::Uuid,
2068                Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
2069            ),
2070            (Attribute::GidNumber, Value::Uint32(1000))
2071        )
2072        .into_sealed_new();
2073
2074        let f_t1a = filter_resolved!(f_and!([
2075            f_eq(Attribute::UserId, PartialValue::new_iutf8("william")),
2076            f_eq(Attribute::GidNumber, PartialValue::Uint32(1000)),
2077        ]));
2078        assert!(e.entry_match_no_index(&f_t1a));
2079
2080        let f_t2a = filter_resolved!(f_and!([
2081            f_eq(Attribute::UserId, PartialValue::new_iutf8("william")),
2082            f_eq(Attribute::GidNumber, PartialValue::Uint32(1001)),
2083        ]));
2084        assert!(!e.entry_match_no_index(&f_t2a));
2085
2086        let f_t3a = filter_resolved!(f_and!([
2087            f_eq(Attribute::UserId, PartialValue::new_iutf8("alice")),
2088            f_eq(Attribute::GidNumber, PartialValue::Uint32(1000)),
2089        ]));
2090        assert!(!e.entry_match_no_index(&f_t3a));
2091
2092        let f_t4a = filter_resolved!(f_and!([
2093            f_eq(Attribute::UserId, PartialValue::new_iutf8("alice")),
2094            f_eq(Attribute::GidNumber, PartialValue::Uint32(1001)),
2095        ]));
2096        assert!(!e.entry_match_no_index(&f_t4a));
2097    }
2098
2099    #[test]
2100    fn test_not_entry_filter() {
2101        let e1 = entry_init!(
2102            (Attribute::UserId, Value::new_iutf8("william")),
2103            (
2104                Attribute::Uuid,
2105                Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
2106            ),
2107            (Attribute::GidNumber, Value::Uint32(1000))
2108        )
2109        .into_sealed_new();
2110
2111        let f_t1a = filter_resolved!(f_andnot(f_eq(
2112            Attribute::UserId,
2113            PartialValue::new_iutf8("alice")
2114        )));
2115        assert!(e1.entry_match_no_index(&f_t1a));
2116
2117        let f_t2a = filter_resolved!(f_andnot(f_eq(
2118            Attribute::UserId,
2119            PartialValue::new_iutf8("william")
2120        )));
2121        assert!(!e1.entry_match_no_index(&f_t2a));
2122    }
2123
2124    #[test]
2125    fn test_nested_entry_filter() {
2126        let e1 = entry_init!(
2127            (Attribute::Class, EntryClass::Person.to_value().clone()),
2128            (
2129                Attribute::Uuid,
2130                Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
2131            ),
2132            (Attribute::GidNumber, Value::Uint32(1000))
2133        )
2134        .into_sealed_new();
2135
2136        let e2 = entry_init!(
2137            (Attribute::Class, EntryClass::Person.to_value().clone()),
2138            (
2139                Attribute::Uuid,
2140                Value::Uuid(uuid::uuid!("4b6228ab-1dbe-42a4-a9f5-f6368222438e"))
2141            ),
2142            (Attribute::GidNumber, Value::Uint32(1001))
2143        )
2144        .into_sealed_new();
2145
2146        let e3 = entry_init!(
2147            (Attribute::Class, EntryClass::Person.to_value()),
2148            (
2149                Attribute::Uuid,
2150                Value::Uuid(uuid::uuid!("7b23c99d-c06b-4a9a-a958-3afa56383e1d"))
2151            ),
2152            (Attribute::GidNumber, Value::Uint32(1002))
2153        )
2154        .into_sealed_new();
2155
2156        let e4 = entry_init!(
2157            (Attribute::Class, EntryClass::Group.to_value()),
2158            (
2159                Attribute::Uuid,
2160                Value::Uuid(uuid::uuid!("21d816b5-1f6a-4696-b7c1-6ed06d22ed81"))
2161            ),
2162            (Attribute::GidNumber, Value::Uint32(1000))
2163        )
2164        .into_sealed_new();
2165
2166        let f_t1a = filter_resolved!(f_and!([
2167            f_eq(Attribute::Class, EntryClass::Person.into()),
2168            f_or!([
2169                f_eq(Attribute::GidNumber, PartialValue::Uint32(1001)),
2170                f_eq(Attribute::GidNumber, PartialValue::Uint32(1000))
2171            ])
2172        ]));
2173
2174        assert!(e1.entry_match_no_index(&f_t1a));
2175        assert!(e2.entry_match_no_index(&f_t1a));
2176        assert!(!e3.entry_match_no_index(&f_t1a));
2177        assert!(!e4.entry_match_no_index(&f_t1a));
2178    }
2179
2180    #[test]
2181    fn test_attr_set_filter() {
2182        let mut f_expect = BTreeSet::new();
2183        f_expect.insert(Attribute::from("userid"));
2184        f_expect.insert(Attribute::Class);
2185        // Given filters, get their expected attribute sets - this is used by access control profiles
2186        // to determine what attrs we are requesting regardless of the partialvalue.
2187        let f_t1a = filter_valid!(f_and!([
2188            f_eq(Attribute::UserId, PartialValue::new_iutf8("alice")),
2189            f_eq(Attribute::Class, PartialValue::new_iutf8("1001")),
2190        ]));
2191
2192        assert_eq!(f_t1a.get_attr_set(), f_expect);
2193
2194        let f_t2a = filter_valid!(f_and!([
2195            f_eq(Attribute::UserId, PartialValue::new_iutf8("alice")),
2196            f_eq(Attribute::Class, PartialValue::new_iutf8("1001")),
2197            f_eq(Attribute::UserId, PartialValue::new_iutf8("claire")),
2198        ]));
2199
2200        assert_eq!(f_t2a.get_attr_set(), f_expect);
2201    }
2202
2203    #[qs_test]
2204    async fn test_filter_resolve_value(server: &QueryServer) {
2205        let time_p1 = duration_from_epoch_now();
2206        let time_p2 = time_p1 + Duration::from_secs(CHANGELOG_MAX_AGE * 2);
2207        let time_p3 = time_p2 + Duration::from_secs(CHANGELOG_MAX_AGE * 2);
2208
2209        let mut server_txn = server.write(time_p1).await.expect("txn");
2210
2211        let e1 = entry_init!(
2212            (Attribute::Class, EntryClass::Object.to_value()),
2213            (Attribute::Class, EntryClass::Account.to_value()),
2214            (Attribute::Class, EntryClass::Person.to_value()),
2215            (Attribute::Name, Value::new_iname("testperson1")),
2216            (
2217                Attribute::Uuid,
2218                Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2219            ),
2220            (Attribute::Description, Value::new_utf8s("testperson1")),
2221            (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2222        );
2223
2224        let e2 = entry_init!(
2225            (Attribute::Class, EntryClass::Object.to_value()),
2226            (Attribute::Class, EntryClass::Account.to_value()),
2227            (Attribute::Class, EntryClass::Person.to_value()),
2228            (Attribute::Name, Value::new_iname("testperson2")),
2229            (
2230                Attribute::Uuid,
2231                Value::Uuid(uuid::uuid!("a67c0c71-0b35-4218-a6b0-22d23d131d27"))
2232            ),
2233            (Attribute::Description, Value::new_utf8s("testperson2")),
2234            (Attribute::DisplayName, Value::new_utf8s("testperson2"))
2235        );
2236
2237        // We need to add these and then push through the state machine.
2238        let e_ts = entry_init!(
2239            (Attribute::Class, EntryClass::Object.to_value()),
2240            (Attribute::Class, EntryClass::Account.to_value()),
2241            (Attribute::Class, EntryClass::Person.to_value()),
2242            (Attribute::Name, Value::new_iname("testperson3")),
2243            (
2244                Attribute::Uuid,
2245                Value::Uuid(uuid!("9557f49c-97a5-4277-a9a5-097d17eb8317"))
2246            ),
2247            (Attribute::Description, Value::new_utf8s("testperson3")),
2248            (Attribute::DisplayName, Value::new_utf8s("testperson3"))
2249        );
2250
2251        let ce = CreateEvent::new_internal(vec![e1, e2, e_ts]);
2252        let cr = server_txn.create(&ce);
2253        assert!(cr.is_ok());
2254
2255        let de_sin = DeleteEvent::new_internal_invalid(filter!(f_or!([f_eq(
2256            Attribute::Name,
2257            PartialValue::new_iname("testperson3")
2258        )])));
2259        assert!(server_txn.delete(&de_sin).is_ok());
2260
2261        // Commit
2262        assert!(server_txn.commit().is_ok());
2263
2264        // Now, establish enough time for the recycled items to be purged.
2265        let mut server_txn = server.write(time_p2).await.expect("txn");
2266        assert!(server_txn.purge_recycled().is_ok());
2267        assert!(server_txn.commit().is_ok());
2268
2269        let mut server_txn = server.write(time_p3).await.expect("txn");
2270        assert!(server_txn.purge_tombstones().is_ok());
2271
2272        // ===== ✅ now ready to test!
2273
2274        // Resolving most times should yield expected results
2275        let t1 = vs_utf8!["teststring".to_string()] as _;
2276        let r1 = server_txn.resolve_valueset(&t1);
2277        assert_eq!(r1, Ok(vec!["teststring".to_string()]));
2278
2279        // Resolve UUID with matching spn
2280        let t_uuid = vs_refer![uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")] as _;
2281        let r_uuid = server_txn.resolve_valueset(&t_uuid);
2282        debug!("{:?}", r_uuid);
2283        assert_eq!(r_uuid, Ok(vec!["testperson1@example.com".to_string()]));
2284
2285        // Resolve UUID with matching name
2286        let t_uuid = vs_refer![uuid!("a67c0c71-0b35-4218-a6b0-22d23d131d27")] as _;
2287        let r_uuid = server_txn.resolve_valueset(&t_uuid);
2288        debug!("{:?}", r_uuid);
2289        assert_eq!(r_uuid, Ok(vec!["testperson2@example.com".to_string()]));
2290
2291        // Resolve UUID non-exist
2292        let t_uuid_non = vs_refer![uuid!("b83e98f0-3d2e-41d2-9796-d8d993289c86")] as _;
2293        let r_uuid_non = server_txn.resolve_valueset(&t_uuid_non);
2294        debug!("{:?}", r_uuid_non);
2295        assert_eq!(
2296            r_uuid_non,
2297            Ok(vec!["b83e98f0-3d2e-41d2-9796-d8d993289c86".to_string()])
2298        );
2299
2300        // Resolve UUID to tombstone/recycled (same an non-exst)
2301        let t_uuid_ts = vs_refer![uuid!("9557f49c-97a5-4277-a9a5-097d17eb8317")] as _;
2302        let r_uuid_ts = server_txn.resolve_valueset(&t_uuid_ts);
2303        debug!("{:?}", r_uuid_ts);
2304        assert_eq!(
2305            r_uuid_ts,
2306            Ok(vec!["9557f49c-97a5-4277-a9a5-097d17eb8317".to_string()])
2307        );
2308    }
2309
2310    #[qs_test]
2311    async fn test_filter_depth_limits(server: &QueryServer) {
2312        let mut r_txn = server.read().await.unwrap();
2313
2314        let mut inv_proto = ProtoFilter::Pres(Attribute::Class.to_string());
2315        for _i in 0..(DEFAULT_LIMIT_FILTER_DEPTH_MAX + 1) {
2316            inv_proto = ProtoFilter::And(vec![inv_proto]);
2317        }
2318
2319        let mut inv_ldap = LdapFilter::Present(Attribute::Class.to_string());
2320        for _i in 0..(DEFAULT_LIMIT_FILTER_DEPTH_MAX + 1) {
2321            inv_ldap = LdapFilter::And(vec![inv_ldap]);
2322        }
2323
2324        let ev = Identity::from_internal();
2325
2326        // Test proto + read
2327        let res = Filter::from_ro(&ev, &inv_proto, &mut r_txn);
2328        assert_eq!(res, Err(OperationError::ResourceLimit));
2329
2330        // ldap
2331        let res = Filter::from_ldap_ro(&ev, &inv_ldap, &mut r_txn);
2332        assert_eq!(res, Err(OperationError::ResourceLimit));
2333
2334        // Can only have one db conn at a time.
2335        std::mem::drop(r_txn);
2336
2337        // proto + write
2338        let mut wr_txn = server.write(duration_from_epoch_now()).await.expect("txn");
2339        let res = Filter::from_rw(&ev, &inv_proto, &mut wr_txn);
2340        assert_eq!(res, Err(OperationError::ResourceLimit));
2341    }
2342
2343    #[qs_test]
2344    async fn test_filter_max_element_limits(server: &QueryServer) {
2345        const LIMIT: usize = 4;
2346        let mut r_txn = server.read().await.unwrap();
2347
2348        let inv_proto = ProtoFilter::And(
2349            (0..(LIMIT * 2))
2350                .map(|_| ProtoFilter::Pres(Attribute::Class.to_string()))
2351                .collect(),
2352        );
2353
2354        let inv_ldap = LdapFilter::And(
2355            (0..(LIMIT * 2))
2356                .map(|_| LdapFilter::Present(Attribute::Class.to_string()))
2357                .collect(),
2358        );
2359
2360        let mut ev = Identity::from_internal();
2361        ev.limits_mut().filter_max_elements = LIMIT;
2362
2363        // Test proto + read
2364        let res = Filter::from_ro(&ev, &inv_proto, &mut r_txn);
2365        assert_eq!(res, Err(OperationError::ResourceLimit));
2366
2367        // ldap
2368        let res = Filter::from_ldap_ro(&ev, &inv_ldap, &mut r_txn);
2369        assert_eq!(res, Err(OperationError::ResourceLimit));
2370
2371        // Can only have one db conn at a time.
2372        std::mem::drop(r_txn);
2373
2374        // proto + write
2375        let mut wr_txn = server.write(duration_from_epoch_now()).await.expect("txn");
2376        let res = Filter::from_rw(&ev, &inv_proto, &mut wr_txn);
2377        assert_eq!(res, Err(OperationError::ResourceLimit));
2378    }
2379}