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 => ev.get_uuid().map(|uuid| {
1506                let idxkref = IdxKeyRef::new(Attribute::Uuid.as_ref(), &IndexType::Equality);
1507                let idx = idxmeta
1508                    .get(&idxkref as &dyn IdxKeyToRef)
1509                    .copied()
1510                    .and_then(NonZeroU8::new);
1511                FilterResolved::Eq(Attribute::Uuid, PartialValue::Uuid(uuid), idx)
1512            }),
1513            FilterComp::Cnt(a, v) => {
1514                let idxkref = IdxKeyRef::new(&a, &IndexType::SubString);
1515                let idx = idxmeta
1516                    .get(&idxkref as &dyn IdxKeyToRef)
1517                    .copied()
1518                    .and_then(NonZeroU8::new);
1519                Some(FilterResolved::Cnt(a, v, idx))
1520            }
1521            FilterComp::Stw(a, v) => {
1522                let idxkref = IdxKeyRef::new(&a, &IndexType::SubString);
1523                let idx = idxmeta
1524                    .get(&idxkref as &dyn IdxKeyToRef)
1525                    .copied()
1526                    .and_then(NonZeroU8::new);
1527                Some(FilterResolved::Stw(a, v, idx))
1528            }
1529            FilterComp::Enw(a, v) => {
1530                let idxkref = IdxKeyRef::new(&a, &IndexType::SubString);
1531                let idx = idxmeta
1532                    .get(&idxkref as &dyn IdxKeyToRef)
1533                    .copied()
1534                    .and_then(NonZeroU8::new);
1535                Some(FilterResolved::Enw(a, v, idx))
1536            }
1537            FilterComp::Pres(a) => {
1538                let idxkref = IdxKeyRef::new(&a, &IndexType::Presence);
1539                let idx = idxmeta
1540                    .get(&idxkref as &dyn IdxKeyToRef)
1541                    .copied()
1542                    .and_then(NonZeroU8::new);
1543                Some(FilterResolved::Pres(a, idx))
1544            }
1545            FilterComp::LessThan(a, v) => {
1546                let idxkref = IdxKeyRef::new(&a, &IndexType::Ordering);
1547                let idx = idxmeta
1548                    .get(&idxkref as &dyn IdxKeyToRef)
1549                    .copied()
1550                    .and_then(NonZeroU8::new);
1551                Some(FilterResolved::LessThan(a, v, idx))
1552            }
1553            // We set the compound filters slope factor to "None" here, because when we do
1554            // optimise we'll actually fill in the correct slope factors after we sort those
1555            // inner terms in a more optimal way.
1556            FilterComp::Or(vs) => {
1557                let fi: Option<Vec<_>> = vs
1558                    .into_iter()
1559                    .map(|f| FilterResolved::resolve_idx(f, ev, idxmeta))
1560                    .collect();
1561                fi.map(|fi| FilterResolved::Or(fi, None))
1562            }
1563            FilterComp::And(vs) => {
1564                let fi: Option<Vec<_>> = vs
1565                    .into_iter()
1566                    .map(|f| FilterResolved::resolve_idx(f, ev, idxmeta))
1567                    .collect();
1568                fi.map(|fi| FilterResolved::And(fi, None))
1569            }
1570            FilterComp::Inclusion(vs) => {
1571                let fi: Option<Vec<_>> = vs
1572                    .into_iter()
1573                    .map(|f| FilterResolved::resolve_idx(f, ev, idxmeta))
1574                    .collect();
1575                fi.map(|fi| FilterResolved::Inclusion(fi, None))
1576            }
1577            FilterComp::AndNot(f) => {
1578                // TODO: pattern match box here. (AndNot(box f)).
1579                // We have to clone f into our space here because pattern matching can
1580                // not today remove the box, and we need f in our ownership. Since
1581                // AndNot currently is a rare request, cloning is not the worst thing
1582                // here ...
1583                FilterResolved::resolve_idx((*f).clone(), ev, idxmeta)
1584                    .map(|fi| FilterResolved::AndNot(Box::new(fi), None))
1585            }
1586            FilterComp::Invalid(attr) => Some(FilterResolved::Invalid(attr)),
1587        }
1588    }
1589
1590    fn resolve_no_idx(fc: FilterComp, ev: &Identity) -> Option<Self> {
1591        // ⚠️  ⚠️  ⚠️  ⚠️
1592        // Remember, this function means we have NO INDEX METADATA so we can only
1593        // assign slopes to values we can GUARANTEE will EXIST.
1594        match fc {
1595            FilterComp::Eq(a, v) => {
1596                // Since we have no index data, we manually configure a reasonable
1597                // slope and indicate the presence of some expected basic
1598                // indexes.
1599                let idx = matches!(a.as_str(), ATTR_NAME | ATTR_UUID);
1600                let idx = NonZeroU8::new(idx as u8);
1601                Some(FilterResolved::Eq(a, v, idx))
1602            }
1603            FilterComp::SelfUuid => ev.get_uuid().map(|uuid| {
1604                FilterResolved::Eq(
1605                    Attribute::Uuid,
1606                    PartialValue::Uuid(uuid),
1607                    NonZeroU8::new(true as u8),
1608                )
1609            }),
1610            FilterComp::Cnt(a, v) => Some(FilterResolved::Cnt(a, v, None)),
1611            FilterComp::Stw(a, v) => Some(FilterResolved::Stw(a, v, None)),
1612            FilterComp::Enw(a, v) => Some(FilterResolved::Enw(a, v, None)),
1613            FilterComp::Pres(a) => Some(FilterResolved::Pres(a, None)),
1614            FilterComp::LessThan(a, v) => Some(FilterResolved::LessThan(a, v, None)),
1615            FilterComp::Or(vs) => {
1616                let fi: Option<Vec<_>> = vs
1617                    .into_iter()
1618                    .map(|f| FilterResolved::resolve_no_idx(f, ev))
1619                    .collect();
1620                fi.map(|fi| FilterResolved::Or(fi, None))
1621            }
1622            FilterComp::And(vs) => {
1623                let fi: Option<Vec<_>> = vs
1624                    .into_iter()
1625                    .map(|f| FilterResolved::resolve_no_idx(f, ev))
1626                    .collect();
1627                fi.map(|fi| FilterResolved::And(fi, None))
1628            }
1629            FilterComp::Inclusion(vs) => {
1630                let fi: Option<Vec<_>> = vs
1631                    .into_iter()
1632                    .map(|f| FilterResolved::resolve_no_idx(f, ev))
1633                    .collect();
1634                fi.map(|fi| FilterResolved::Inclusion(fi, None))
1635            }
1636            FilterComp::AndNot(f) => {
1637                // TODO: pattern match box here. (AndNot(box f)).
1638                // We have to clone f into our space here because pattern matching can
1639                // not today remove the box, and we need f in our ownership. Since
1640                // AndNot currently is a rare request, cloning is not the worst thing
1641                // here ...
1642                FilterResolved::resolve_no_idx((*f).clone(), ev)
1643                    .map(|fi| FilterResolved::AndNot(Box::new(fi), None))
1644            }
1645            FilterComp::Invalid(attr) => Some(FilterResolved::Invalid(attr)),
1646        }
1647    }
1648
1649    // This is an optimise that only attempts to optimise the outer terms.
1650    fn fast_optimise(self) -> Self {
1651        match self {
1652            FilterResolved::Inclusion(mut f_list, _) => {
1653                f_list.sort_unstable();
1654                f_list.dedup();
1655                let sf = f_list.last().and_then(|f| f.get_slopeyness_factor());
1656                FilterResolved::Inclusion(f_list, sf)
1657            }
1658            FilterResolved::And(mut f_list, _) => {
1659                f_list.sort_unstable();
1660                f_list.dedup();
1661                let sf = f_list.first().and_then(|f| f.get_slopeyness_factor());
1662                FilterResolved::And(f_list, sf)
1663            }
1664            v => v,
1665        }
1666    }
1667
1668    fn optimise(&self) -> Self {
1669        // Most optimisations only matter around or/and terms.
1670        match self {
1671            FilterResolved::Inclusion(f_list, _) => {
1672                // first, optimise all our inner elements
1673                let (f_list_inc, mut f_list_new): (Vec<_>, Vec<_>) = f_list
1674                    .iter()
1675                    .map(|f_ref| f_ref.optimise())
1676                    .partition(|f| matches!(f, FilterResolved::Inclusion(_, _)));
1677
1678                f_list_inc.into_iter().for_each(|fc| {
1679                    if let FilterResolved::Inclusion(mut l, _) = fc {
1680                        f_list_new.append(&mut l)
1681                    }
1682                });
1683                // finally, optimise this list by sorting.
1684                f_list_new.sort_unstable();
1685                f_list_new.dedup();
1686                // Inclusions are similar to or, so what's our worst case?
1687                let sf = f_list_new.last().and_then(|f| f.get_slopeyness_factor());
1688                FilterResolved::Inclusion(f_list_new, sf)
1689            }
1690            FilterResolved::And(f_list, _) => {
1691                // first, optimise all our inner elements
1692                let (f_list_and, mut f_list_new): (Vec<_>, Vec<_>) = f_list
1693                    .iter()
1694                    .map(|f_ref| f_ref.optimise())
1695                    .partition(|f| matches!(f, FilterResolved::And(_, _)));
1696
1697                // now, iterate over this list - for each "and" term, fold
1698                // it's elements to this level.
1699                // This is one of the most important improvements because it means
1700                // that we can compare terms such that:
1701                //
1702                // (&(class=*)(&(uid=foo)))
1703                // if we did not and fold, this would remain as is. However, by and
1704                // folding, we can optimise to:
1705                // (&(uid=foo)(class=*))
1706                // Which will be faster when indexed as the uid=foo will trigger
1707                // shortcutting
1708
1709                f_list_and.into_iter().for_each(|fc| {
1710                    if let FilterResolved::And(mut l, _) = fc {
1711                        f_list_new.append(&mut l)
1712                    }
1713                });
1714
1715                // If the f_list_and only has one element, pop it and return.
1716                if f_list_new.len() == 1 {
1717                    f_list_new.remove(0)
1718                } else {
1719                    // finally, optimise this list by sorting.
1720                    f_list_new.sort_unstable();
1721                    f_list_new.dedup();
1722                    // Which ever element as the head is first must be the min SF
1723                    // so we use this in our And to represent the "best possible" value
1724                    // of how indexes will perform.
1725                    let sf = f_list_new.first().and_then(|f| f.get_slopeyness_factor());
1726                    //
1727                    // return!
1728                    FilterResolved::And(f_list_new, sf)
1729                }
1730            }
1731            FilterResolved::Or(f_list, _) => {
1732                let (f_list_or, mut f_list_new): (Vec<_>, Vec<_>) = f_list
1733                    .iter()
1734                    // Optimise all inner items.
1735                    .map(|f_ref| f_ref.optimise())
1736                    // Split out inner-or terms to fold into this term.
1737                    .partition(|f| matches!(f, FilterResolved::Or(_, _)));
1738
1739                // Append the inner terms.
1740                f_list_or.into_iter().for_each(|fc| {
1741                    if let FilterResolved::Or(mut l, _) = fc {
1742                        f_list_new.append(&mut l)
1743                    }
1744                });
1745
1746                // If the f_list_or only has one element, pop it and return.
1747                if f_list_new.len() == 1 {
1748                    f_list_new.remove(0)
1749                } else {
1750                    // sort, but reverse so that sub-optimal elements are earlier
1751                    // to promote fast-failure.
1752                    // We have to allow this on clippy, which attempts to suggest:
1753                    //   f_list_new.sort_unstable_by_key(|&b| Reverse(b))
1754                    // However, because these are references, it causes a lifetime
1755                    // issue, and fails to compile.
1756                    #[allow(clippy::unnecessary_sort_by)]
1757                    f_list_new.sort_unstable_by(|a, b| b.cmp(a));
1758                    f_list_new.dedup();
1759                    // The *last* element is the most here, and our worst case factor.
1760                    let sf = f_list_new.last().and_then(|f| f.get_slopeyness_factor());
1761                    FilterResolved::Or(f_list_new, sf)
1762                }
1763            }
1764            f => f.clone(),
1765        }
1766    }
1767
1768    pub fn is_andnot(&self) -> bool {
1769        matches!(self, FilterResolved::AndNot(_, _))
1770    }
1771
1772    #[inline(always)]
1773    fn get_slopeyness_factor(&self) -> Option<NonZeroU8> {
1774        match self {
1775            FilterResolved::Eq(_, _, sf)
1776            | FilterResolved::Cnt(_, _, sf)
1777            | FilterResolved::Stw(_, _, sf)
1778            | FilterResolved::Enw(_, _, sf)
1779            | FilterResolved::Pres(_, sf)
1780            | FilterResolved::LessThan(_, _, sf)
1781            | FilterResolved::Or(_, sf)
1782            | FilterResolved::And(_, sf)
1783            | FilterResolved::Inclusion(_, sf)
1784            | FilterResolved::AndNot(_, sf) => *sf,
1785            // We hard code 1 because there is no slope for an invalid filter
1786            FilterResolved::Invalid(_) => NonZeroU8::new(1),
1787        }
1788    }
1789}
1790
1791#[cfg(test)]
1792mod tests {
1793    use std::cmp::{Ordering, PartialOrd};
1794    use std::collections::BTreeSet;
1795    use std::time::Duration;
1796
1797    use kanidm_proto::internal::Filter as ProtoFilter;
1798    use ldap3_proto::simple::LdapFilter;
1799
1800    use crate::event::{CreateEvent, DeleteEvent};
1801    use crate::filter::{Filter, FilterInvalid, DEFAULT_LIMIT_FILTER_DEPTH_MAX};
1802    use crate::prelude::*;
1803
1804    #[test]
1805    fn test_filter_simple() {
1806        // Test construction.
1807        let _filt: Filter<FilterInvalid> = filter!(f_eq(Attribute::Class, EntryClass::User.into()));
1808
1809        // AFTER
1810        let _complex_filt: Filter<FilterInvalid> = filter!(f_and!([
1811            f_or!([
1812                f_eq(Attribute::UserId, PartialValue::new_iutf8("test_a")),
1813                f_eq(Attribute::UserId, PartialValue::new_iutf8("test_b")),
1814            ]),
1815            f_sub(Attribute::Class, EntryClass::User.into()),
1816        ]));
1817    }
1818
1819    macro_rules! filter_optimise_assert {
1820        (
1821            $init:expr,
1822            $expect:expr
1823        ) => {{
1824            #[allow(unused_imports)]
1825            use crate::filter::{f_and, f_andnot, f_eq, f_or, f_pres, f_sub};
1826            use crate::filter::{Filter, FilterInvalid};
1827            let f_init: Filter<FilterInvalid> = Filter::new($init);
1828            let f_expect: Filter<FilterInvalid> = Filter::new($expect);
1829            // Create a resolved filter, via the most unsafe means possible!
1830            let f_init_r = f_init.into_valid_resolved();
1831            let f_init_o = f_init_r.optimise();
1832            let f_init_e = f_expect.into_valid_resolved();
1833            debug!("--");
1834            debug!("init   --> {:?}", f_init_r);
1835            debug!("opt    --> {:?}", f_init_o);
1836            debug!("expect --> {:?}", f_init_e);
1837            assert_eq!(f_init_o, f_init_e);
1838        }};
1839    }
1840
1841    #[test]
1842    fn test_filter_optimise() {
1843        sketching::test_init();
1844        // Given sets of "optimisable" filters, optimise them.
1845        filter_optimise_assert!(
1846            f_and(vec![f_and(vec![f_eq(
1847                Attribute::Class,
1848                EntryClass::TestClass.into()
1849            )])]),
1850            f_eq(Attribute::Class, EntryClass::TestClass.into())
1851        );
1852
1853        filter_optimise_assert!(
1854            f_or(vec![f_or(vec![f_eq(
1855                Attribute::Class,
1856                EntryClass::TestClass.into()
1857            )])]),
1858            f_eq(Attribute::Class, EntryClass::TestClass.into())
1859        );
1860
1861        filter_optimise_assert!(
1862            f_and(vec![f_or(vec![f_and(vec![f_eq(
1863                Attribute::Class,
1864                EntryClass::TestClass.to_partialvalue()
1865            )])])]),
1866            f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue())
1867        );
1868
1869        // Later this can test duplicate filter detection.
1870        filter_optimise_assert!(
1871            f_and(vec![
1872                f_and(vec![f_eq(
1873                    Attribute::Class,
1874                    EntryClass::TestClass.to_partialvalue()
1875                )]),
1876                f_sub(Attribute::Class, PartialValue::new_iutf8("te")),
1877                f_pres(Attribute::Class),
1878                f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue())
1879            ]),
1880            f_and(vec![
1881                f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue()),
1882                f_pres(Attribute::Class),
1883                f_sub(Attribute::Class, PartialValue::new_iutf8("te")),
1884            ])
1885        );
1886
1887        // Test dedup removes only the correct element despite padding.
1888        filter_optimise_assert!(
1889            f_and(vec![
1890                f_and(vec![
1891                    f_eq(Attribute::Class, PartialValue::new_iutf8("foo")),
1892                    f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue()),
1893                    f_eq(Attribute::Uid, PartialValue::new_iutf8("bar")),
1894                ]),
1895                f_sub(Attribute::Class, PartialValue::new_iutf8("te")),
1896                f_pres(Attribute::Class),
1897                f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue())
1898            ]),
1899            f_and(vec![
1900                f_eq(Attribute::Class, PartialValue::new_iutf8("foo")),
1901                f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue()),
1902                f_pres(Attribute::Class),
1903                f_eq(Attribute::Uid, PartialValue::new_iutf8("bar")),
1904                f_sub(Attribute::Class, PartialValue::new_iutf8("te")),
1905            ])
1906        );
1907
1908        filter_optimise_assert!(
1909            f_or(vec![
1910                f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue()),
1911                f_pres(Attribute::Class),
1912                f_sub(Attribute::Class, PartialValue::new_iutf8("te")),
1913                f_or(vec![f_eq(
1914                    Attribute::Class,
1915                    EntryClass::TestClass.to_partialvalue()
1916                )]),
1917            ]),
1918            f_or(vec![
1919                f_sub(Attribute::Class, PartialValue::new_iutf8("te")),
1920                f_pres(Attribute::Class),
1921                f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue())
1922            ])
1923        );
1924
1925        // Test dedup doesn't affect nested items incorrectly.
1926        filter_optimise_assert!(
1927            f_or(vec![
1928                f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue()),
1929                f_and(vec![
1930                    f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue()),
1931                    f_eq(Attribute::Term, EntryClass::TestClass.to_partialvalue()),
1932                    f_or(vec![f_eq(
1933                        Attribute::Class,
1934                        EntryClass::TestClass.to_partialvalue()
1935                    )])
1936                ]),
1937            ]),
1938            f_or(vec![
1939                f_and(vec![
1940                    f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue()),
1941                    f_eq(Attribute::Term, EntryClass::TestClass.to_partialvalue())
1942                ]),
1943                f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue()),
1944            ])
1945        );
1946    }
1947
1948    #[test]
1949    fn test_filter_eq() {
1950        let f_t1a = filter!(f_pres(Attribute::UserId));
1951        let f_t1b = filter!(f_pres(Attribute::UserId));
1952        let f_t1c = filter!(f_pres(Attribute::NonExist));
1953
1954        assert_eq!(f_t1a, f_t1b);
1955        assert!(f_t1a != f_t1c);
1956        assert!(f_t1b != f_t1c);
1957
1958        let f_t2a = filter!(f_and!([f_pres(Attribute::UserId)]));
1959        let f_t2b = filter!(f_and!([f_pres(Attribute::UserId)]));
1960        let f_t2c = filter!(f_and!([f_pres(Attribute::NonExist)]));
1961        assert_eq!(f_t2a, f_t2b);
1962        assert!(f_t2a != f_t2c);
1963        assert!(f_t2b != f_t2c);
1964
1965        assert!(f_t2c != f_t1a);
1966        assert!(f_t2c != f_t1c);
1967    }
1968
1969    #[test]
1970    fn test_filter_ord() {
1971        // Test that we uphold the rules of partialOrd
1972        // Basic equality
1973        // Test the two major paths here (str vs list)
1974        let f_t1a = filter_resolved!(f_pres(Attribute::UserId));
1975        let f_t1b = filter_resolved!(f_pres(Attribute::UserId));
1976
1977        assert_eq!(f_t1a.partial_cmp(&f_t1b), Some(Ordering::Equal));
1978        assert_eq!(f_t1b.partial_cmp(&f_t1a), Some(Ordering::Equal));
1979
1980        let f_t2a = filter_resolved!(f_and!([]));
1981        let f_t2b = filter_resolved!(f_and!([]));
1982        assert_eq!(f_t2a.partial_cmp(&f_t2b), Some(Ordering::Equal));
1983        assert_eq!(f_t2b.partial_cmp(&f_t2a), Some(Ordering::Equal));
1984
1985        // antisymmetry: if a < b then !(a > b), as well as a > b implying !(a < b); and
1986        // These are unindexed so we have to check them this way.
1987        let f_t3b = filter_resolved!(f_eq(Attribute::UserId, PartialValue::new_iutf8("")));
1988        assert_eq!(f_t1a.partial_cmp(&f_t3b), Some(Ordering::Greater));
1989        assert_eq!(f_t3b.partial_cmp(&f_t1a), Some(Ordering::Less));
1990
1991        // transitivity: a < b and b < c implies a < c. The same must hold for both == and >.
1992        let f_t4b = filter_resolved!(f_sub(Attribute::UserId, PartialValue::new_iutf8("")));
1993        assert_eq!(f_t1a.partial_cmp(&f_t4b), Some(Ordering::Less));
1994        assert_eq!(f_t3b.partial_cmp(&f_t4b), Some(Ordering::Less));
1995
1996        assert_eq!(f_t4b.partial_cmp(&f_t1a), Some(Ordering::Greater));
1997        assert_eq!(f_t4b.partial_cmp(&f_t3b), Some(Ordering::Greater));
1998    }
1999
2000    #[test]
2001    fn test_filter_clone() {
2002        // Test that cloning filters yields the same result regardless of
2003        // complexity.
2004        let f_t1a = filter_resolved!(f_pres(Attribute::UserId));
2005        let f_t1b = f_t1a.clone();
2006        let f_t1c = filter_resolved!(f_pres(Attribute::NonExist));
2007
2008        assert_eq!(f_t1a, f_t1b);
2009        assert!(f_t1a != f_t1c);
2010
2011        let f_t2a = filter_resolved!(f_and!([f_pres(Attribute::UserId)]));
2012        let f_t2b = f_t2a.clone();
2013        let f_t2c = filter_resolved!(f_and!([f_pres(Attribute::NonExist)]));
2014
2015        assert_eq!(f_t2a, f_t2b);
2016        assert!(f_t2a != f_t2c);
2017    }
2018
2019    #[test]
2020    fn test_lessthan_entry_filter() {
2021        let e = entry_init!(
2022            (Attribute::UserId, Value::new_iutf8("william")),
2023            (
2024                Attribute::Uuid,
2025                Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
2026            ),
2027            (Attribute::GidNumber, Value::Uint32(1000))
2028        )
2029        .into_sealed_new();
2030
2031        let f_t1a = filter_resolved!(f_lt(Attribute::GidNumber, PartialValue::new_uint32(500)));
2032        assert!(!e.entry_match_no_index(&f_t1a));
2033
2034        let f_t1b = filter_resolved!(f_lt(Attribute::GidNumber, PartialValue::new_uint32(1000)));
2035        assert!(!e.entry_match_no_index(&f_t1b));
2036
2037        let f_t1c = filter_resolved!(f_lt(Attribute::GidNumber, PartialValue::new_uint32(1001)));
2038        assert!(e.entry_match_no_index(&f_t1c));
2039    }
2040
2041    #[test]
2042    fn test_or_entry_filter() {
2043        let e = entry_init!(
2044            (Attribute::UserId, Value::new_iutf8("william")),
2045            (
2046                Attribute::Uuid,
2047                Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
2048            ),
2049            (Attribute::GidNumber, Value::Uint32(1000))
2050        )
2051        .into_sealed_new();
2052
2053        let f_t1a = filter_resolved!(f_or!([
2054            f_eq(Attribute::UserId, PartialValue::new_iutf8("william")),
2055            f_eq(Attribute::GidNumber, PartialValue::Uint32(1000)),
2056        ]));
2057        assert!(e.entry_match_no_index(&f_t1a));
2058
2059        let f_t2a = 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_t2a));
2064
2065        let f_t3a = filter_resolved!(f_or!([
2066            f_eq(Attribute::UserId, PartialValue::new_iutf8("alice")),
2067            f_eq(Attribute::GidNumber, PartialValue::Uint32(1000)),
2068        ]));
2069        assert!(e.entry_match_no_index(&f_t3a));
2070
2071        let f_t4a = filter_resolved!(f_or!([
2072            f_eq(Attribute::UserId, PartialValue::new_iutf8("alice")),
2073            f_eq(Attribute::GidNumber, PartialValue::Uint32(1001)),
2074        ]));
2075        assert!(!e.entry_match_no_index(&f_t4a));
2076    }
2077
2078    #[test]
2079    fn test_and_entry_filter() {
2080        let e = entry_init!(
2081            (Attribute::UserId, Value::new_iutf8("william")),
2082            (
2083                Attribute::Uuid,
2084                Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
2085            ),
2086            (Attribute::GidNumber, Value::Uint32(1000))
2087        )
2088        .into_sealed_new();
2089
2090        let f_t1a = filter_resolved!(f_and!([
2091            f_eq(Attribute::UserId, PartialValue::new_iutf8("william")),
2092            f_eq(Attribute::GidNumber, PartialValue::Uint32(1000)),
2093        ]));
2094        assert!(e.entry_match_no_index(&f_t1a));
2095
2096        let f_t2a = filter_resolved!(f_and!([
2097            f_eq(Attribute::UserId, PartialValue::new_iutf8("william")),
2098            f_eq(Attribute::GidNumber, PartialValue::Uint32(1001)),
2099        ]));
2100        assert!(!e.entry_match_no_index(&f_t2a));
2101
2102        let f_t3a = filter_resolved!(f_and!([
2103            f_eq(Attribute::UserId, PartialValue::new_iutf8("alice")),
2104            f_eq(Attribute::GidNumber, PartialValue::Uint32(1000)),
2105        ]));
2106        assert!(!e.entry_match_no_index(&f_t3a));
2107
2108        let f_t4a = filter_resolved!(f_and!([
2109            f_eq(Attribute::UserId, PartialValue::new_iutf8("alice")),
2110            f_eq(Attribute::GidNumber, PartialValue::Uint32(1001)),
2111        ]));
2112        assert!(!e.entry_match_no_index(&f_t4a));
2113    }
2114
2115    #[test]
2116    fn test_not_entry_filter() {
2117        let e1 = entry_init!(
2118            (Attribute::UserId, Value::new_iutf8("william")),
2119            (
2120                Attribute::Uuid,
2121                Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
2122            ),
2123            (Attribute::GidNumber, Value::Uint32(1000))
2124        )
2125        .into_sealed_new();
2126
2127        let f_t1a = filter_resolved!(f_andnot(f_eq(
2128            Attribute::UserId,
2129            PartialValue::new_iutf8("alice")
2130        )));
2131        assert!(e1.entry_match_no_index(&f_t1a));
2132
2133        let f_t2a = filter_resolved!(f_andnot(f_eq(
2134            Attribute::UserId,
2135            PartialValue::new_iutf8("william")
2136        )));
2137        assert!(!e1.entry_match_no_index(&f_t2a));
2138    }
2139
2140    #[test]
2141    fn test_nested_entry_filter() {
2142        let e1 = entry_init!(
2143            (Attribute::Class, EntryClass::Person.to_value().clone()),
2144            (
2145                Attribute::Uuid,
2146                Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
2147            ),
2148            (Attribute::GidNumber, Value::Uint32(1000))
2149        )
2150        .into_sealed_new();
2151
2152        let e2 = entry_init!(
2153            (Attribute::Class, EntryClass::Person.to_value().clone()),
2154            (
2155                Attribute::Uuid,
2156                Value::Uuid(uuid::uuid!("4b6228ab-1dbe-42a4-a9f5-f6368222438e"))
2157            ),
2158            (Attribute::GidNumber, Value::Uint32(1001))
2159        )
2160        .into_sealed_new();
2161
2162        let e3 = entry_init!(
2163            (Attribute::Class, EntryClass::Person.to_value()),
2164            (
2165                Attribute::Uuid,
2166                Value::Uuid(uuid::uuid!("7b23c99d-c06b-4a9a-a958-3afa56383e1d"))
2167            ),
2168            (Attribute::GidNumber, Value::Uint32(1002))
2169        )
2170        .into_sealed_new();
2171
2172        let e4 = entry_init!(
2173            (Attribute::Class, EntryClass::Group.to_value()),
2174            (
2175                Attribute::Uuid,
2176                Value::Uuid(uuid::uuid!("21d816b5-1f6a-4696-b7c1-6ed06d22ed81"))
2177            ),
2178            (Attribute::GidNumber, Value::Uint32(1000))
2179        )
2180        .into_sealed_new();
2181
2182        let f_t1a = filter_resolved!(f_and!([
2183            f_eq(Attribute::Class, EntryClass::Person.into()),
2184            f_or!([
2185                f_eq(Attribute::GidNumber, PartialValue::Uint32(1001)),
2186                f_eq(Attribute::GidNumber, PartialValue::Uint32(1000))
2187            ])
2188        ]));
2189
2190        assert!(e1.entry_match_no_index(&f_t1a));
2191        assert!(e2.entry_match_no_index(&f_t1a));
2192        assert!(!e3.entry_match_no_index(&f_t1a));
2193        assert!(!e4.entry_match_no_index(&f_t1a));
2194    }
2195
2196    #[test]
2197    fn test_attr_set_filter() {
2198        let mut f_expect = BTreeSet::new();
2199        f_expect.insert(Attribute::from("userid"));
2200        f_expect.insert(Attribute::Class);
2201        // Given filters, get their expected attribute sets - this is used by access control profiles
2202        // to determine what attrs we are requesting regardless of the partialvalue.
2203        let f_t1a = filter_valid!(f_and!([
2204            f_eq(Attribute::UserId, PartialValue::new_iutf8("alice")),
2205            f_eq(Attribute::Class, PartialValue::new_iutf8("1001")),
2206        ]));
2207
2208        assert_eq!(f_t1a.get_attr_set(), f_expect);
2209
2210        let f_t2a = filter_valid!(f_and!([
2211            f_eq(Attribute::UserId, PartialValue::new_iutf8("alice")),
2212            f_eq(Attribute::Class, PartialValue::new_iutf8("1001")),
2213            f_eq(Attribute::UserId, PartialValue::new_iutf8("claire")),
2214        ]));
2215
2216        assert_eq!(f_t2a.get_attr_set(), f_expect);
2217    }
2218
2219    #[qs_test]
2220    async fn test_filter_resolve_value(server: &QueryServer) {
2221        let time_p1 = duration_from_epoch_now();
2222        let time_p2 = time_p1 + Duration::from_secs(CHANGELOG_MAX_AGE * 2);
2223        let time_p3 = time_p2 + Duration::from_secs(CHANGELOG_MAX_AGE * 2);
2224
2225        let mut server_txn = server.write(time_p1).await.expect("txn");
2226
2227        let e1 = entry_init!(
2228            (Attribute::Class, EntryClass::Object.to_value()),
2229            (Attribute::Class, EntryClass::Account.to_value()),
2230            (Attribute::Class, EntryClass::Person.to_value()),
2231            (Attribute::Name, Value::new_iname("testperson1")),
2232            (
2233                Attribute::Uuid,
2234                Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2235            ),
2236            (Attribute::Description, Value::new_utf8s("testperson1")),
2237            (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2238        );
2239
2240        let e2 = entry_init!(
2241            (Attribute::Class, EntryClass::Object.to_value()),
2242            (Attribute::Class, EntryClass::Account.to_value()),
2243            (Attribute::Class, EntryClass::Person.to_value()),
2244            (Attribute::Name, Value::new_iname("testperson2")),
2245            (
2246                Attribute::Uuid,
2247                Value::Uuid(uuid::uuid!("a67c0c71-0b35-4218-a6b0-22d23d131d27"))
2248            ),
2249            (Attribute::Description, Value::new_utf8s("testperson2")),
2250            (Attribute::DisplayName, Value::new_utf8s("testperson2"))
2251        );
2252
2253        // We need to add these and then push through the state machine.
2254        let e_ts = entry_init!(
2255            (Attribute::Class, EntryClass::Object.to_value()),
2256            (Attribute::Class, EntryClass::Account.to_value()),
2257            (Attribute::Class, EntryClass::Person.to_value()),
2258            (Attribute::Name, Value::new_iname("testperson3")),
2259            (
2260                Attribute::Uuid,
2261                Value::Uuid(uuid!("9557f49c-97a5-4277-a9a5-097d17eb8317"))
2262            ),
2263            (Attribute::Description, Value::new_utf8s("testperson3")),
2264            (Attribute::DisplayName, Value::new_utf8s("testperson3"))
2265        );
2266
2267        let ce = CreateEvent::new_internal(vec![e1, e2, e_ts]);
2268        let cr = server_txn.create(&ce);
2269        assert!(cr.is_ok());
2270
2271        let de_sin = DeleteEvent::new_internal_invalid(filter!(f_or!([f_eq(
2272            Attribute::Name,
2273            PartialValue::new_iname("testperson3")
2274        )])));
2275        assert!(server_txn.delete(&de_sin).is_ok());
2276
2277        // Commit
2278        assert!(server_txn.commit().is_ok());
2279
2280        // Now, establish enough time for the recycled items to be purged.
2281        let mut server_txn = server.write(time_p2).await.expect("txn");
2282        assert!(server_txn.purge_recycled().is_ok());
2283        assert!(server_txn.commit().is_ok());
2284
2285        let mut server_txn = server.write(time_p3).await.expect("txn");
2286        assert!(server_txn.purge_tombstones().is_ok());
2287
2288        // ===== ✅ now ready to test!
2289
2290        // Resolving most times should yield expected results
2291        let t1 = vs_utf8!["teststring".to_string()] as _;
2292        let r1 = server_txn.resolve_valueset(&t1);
2293        assert_eq!(r1, Ok(vec!["teststring".to_string()]));
2294
2295        // Resolve UUID with matching spn
2296        let t_uuid = vs_refer![uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")] as _;
2297        let r_uuid = server_txn.resolve_valueset(&t_uuid);
2298        debug!("{:?}", r_uuid);
2299        assert_eq!(r_uuid, Ok(vec!["testperson1@example.com".to_string()]));
2300
2301        // Resolve UUID with matching name
2302        let t_uuid = vs_refer![uuid!("a67c0c71-0b35-4218-a6b0-22d23d131d27")] as _;
2303        let r_uuid = server_txn.resolve_valueset(&t_uuid);
2304        debug!("{:?}", r_uuid);
2305        assert_eq!(r_uuid, Ok(vec!["testperson2@example.com".to_string()]));
2306
2307        // Resolve UUID non-exist
2308        let t_uuid_non = vs_refer![uuid!("b83e98f0-3d2e-41d2-9796-d8d993289c86")] as _;
2309        let r_uuid_non = server_txn.resolve_valueset(&t_uuid_non);
2310        debug!("{:?}", r_uuid_non);
2311        assert_eq!(
2312            r_uuid_non,
2313            Ok(vec!["b83e98f0-3d2e-41d2-9796-d8d993289c86".to_string()])
2314        );
2315
2316        // Resolve UUID to tombstone/recycled (same an non-exst)
2317        let t_uuid_ts = vs_refer![uuid!("9557f49c-97a5-4277-a9a5-097d17eb8317")] as _;
2318        let r_uuid_ts = server_txn.resolve_valueset(&t_uuid_ts);
2319        debug!("{:?}", r_uuid_ts);
2320        assert_eq!(
2321            r_uuid_ts,
2322            Ok(vec!["9557f49c-97a5-4277-a9a5-097d17eb8317".to_string()])
2323        );
2324    }
2325
2326    #[qs_test]
2327    async fn test_filter_depth_limits(server: &QueryServer) {
2328        let mut r_txn = server.read().await.unwrap();
2329
2330        let mut inv_proto = ProtoFilter::Pres(Attribute::Class.to_string());
2331        for _i in 0..(DEFAULT_LIMIT_FILTER_DEPTH_MAX + 1) {
2332            inv_proto = ProtoFilter::And(vec![inv_proto]);
2333        }
2334
2335        let mut inv_ldap = LdapFilter::Present(Attribute::Class.to_string());
2336        for _i in 0..(DEFAULT_LIMIT_FILTER_DEPTH_MAX + 1) {
2337            inv_ldap = LdapFilter::And(vec![inv_ldap]);
2338        }
2339
2340        let ev = Identity::from_internal();
2341
2342        // Test proto + read
2343        let res = Filter::from_ro(&ev, &inv_proto, &mut r_txn);
2344        assert_eq!(res, Err(OperationError::ResourceLimit));
2345
2346        // ldap
2347        let res = Filter::from_ldap_ro(&ev, &inv_ldap, &mut r_txn);
2348        assert_eq!(res, Err(OperationError::ResourceLimit));
2349
2350        // Can only have one db conn at a time.
2351        std::mem::drop(r_txn);
2352
2353        // proto + write
2354        let mut wr_txn = server.write(duration_from_epoch_now()).await.expect("txn");
2355        let res = Filter::from_rw(&ev, &inv_proto, &mut wr_txn);
2356        assert_eq!(res, Err(OperationError::ResourceLimit));
2357    }
2358
2359    #[qs_test]
2360    async fn test_filter_max_element_limits(server: &QueryServer) {
2361        const LIMIT: usize = 4;
2362        let mut r_txn = server.read().await.unwrap();
2363
2364        let inv_proto = ProtoFilter::And(
2365            (0..(LIMIT * 2))
2366                .map(|_| ProtoFilter::Pres(Attribute::Class.to_string()))
2367                .collect(),
2368        );
2369
2370        let inv_ldap = LdapFilter::And(
2371            (0..(LIMIT * 2))
2372                .map(|_| LdapFilter::Present(Attribute::Class.to_string()))
2373                .collect(),
2374        );
2375
2376        let mut ev = Identity::from_internal();
2377        ev.limits_mut().filter_max_elements = LIMIT;
2378
2379        // Test proto + read
2380        let res = Filter::from_ro(&ev, &inv_proto, &mut r_txn);
2381        assert_eq!(res, Err(OperationError::ResourceLimit));
2382
2383        // ldap
2384        let res = Filter::from_ldap_ro(&ev, &inv_ldap, &mut r_txn);
2385        assert_eq!(res, Err(OperationError::ResourceLimit));
2386
2387        // Can only have one db conn at a time.
2388        std::mem::drop(r_txn);
2389
2390        // proto + write
2391        let mut wr_txn = server.write(duration_from_epoch_now()).await.expect("txn");
2392        let res = Filter::from_rw(&ev, &inv_proto, &mut wr_txn);
2393        assert_eq!(res, Err(OperationError::ResourceLimit));
2394    }
2395}