Skip to main content

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