kanidmd_lib/server/
mod.rs

1//! `server` contains the query server, which is the main high level construction
2//! to coordinate queries and operations in the server.
3
4use self::access::{
5    profiles::{
6        AccessControlCreate, AccessControlDelete, AccessControlModify, AccessControlSearch,
7    },
8    AccessControls, AccessControlsReadTransaction, AccessControlsTransaction,
9    AccessControlsWriteTransaction,
10};
11use self::keys::{
12    KeyObject, KeyProvider, KeyProviders, KeyProvidersReadTransaction, KeyProvidersTransaction,
13    KeyProvidersWriteTransaction,
14};
15use crate::be::{Backend, BackendReadTransaction, BackendTransaction, BackendWriteTransaction};
16use crate::filter::{
17    Filter, FilterInvalid, FilterValid, FilterValidResolved, ResolveFilterCache,
18    ResolveFilterCacheReadTxn,
19};
20use crate::plugins::{
21    self,
22    dyngroup::{DynGroup, DynGroupCache},
23    Plugins,
24};
25use crate::prelude::*;
26use crate::repl::cid::Cid;
27use crate::repl::proto::ReplRuvRange;
28use crate::repl::ruv::ReplicationUpdateVectorTransaction;
29use crate::schema::{
30    Schema, SchemaAttribute, SchemaClass, SchemaReadTransaction, SchemaTransaction,
31    SchemaWriteTransaction,
32};
33use crate::value::{CredentialType, EXTRACT_VAL_DN};
34use crate::valueset::*;
35use concread::arcache::{ARCacheBuilder, ARCacheReadTxn, ARCacheWriteTxn};
36use concread::cowcell::*;
37use crypto_glue::{hmac_s256::HmacSha256Key, s256::Sha256Output};
38use hashbrown::{HashMap, HashSet};
39use kanidm_proto::internal::{DomainInfo as ProtoDomainInfo, ImageValue, UiHint};
40use kanidm_proto::scim_v1::{
41    server::{ScimListResponse, ScimOAuth2ClaimMap, ScimOAuth2ScopeMap, ScimReference},
42    JsonValue, ScimEntryGetQuery, ScimFilter,
43};
44use std::collections::{BTreeMap, BTreeSet};
45use std::num::NonZeroU64;
46use std::str::FromStr;
47use std::sync::Arc;
48use time::OffsetDateTime;
49use tokio::sync::{Semaphore, SemaphorePermit};
50use tracing::trace;
51
52pub(crate) mod access;
53pub mod assert;
54pub mod batch_modify;
55pub mod create;
56pub mod delete;
57pub mod identity;
58pub(crate) mod keys;
59pub(crate) mod migrations;
60pub mod modify;
61pub(crate) mod recycle;
62pub mod scim;
63pub(crate) mod utils;
64
65const RESOLVE_FILTER_CACHE_MAX: usize = 256;
66const RESOLVE_FILTER_CACHE_LOCAL: usize = 8;
67
68#[derive(Debug, Clone, Copy, PartialOrd, PartialEq, Eq)]
69pub(crate) enum ServerPhase {
70    Bootstrap,
71    SchemaReady,
72    DomainInfoReady,
73    Running,
74}
75
76/// Domain Information. This should not contain sensitive information, the data within
77/// this structure may be used for public presentation.
78#[derive(Debug, Clone, PartialEq, Eq)]
79pub struct DomainInfo {
80    pub(crate) d_uuid: Uuid,
81    pub(crate) d_name: String,
82    pub(crate) d_display: String,
83    pub(crate) d_vers: DomainVersion,
84    pub(crate) d_patch_level: u32,
85    pub(crate) d_devel_taint: bool,
86    pub(crate) d_ldap_allow_unix_pw_bind: bool,
87    pub(crate) d_allow_easter_eggs: bool,
88    // In future this should be image reference instead of the image itself.
89    d_image: Option<ImageValue>,
90}
91
92impl DomainInfo {
93    pub fn name(&self) -> &str {
94        self.d_name.as_str()
95    }
96
97    pub fn display_name(&self) -> &str {
98        self.d_display.as_str()
99    }
100
101    pub fn devel_taint(&self) -> bool {
102        self.d_devel_taint
103    }
104
105    pub fn image(&self) -> Option<&ImageValue> {
106        self.d_image.as_ref()
107    }
108
109    pub fn has_custom_image(&self) -> bool {
110        self.d_image.is_some()
111    }
112
113    pub fn allow_easter_eggs(&self) -> bool {
114        self.d_allow_easter_eggs
115    }
116
117    #[cfg(feature = "test")]
118    pub fn new_test() -> CowCell<Self> {
119        concread::cowcell::CowCell::new(Self {
120            d_uuid: Uuid::new_v4(),
121            d_name: "test domain".to_string(),
122            d_display: "Test Domain".to_string(),
123            d_vers: 1,
124            d_patch_level: 0,
125            d_devel_taint: false,
126            d_ldap_allow_unix_pw_bind: false,
127            d_allow_easter_eggs: false,
128            d_image: None,
129        })
130    }
131}
132
133#[derive(Debug, Clone, PartialEq, Eq, Default)]
134pub struct SystemConfig {
135    pub(crate) denied_names: HashSet<String>,
136    pub(crate) pw_badlist: HashSet<String>,
137}
138
139#[derive(Clone, Default)]
140pub struct HmacNameHistoryConfig {
141    pub(crate) enabled: bool,
142    pub(crate) key: HmacSha256Key,
143}
144
145#[derive(Clone, Default)]
146pub struct FeatureConfig {
147    pub(crate) hmac_name_history: HmacNameHistoryConfig,
148}
149
150#[derive(Clone)]
151pub struct QueryServer {
152    phase: Arc<CowCell<ServerPhase>>,
153    pub(crate) d_info: Arc<CowCell<DomainInfo>>,
154    system_config: Arc<CowCell<SystemConfig>>,
155    feature_config: Arc<CowCell<FeatureConfig>>,
156    be: Backend,
157    schema: Arc<Schema>,
158    accesscontrols: Arc<AccessControls>,
159    db_tickets: Arc<Semaphore>,
160    read_tickets: Arc<Semaphore>,
161    write_ticket: Arc<Semaphore>,
162    resolve_filter_cache: Arc<ResolveFilterCache>,
163    dyngroup_cache: Arc<CowCell<DynGroupCache>>,
164    cid_max: Arc<CowCell<Cid>>,
165    key_providers: Arc<KeyProviders>,
166}
167
168pub struct QueryServerReadTransaction<'a> {
169    be_txn: BackendReadTransaction<'a>,
170    // Anything else? In the future, we'll need to have a schema transaction
171    // type, maybe others?
172    pub(crate) d_info: CowCellReadTxn<DomainInfo>,
173    system_config: CowCellReadTxn<SystemConfig>,
174    feature_config: CowCellReadTxn<FeatureConfig>,
175    schema: SchemaReadTransaction,
176    accesscontrols: AccessControlsReadTransaction<'a>,
177    key_providers: KeyProvidersReadTransaction,
178    _db_ticket: SemaphorePermit<'a>,
179    _read_ticket: SemaphorePermit<'a>,
180    resolve_filter_cache: ResolveFilterCacheReadTxn<'a>,
181    // Future we may need this.
182    // cid_max: CowCellReadTxn<Cid>,
183    trim_cid: Cid,
184    txn_name_to_uuid: BTreeMap<String, Uuid>,
185}
186
187unsafe impl Sync for QueryServerReadTransaction<'_> {}
188
189unsafe impl Send for QueryServerReadTransaction<'_> {}
190
191bitflags::bitflags! {
192    #[derive(Copy, Clone, Debug)]
193    pub struct ChangeFlag: u64 {
194        const SCHEMA =                      0b0000_0000_0000_0001;
195        const ACP =                         0b0000_0000_0000_0010;
196        const OAUTH2 =                      0b0000_0000_0000_0100;
197        const DOMAIN =                      0b0000_0000_0000_1000;
198        const SYSTEM_CONFIG =               0b0000_0000_0001_0000;
199        const SYNC_AGREEMENT =              0b0000_0000_0010_0000;
200        const KEY_MATERIAL   =              0b0000_0000_0100_0000;
201        const APPLICATION    =              0b0000_0000_1000_0000;
202        const OAUTH2_CLIENT            =    0b0000_0001_0000_0000;
203        const FEATURE                  =    0b0000_0010_0000_0000;
204    }
205}
206
207pub struct QueryServerWriteTransaction<'a> {
208    committed: bool,
209    phase: CowCellWriteTxn<'a, ServerPhase>,
210    d_info: CowCellWriteTxn<'a, DomainInfo>,
211    system_config: CowCellWriteTxn<'a, SystemConfig>,
212    feature_config: CowCellWriteTxn<'a, FeatureConfig>,
213    curtime: Duration,
214    cid: CowCellWriteTxn<'a, Cid>,
215    trim_cid: Cid,
216    pub(crate) be_txn: BackendWriteTransaction<'a>,
217    pub(crate) schema: SchemaWriteTransaction<'a>,
218    accesscontrols: AccessControlsWriteTransaction<'a>,
219    key_providers: KeyProvidersWriteTransaction<'a>,
220    // We store a set of flags that indicate we need a reload of
221    // schema or acp, which is tested by checking the classes of the
222    // changing content.
223    pub(super) changed_flags: ChangeFlag,
224
225    // Store the list of changed uuids for other invalidation needs?
226    pub(super) changed_uuid: HashSet<Uuid>,
227    _db_ticket: SemaphorePermit<'a>,
228    _write_ticket: SemaphorePermit<'a>,
229    resolve_filter_cache_clear: bool,
230    resolve_filter_cache_write: ARCacheWriteTxn<
231        'a,
232        (IdentityId, Arc<Filter<FilterValid>>),
233        Arc<Filter<FilterValidResolved>>,
234        (),
235    >,
236    resolve_filter_cache: ARCacheReadTxn<
237        'a,
238        (IdentityId, Arc<Filter<FilterValid>>),
239        Arc<Filter<FilterValidResolved>>,
240        (),
241    >,
242    dyngroup_cache: CowCellWriteTxn<'a, DynGroupCache>,
243    txn_name_to_uuid: BTreeMap<String, Uuid>,
244}
245
246impl QueryServerWriteTransaction<'_> {
247    pub(crate) fn trim_cid(&self) -> &Cid {
248        &self.trim_cid
249    }
250}
251
252/// The `QueryServerTransaction` trait provides a set of common read only operations to be
253/// shared between [`QueryServerReadTransaction`] and [`QueryServerWriteTransaction`]s.
254///
255/// These operations tend to be high level constructions, generally different types of searches
256/// that are capable of taking different types of parameters and applying access controls or not,
257/// impersonating accounts, or bypassing these via internal searches.
258///
259/// [`QueryServerReadTransaction`]: struct.QueryServerReadTransaction.html
260/// [`QueryServerWriteTransaction`]: struct.QueryServerWriteTransaction.html
261pub trait QueryServerTransaction<'a> {
262    type BackendTransactionType: BackendTransaction;
263    fn get_be_txn(&mut self) -> &mut Self::BackendTransactionType;
264
265    type SchemaTransactionType: SchemaTransaction;
266    fn get_schema<'b>(&self) -> &'b Self::SchemaTransactionType;
267
268    type AccessControlsTransactionType: AccessControlsTransaction<'a>;
269    fn get_accesscontrols(&self) -> &Self::AccessControlsTransactionType;
270
271    type KeyProvidersTransactionType: KeyProvidersTransaction;
272    fn get_key_providers(&self) -> &Self::KeyProvidersTransactionType;
273
274    fn pw_badlist(&self) -> &HashSet<String>;
275
276    fn denied_names(&self) -> &HashSet<String>;
277
278    fn get_domain_version(&self) -> DomainVersion;
279
280    fn get_domain_patch_level(&self) -> u32;
281
282    fn get_domain_development_taint(&self) -> bool;
283
284    fn get_domain_uuid(&self) -> Uuid;
285
286    fn get_domain_name(&self) -> &str;
287
288    fn get_domain_display_name(&self) -> &str;
289
290    fn get_domain_image_value(&self) -> Option<ImageValue>;
291
292    fn get_resolve_filter_cache(&mut self) -> Option<&mut ResolveFilterCacheReadTxn<'a>>;
293
294    fn get_feature_hmac_name_history_config(&self) -> &HmacNameHistoryConfig;
295
296    fn txn_name_to_uuid(&mut self) -> &mut BTreeMap<String, Uuid>;
297
298    // Because of how borrowck in rust works, if we need to get two inner types we have to get them
299    // in a single fn.
300
301    fn get_resolve_filter_cache_and_be_txn(
302        &mut self,
303    ) -> (
304        &mut Self::BackendTransactionType,
305        Option<&mut ResolveFilterCacheReadTxn<'a>>,
306    );
307
308    /// Conduct a search and apply access controls to yield a set of entries that
309    /// have been reduced to the set of user visible avas. Note that if you provide
310    /// a `SearchEvent` for the internal user, this query will fail. It is invalid for
311    /// the [`access`] module to attempt to reduce avas for internal searches, and you
312    /// should use [`fn search`] instead.
313    ///
314    /// [`SearchEvent`]: ../event/struct.SearchEvent.html
315    /// [`access`]: ../access/index.html
316    /// [`fn search`]: trait.QueryServerTransaction.html#method.search
317    #[instrument(level = "debug", skip_all)]
318    fn search_ext(
319        &mut self,
320        se: &SearchEvent,
321    ) -> Result<Vec<EntryReducedCommitted>, OperationError> {
322        /*
323         * This just wraps search, but it's for the external interface
324         * so as a result it also reduces the entry set's attributes at
325         * the end.
326         */
327        let entries = self.search(se)?;
328
329        let access = self.get_accesscontrols();
330        access
331            .search_filter_entry_attributes(se, entries)
332            .map_err(|e| {
333                // Log and fail if something went wrong.
334                admin_error!(?e, "Failed to filter entry attributes");
335                e
336            })
337        // This now returns the reduced vec.
338    }
339
340    #[instrument(level = "debug", skip_all)]
341    fn search(
342        &mut self,
343        se: &SearchEvent,
344    ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
345        if se.ident.is_internal() {
346            trace!(internal_filter = ?se.filter, "search");
347        } else {
348            security_info!(initiator = %se.ident, "search");
349            admin_debug!(external_filter = ?se.filter, "search");
350        }
351
352        // This is an important security step because it prevents us from
353        // performing un-indexed searches on attr's that don't exist in the
354        // server. This is why ExtensibleObject can only take schema that
355        // exists in the server, not arbitrary attr names.
356        //
357        // This normalises and validates in a single step.
358        //
359        // NOTE: Filters are validated in event conversion.
360
361        let (be_txn, resolve_filter_cache) = self.get_resolve_filter_cache_and_be_txn();
362
363        let idxmeta = be_txn.get_idxmeta_ref();
364
365        trace!(resolve_filter_cache = %resolve_filter_cache.is_some());
366
367        // Now resolve all references and indexes.
368        let vfr = se
369            .filter
370            .resolve(&se.ident, Some(idxmeta), resolve_filter_cache)
371            .map_err(|e| {
372                admin_error!(?e, "search filter resolve failure");
373                e
374            })?;
375
376        let lims = se.ident.limits();
377
378        // NOTE: We currently can't build search plugins due to the inability to hand
379        // the QS wr/ro to the plugin trait. However, there shouldn't be a need for search
380        // plugins, because all data transforms should be in the write path.
381
382        let res = self.get_be_txn().search(lims, &vfr).map_err(|e| {
383            admin_error!(?e, "backend failure");
384            OperationError::Backend
385        })?;
386
387        // Apply ACP before we let the plugins "have at it".
388        // WARNING; for external searches this is NOT the only
389        // ACP application. There is a second application to reduce the
390        // attribute set on the entries!
391        //
392        let access = self.get_accesscontrols();
393        access.search_filter_entries(se, res).map_err(|e| {
394            admin_error!(?e, "Unable to access filter entries");
395            e
396        })
397    }
398
399    #[instrument(level = "debug", skip_all)]
400    fn exists(&mut self, ee: &ExistsEvent) -> Result<bool, OperationError> {
401        let (be_txn, resolve_filter_cache) = self.get_resolve_filter_cache_and_be_txn();
402        let idxmeta = be_txn.get_idxmeta_ref();
403
404        let vfr = ee
405            .filter
406            .resolve(&ee.ident, Some(idxmeta), resolve_filter_cache)
407            .map_err(|e| {
408                admin_error!(?e, "Failed to resolve filter");
409                e
410            })?;
411
412        let lims = ee.ident.limits();
413
414        if ee.ident.is_internal() {
415            // We take a fast-path on internal because we can skip loading entries
416            // at all in this case.
417            be_txn.exists(lims, &vfr).map_err(|e| {
418                admin_error!(?e, "backend failure");
419                OperationError::Backend
420            })
421        } else {
422            // For external idents, we need to load the entries else we can't apply
423            // access controls to them.
424            let res = self.get_be_txn().search(lims, &vfr).map_err(|e| {
425                admin_error!(?e, "backend failure");
426                OperationError::Backend
427            })?;
428
429            // ⚠️  Compare / Exists is annoying security wise. It has the
430            // capability to easily leak information based on comparisons
431            // that have been made. In the external account case, we need
432            // to filter entries as a result.
433
434            // Apply ACP before we return the bool state.
435            let access = self.get_accesscontrols();
436            access
437                .filter_entries(&ee.ident, &ee.filter_orig, res)
438                .map_err(|e| {
439                    admin_error!(?e, "Unable to access filter entries");
440                    e
441                })
442                .map(|entries| !entries.is_empty())
443        }
444    }
445
446    fn name_to_uuid(&mut self, name: &str) -> Result<Uuid, OperationError> {
447        // There are some contexts where we will be passed an rdn or dn. We need
448        // to remove these elements if they exist.
449        //
450        // Why is it okay to ignore the attr and dn here? In Kani spn and name are
451        // always unique and absolutes, so even if the dn/rdn are not expected, there
452        // is only a single correct answer that *can* match these values. This also
453        // hugely simplifies the process of matching when we have app based searches
454        // in future too.
455        let work = EXTRACT_VAL_DN
456            .captures(name)
457            .and_then(|caps| caps.name("val"))
458            .map(|v| v.as_str().to_lowercase())
459            .ok_or(OperationError::InvalidValueState)?;
460
461        // Is it just a uuid?
462        if let Ok(uuid) = Uuid::parse_str(&work) {
463            return Ok(uuid);
464        }
465
466        if let Some(uuid) = self.get_be_txn().name2uuid(&work)? {
467            return Ok(uuid);
468        }
469
470        if let Some(uuid) = self.txn_name_to_uuid().get(name) {
471            Ok(*uuid)
472        } else {
473            Err(OperationError::NoMatchingEntries)
474        }
475    }
476
477    // Similar to name, but where we lookup from external_id instead.
478    fn sync_external_id_to_uuid(
479        &mut self,
480        external_id: &str,
481    ) -> Result<Option<Uuid>, OperationError> {
482        // Is it just a uuid?
483        Uuid::parse_str(external_id).map(Some).or_else(|_| {
484            let lname = external_id.to_lowercase();
485            self.get_be_txn().externalid2uuid(lname.as_str())
486        })
487    }
488
489    fn uuid_to_spn(&mut self, uuid: Uuid) -> Result<Option<Value>, OperationError> {
490        let r = self.get_be_txn().uuid2spn(uuid)?;
491
492        if let Some(ref n) = r {
493            // Shouldn't we be doing more graceful error handling here?
494            // Or, if we know it will always be true, we should remove this.
495            debug_assert!(n.is_spn() || n.is_iname());
496        }
497
498        Ok(r)
499    }
500
501    fn uuid_to_rdn(&mut self, uuid: Uuid) -> Result<String, OperationError> {
502        // If we have a some, pass it on, else unwrap into a default.
503        self.get_be_txn()
504            .uuid2rdn(uuid)
505            .map(|v| v.unwrap_or_else(|| format!("uuid={}", uuid.as_hyphenated())))
506    }
507
508    /// From internal, generate an "exists" event and dispatch
509    #[instrument(level = "debug", skip_all)]
510    fn internal_exists(&mut self, filter: &Filter<FilterInvalid>) -> Result<bool, OperationError> {
511        // Check the filter
512        let f_valid = filter
513            .validate(self.get_schema())
514            .map_err(OperationError::SchemaViolation)?;
515        // Build an exists event
516        let ee = ExistsEvent::new_internal(f_valid);
517        // Submit it
518        self.exists(&ee)
519    }
520
521    #[instrument(level = "debug", skip_all)]
522    fn internal_exists_uuid(&mut self, uuid: Uuid) -> Result<bool, OperationError> {
523        let filter = filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
524        self.internal_exists(&filter)
525    }
526
527    #[instrument(level = "debug", skip_all)]
528    fn internal_search(
529        &mut self,
530        filter: Filter<FilterInvalid>,
531    ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
532        let f_valid = filter
533            .validate(self.get_schema())
534            .map_err(OperationError::SchemaViolation)?;
535        let se = SearchEvent::new_internal(f_valid);
536        self.search(&se)
537    }
538
539    #[instrument(level = "debug", skip_all)]
540    fn impersonate_search_valid(
541        &mut self,
542        f_valid: Filter<FilterValid>,
543        f_intent_valid: Filter<FilterValid>,
544        event: &Identity,
545    ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
546        let se = SearchEvent::new_impersonate(event, f_valid, f_intent_valid);
547        self.search(&se)
548    }
549
550    /// Applies ACP to filter result entries.
551    fn impersonate_search_ext_valid(
552        &mut self,
553        f_valid: Filter<FilterValid>,
554        f_intent_valid: Filter<FilterValid>,
555        event: &Identity,
556    ) -> Result<Vec<Entry<EntryReduced, EntryCommitted>>, OperationError> {
557        let se = SearchEvent::new_impersonate(event, f_valid, f_intent_valid);
558        self.search_ext(&se)
559    }
560
561    // Who they are will go here
562    fn impersonate_search(
563        &mut self,
564        filter: Filter<FilterInvalid>,
565        filter_intent: Filter<FilterInvalid>,
566        event: &Identity,
567    ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
568        let f_valid = filter
569            .validate(self.get_schema())
570            .map_err(OperationError::SchemaViolation)?;
571        let f_intent_valid = filter_intent
572            .validate(self.get_schema())
573            .map_err(OperationError::SchemaViolation)?;
574        self.impersonate_search_valid(f_valid, f_intent_valid, event)
575    }
576
577    #[instrument(level = "debug", skip_all)]
578    fn impersonate_search_ext(
579        &mut self,
580        filter: Filter<FilterInvalid>,
581        filter_intent: Filter<FilterInvalid>,
582        event: &Identity,
583    ) -> Result<Vec<Entry<EntryReduced, EntryCommitted>>, OperationError> {
584        let f_valid = filter
585            .validate(self.get_schema())
586            .map_err(OperationError::SchemaViolation)?;
587        let f_intent_valid = filter_intent
588            .validate(self.get_schema())
589            .map_err(OperationError::SchemaViolation)?;
590        self.impersonate_search_ext_valid(f_valid, f_intent_valid, event)
591    }
592
593    /// Get a single entry by its UUID. This is used heavily for internal
594    /// server operations, especially in login and ACP checks.
595    #[instrument(level = "debug", skip_all)]
596    fn internal_search_uuid(
597        &mut self,
598        uuid: Uuid,
599    ) -> Result<Arc<EntrySealedCommitted>, OperationError> {
600        let filter = filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
601        let f_valid = filter.validate(self.get_schema()).map_err(|e| {
602            error!(?e, "Filter Validate - SchemaViolation");
603            OperationError::SchemaViolation(e)
604        })?;
605        let se = SearchEvent::new_internal(f_valid);
606
607        let mut vs = self.search(&se)?;
608        match vs.pop() {
609            Some(entry) if vs.is_empty() => Ok(entry),
610            _ => Err(OperationError::NoMatchingEntries),
611        }
612    }
613
614    /// Get a single entry by its UUID, even if the entry in question
615    /// is in a masked state (recycled, tombstoned).
616    #[instrument(level = "debug", skip_all)]
617    fn internal_search_all_uuid(
618        &mut self,
619        uuid: Uuid,
620    ) -> Result<Arc<EntrySealedCommitted>, OperationError> {
621        let filter = filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
622        let f_valid = filter.validate(self.get_schema()).map_err(|e| {
623            error!(?e, "Filter Validate - SchemaViolation");
624            OperationError::SchemaViolation(e)
625        })?;
626        let se = SearchEvent::new_internal(f_valid);
627
628        let mut vs = self.search(&se)?;
629        match vs.pop() {
630            Some(entry) if vs.is_empty() => Ok(entry),
631            _ => Err(OperationError::NoMatchingEntries),
632        }
633    }
634
635    /// Get all conflict entries that originated from a source uuid.
636    #[instrument(level = "debug", skip_all)]
637    fn internal_search_conflict_uuid(
638        &mut self,
639        uuid: Uuid,
640    ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
641        let filter = filter_all!(f_and(vec![
642            f_eq(Attribute::SourceUuid, PartialValue::Uuid(uuid)),
643            f_eq(Attribute::Class, EntryClass::Conflict.into())
644        ]));
645        let f_valid = filter.validate(self.get_schema()).map_err(|e| {
646            error!(?e, "Filter Validate - SchemaViolation");
647            OperationError::SchemaViolation(e)
648        })?;
649        let se = SearchEvent::new_internal(f_valid);
650
651        self.search(&se)
652    }
653
654    #[instrument(level = "debug", skip_all)]
655    fn impersonate_search_ext_uuid(
656        &mut self,
657        uuid: Uuid,
658        event: &Identity,
659    ) -> Result<Entry<EntryReduced, EntryCommitted>, OperationError> {
660        let filter_intent = filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
661        let filter = filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
662
663        let mut vs = self.impersonate_search_ext(filter, filter_intent, event)?;
664        match vs.pop() {
665            Some(entry) if vs.is_empty() => Ok(entry),
666            _ => {
667                if vs.is_empty() {
668                    Err(OperationError::NoMatchingEntries)
669                } else {
670                    // Multiple entries matched, should not be possible!
671                    Err(OperationError::UniqueConstraintViolation)
672                }
673            }
674        }
675    }
676
677    #[instrument(level = "debug", skip_all)]
678    fn impersonate_search_uuid(
679        &mut self,
680        uuid: Uuid,
681        event: &Identity,
682    ) -> Result<Arc<EntrySealedCommitted>, OperationError> {
683        let filter_intent = filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
684        let filter = filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
685
686        let mut vs = self.impersonate_search(filter, filter_intent, event)?;
687        match vs.pop() {
688            Some(entry) if vs.is_empty() => Ok(entry),
689            _ => Err(OperationError::NoMatchingEntries),
690        }
691    }
692
693    /// Given an entries UUID, return an impersonation session for that account. This
694    /// should be used with care, as it allows you to perform actions internally on
695    /// behalf of a another user, without checking the permissions of the original
696    /// user.
697    fn impersonate_uuid_as_readwrite_identity(
698        &mut self,
699        uuid: Uuid,
700    ) -> Result<Identity, OperationError> {
701        self.internal_search_uuid(uuid)
702            .map(Identity::from_impersonate_entry_readwrite)
703    }
704
705    /// Do a schema aware conversion from a String:String to String:Value for modification
706    /// present.
707    fn clone_value(&mut self, attr: &Attribute, value: &str) -> Result<Value, OperationError> {
708        let schema = self.get_schema();
709
710        // Should this actually be a fn of Value - no - I think that introduces issues with the
711        // monomorphisation of the trait for transactions, so we should have this here.
712
713        // Lookup the attr
714        match schema.get_attributes().get(attr) {
715            Some(schema_a) => {
716                match schema_a.syntax {
717                    SyntaxType::Utf8String => Ok(Value::new_utf8(value.to_string())),
718                    SyntaxType::Utf8StringInsensitive => Ok(Value::new_iutf8(value)),
719                    SyntaxType::Utf8StringIname => Ok(Value::new_iname(value)),
720                    SyntaxType::Boolean => Value::new_bools(value)
721                        .ok_or_else(|| OperationError::InvalidAttribute("Invalid boolean syntax".to_string())),
722                    SyntaxType::SyntaxId => Value::new_syntaxs(value)
723                        .ok_or_else(|| OperationError::InvalidAttribute("Invalid Syntax syntax".to_string())),
724                    SyntaxType::IndexId => Value::new_indexes(value)
725                        .ok_or_else(|| OperationError::InvalidAttribute("Invalid Index syntax".to_string())),
726                    SyntaxType::CredentialType => CredentialType::try_from(value)
727                        .map(Value::CredentialType)
728                        .map_err(|()| OperationError::InvalidAttribute("Invalid CredentialType syntax".to_string())),
729                    SyntaxType::Uuid => {
730                        // Attempt to resolve this name to a uuid. If it's already a uuid, then
731                        // name to uuid will "do the right thing" and give us the Uuid back.
732                        let un = self
733                            .name_to_uuid(value)
734                            .unwrap_or(UUID_DOES_NOT_EXIST);
735                        Ok(Value::Uuid(un))
736                    }
737                    SyntaxType::ReferenceUuid => {
738                        let un = self
739                            .name_to_uuid(value)
740                            .unwrap_or(UUID_DOES_NOT_EXIST);
741                        Ok(Value::Refer(un))
742                    }
743                    SyntaxType::JsonFilter => Value::new_json_filter_s(value)
744                        .ok_or_else(|| OperationError::InvalidAttribute("Invalid Filter syntax".to_string())),
745                    SyntaxType::Image => Value::new_image(value),
746
747                    SyntaxType::Credential => Err(OperationError::InvalidAttribute("Credentials can not be supplied through modification - please use the IDM api".to_string())),
748                    SyntaxType::SecretUtf8String => Err(OperationError::InvalidAttribute("Radius secrets can not be supplied through modification - please use the IDM api".to_string())),
749                    SyntaxType::SshKey => Err(OperationError::InvalidAttribute("SSH public keys can not be supplied through modification - please use the IDM api".to_string())),
750                    SyntaxType::SecurityPrincipalName => Err(OperationError::InvalidAttribute("SPNs are generated and not able to be set.".to_string())),
751                    SyntaxType::Uint32 => Value::new_uint32_str(value)
752                        .ok_or_else(|| OperationError::InvalidAttribute("Invalid uint32 syntax".to_string())),
753                    SyntaxType::Int64 => Value::new_int64_str(value)
754                        .ok_or_else(|| OperationError::InvalidAttribute("Invalid int64 syntax".to_string())),
755                    SyntaxType::Uint64 => Value::new_uint64_str(value)
756                        .ok_or_else(|| OperationError::InvalidAttribute("Invalid uint64 syntax".to_string())),
757                    SyntaxType::Cid => Err(OperationError::InvalidAttribute("CIDs are generated and not able to be set.".to_string())),
758                    SyntaxType::NsUniqueId => Value::new_nsuniqueid_s(value)
759                        .ok_or_else(|| OperationError::InvalidAttribute("Invalid NsUniqueId syntax".to_string())),
760                    SyntaxType::DateTime => Value::new_datetime_s(value)
761                        .ok_or_else(|| OperationError::InvalidAttribute("Invalid DateTime (rfc3339) syntax".to_string())),
762                    SyntaxType::EmailAddress => Value::new_email_address_s(value)
763                        .ok_or_else(|| OperationError::InvalidAttribute("Invalid Email Address syntax".to_string())),
764                    SyntaxType::Url => Value::new_url_s(value)
765                        .ok_or_else(|| OperationError::InvalidAttribute("Invalid Url (whatwg/url) syntax".to_string())),
766                    SyntaxType::OauthScope => Value::new_oauthscope(value)
767                        .ok_or_else(|| OperationError::InvalidAttribute("Invalid Oauth Scope syntax".to_string())),
768                    SyntaxType::WebauthnAttestationCaList => Value::new_webauthn_attestation_ca_list(value)
769                        .ok_or_else(|| OperationError::InvalidAttribute("Invalid Webauthn Attestation CA List".to_string())),
770                    SyntaxType::OauthScopeMap => Err(OperationError::InvalidAttribute("Oauth Scope Maps can not be supplied through modification - please use the IDM api".to_string())),
771                    SyntaxType::OauthClaimMap => Err(OperationError::InvalidAttribute("Oauth Claim Maps can not be supplied through modification - please use the IDM api".to_string())),
772                    SyntaxType::PrivateBinary => Err(OperationError::InvalidAttribute("Private Binary Values can not be supplied through modification".to_string())),
773                    SyntaxType::IntentToken => Err(OperationError::InvalidAttribute("Intent Token Values can not be supplied through modification".to_string())),
774                    SyntaxType::Passkey => Err(OperationError::InvalidAttribute("Passkey Values can not be supplied through modification".to_string())),
775                    SyntaxType::AttestedPasskey => Err(OperationError::InvalidAttribute("AttestedPasskey Values can not be supplied through modification".to_string())),
776                    SyntaxType::Session => Err(OperationError::InvalidAttribute("Session Values can not be supplied through modification".to_string())),
777                    SyntaxType::ApiToken => Err(OperationError::InvalidAttribute("ApiToken Values can not be supplied through modification".to_string())),
778                    SyntaxType::JwsKeyEs256 => Err(OperationError::InvalidAttribute("JwsKeyEs256 Values can not be supplied through modification".to_string())),
779                    SyntaxType::JwsKeyRs256 => Err(OperationError::InvalidAttribute("JwsKeyRs256 Values can not be supplied through modification".to_string())),
780                    SyntaxType::Oauth2Session => Err(OperationError::InvalidAttribute("Oauth2Session Values can not be supplied through modification".to_string())),
781                    SyntaxType::UiHint => UiHint::from_str(value)
782                        .map(Value::UiHint)
783                        .map_err(|()| OperationError::InvalidAttribute("Invalid uihint syntax".to_string())),
784                    SyntaxType::TotpSecret => Err(OperationError::InvalidAttribute("TotpSecret Values can not be supplied through modification".to_string())),
785                    SyntaxType::AuditLogString => Err(OperationError::InvalidAttribute("Audit logs are generated and not able to be set.".to_string())),
786                    SyntaxType::EcKeyPrivate => Err(OperationError::InvalidAttribute("Ec keys are generated and not able to be set.".to_string())),
787                    SyntaxType::KeyInternal => Err(OperationError::InvalidAttribute("Internal keys are generated and not able to be set.".to_string())),
788                    SyntaxType::HexString => Value::new_hex_string_s(value)
789                        .ok_or_else(|| OperationError::InvalidAttribute("Invalid hex string syntax".to_string())),
790                    SyntaxType::Certificate => Value::new_certificate_s(value)
791                        .ok_or_else(|| OperationError::InvalidAttribute("Invalid x509 certificate syntax".to_string())),
792                    SyntaxType::ApplicationPassword => Err(OperationError::InvalidAttribute("ApplicationPassword values can not be supplied through modification".to_string())),
793                    SyntaxType::Json => Err(OperationError::InvalidAttribute("Json values can not be supplied through modification".to_string())),
794                    SyntaxType::Sha256 => Err(OperationError::InvalidAttribute("SHA256 values can not be supplied through modification".to_string())),
795                    SyntaxType::Message => Err(OperationError::InvalidAttribute("Message values can not be supplied through modification".to_string())),
796                }
797            }
798            None => {
799                // No attribute of this name exists - fail fast, there is no point to
800                // proceed, as nothing can be satisfied.
801                Err(OperationError::InvalidAttributeName(attr.to_string()))
802            }
803        }
804    }
805
806    fn clone_partialvalue(
807        &mut self,
808        attr: &Attribute,
809        value: &str,
810    ) -> Result<PartialValue, OperationError> {
811        let schema = self.get_schema();
812
813        // Lookup the attr
814        match schema.get_attributes().get(attr) {
815            Some(schema_a) => {
816                match schema_a.syntax {
817                    SyntaxType::Utf8String | SyntaxType::TotpSecret => {
818                        Ok(PartialValue::new_utf8(value.to_string()))
819                    }
820                    SyntaxType::Utf8StringInsensitive
821                    | SyntaxType::JwsKeyEs256
822                    | SyntaxType::JwsKeyRs256 => Ok(PartialValue::new_iutf8(value)),
823                    SyntaxType::Utf8StringIname => Ok(PartialValue::new_iname(value)),
824                    SyntaxType::Boolean => PartialValue::new_bools(value).ok_or_else(|| {
825                        OperationError::InvalidAttribute("Invalid boolean syntax".to_string())
826                    }),
827                    SyntaxType::SyntaxId => PartialValue::new_syntaxs(value).ok_or_else(|| {
828                        OperationError::InvalidAttribute("Invalid Syntax syntax".to_string())
829                    }),
830                    SyntaxType::IndexId => PartialValue::new_indexes(value).ok_or_else(|| {
831                        OperationError::InvalidAttribute("Invalid Index syntax".to_string())
832                    }),
833                    SyntaxType::CredentialType => CredentialType::try_from(value)
834                        .map(PartialValue::CredentialType)
835                        .map_err(|()| {
836                            OperationError::InvalidAttribute(
837                                "Invalid credentialtype syntax".to_string(),
838                            )
839                        }),
840                    SyntaxType::Uuid => {
841                        let un = self.name_to_uuid(value).unwrap_or(UUID_DOES_NOT_EXIST);
842                        Ok(PartialValue::Uuid(un))
843                    }
844                    // ⚠️   Any types here need to also be added to update_attributes in
845                    // schema.rs for reference type / cache awareness during referential
846                    // integrity processing. Exceptions are self-contained value types!
847                    SyntaxType::ReferenceUuid
848                    | SyntaxType::OauthScopeMap
849                    | SyntaxType::Session
850                    | SyntaxType::ApiToken
851                    | SyntaxType::Oauth2Session
852                    | SyntaxType::ApplicationPassword => {
853                        let un = self.name_to_uuid(value).unwrap_or(UUID_DOES_NOT_EXIST);
854                        Ok(PartialValue::Refer(un))
855                    }
856                    SyntaxType::OauthClaimMap => self
857                        .name_to_uuid(value)
858                        .map(PartialValue::Refer)
859                        .or_else(|_| Ok(PartialValue::new_iutf8(value))),
860
861                    SyntaxType::JsonFilter => {
862                        PartialValue::new_json_filter_s(value).ok_or_else(|| {
863                            OperationError::InvalidAttribute("Invalid Filter syntax".to_string())
864                        })
865                    }
866                    SyntaxType::Credential => Ok(PartialValue::new_credential_tag(value)),
867                    SyntaxType::SecretUtf8String => Ok(PartialValue::new_secret_str()),
868                    SyntaxType::SshKey => Ok(PartialValue::new_sshkey_tag_s(value)),
869                    SyntaxType::SecurityPrincipalName => {
870                        PartialValue::new_spn_s(value).ok_or_else(|| {
871                            OperationError::InvalidAttribute("Invalid spn syntax".to_string())
872                        })
873                    }
874                    SyntaxType::Uint32 => PartialValue::new_uint32_str(value).ok_or_else(|| {
875                        OperationError::InvalidAttribute("Invalid uint32 syntax".to_string())
876                    }),
877                    SyntaxType::Uint64 => PartialValue::new_uint64_str(value).ok_or_else(|| {
878                        OperationError::InvalidAttribute("Invalid uint64 syntax".to_string())
879                    }),
880                    SyntaxType::Int64 => PartialValue::new_int64_str(value).ok_or_else(|| {
881                        OperationError::InvalidAttribute("Invalid int64 syntax".to_string())
882                    }),
883                    SyntaxType::Cid => PartialValue::new_cid_s(value).ok_or_else(|| {
884                        OperationError::InvalidAttribute("Invalid cid syntax".to_string())
885                    }),
886                    SyntaxType::NsUniqueId => Ok(PartialValue::new_nsuniqueid_s(value)),
887                    SyntaxType::DateTime => PartialValue::new_datetime_s(value).ok_or_else(|| {
888                        OperationError::InvalidAttribute(
889                            "Invalid DateTime (rfc3339) syntax".to_string(),
890                        )
891                    }),
892                    SyntaxType::EmailAddress => Ok(PartialValue::new_email_address_s(value)),
893                    SyntaxType::Url => PartialValue::new_url_s(value).ok_or_else(|| {
894                        OperationError::InvalidAttribute(
895                            "Invalid Url (whatwg/url) syntax".to_string(),
896                        )
897                    }),
898                    SyntaxType::OauthScope => Ok(PartialValue::new_oauthscope(value)),
899                    SyntaxType::PrivateBinary => Ok(PartialValue::PrivateBinary),
900                    SyntaxType::IntentToken => PartialValue::new_intenttoken_s(value.to_string())
901                        .ok_or_else(|| {
902                            OperationError::InvalidAttribute(
903                                "Invalid Intent Token ID (uuid) syntax".to_string(),
904                            )
905                        }),
906                    SyntaxType::Passkey => PartialValue::new_passkey_s(value).ok_or_else(|| {
907                        OperationError::InvalidAttribute("Invalid Passkey UUID syntax".to_string())
908                    }),
909                    SyntaxType::AttestedPasskey => PartialValue::new_attested_passkey_s(value)
910                        .ok_or_else(|| {
911                            OperationError::InvalidAttribute(
912                                "Invalid AttestedPasskey UUID syntax".to_string(),
913                            )
914                        }),
915                    SyntaxType::UiHint => UiHint::from_str(value)
916                        .map(PartialValue::UiHint)
917                        .map_err(|()| {
918                            OperationError::InvalidAttribute("Invalid uihint syntax".to_string())
919                        }),
920                    SyntaxType::AuditLogString => Ok(PartialValue::new_utf8s(value)),
921                    SyntaxType::EcKeyPrivate => Ok(PartialValue::SecretValue),
922                    SyntaxType::Image => Ok(PartialValue::new_utf8s(value)),
923                    SyntaxType::WebauthnAttestationCaList => Err(OperationError::InvalidAttribute(
924                        "Invalid - unable to query attestation CA list".to_string(),
925                    )),
926                    SyntaxType::HexString | SyntaxType::KeyInternal | SyntaxType::Certificate => {
927                        PartialValue::new_hex_string_s(value).ok_or_else(|| {
928                            OperationError::InvalidAttribute(
929                                "Invalid syntax, expected hex string".to_string(),
930                            )
931                        })
932                    }
933                    SyntaxType::Sha256 => {
934                        let mut sha256bytes = Sha256Output::default();
935                        if hex::decode_to_slice(value, &mut sha256bytes).is_ok() {
936                            Ok(PartialValue::Sha256(sha256bytes))
937                        } else {
938                            Err(OperationError::InvalidAttribute(
939                                "Invalid syntax, expected sha256 hex string containing 64 characters".to_string(),
940                            ))
941                        }
942                    }
943                    SyntaxType::Json => Err(OperationError::InvalidAttribute(
944                        "Json values can not be validated by this interface".to_string(),
945                    )),
946                    SyntaxType::Message => Err(OperationError::InvalidAttribute(
947                        "Message values can not be validated by this interface".to_string(),
948                    )),
949                }
950            }
951            None => {
952                // No attribute of this name exists - fail fast, there is no point to
953                // proceed, as nothing can be satisfied.
954                Err(OperationError::InvalidAttributeName(attr.to_string()))
955            }
956        }
957    }
958
959    fn resolve_scim_interim(
960        &mut self,
961        scim_value_intermediate: ScimValueIntermediate,
962    ) -> Result<Option<ScimValueKanidm>, OperationError> {
963        match scim_value_intermediate {
964            ScimValueIntermediate::References(uuids) => {
965                let scim_references = uuids
966                    .into_iter()
967                    .map(|uuid| {
968                        self.uuid_to_spn(uuid)
969                            .and_then(|maybe_value| {
970                                maybe_value.ok_or(OperationError::InvalidValueState)
971                            })
972                            .map(|value| ScimReference {
973                                uuid,
974                                value: value.to_proto_string_clone(),
975                            })
976                    })
977                    .collect::<Result<Vec<_>, _>>()?;
978                Ok(Some(ScimValueKanidm::EntryReferences(scim_references)))
979            }
980            ScimValueIntermediate::Oauth2ClaimMap(unresolved_maps) => {
981                let scim_claim_maps = unresolved_maps
982                    .into_iter()
983                    .map(
984                        |UnresolvedScimValueOauth2ClaimMap {
985                             group_uuid,
986                             claim,
987                             join_char,
988                             values,
989                         }| {
990                            self.uuid_to_spn(group_uuid)
991                                .and_then(|maybe_value| {
992                                    maybe_value.ok_or(OperationError::InvalidValueState)
993                                })
994                                .map(|value| ScimOAuth2ClaimMap {
995                                    group: value.to_proto_string_clone(),
996                                    group_uuid,
997                                    claim,
998                                    join_char,
999                                    values,
1000                                })
1001                        },
1002                    )
1003                    .collect::<Result<Vec<_>, _>>()?;
1004
1005                Ok(Some(ScimValueKanidm::OAuth2ClaimMap(scim_claim_maps)))
1006            }
1007
1008            ScimValueIntermediate::Oauth2ScopeMap(unresolved_maps) => {
1009                let scim_claim_maps = unresolved_maps
1010                    .into_iter()
1011                    .map(|UnresolvedScimValueOauth2ScopeMap { group_uuid, scopes }| {
1012                        self.uuid_to_spn(group_uuid)
1013                            .and_then(|maybe_value| {
1014                                maybe_value.ok_or(OperationError::InvalidValueState)
1015                            })
1016                            .map(|value| ScimOAuth2ScopeMap {
1017                                group: value.to_proto_string_clone(),
1018                                group_uuid,
1019                                scopes,
1020                            })
1021                    })
1022                    .collect::<Result<Vec<_>, _>>()?;
1023
1024                Ok(Some(ScimValueKanidm::OAuth2ScopeMap(scim_claim_maps)))
1025            }
1026        }
1027    }
1028
1029    fn resolve_scim_json_get(
1030        &mut self,
1031        attr: &Attribute,
1032        value: &JsonValue,
1033    ) -> Result<PartialValue, OperationError> {
1034        let schema = self.get_schema();
1035        // Lookup the attr
1036        let Some(schema_a) = schema.get_attributes().get(attr) else {
1037            // No attribute of this name exists - fail fast, there is no point to
1038            // proceed, as nothing can be satisfied.
1039            return Err(OperationError::InvalidAttributeName(attr.to_string()));
1040        };
1041
1042        debug!(schema_syntax = ?schema_a.syntax, ?value);
1043
1044        match schema_a.syntax {
1045            SyntaxType::Utf8String => {
1046                let JsonValue::String(value) = value else {
1047                    return Err(OperationError::InvalidAttribute(attr.to_string()));
1048                };
1049                Ok(PartialValue::Utf8(value.to_string()))
1050            }
1051            SyntaxType::Utf8StringInsensitive => {
1052                let JsonValue::String(value) = value else {
1053                    return Err(OperationError::InvalidAttribute(attr.to_string()));
1054                };
1055                Ok(PartialValue::new_iutf8(value))
1056            }
1057            SyntaxType::Utf8StringIname => {
1058                let JsonValue::String(value) = value else {
1059                    return Err(OperationError::InvalidAttribute(attr.to_string()));
1060                };
1061                Ok(PartialValue::new_iname(value))
1062            }
1063            SyntaxType::Uuid => {
1064                let JsonValue::String(value) = value else {
1065                    return Err(OperationError::InvalidAttribute(attr.to_string()));
1066                };
1067
1068                let un = self.name_to_uuid(value).unwrap_or(UUID_DOES_NOT_EXIST);
1069                Ok(PartialValue::Uuid(un))
1070            }
1071            SyntaxType::Boolean => {
1072                let JsonValue::Bool(value) = value else {
1073                    return Err(OperationError::InvalidAttribute(attr.to_string()));
1074                };
1075                Ok(PartialValue::Bool(*value))
1076            }
1077            SyntaxType::SyntaxId => {
1078                let JsonValue::String(value) = value else {
1079                    return Err(OperationError::InvalidAttribute(attr.to_string()));
1080                };
1081                let Ok(value) = SyntaxType::try_from(value.as_str()) else {
1082                    return Err(OperationError::InvalidAttribute(attr.to_string()));
1083                };
1084                Ok(PartialValue::Syntax(value))
1085            }
1086            SyntaxType::ReferenceUuid
1087            | SyntaxType::OauthScopeMap
1088            | SyntaxType::Session
1089            | SyntaxType::ApiToken
1090            | SyntaxType::Oauth2Session
1091            | SyntaxType::ApplicationPassword => {
1092                let JsonValue::String(value) = value else {
1093                    return Err(OperationError::InvalidAttribute(attr.to_string()));
1094                };
1095
1096                let un = self.name_to_uuid(value).unwrap_or(UUID_DOES_NOT_EXIST);
1097                Ok(PartialValue::Refer(un))
1098            }
1099
1100            _ => Err(OperationError::InvalidAttribute(attr.to_string())),
1101        }
1102    }
1103
1104    fn resolve_valueset_intermediate(
1105        &mut self,
1106        vs_inter: ValueSetIntermediate,
1107    ) -> Result<ValueSet, OperationError> {
1108        match vs_inter {
1109            ValueSetIntermediate::References {
1110                mut resolved,
1111                unresolved,
1112            } => {
1113                for value in unresolved {
1114                    let un = self.name_to_uuid(value.as_str()).unwrap_or_else(|_| {
1115                        warn!(
1116                            ?value,
1117                            "Value can not be resolved to a uuid - assuming it does not exist."
1118                        );
1119                        UUID_DOES_NOT_EXIST
1120                    });
1121
1122                    resolved.insert(un);
1123                }
1124
1125                let vs = ValueSetRefer::from_set(resolved);
1126                Ok(vs)
1127            }
1128
1129            ValueSetIntermediate::Oauth2ClaimMap {
1130                mut resolved,
1131                unresolved,
1132            } => {
1133                resolved.extend(unresolved.into_iter().map(
1134                    |UnresolvedValueSetOauth2ClaimMap {
1135                         group_name,
1136                         claim,
1137                         join_char,
1138                         claim_values,
1139                     }| {
1140                        let group_uuid =
1141                            self.name_to_uuid(group_name.as_str()).unwrap_or_else(|_| {
1142                                warn!(
1143                            ?group_name,
1144                            "Value can not be resolved to a uuid - assuming it does not exist."
1145                        );
1146                                UUID_DOES_NOT_EXIST
1147                            });
1148
1149                        ResolvedValueSetOauth2ClaimMap {
1150                            group_uuid,
1151                            claim,
1152                            join_char,
1153                            claim_values,
1154                        }
1155                    },
1156                ));
1157
1158                let vs = ValueSetOauthClaimMap::from_set(resolved);
1159                Ok(vs)
1160            }
1161
1162            ValueSetIntermediate::Oauth2ScopeMap {
1163                mut resolved,
1164                unresolved,
1165            } => {
1166                resolved.extend(unresolved.into_iter().map(
1167                    |UnresolvedValueSetOauth2ScopeMap { group_name, scopes }| {
1168                        let group_uuid =
1169                            self.name_to_uuid(group_name.as_str()).unwrap_or_else(|_| {
1170                                warn!(
1171                            ?group_name,
1172                            "Value can not be resolved to a uuid - assuming it does not exist."
1173                        );
1174                                UUID_DOES_NOT_EXIST
1175                            });
1176
1177                        ResolvedValueSetOauth2ScopeMap { group_uuid, scopes }
1178                    },
1179                ));
1180
1181                let vs = ValueSetOauthScopeMap::from_set(resolved);
1182                Ok(vs)
1183            }
1184        }
1185    }
1186
1187    // In the opposite direction, we can resolve values for presentation
1188    fn resolve_valueset(&mut self, value: &ValueSet) -> Result<Vec<String>, OperationError> {
1189        if let Some(r_set) = value.as_refer_set() {
1190            let v: Result<Vec<_>, _> = r_set
1191                .iter()
1192                .copied()
1193                .map(|ur| {
1194                    let nv = self.uuid_to_spn(ur)?;
1195                    match nv {
1196                        Some(v) => Ok(v.to_proto_string_clone()),
1197                        None => Ok(uuid_to_proto_string(ur)),
1198                    }
1199                })
1200                .collect();
1201            v
1202        } else if let Some(r_map) = value.as_oauthscopemap() {
1203            let v: Result<Vec<_>, _> = r_map
1204                .iter()
1205                .map(|(u, m)| {
1206                    let nv = self.uuid_to_spn(*u)?;
1207                    let u = match nv {
1208                        Some(v) => v.to_proto_string_clone(),
1209                        None => uuid_to_proto_string(*u),
1210                    };
1211                    Ok(format!("{u}: {m:?}"))
1212                })
1213                .collect();
1214            v
1215        } else if let Some(r_map) = value.as_oauthclaim_map() {
1216            let mut v = Vec::with_capacity(0);
1217            for (claim_name, mapping) in r_map.iter() {
1218                for (group_ref, claims) in mapping.values() {
1219                    let join_char = mapping.join().to_str();
1220
1221                    let nv = self.uuid_to_spn(*group_ref)?;
1222                    let resolved_id = match nv {
1223                        Some(v) => v.to_proto_string_clone(),
1224                        None => uuid_to_proto_string(*group_ref),
1225                    };
1226
1227                    let joined = str_concat!(claims, ",");
1228
1229                    v.push(format!("{claim_name}:{resolved_id}:{join_char}:{joined:?}"))
1230                }
1231            }
1232            Ok(v)
1233        } else {
1234            let v: Vec<_> = value.to_proto_string_clone_iter().collect();
1235            Ok(v)
1236        }
1237    }
1238
1239    fn resolve_valueset_ldap(
1240        &mut self,
1241        value: &ValueSet,
1242        basedn: &str,
1243    ) -> Result<Vec<Vec<u8>>, OperationError> {
1244        if let Some(r_set) = value.as_refer_set() {
1245            let v: Result<Vec<_>, _> = r_set
1246                .iter()
1247                .copied()
1248                .map(|ur| {
1249                    let rdn = self.uuid_to_rdn(ur)?;
1250                    Ok(format!("{rdn},{basedn}").into_bytes())
1251                })
1252                .collect();
1253            v
1254        // We have to special case ssh keys here as the proto form isn't valid for
1255        // sss_ssh_authorized_keys to consume.
1256        } else if let Some(key_iter) = value.as_sshpubkey_string_iter() {
1257            let v: Vec<_> = key_iter.map(|s| s.into_bytes()).collect();
1258            Ok(v)
1259        } else {
1260            let v: Vec<_> = value
1261                .to_proto_string_clone_iter()
1262                .map(|s| s.into_bytes())
1263                .collect();
1264            Ok(v)
1265        }
1266    }
1267
1268    fn get_db_domain(&mut self) -> Result<Arc<EntrySealedCommitted>, OperationError> {
1269        self.internal_search_uuid(UUID_DOMAIN_INFO)
1270    }
1271
1272    fn get_domain_key_object_handle(&self) -> Result<Arc<KeyObject>, OperationError> {
1273        self.get_key_providers()
1274            .get_key_object_handle(UUID_DOMAIN_INFO)
1275            .ok_or(OperationError::KP0031KeyObjectNotFound)
1276    }
1277
1278    fn get_domain_es256_private_key(&mut self) -> Result<Vec<u8>, OperationError> {
1279        self.internal_search_uuid(UUID_DOMAIN_INFO)
1280            .and_then(|e| {
1281                e.get_ava_single_private_binary(Attribute::Es256PrivateKeyDer)
1282                    .map(|s| s.to_vec())
1283                    .ok_or(OperationError::InvalidEntryState)
1284            })
1285            .map_err(|e| {
1286                admin_error!(?e, "Error getting domain es256 key");
1287                e
1288            })
1289    }
1290
1291    fn get_domain_ldap_allow_unix_pw_bind(&mut self) -> Result<bool, OperationError> {
1292        self.internal_search_uuid(UUID_DOMAIN_INFO).map(|entry| {
1293            entry
1294                .get_ava_single_bool(Attribute::LdapAllowUnixPwBind)
1295                .unwrap_or(true)
1296        })
1297    }
1298
1299    /// Get the password badlist from the system config. You should not call this directly
1300    /// as this value is cached in the system_config() value.
1301    fn get_sc_password_badlist(&mut self) -> Result<HashSet<String>, OperationError> {
1302        self.internal_search_uuid(UUID_SYSTEM_CONFIG)
1303            .map(|e| match e.get_ava_iter_iutf8(Attribute::BadlistPassword) {
1304                Some(vs_str_iter) => vs_str_iter.map(str::to_string).collect::<HashSet<_>>(),
1305                None => HashSet::default(),
1306            })
1307            .map_err(|e| {
1308                error!(
1309                    ?e,
1310                    "Failed to retrieve password badlist from system configuration"
1311                );
1312                e
1313            })
1314    }
1315
1316    /// Get the denied name set from the system config. You should not call this directly
1317    /// as this value is cached in the system_config() value.
1318    fn get_sc_denied_names(&mut self) -> Result<HashSet<String>, OperationError> {
1319        self.internal_search_uuid(UUID_SYSTEM_CONFIG)
1320            .map(|e| match e.get_ava_iter_iname(Attribute::DeniedName) {
1321                Some(vs_str_iter) => vs_str_iter.map(str::to_string).collect::<HashSet<_>>(),
1322                None => HashSet::default(),
1323            })
1324            .map_err(|e| {
1325                error!(
1326                    ?e,
1327                    "Failed to retrieve denied names from system configuration"
1328                );
1329                e
1330            })
1331    }
1332
1333    fn get_oauth2rs_set(&mut self) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
1334        self.internal_search(filter!(f_eq(
1335            Attribute::Class,
1336            EntryClass::OAuth2ResourceServer.into(),
1337        )))
1338    }
1339
1340    fn get_applications_set(&mut self) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
1341        self.internal_search(filter!(f_eq(
1342            Attribute::Class,
1343            EntryClass::Application.into(),
1344        )))
1345    }
1346
1347    #[instrument(level = "debug", skip_all)]
1348    fn consumer_get_state(&mut self) -> Result<ReplRuvRange, OperationError> {
1349        // Get the current state of "where we are up to"
1350        //
1351        // There are two approaches we can use here. We can either store a cookie
1352        // related to the supplier we are fetching from, or we can use our RUV state.
1353        //
1354        // Initially I'm using RUV state, because it lets us select exactly what has
1355        // changed, where the cookie approach is more coarse grained. The cookie also
1356        // requires some more knowledge about what supplier we are communicating too
1357        // where the RUV approach doesn't since the supplier calcs the diff.
1358        //
1359        // We need the RUV as a state of
1360        //
1361        // [ s_uuid, cid_min, cid_max ]
1362        // [ s_uuid, cid_min, cid_max ]
1363        // [ s_uuid, cid_min, cid_max ]
1364        // ...
1365        //
1366        // This way the remote can diff against it's knowledge and work out:
1367        //
1368        // [ s_uuid, from_cid, to_cid ]
1369        // [ s_uuid, from_cid, to_cid ]
1370        //
1371        // ...
1372
1373        let domain_uuid = self.get_domain_uuid();
1374
1375        // Which then the supplier will use to actually retrieve the set of entries.
1376        // and the needed attributes we need.
1377        let ruv_snapshot = self.get_be_txn().get_ruv();
1378
1379        // What's the current set of ranges?
1380        ruv_snapshot
1381            .current_ruv_range()
1382            .map(|ranges| ReplRuvRange::V1 {
1383                domain_uuid,
1384                ranges,
1385            })
1386    }
1387}
1388
1389// Actually conduct a search request
1390// This is the core of the server, as it processes the entire event
1391// applies all parts required in order and more.
1392impl<'a> QueryServerTransaction<'a> for QueryServerReadTransaction<'a> {
1393    type AccessControlsTransactionType = AccessControlsReadTransaction<'a>;
1394    type BackendTransactionType = BackendReadTransaction<'a>;
1395    type SchemaTransactionType = SchemaReadTransaction;
1396    type KeyProvidersTransactionType = KeyProvidersReadTransaction;
1397
1398    fn get_be_txn(&mut self) -> &mut BackendReadTransaction<'a> {
1399        &mut self.be_txn
1400    }
1401
1402    fn get_schema<'b>(&self) -> &'b SchemaReadTransaction {
1403        // Strip the lifetime here. Schema is a sub-component of the transaction and is
1404        // *never* changed excepting in a write TXN, so we want to allow the schema to
1405        // be borrowed while the rest of the read txn is under a mut.
1406        unsafe {
1407            let s = (&self.schema) as *const _;
1408            &*s
1409        }
1410    }
1411
1412    fn get_accesscontrols(&self) -> &AccessControlsReadTransaction<'a> {
1413        &self.accesscontrols
1414    }
1415
1416    fn get_key_providers(&self) -> &KeyProvidersReadTransaction {
1417        &self.key_providers
1418    }
1419
1420    fn get_resolve_filter_cache(&mut self) -> Option<&mut ResolveFilterCacheReadTxn<'a>> {
1421        Some(&mut self.resolve_filter_cache)
1422    }
1423
1424    fn get_feature_hmac_name_history_config(&self) -> &HmacNameHistoryConfig {
1425        &self.feature_config.hmac_name_history
1426    }
1427
1428    fn txn_name_to_uuid(&mut self) -> &mut BTreeMap<String, Uuid> {
1429        &mut self.txn_name_to_uuid
1430    }
1431
1432    fn get_resolve_filter_cache_and_be_txn(
1433        &mut self,
1434    ) -> (
1435        &mut BackendReadTransaction<'a>,
1436        Option<&mut ResolveFilterCacheReadTxn<'a>>,
1437    ) {
1438        (&mut self.be_txn, Some(&mut self.resolve_filter_cache))
1439    }
1440
1441    fn pw_badlist(&self) -> &HashSet<String> {
1442        &self.system_config.pw_badlist
1443    }
1444
1445    fn denied_names(&self) -> &HashSet<String> {
1446        &self.system_config.denied_names
1447    }
1448
1449    fn get_domain_version(&self) -> DomainVersion {
1450        self.d_info.d_vers
1451    }
1452
1453    fn get_domain_patch_level(&self) -> u32 {
1454        self.d_info.d_patch_level
1455    }
1456
1457    fn get_domain_development_taint(&self) -> bool {
1458        self.d_info.d_devel_taint
1459    }
1460
1461    fn get_domain_uuid(&self) -> Uuid {
1462        self.d_info.d_uuid
1463    }
1464
1465    fn get_domain_name(&self) -> &str {
1466        &self.d_info.d_name
1467    }
1468
1469    fn get_domain_display_name(&self) -> &str {
1470        &self.d_info.d_display
1471    }
1472
1473    fn get_domain_image_value(&self) -> Option<ImageValue> {
1474        self.d_info.d_image.clone()
1475    }
1476}
1477
1478impl QueryServerReadTransaction<'_> {
1479    pub(crate) fn trim_cid(&self) -> &Cid {
1480        &self.trim_cid
1481    }
1482
1483    /// Retrieve the domain info of this server
1484    pub fn domain_info(&mut self) -> Result<ProtoDomainInfo, OperationError> {
1485        let d_info = &self.d_info;
1486
1487        Ok(ProtoDomainInfo {
1488            name: d_info.d_name.clone(),
1489            displayname: d_info.d_display.clone(),
1490            uuid: d_info.d_uuid,
1491            level: d_info.d_vers,
1492        })
1493    }
1494
1495    /// Verify the data content of the server is as expected. This will probably
1496    /// call various functions for validation, including possibly plugin
1497    /// verifications.
1498    pub(crate) fn verify(&mut self) -> Vec<Result<(), ConsistencyError>> {
1499        // If we fail after backend, we need to return NOW because we can't
1500        // assert any other faith in the DB states.
1501        //  * backend
1502        let be_errs = self.get_be_txn().verify();
1503
1504        if !be_errs.is_empty() {
1505            return be_errs;
1506        }
1507
1508        //  * in memory schema consistency.
1509        let sc_errs = self.get_schema().validate();
1510
1511        if !sc_errs.is_empty() {
1512            return sc_errs;
1513        }
1514
1515        // The schema is now valid, so we load this up
1516
1517        //  * Indexing (req be + sch )
1518        let idx_errs = self.get_be_txn().verify_indexes();
1519
1520        if !idx_errs.is_empty() {
1521            return idx_errs;
1522        }
1523
1524        // If anything error to this point we can't trust the verifications below. From
1525        // here we can just amass results.
1526        let mut results = Vec::with_capacity(0);
1527
1528        // Verify all our entries. Weird flex I know, but it's needed for verifying
1529        // the entry changelogs are consistent to their entries.
1530        let schema = self.get_schema();
1531
1532        let filt_all = filter!(f_pres(Attribute::Class));
1533        let all_entries = match self.internal_search(filt_all) {
1534            Ok(a) => a,
1535            Err(_e) => return vec![Err(ConsistencyError::QueryServerSearchFailure)],
1536        };
1537
1538        for e in all_entries {
1539            e.verify(schema, &mut results)
1540        }
1541
1542        // Verify the RUV to the entry changelogs now.
1543        self.get_be_txn().verify_ruv(&mut results);
1544
1545        // Ok entries passed, lets move on to the content.
1546        // Most of our checks are in the plugins, so we let them
1547        // do their job.
1548
1549        // Now, call the plugins verification system.
1550        Plugins::run_verify(self, &mut results);
1551        // Finished
1552
1553        results
1554    }
1555
1556    #[instrument(level = "debug", skip_all)]
1557    pub fn scim_entry_id_get_ext(
1558        &mut self,
1559        uuid: Uuid,
1560        class: EntryClass,
1561        query: ScimEntryGetQuery,
1562        ident: Identity,
1563    ) -> Result<ScimEntryKanidm, OperationError> {
1564        let filter_intent = filter!(f_and!([
1565            f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)),
1566            f_eq(Attribute::Class, class.into())
1567        ]));
1568
1569        let f_intent_valid = filter_intent
1570            .validate(self.get_schema())
1571            .map_err(OperationError::SchemaViolation)?;
1572
1573        let f_valid = f_intent_valid.clone().into_ignore_hidden();
1574
1575        let r_attrs = query
1576            .attributes
1577            .map(|attr_set| attr_set.into_iter().collect());
1578
1579        let se = SearchEvent {
1580            ident,
1581            filter: f_valid,
1582            filter_orig: f_intent_valid,
1583            attrs: r_attrs,
1584            effective_access_check: query.ext_access_check,
1585        };
1586
1587        let mut vs = self.search_ext(&se)?;
1588        match vs.pop() {
1589            Some(entry) if vs.is_empty() => entry.to_scim_kanidm(self),
1590            _ => {
1591                if vs.is_empty() {
1592                    Err(OperationError::NoMatchingEntries)
1593                } else {
1594                    // Multiple entries matched, should not be possible!
1595                    Err(OperationError::UniqueConstraintViolation)
1596                }
1597            }
1598        }
1599    }
1600
1601    #[instrument(level = "debug", skip_all)]
1602    pub fn scim_search_ext(
1603        &mut self,
1604        ident: Identity,
1605        filter: ScimFilter,
1606        query: ScimEntryGetQuery,
1607    ) -> Result<ScimListResponse, OperationError> {
1608        let filter = if let Some(ref user_filter) = query.filter {
1609            ScimFilter::And(Box::new(filter), Box::new(user_filter.clone()))
1610        } else {
1611            filter
1612        };
1613
1614        let filter_intent = Filter::from_scim_ro(&ident, &filter, self)?;
1615
1616        self.scim_search_filter_ext(ident, &filter_intent, query)
1617    }
1618
1619    pub fn scim_search_filter_ext(
1620        &mut self,
1621        ident: Identity,
1622        filter_intent: &Filter<FilterInvalid>,
1623        query: ScimEntryGetQuery,
1624    ) -> Result<ScimListResponse, OperationError> {
1625        let f_intent_valid = filter_intent
1626            .validate(self.get_schema())
1627            .map_err(OperationError::SchemaViolation)?;
1628
1629        let f_valid = f_intent_valid.clone().into_ignore_hidden();
1630
1631        let r_attrs = query
1632            .attributes
1633            .map(|attr_set| attr_set.into_iter().collect());
1634
1635        let se = SearchEvent {
1636            ident,
1637            filter: f_valid,
1638            filter_orig: f_intent_valid,
1639            attrs: r_attrs,
1640            effective_access_check: query.ext_access_check,
1641        };
1642
1643        let mut result_set = self.search_ext(&se)?;
1644
1645        // We need to know total_results before we paginate.
1646        let total_results = result_set.len() as u64;
1647
1648        // These are STUPID ways to do this, but they demonstrate that the feature
1649        // works and it's viable on small datasets. We will make this use indexes
1650        // in the future!
1651
1652        // First, sort if any.
1653        if let Some(sort_attr) = query.sort_by {
1654            result_set.sort_unstable_by(|entry_left, entry_right| {
1655                let left = entry_left.get_ava_set(&sort_attr);
1656                let right = entry_right.get_ava_set(&sort_attr);
1657                match (left, right) {
1658                    (Some(left), Some(right)) => left.cmp(right),
1659                    (Some(_), None) => std::cmp::Ordering::Less,
1660                    (None, Some(_)) => std::cmp::Ordering::Greater,
1661                    (None, None) => std::cmp::Ordering::Equal,
1662                }
1663            });
1664        }
1665
1666        // Paginate, if any.
1667        let (items_per_page, start_index, paginated_result_set) = if let Some(count) = query.count {
1668            let count: u64 = count.get();
1669            // User wants pagination. Count is how many elements they want.
1670
1671            let start_index: u64 = query
1672                .start_index
1673                .map(|non_zero_index|
1674                    // SCIM pagination is 1 indexed, not 0.
1675                    non_zero_index.get() - 1)
1676                .unwrap_or_default();
1677
1678            // First, check that our start_index is valid.
1679            if start_index as usize > result_set.len() {
1680                // SCIM rfc doesn't define what happens if start index
1681                // is OOB of the result set.
1682                return Err(OperationError::SC0029PaginationOutOfBounds);
1683            }
1684
1685            let mut result_set = result_set.split_off(start_index as usize);
1686            result_set.truncate(count as usize);
1687
1688            (
1689                NonZeroU64::new(count),
1690                NonZeroU64::new(start_index + 1),
1691                result_set,
1692            )
1693        } else {
1694            // Unchanged
1695            (None, None, result_set)
1696        };
1697
1698        let resources = paginated_result_set
1699            .into_iter()
1700            .map(|entry| entry.to_scim_kanidm(self))
1701            .collect::<Result<Vec<_>, _>>()?;
1702
1703        Ok(ScimListResponse {
1704            // Requires other schema changes in future.
1705            schemas: Vec::with_capacity(0),
1706            total_results,
1707            items_per_page,
1708            start_index,
1709            resources,
1710        })
1711    }
1712
1713    #[instrument(level = "debug", skip_all)]
1714    pub fn scim_search_message_ready_ext(
1715        &mut self,
1716        ident: Identity,
1717        curtime: Duration,
1718    ) -> Result<ScimListResponse, OperationError> {
1719        let curtime_odt = OffsetDateTime::UNIX_EPOCH + curtime;
1720
1721        let filter_intent = filter_all!(f_and(vec![
1722            f_eq(Attribute::Class, EntryClass::OutboundMessage.into()),
1723            f_lt(Attribute::SendAfter, PartialValue::DateTime(curtime_odt)),
1724            f_andnot(f_pres(Attribute::SentAt))
1725        ]));
1726
1727        let query = ScimEntryGetQuery::default();
1728
1729        self.scim_search_filter_ext(ident, &filter_intent, query)
1730    }
1731}
1732
1733impl<'a> QueryServerTransaction<'a> for QueryServerWriteTransaction<'a> {
1734    type AccessControlsTransactionType = AccessControlsWriteTransaction<'a>;
1735    type BackendTransactionType = BackendWriteTransaction<'a>;
1736    type SchemaTransactionType = SchemaWriteTransaction<'a>;
1737    type KeyProvidersTransactionType = KeyProvidersWriteTransaction<'a>;
1738
1739    fn get_be_txn(&mut self) -> &mut BackendWriteTransaction<'a> {
1740        &mut self.be_txn
1741    }
1742
1743    fn get_schema<'b>(&self) -> &'b SchemaWriteTransaction<'a> {
1744        // Strip the lifetime here. Schema is a sub-component of the transaction and is
1745        // *never* changed excepting in a write TXN, so we want to allow the schema to
1746        // be borrowed while the rest of the read txn is under a mut.
1747        unsafe {
1748            let s = (&self.schema) as *const _;
1749            &*s
1750        }
1751    }
1752
1753    fn get_accesscontrols(&self) -> &AccessControlsWriteTransaction<'a> {
1754        &self.accesscontrols
1755    }
1756
1757    fn get_key_providers(&self) -> &KeyProvidersWriteTransaction<'a> {
1758        &self.key_providers
1759    }
1760
1761    fn get_resolve_filter_cache(&mut self) -> Option<&mut ResolveFilterCacheReadTxn<'a>> {
1762        if self.resolve_filter_cache_clear || *self.phase < ServerPhase::SchemaReady {
1763            None
1764        } else {
1765            Some(&mut self.resolve_filter_cache)
1766        }
1767    }
1768
1769    fn get_feature_hmac_name_history_config(&self) -> &HmacNameHistoryConfig {
1770        &self.feature_config.hmac_name_history
1771    }
1772
1773    fn txn_name_to_uuid(&mut self) -> &mut BTreeMap<String, Uuid> {
1774        &mut self.txn_name_to_uuid
1775    }
1776
1777    fn get_resolve_filter_cache_and_be_txn(
1778        &mut self,
1779    ) -> (
1780        &mut BackendWriteTransaction<'a>,
1781        Option<&mut ResolveFilterCacheReadTxn<'a>>,
1782    ) {
1783        if self.resolve_filter_cache_clear || *self.phase < ServerPhase::SchemaReady {
1784            (&mut self.be_txn, None)
1785        } else {
1786            (&mut self.be_txn, Some(&mut self.resolve_filter_cache))
1787        }
1788    }
1789
1790    fn pw_badlist(&self) -> &HashSet<String> {
1791        &self.system_config.pw_badlist
1792    }
1793
1794    fn denied_names(&self) -> &HashSet<String> {
1795        &self.system_config.denied_names
1796    }
1797
1798    fn get_domain_version(&self) -> DomainVersion {
1799        self.d_info.d_vers
1800    }
1801
1802    fn get_domain_patch_level(&self) -> u32 {
1803        self.d_info.d_patch_level
1804    }
1805
1806    fn get_domain_development_taint(&self) -> bool {
1807        self.d_info.d_devel_taint
1808    }
1809
1810    fn get_domain_uuid(&self) -> Uuid {
1811        self.d_info.d_uuid
1812    }
1813
1814    /// Gets the in-memory domain_name element
1815    fn get_domain_name(&self) -> &str {
1816        &self.d_info.d_name
1817    }
1818
1819    fn get_domain_display_name(&self) -> &str {
1820        &self.d_info.d_display
1821    }
1822
1823    fn get_domain_image_value(&self) -> Option<ImageValue> {
1824        self.d_info.d_image.clone()
1825    }
1826}
1827
1828impl QueryServer {
1829    pub fn new(
1830        be: Backend,
1831        schema: Schema,
1832        domain_name: String,
1833        curtime: Duration,
1834    ) -> Result<Self, OperationError> {
1835        let (s_uuid, d_uuid, ts_max) = {
1836            let mut wr = be.write()?;
1837            let s_uuid = wr.get_db_s_uuid()?;
1838            let d_uuid = wr.get_db_d_uuid()?;
1839            let ts_max = wr.get_db_ts_max(curtime)?;
1840            wr.commit()?;
1841            (s_uuid, d_uuid, ts_max)
1842        };
1843
1844        let pool_size = be.get_pool_size();
1845
1846        debug!("Server UUID -> {:?}", s_uuid);
1847        debug!("Domain UUID -> {:?}", d_uuid);
1848        debug!("Domain Name -> {:?}", domain_name);
1849
1850        let d_info = Arc::new(CowCell::new(DomainInfo {
1851            d_uuid,
1852            // Start with our level as zero.
1853            // This will be reloaded from the DB shortly :)
1854            d_vers: DOMAIN_LEVEL_0,
1855            d_patch_level: 0,
1856            d_name: domain_name.clone(),
1857            // we set the domain_display_name to the configuration file's domain_name
1858            // here because the database is not started, so we cannot pull it from there.
1859            d_display: domain_name,
1860            // Automatically derive our current taint mode based on the PRERELEASE setting.
1861            d_devel_taint: option_env!("KANIDM_PRE_RELEASE").is_some(),
1862            d_ldap_allow_unix_pw_bind: false,
1863            d_allow_easter_eggs: false,
1864            d_image: None,
1865        }));
1866
1867        let cid = Cid::new_lamport(s_uuid, curtime, &ts_max);
1868        let cid_max = Arc::new(CowCell::new(cid));
1869
1870        // These default to empty, but they'll be populated shortly.
1871        let system_config = Arc::new(CowCell::new(SystemConfig::default()));
1872
1873        let feature_config = Arc::new(CowCell::new(FeatureConfig::default()));
1874
1875        let dyngroup_cache = Arc::new(CowCell::new(DynGroupCache::default()));
1876
1877        let phase = Arc::new(CowCell::new(ServerPhase::Bootstrap));
1878
1879        let resolve_filter_cache = Arc::new(
1880            ARCacheBuilder::new()
1881                .set_size(RESOLVE_FILTER_CACHE_MAX, RESOLVE_FILTER_CACHE_LOCAL)
1882                .set_reader_quiesce(true)
1883                .build()
1884                .ok_or_else(|| {
1885                    error!("Failed to build filter resolve cache");
1886                    OperationError::DB0003FilterResolveCacheBuild
1887                })?,
1888        );
1889
1890        let key_providers = Arc::new(KeyProviders::default());
1891
1892        // These needs to be pool_size minus one to always leave a DB ticket
1893        // for a writer. But it also needs to be at least one :)
1894        debug_assert!(pool_size > 0);
1895        let read_ticket_pool = std::cmp::max(pool_size - 1, 1);
1896
1897        Ok(QueryServer {
1898            phase,
1899            d_info,
1900            system_config,
1901            feature_config,
1902            be,
1903            schema: Arc::new(schema),
1904            accesscontrols: Arc::new(AccessControls::default()),
1905            db_tickets: Arc::new(Semaphore::new(pool_size as usize)),
1906            read_tickets: Arc::new(Semaphore::new(read_ticket_pool as usize)),
1907            write_ticket: Arc::new(Semaphore::new(1)),
1908            resolve_filter_cache,
1909            dyngroup_cache,
1910            cid_max,
1911            key_providers,
1912        })
1913    }
1914
1915    pub fn try_quiesce(&self) {
1916        self.be.try_quiesce();
1917        self.accesscontrols.try_quiesce();
1918        self.resolve_filter_cache.try_quiesce();
1919    }
1920
1921    #[instrument(level = "debug", skip_all)]
1922    async fn read_acquire_ticket(&self) -> Option<(SemaphorePermit<'_>, SemaphorePermit<'_>)> {
1923        // Get a read ticket. Basically this forces us to queue with other readers, while preventing
1924        // us from competing with writers on the db tickets. This tilts us to write prioritising
1925        // on db operations by always making sure a writer can get a db ticket.
1926        let read_ticket = if cfg!(test) {
1927            self.read_tickets
1928                .try_acquire()
1929                .inspect_err(|err| {
1930                    error!(?err, "Unable to acquire read ticket!");
1931                })
1932                .ok()?
1933        } else {
1934            let fut = tokio::time::timeout(
1935                Duration::from_millis(DB_LOCK_ACQUIRE_TIMEOUT_MILLIS),
1936                self.read_tickets.acquire(),
1937            );
1938
1939            match fut.await {
1940                Ok(Ok(ticket)) => ticket,
1941                Ok(Err(_)) => {
1942                    error!("Failed to acquire read ticket, may be poisoned.");
1943                    return None;
1944                }
1945                Err(_) => {
1946                    error!("Failed to acquire read ticket, server is overloaded.");
1947                    return None;
1948                }
1949            }
1950        };
1951
1952        // We need to ensure a db conn will be available. At this point either a db ticket
1953        // *must* be available because pool_size >= 2 and the only other holders are write
1954        // and read ticket holders, OR pool_size == 1, and we are waiting on the writer to now
1955        // complete.
1956        let db_ticket = if cfg!(test) {
1957            self.db_tickets
1958                .try_acquire()
1959                .inspect_err(|err| {
1960                    error!(?err, "Unable to acquire database ticket!");
1961                })
1962                .ok()?
1963        } else {
1964            self.db_tickets
1965                .acquire()
1966                .await
1967                .inspect_err(|err| {
1968                    error!(?err, "Unable to acquire database ticket!");
1969                })
1970                .ok()?
1971        };
1972
1973        Some((read_ticket, db_ticket))
1974    }
1975
1976    pub async fn read(&self) -> Result<QueryServerReadTransaction<'_>, OperationError> {
1977        let (read_ticket, db_ticket) = self
1978            .read_acquire_ticket()
1979            .await
1980            .ok_or(OperationError::DatabaseLockAcquisitionTimeout)?;
1981        // Point of no return - we now have a DB thread AND the read ticket, we MUST complete
1982        // as soon as possible! The following locks and elements below are SYNCHRONOUS but
1983        // will never be contented at this point, and will always progress.
1984        let schema = self.schema.read();
1985
1986        let cid_max = self.cid_max.read();
1987        let trim_cid = cid_max.sub_secs(CHANGELOG_MAX_AGE)?;
1988
1989        let be_txn = self.be.read()?;
1990
1991        Ok(QueryServerReadTransaction {
1992            be_txn,
1993            schema,
1994            d_info: self.d_info.read(),
1995            system_config: self.system_config.read(),
1996            feature_config: self.feature_config.read(),
1997            accesscontrols: self.accesscontrols.read(),
1998            key_providers: self.key_providers.read(),
1999            _db_ticket: db_ticket,
2000            _read_ticket: read_ticket,
2001            resolve_filter_cache: self.resolve_filter_cache.read(),
2002            trim_cid,
2003            txn_name_to_uuid: Default::default(),
2004        })
2005    }
2006
2007    #[instrument(level = "debug", skip_all)]
2008    async fn write_acquire_ticket(&self) -> Option<(SemaphorePermit<'_>, SemaphorePermit<'_>)> {
2009        // Guarantee we are the only writer on the thread pool
2010        let write_ticket = if cfg!(test) {
2011            self.write_ticket
2012                .try_acquire()
2013                .inspect_err(|err| {
2014                    error!(?err, "Unable to acquire write ticket!");
2015                })
2016                .ok()?
2017        } else {
2018            let fut = tokio::time::timeout(
2019                Duration::from_millis(DB_LOCK_ACQUIRE_TIMEOUT_MILLIS),
2020                self.write_ticket.acquire(),
2021            );
2022
2023            match fut.await {
2024                Ok(Ok(ticket)) => ticket,
2025                Ok(Err(_)) => {
2026                    error!("Failed to acquire write ticket, may be poisoned.");
2027                    return None;
2028                }
2029                Err(_) => {
2030                    error!("Failed to acquire write ticket, server is overloaded.");
2031                    return None;
2032                }
2033            }
2034        };
2035
2036        // We need to ensure a db conn will be available. At this point either a db ticket
2037        // *must* be available because pool_size >= 2 and the only other are readers, or
2038        // pool_size == 1 and we are waiting on a single reader to now complete
2039        let db_ticket = if cfg!(test) {
2040            self.db_tickets
2041                .try_acquire()
2042                .inspect_err(|err| {
2043                    error!(?err, "Unable to acquire write db_ticket!");
2044                })
2045                .ok()?
2046        } else {
2047            self.db_tickets
2048                .acquire()
2049                .await
2050                .inspect_err(|err| {
2051                    error!(?err, "Unable to acquire write db_ticket!");
2052                })
2053                .ok()?
2054        };
2055
2056        Some((write_ticket, db_ticket))
2057    }
2058
2059    pub async fn write(
2060        &self,
2061        curtime: Duration,
2062    ) -> Result<QueryServerWriteTransaction<'_>, OperationError> {
2063        let (write_ticket, db_ticket) = self
2064            .write_acquire_ticket()
2065            .await
2066            .ok_or(OperationError::DatabaseLockAcquisitionTimeout)?;
2067
2068        // Point of no return - we now have a DB thread AND the write ticket, we MUST complete
2069        // as soon as possible! The following locks and elements below are SYNCHRONOUS but
2070        // will never be contented at this point, and will always progress.
2071
2072        let be_txn = self.be.write()?;
2073
2074        let schema_write = self.schema.write();
2075        let d_info = self.d_info.write();
2076        let system_config = self.system_config.write();
2077        let feature_config = self.feature_config.write();
2078        let phase = self.phase.write();
2079
2080        let mut cid = self.cid_max.write();
2081        // Update the cid now.
2082        *cid = Cid::new_lamport(cid.s_uuid, curtime, &cid.ts);
2083
2084        let trim_cid = cid.sub_secs(CHANGELOG_MAX_AGE)?;
2085
2086        Ok(QueryServerWriteTransaction {
2087            // I think this is *not* needed, because commit is mut self which should
2088            // take ownership of the value, and cause the commit to "only be run
2089            // once".
2090            //
2091            // The committed flag is however used for abort-specific code in drop
2092            // which today I don't think we have ... yet.
2093            committed: false,
2094            phase,
2095            d_info,
2096            system_config,
2097            feature_config,
2098            curtime,
2099            cid,
2100            trim_cid,
2101            be_txn,
2102            schema: schema_write,
2103            accesscontrols: self.accesscontrols.write(),
2104            changed_flags: ChangeFlag::empty(),
2105            changed_uuid: HashSet::new(),
2106            _db_ticket: db_ticket,
2107            _write_ticket: write_ticket,
2108            resolve_filter_cache: self.resolve_filter_cache.read(),
2109            resolve_filter_cache_clear: false,
2110            resolve_filter_cache_write: self.resolve_filter_cache.write(),
2111            dyngroup_cache: self.dyngroup_cache.write(),
2112            key_providers: self.key_providers.write(),
2113            txn_name_to_uuid: Default::default(),
2114        })
2115    }
2116
2117    #[cfg(any(test, debug_assertions))]
2118    pub async fn clear_cache(&self) -> Result<(), OperationError> {
2119        let ct = duration_from_epoch_now();
2120        let mut w_txn = self.write(ct).await?;
2121        w_txn.clear_cache()?;
2122        w_txn.commit()
2123    }
2124
2125    pub async fn verify(&self) -> Vec<Result<(), ConsistencyError>> {
2126        let current_time = duration_from_epoch_now();
2127        // Before we can proceed, command the QS to load schema in full.
2128        // IMPORTANT: While we take a write txn, this does no writes to the
2129        // actual db, it's only so we can write to the in memory schema
2130        // structures.
2131        if self
2132            .write(current_time)
2133            .await
2134            .and_then(|mut txn| {
2135                txn.force_schema_reload();
2136                txn.commit()
2137            })
2138            .is_err()
2139        {
2140            return vec![Err(ConsistencyError::Unknown)];
2141        };
2142
2143        match self.read().await {
2144            Ok(mut r_txn) => r_txn.verify(),
2145            Err(_) => vec![Err(ConsistencyError::Unknown)],
2146        }
2147    }
2148}
2149
2150impl<'a> QueryServerWriteTransaction<'a> {
2151    pub(crate) fn get_server_uuid(&self) -> Uuid {
2152        // Cid has our server id within
2153        self.cid.s_uuid
2154    }
2155
2156    pub(crate) fn reset_server_uuid(&mut self) -> Result<(), OperationError> {
2157        let s_uuid = self.be_txn.reset_db_s_uuid().map_err(|err| {
2158            error!(?err, "Failed to reset server replication uuid");
2159            err
2160        })?;
2161
2162        debug!(?s_uuid, "reset server replication uuid");
2163
2164        self.cid.s_uuid = s_uuid;
2165
2166        Ok(())
2167    }
2168
2169    pub(crate) fn get_curtime(&self) -> Duration {
2170        self.curtime
2171    }
2172
2173    pub(crate) fn get_curtime_odt(&self) -> OffsetDateTime {
2174        OffsetDateTime::UNIX_EPOCH + self.curtime
2175    }
2176
2177    pub(crate) fn get_cid(&self) -> &Cid {
2178        &self.cid
2179    }
2180
2181    pub(crate) fn get_key_providers_mut(&mut self) -> &mut KeyProvidersWriteTransaction<'a> {
2182        &mut self.key_providers
2183    }
2184
2185    pub(crate) fn get_dyngroup_cache(&mut self) -> &mut DynGroupCache {
2186        &mut self.dyngroup_cache
2187    }
2188
2189    pub fn domain_raise(&mut self, level: u32) -> Result<(), OperationError> {
2190        if level > DOMAIN_MAX_LEVEL {
2191            return Err(OperationError::MG0002RaiseDomainLevelExceedsMaximum);
2192        }
2193
2194        let modl = ModifyList::new_purge_and_set(Attribute::Version, Value::Uint32(level));
2195        let udi = PVUUID_DOMAIN_INFO.clone();
2196        let filt = filter_all!(f_eq(Attribute::Uuid, udi));
2197        self.internal_modify(&filt, &modl)
2198    }
2199
2200    pub fn domain_remigrate(&mut self, level: u32) -> Result<(), OperationError> {
2201        let mut_d_info = self.d_info.get_mut();
2202
2203        // NOTE: See reload_domain_info_version which asserts that we are not attempting
2204        // an unsupported remigration. This check is just a smoke check to no-op if we
2205        // are not requesting any meaningful action.
2206        if level > mut_d_info.d_vers {
2207            // Nothing to do.
2208            return Ok(());
2209        };
2210
2211        info!(
2212            "Preparing to re-migrate from {} -> {}",
2213            level, mut_d_info.d_vers
2214        );
2215        mut_d_info.d_vers = level;
2216        self.changed_flags.insert(ChangeFlag::DOMAIN);
2217
2218        Ok(())
2219    }
2220
2221    #[instrument(level = "debug", skip_all)]
2222    pub(crate) fn reload_schema(&mut self) -> Result<(), OperationError> {
2223        // supply entries to the writable schema to reload from.
2224        // find all attributes.
2225        let filt = filter!(f_eq(Attribute::Class, EntryClass::AttributeType.into()));
2226        let res = self.internal_search(filt).map_err(|e| {
2227            admin_error!("reload schema internal search failed {:?}", e);
2228            e
2229        })?;
2230        // load them.
2231        let attributetypes: Result<Vec<_>, _> =
2232            res.iter().map(|e| SchemaAttribute::try_from(e)).collect();
2233
2234        let attributetypes = attributetypes.map_err(|e| {
2235            admin_error!("reload schema attributetypes {:?}", e);
2236            e
2237        })?;
2238
2239        self.schema.update_attributes(attributetypes).map_err(|e| {
2240            admin_error!("reload schema update attributetypes {:?}", e);
2241            e
2242        })?;
2243
2244        // find all classes
2245        let filt = filter!(f_eq(Attribute::Class, EntryClass::ClassType.into()));
2246        let res = self.internal_search(filt).map_err(|e| {
2247            admin_error!("reload schema internal search failed {:?}", e);
2248            e
2249        })?;
2250        // load them.
2251        let classtypes: Result<Vec<_>, _> = res.iter().map(|e| SchemaClass::try_from(e)).collect();
2252        let classtypes = classtypes.map_err(|e| {
2253            admin_error!("reload schema classtypes {:?}", e);
2254            e
2255        })?;
2256
2257        self.schema.update_classes(classtypes).map_err(|e| {
2258            admin_error!("reload schema update classtypes {:?}", e);
2259            e
2260        })?;
2261
2262        // validate.
2263        let valid_r = self.schema.validate();
2264
2265        // Translate the result.
2266        if valid_r.is_empty() {
2267            // Now use this to reload the backend idxmeta
2268            trace!("Reloading idxmeta ...");
2269            self.be_txn
2270                .update_idxmeta(self.schema.reload_idxmeta())
2271                .map_err(|e| {
2272                    admin_error!("reload schema update idxmeta {:?}", e);
2273                    e
2274                })
2275        } else {
2276            // Log the failures?
2277            admin_error!("Schema reload failed -> {:?}", valid_r);
2278            Err(OperationError::ConsistencyError(
2279                valid_r.into_iter().filter_map(|v| v.err()).collect(),
2280            ))
2281        }?;
2282
2283        // Since we reloaded the schema, we need to reload the filter cache since it
2284        // may have incorrect or outdated information about indexes now.
2285        self.resolve_filter_cache_clear = true;
2286
2287        // Trigger reloads on services that require post-schema reloads.
2288        // Mainly this is plugins.
2289        DynGroup::reload(self)?;
2290
2291        Ok(())
2292    }
2293
2294    #[instrument(level = "debug", skip_all)]
2295    fn reload_accesscontrols(&mut self) -> Result<(), OperationError> {
2296        // supply entries to the writable access controls to reload from.
2297        // This has to be done in FOUR passes - one for each type!
2298        //
2299        // Note, we have to do the search, parse, then submit here, because of the
2300        // requirement to have the write query server reference in the parse stage - this
2301        // would cause a rust double-borrow if we had AccessControls to try to handle
2302        // the entry lists themself.
2303        trace!("ACP reload started ...");
2304
2305        // Update the set of sync agreements
2306
2307        let filt = filter!(f_eq(Attribute::Class, EntryClass::SyncAccount.into()));
2308
2309        let res = self.internal_search(filt).map_err(|e| {
2310            admin_error!(
2311                err = ?e,
2312                "reload accesscontrols internal search failed",
2313            );
2314            e
2315        })?;
2316
2317        let sync_agreement_map: HashMap<Uuid, BTreeSet<Attribute>> = res
2318            .iter()
2319            .filter_map(|e| {
2320                e.get_ava_as_iutf8(Attribute::SyncYieldAuthority)
2321                    .map(|set| {
2322                        let set: BTreeSet<_> =
2323                            set.iter().map(|s| Attribute::from(s.as_str())).collect();
2324                        (e.get_uuid(), set)
2325                    })
2326            })
2327            .collect();
2328
2329        self.accesscontrols
2330            .update_sync_agreements(sync_agreement_map);
2331
2332        // Update search
2333        let filt = filter!(f_and!([
2334            f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2335            f_eq(Attribute::Class, EntryClass::AccessControlSearch.into()),
2336            f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2337        ]));
2338
2339        let res = self.internal_search(filt).map_err(|e| {
2340            admin_error!(
2341                err = ?e,
2342                "reload accesscontrols internal search failed",
2343            );
2344            e
2345        })?;
2346        let search_acps: Result<Vec<_>, _> = res
2347            .iter()
2348            .map(|e| AccessControlSearch::try_from(self, e))
2349            .collect();
2350
2351        let search_acps = search_acps.map_err(|e| {
2352            admin_error!(err = ?e, "Unable to parse search accesscontrols");
2353            e
2354        })?;
2355
2356        self.accesscontrols
2357            .update_search(search_acps)
2358            .map_err(|e| {
2359                admin_error!(err = ?e, "Failed to update search accesscontrols");
2360                e
2361            })?;
2362        // Update create
2363        let filt = filter!(f_and!([
2364            f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2365            f_eq(Attribute::Class, EntryClass::AccessControlCreate.into()),
2366            f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2367        ]));
2368
2369        let res = self.internal_search(filt).map_err(|e| {
2370            admin_error!(
2371                err = ?e,
2372                "reload accesscontrols internal search failed"
2373            );
2374            e
2375        })?;
2376        let create_acps: Result<Vec<_>, _> = res
2377            .iter()
2378            .map(|e| AccessControlCreate::try_from(self, e))
2379            .collect();
2380
2381        let create_acps = create_acps.map_err(|e| {
2382            admin_error!(err = ?e, "Unable to parse create accesscontrols");
2383            e
2384        })?;
2385
2386        self.accesscontrols
2387            .update_create(create_acps)
2388            .map_err(|e| {
2389                admin_error!(err = ?e, "Failed to update create accesscontrols");
2390                e
2391            })?;
2392        // Update modify
2393        let filt = filter!(f_and!([
2394            f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2395            f_eq(Attribute::Class, EntryClass::AccessControlModify.into()),
2396            f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2397        ]));
2398
2399        let res = self.internal_search(filt).map_err(|e| {
2400            admin_error!("reload accesscontrols internal search failed {:?}", e);
2401            e
2402        })?;
2403        let modify_acps: Result<Vec<_>, _> = res
2404            .iter()
2405            .map(|e| AccessControlModify::try_from(self, e))
2406            .collect();
2407
2408        let modify_acps = modify_acps.map_err(|e| {
2409            admin_error!("Unable to parse modify accesscontrols {:?}", e);
2410            e
2411        })?;
2412
2413        self.accesscontrols
2414            .update_modify(modify_acps)
2415            .map_err(|e| {
2416                admin_error!("Failed to update modify accesscontrols {:?}", e);
2417                e
2418            })?;
2419        // Update delete
2420        let filt = filter!(f_and!([
2421            f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2422            f_eq(Attribute::Class, EntryClass::AccessControlDelete.into()),
2423            f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2424        ]));
2425
2426        let res = self.internal_search(filt).map_err(|e| {
2427            admin_error!("reload accesscontrols internal search failed {:?}", e);
2428            e
2429        })?;
2430        let delete_acps: Result<Vec<_>, _> = res
2431            .iter()
2432            .map(|e| AccessControlDelete::try_from(self, e))
2433            .collect();
2434
2435        let delete_acps = delete_acps.map_err(|e| {
2436            admin_error!("Unable to parse delete accesscontrols {:?}", e);
2437            e
2438        })?;
2439
2440        self.accesscontrols.update_delete(delete_acps).map_err(|e| {
2441            admin_error!("Failed to update delete accesscontrols {:?}", e);
2442            e
2443        })
2444    }
2445
2446    #[instrument(level = "debug", skip_all)]
2447    pub(crate) fn reload_key_material(&mut self) -> Result<(), OperationError> {
2448        let filt = filter!(f_eq(Attribute::Class, EntryClass::KeyProvider.into()));
2449
2450        let res = self.internal_search(filt).map_err(|e| {
2451            admin_error!(
2452                err = ?e,
2453                "reload key providers internal search failed",
2454            );
2455            e
2456        })?;
2457
2458        // FUTURE: During this reload we may need to access the PIN or other data
2459        // to access the provider.
2460        let providers = res
2461            .iter()
2462            .map(|e| KeyProvider::try_from(e).and_then(|kp| kp.test().map(|()| kp)))
2463            .collect::<Result<Vec<_>, _>>()?;
2464
2465        self.key_providers.update_providers(providers)?;
2466
2467        let filt = filter!(f_eq(Attribute::Class, EntryClass::KeyObject.into()));
2468
2469        let res = self.internal_search(filt).map_err(|e| {
2470            admin_error!(
2471                err = ?e,
2472                "reload key objects internal search failed",
2473            );
2474            e
2475        })?;
2476
2477        res.iter()
2478            .try_for_each(|entry| self.key_providers.load_key_object(entry.as_ref()))
2479    }
2480
2481    #[instrument(level = "debug", skip_all)]
2482    pub(crate) fn reload_system_config(&mut self) -> Result<(), OperationError> {
2483        let denied_names = self.get_sc_denied_names()?;
2484        let pw_badlist = self.get_sc_password_badlist()?;
2485
2486        let mut_system_config = self.system_config.get_mut();
2487        mut_system_config.denied_names = denied_names;
2488        mut_system_config.pw_badlist = pw_badlist;
2489        Ok(())
2490    }
2491
2492    /// Pulls the domain name from the database and updates the DomainInfo data in memory
2493    #[instrument(level = "debug", skip_all)]
2494    pub(crate) fn reload_domain_info_version(&mut self) -> Result<(), OperationError> {
2495        let domain_info = self.internal_search_uuid(UUID_DOMAIN_INFO).map_err(|err| {
2496            error!(?err, "Error getting domain info");
2497            err
2498        })?;
2499
2500        let domain_info_version = domain_info
2501            .get_ava_single_uint32(Attribute::Version)
2502            .ok_or_else(|| {
2503                error!("domain info missing attribute version");
2504                OperationError::InvalidEntryState
2505            })?;
2506
2507        let domain_info_patch_level = domain_info
2508            .get_ava_single_uint32(Attribute::PatchLevel)
2509            .unwrap_or(0);
2510
2511        // If we have moved from stable to dev, this triggers the taint. If we
2512        // are moving from dev to stable, the db will be true triggering the
2513        // taint flag. If we are stable to stable this will be false.
2514        let current_devel_flag = option_env!("KANIDM_PRE_RELEASE").is_some();
2515        let domain_info_devel_taint = current_devel_flag
2516            || domain_info
2517                .get_ava_single_bool(Attribute::DomainDevelopmentTaint)
2518                .unwrap_or_default();
2519
2520        let domain_allow_easter_eggs = domain_info
2521            .get_ava_single_bool(Attribute::DomainAllowEasterEggs)
2522            // This defaults to false for release versions, and true in development
2523            .unwrap_or(option_env!("KANIDM_PRE_RELEASE").is_some());
2524
2525        // We have to set the domain version here so that features which check for it
2526        // will now see it's been increased. This also prevents recursion during reloads
2527        // inside of a domain migration.
2528        let mut_d_info = self.d_info.get_mut();
2529        debug!(?mut_d_info);
2530        // This is the value that is set as part of re-migrate.
2531        let previous_version = mut_d_info.d_vers;
2532        let previous_patch_level = mut_d_info.d_patch_level;
2533        mut_d_info.d_vers = domain_info_version;
2534        mut_d_info.d_patch_level = domain_info_patch_level;
2535        mut_d_info.d_devel_taint = domain_info_devel_taint;
2536        mut_d_info.d_allow_easter_eggs = domain_allow_easter_eggs;
2537
2538        debug!(?mut_d_info);
2539
2540        // We must both be at the correct domain version *and* the correct patch level. If we are
2541        // not, then we only proceed to migrate *if* our server boot phase is correct.
2542        if (previous_version == domain_info_version
2543            && previous_patch_level == domain_info_patch_level)
2544            || *self.phase < ServerPhase::DomainInfoReady
2545        {
2546            return Ok(());
2547        }
2548
2549        debug!(domain_previous_version = ?previous_version, domain_target_version = ?domain_info_version);
2550        debug!(domain_previous_patch_level = ?previous_patch_level, domain_target_patch_level = ?domain_info_patch_level);
2551
2552        // We have to check for DL0 since that's the initialisation level. If we are at DL0 then
2553        // the server was just brought up and there are no other actions to take since we are
2554        // now at TGT level.
2555        if previous_version == DOMAIN_LEVEL_0 {
2556            debug!(
2557                "Server was just brought up, skipping migrations as we are already at target level"
2558            );
2559            return Ok(());
2560        }
2561
2562        if previous_version < DOMAIN_MIN_REMIGRATION_LEVEL {
2563            let valid_levels: Vec<_> =
2564                (DOMAIN_MIN_REMIGRATION_LEVEL..DOMAIN_PREVIOUS_TGT_LEVEL).collect();
2565            error!("UNABLE TO PROCEED. You have requested an initial migration level which is lower than supported.");
2566            error!("For more see: https://kanidm.github.io/kanidm/stable/support.html#upgrade-policy and https://kanidm.github.io/kanidm/stable/server_updates.html");
2567            error!(domain_previous_version = ?previous_version, domain_target_version = ?domain_info_version);
2568            error!(domain_previous_patch_level = ?previous_patch_level, domain_target_patch_level = ?domain_info_patch_level);
2569            error!(?valid_levels);
2570
2571            debug_assert!(false);
2572
2573            return Err(OperationError::MG0001InvalidReMigrationLevel);
2574        }
2575
2576        // Commented as an example of patch application
2577        /*
2578        if previous_patch_level < PATCH_LEVEL_2
2579            && domain_info_patch_level >= PATCH_LEVEL_2
2580            && domain_info_version == DOMAIN_LEVEL_9
2581        {
2582            self.migrate_domain_patch_level_2()?;
2583        }
2584        */
2585
2586        // This is to catch during development if we incorrectly move MIN_REMIGRATION but
2587        // without actually updating these values correctly.
2588        const { assert!(DOMAIN_MIN_REMIGRATION_LEVEL <= DOMAIN_PREVIOUS_TGT_LEVEL) };
2589        const { assert!(DOMAIN_MIN_REMIGRATION_LEVEL >= DOMAIN_MIN_CREATION_LEVEL) };
2590
2591        const { assert!(DOMAIN_MIN_CREATION_LEVEL >= DOMAIN_LEVEL_10) };
2592
2593        //                     /--- This needs to be the minimum creation level.
2594        //                     |                                          /-- This is the minlevel we can remigrate from
2595        //                     v                                          v
2596        if previous_version <= DOMAIN_LEVEL_10 && domain_info_version >= DOMAIN_LEVEL_11 {
2597            // 1.6 -> 1.7
2598            self.migrate_domain_10_to_11()?;
2599        }
2600
2601        if previous_version <= DOMAIN_LEVEL_11 && domain_info_version >= DOMAIN_LEVEL_12 {
2602            // 1.7 -> 1.8
2603            self.migrate_domain_11_to_12()?;
2604        }
2605
2606        if previous_version <= DOMAIN_LEVEL_12 && domain_info_version >= DOMAIN_LEVEL_13 {
2607            // 1.8 -> 1.9
2608            self.migrate_domain_12_to_13()?;
2609        }
2610
2611        if previous_version <= DOMAIN_LEVEL_13 && domain_info_version >= DOMAIN_LEVEL_14 {
2612            // 1.9 -> 1.10
2613            self.migrate_domain_13_to_14()?;
2614        }
2615
2616        if previous_version <= DOMAIN_LEVEL_14 && domain_info_version >= DOMAIN_LEVEL_15 {
2617            // 1.10 -> 1.11
2618            self.migrate_domain_14_to_15()?;
2619        }
2620
2621        // This is here to catch when we increase domain levels but didn't create the migration
2622        // hooks. If this fails it probably means you need to add another migration hook
2623        // in the above.
2624        const { assert!(DOMAIN_MAX_LEVEL == DOMAIN_LEVEL_15) };
2625        debug_assert!(domain_info_version <= DOMAIN_MAX_LEVEL);
2626
2627        Ok(())
2628    }
2629
2630    /// Pulls the domain name from the database and updates the DomainInfo data in memory
2631    #[instrument(level = "debug", skip_all)]
2632    pub(crate) fn reload_domain_info(&mut self) -> Result<(), OperationError> {
2633        let domain_entry = self.get_db_domain()?;
2634
2635        let domain_name = domain_entry
2636            .get_ava_single_iname(Attribute::DomainName)
2637            .map(str::to_string)
2638            .ok_or(OperationError::InvalidEntryState)?;
2639
2640        let display_name = domain_entry
2641            .get_ava_single_utf8(Attribute::DomainDisplayName)
2642            .map(str::to_string)
2643            .unwrap_or_else(|| format!("Kanidm {domain_name}"));
2644
2645        let domain_ldap_allow_unix_pw_bind = domain_entry
2646            .get_ava_single_bool(Attribute::LdapAllowUnixPwBind)
2647            .unwrap_or(true);
2648
2649        let domain_image = domain_entry.get_ava_single_image(Attribute::Image);
2650
2651        let domain_uuid = self.be_txn.get_db_d_uuid()?;
2652
2653        let mut_d_info = self.d_info.get_mut();
2654        mut_d_info.d_ldap_allow_unix_pw_bind = domain_ldap_allow_unix_pw_bind;
2655        if mut_d_info.d_uuid != domain_uuid {
2656            admin_warn!(
2657                "Using domain uuid from the database {} - was {} in memory",
2658                domain_name,
2659                mut_d_info.d_name,
2660            );
2661            mut_d_info.d_uuid = domain_uuid;
2662        }
2663        if mut_d_info.d_name != domain_name {
2664            admin_warn!(
2665                "Using domain name from the database {} - was {} in memory",
2666                domain_name,
2667                mut_d_info.d_name,
2668            );
2669            admin_warn!(
2670                    "If you think this is an error, see https://kanidm.github.io/kanidm/master/domain_rename.html"
2671                );
2672            mut_d_info.d_name = domain_name;
2673        }
2674        mut_d_info.d_display = display_name;
2675        mut_d_info.d_image = domain_image;
2676        Ok(())
2677    }
2678
2679    /// Reloads feature configurations if they have changed in this operation
2680    #[instrument(level = "debug", skip_all)]
2681    pub(crate) fn reload_feature_config(&mut self) -> Result<(), OperationError> {
2682        let filt = filter!(f_eq(Attribute::Class, EntryClass::Feature.into()));
2683
2684        let feature_configs = self.internal_search(filt).inspect_err(|err| {
2685            error!(?err, "reload feature configuration internal search failed",)
2686        })?;
2687
2688        let current_time = self.get_curtime();
2689        let domain_level = self.get_domain_version();
2690
2691        let mut hmac_name_history_fixup = false;
2692
2693        // TODO: How to handle disabling on a delete? Needs thought ... but also
2694        // should be impossible for someone TO delete a feature config entry?
2695
2696        for feature_entry in feature_configs {
2697            match feature_entry.get_uuid() {
2698                UUID_HMAC_NAME_FEATURE => {
2699                    if domain_level < DOMAIN_LEVEL_12 {
2700                        trace!("Skipping hmac name history config");
2701                        continue;
2702                    }
2703
2704                    let key_object = self
2705                        .get_key_providers()
2706                        .get_key_object_handle(UUID_HMAC_NAME_FEATURE)
2707                        .ok_or(OperationError::KP0079KeyObjectNotFound)?;
2708
2709                    let mut key = HmacSha256Key::default();
2710                    key_object.hkdf_s256_expand(
2711                        UUID_HMAC_NAME_FEATURE.as_bytes(),
2712                        key.as_mut_slice(),
2713                        current_time,
2714                    )?;
2715
2716                    drop(key_object);
2717
2718                    let new_feature_enabled_state = feature_entry
2719                        .get_ava_single_bool(Attribute::Enabled)
2720                        .unwrap_or_default();
2721
2722                    let feature_config_txn = self.feature_config.get_mut();
2723
2724                    hmac_name_history_fixup =
2725                        !feature_config_txn.hmac_name_history.enabled && new_feature_enabled_state;
2726
2727                    feature_config_txn.hmac_name_history.enabled = new_feature_enabled_state;
2728
2729                    std::mem::swap(&mut key, &mut feature_config_txn.hmac_name_history.key);
2730                }
2731                feature_uuid => {
2732                    error!(
2733                        ?feature_uuid,
2734                        "Unrecognised feature uuid, unable to proceed"
2735                    );
2736                    return Err(OperationError::KG004UnknownFeatureUuid);
2737                }
2738            }
2739        }
2740
2741        if hmac_name_history_fixup {
2742            plugins::hmac_name_unique::HmacNameUnique::fixup(self)?;
2743        }
2744
2745        Ok(())
2746    }
2747
2748    /// Initiate a domain display name change process. This isn't particularly scary
2749    /// because it's just a wibbly human-facing thing, not used for secure
2750    /// activities (yet)
2751    pub fn set_domain_display_name(&mut self, new_domain_name: &str) -> Result<(), OperationError> {
2752        let modl = ModifyList::new_purge_and_set(
2753            Attribute::DomainDisplayName,
2754            Value::new_utf8(new_domain_name.to_string()),
2755        );
2756        let udi = PVUUID_DOMAIN_INFO.clone();
2757        let filt = filter_all!(f_eq(Attribute::Uuid, udi));
2758        self.internal_modify(&filt, &modl)
2759    }
2760
2761    /// Initiate a domain rename process. This is generally an internal function but it's
2762    /// exposed to the cli for admins to be able to initiate the process.
2763    ///
2764    /// # Safety
2765    /// This is UNSAFE because while it may change the domain name, it doesn't update
2766    /// the running configured version of the domain name that is resident to the
2767    /// query server.
2768    ///
2769    /// Currently it's only used to test what happens if we rename the domain and how
2770    /// that impacts spns, but in the future we may need to reconsider how this is
2771    /// approached, especially if we have a domain re-name replicated to us. It could
2772    /// be that we end up needing to have this as a cow cell or similar?
2773    pub fn danger_domain_rename(&mut self, new_domain_name: &str) -> Result<(), OperationError> {
2774        let modl =
2775            ModifyList::new_purge_and_set(Attribute::DomainName, Value::new_iname(new_domain_name));
2776        let udi = PVUUID_DOMAIN_INFO.clone();
2777        let filt = filter_all!(f_eq(Attribute::Uuid, udi));
2778        self.internal_modify(&filt, &modl)
2779    }
2780
2781    pub fn reindex(&mut self, immediate: bool) -> Result<(), OperationError> {
2782        // initiate a be reindex here. This could have been from first run checking
2783        // the versions, or it could just be from the cli where an admin needs to do an
2784        // indexing.
2785        self.be_txn.reindex(immediate)
2786    }
2787
2788    fn force_schema_reload(&mut self) {
2789        self.changed_flags.insert(ChangeFlag::SCHEMA);
2790    }
2791
2792    fn force_domain_reload(&mut self) {
2793        self.changed_flags.insert(ChangeFlag::DOMAIN);
2794    }
2795
2796    pub(crate) fn upgrade_reindex(&mut self, v: i64) -> Result<(), OperationError> {
2797        self.be_txn.upgrade_reindex(v)
2798    }
2799
2800    #[inline]
2801    pub(crate) fn get_changed_app(&self) -> bool {
2802        self.changed_flags.contains(ChangeFlag::APPLICATION)
2803    }
2804
2805    #[inline]
2806    pub(crate) fn get_changed_oauth2(&self) -> bool {
2807        self.changed_flags.contains(ChangeFlag::OAUTH2)
2808    }
2809
2810    #[inline]
2811    pub(crate) fn clear_changed_oauth2(&mut self) {
2812        self.changed_flags.remove(ChangeFlag::OAUTH2)
2813    }
2814
2815    #[inline]
2816    pub(crate) fn get_changed_oauth2_client(&self) -> bool {
2817        self.changed_flags.contains(ChangeFlag::OAUTH2_CLIENT)
2818    }
2819
2820    /// Indicate that we are about to re-bootstrap this server. You should ONLY
2821    /// call this during a replication refresh!!!
2822    pub(crate) fn set_phase_bootstrap(&mut self) {
2823        *self.phase = ServerPhase::Bootstrap;
2824    }
2825
2826    /// Raise the currently running server phase.
2827    pub(crate) fn set_phase(&mut self, phase: ServerPhase) {
2828        // Phase changes are one way
2829        if phase > *self.phase {
2830            *self.phase = phase
2831        }
2832    }
2833
2834    pub(crate) fn get_phase(&mut self) -> ServerPhase {
2835        *self.phase
2836    }
2837
2838    pub(crate) fn reload(&mut self) -> Result<(), OperationError> {
2839        // First, check if the domain version has changed. This can trigger
2840        // changes to schema, access controls and more.
2841        if self.changed_flags.intersects(ChangeFlag::DOMAIN) {
2842            self.reload_domain_info_version()?;
2843        }
2844
2845        // This could be faster if we cache the set of classes changed
2846        // in an operation so we can check if we need to do the reload or not
2847        //
2848        // Reload the schema from qs.
2849        if self.changed_flags.intersects(ChangeFlag::SCHEMA) {
2850            self.reload_schema()?;
2851
2852            // If the server is in a late phase of start up or is
2853            // operational, then a reindex may be required. After the reindex, the schema
2854            // must also be reloaded so that slope optimisation indexes are loaded correctly.
2855            if *self.phase >= ServerPhase::Running {
2856                self.reindex(false)?;
2857                self.reload_schema()?;
2858            }
2859        }
2860
2861        // We need to reload cryptographic providers before anything else so that
2862        // sync agreements and the domain can access their key material.
2863        if self
2864            .changed_flags
2865            .intersects(ChangeFlag::SCHEMA | ChangeFlag::KEY_MATERIAL)
2866        {
2867            self.reload_key_material()?;
2868        }
2869
2870        // Determine if we need to update access control profiles
2871        // based on any modifications that have occurred.
2872        // IF SCHEMA CHANGED WE MUST ALSO RELOAD!!! IE if schema had an attr removed
2873        // that we rely on we MUST fail this here!!
2874        //
2875        // Also note that changing sync agreements triggers an acp reload since
2876        // access controls need to be aware of these agreements.
2877        if self
2878            .changed_flags
2879            .intersects(ChangeFlag::SCHEMA | ChangeFlag::ACP | ChangeFlag::SYNC_AGREEMENT)
2880        {
2881            self.reload_accesscontrols()?;
2882        } else {
2883            // On a reload the cache is dropped, otherwise we tell accesscontrols
2884            // to drop anything related that was changed.
2885            // self.accesscontrols
2886            //    .invalidate_related_cache(self.changed_uuid.into_inner().as_slice())
2887        }
2888
2889        if self.changed_flags.intersects(ChangeFlag::SYSTEM_CONFIG) {
2890            self.reload_system_config()?;
2891        }
2892
2893        if self.changed_flags.intersects(ChangeFlag::DOMAIN) {
2894            self.reload_domain_info()?;
2895        }
2896
2897        if self
2898            .changed_flags
2899            .intersects(ChangeFlag::FEATURE | ChangeFlag::KEY_MATERIAL)
2900        {
2901            self.reload_feature_config()?;
2902        }
2903
2904        // Clear flags
2905        self.changed_flags.remove(
2906            ChangeFlag::DOMAIN
2907                | ChangeFlag::SCHEMA
2908                | ChangeFlag::FEATURE
2909                | ChangeFlag::SYSTEM_CONFIG
2910                | ChangeFlag::ACP
2911                | ChangeFlag::SYNC_AGREEMENT
2912                | ChangeFlag::KEY_MATERIAL,
2913        );
2914
2915        Ok(())
2916    }
2917
2918    #[cfg(any(test, debug_assertions))]
2919    #[instrument(level = "debug", skip_all)]
2920    pub fn clear_cache(&mut self) -> Result<(), OperationError> {
2921        self.be_txn.clear_cache()
2922    }
2923
2924    #[instrument(level = "debug", name="qswt_commit" skip_all)]
2925    pub fn commit(mut self) -> Result<(), OperationError> {
2926        self.reload()?;
2927
2928        // Now destructure the transaction ready to reset it.
2929        let QueryServerWriteTransaction {
2930            committed,
2931            phase,
2932            d_info,
2933            system_config,
2934            feature_config,
2935            mut be_txn,
2936            schema,
2937            accesscontrols,
2938            cid,
2939            dyngroup_cache,
2940            key_providers,
2941            // Hold these for a bit more ...
2942            _db_ticket,
2943            _write_ticket,
2944            // Ignore values that don't need a commit.
2945            curtime: _,
2946            trim_cid: _,
2947            changed_flags,
2948            changed_uuid: _,
2949            resolve_filter_cache: _,
2950            resolve_filter_cache_clear,
2951            mut resolve_filter_cache_write,
2952            txn_name_to_uuid: _,
2953        } = self;
2954        debug_assert!(!committed);
2955
2956        // Should have been cleared by any reloads.
2957        trace!(
2958            changed = ?changed_flags.iter_names().collect::<Vec<_>>(),
2959        );
2960
2961        // Write the cid to the db. If this fails, we can't assume replication
2962        // will be stable, so return if it fails.
2963        be_txn.set_db_ts_max(cid.ts)?;
2964        cid.commit();
2965
2966        // We don't care if this passes/fails, committing this is fine.
2967        if resolve_filter_cache_clear {
2968            resolve_filter_cache_write.clear();
2969        }
2970        resolve_filter_cache_write.commit();
2971
2972        // Point of no return - everything has been validated and reloaded.
2973        //
2974        // = Lets commit =
2975        schema
2976            .commit()
2977            .map(|_| d_info.commit())
2978            .map(|_| system_config.commit())
2979            .map(|_| feature_config.commit())
2980            .map(|_| phase.commit())
2981            .map(|_| dyngroup_cache.commit())
2982            .and_then(|_| key_providers.commit())
2983            .and_then(|_| accesscontrols.commit())
2984            .and_then(|_| be_txn.commit())
2985    }
2986
2987    pub(crate) fn get_txn_cid(&self) -> &Cid {
2988        &self.cid
2989    }
2990}
2991
2992#[cfg(test)]
2993mod tests {
2994    use crate::prelude::*;
2995    use kanidm_proto::scim_v1::{
2996        server::{ScimListResponse, ScimReference},
2997        JsonValue, ScimEntryGetQuery, ScimFilter,
2998    };
2999    use std::num::NonZeroU64;
3000
3001    #[qs_test]
3002    async fn test_name_to_uuid(server: &QueryServer) {
3003        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3004
3005        let t_uuid = Uuid::new_v4();
3006        assert!(server_txn
3007            .internal_create(vec![entry_init!(
3008                (Attribute::Class, EntryClass::Object.to_value()),
3009                (Attribute::Class, EntryClass::Account.to_value()),
3010                (Attribute::Class, EntryClass::Person.to_value()),
3011                (Attribute::Name, Value::new_iname("testperson1")),
3012                (Attribute::Uuid, Value::Uuid(t_uuid)),
3013                (Attribute::Description, Value::new_utf8s("testperson1")),
3014                (Attribute::DisplayName, Value::new_utf8s("testperson1"))
3015            ),])
3016            .is_ok());
3017
3018        // Name doesn't exist
3019        let r1 = server_txn.name_to_uuid("testpers");
3020        assert!(r1.is_err());
3021        // Name doesn't exist (not syntax normalised)
3022        let r2 = server_txn.name_to_uuid("tEsTpErS");
3023        assert!(r2.is_err());
3024        // Name does exist
3025        let r3 = server_txn.name_to_uuid("testperson1");
3026        assert_eq!(r3, Ok(t_uuid));
3027        // Name is not syntax normalised (but exists)
3028        let r4 = server_txn.name_to_uuid("tEsTpErSoN1");
3029        assert_eq!(r4, Ok(t_uuid));
3030        // Name is an rdn
3031        let r5 = server_txn.name_to_uuid("name=testperson1");
3032        assert_eq!(r5, Ok(t_uuid));
3033        // Name is a dn
3034        let r6 = server_txn.name_to_uuid("name=testperson1,o=example");
3035        assert_eq!(r6, Ok(t_uuid));
3036    }
3037
3038    #[qs_test]
3039    async fn test_external_id_to_uuid(server: &QueryServer) {
3040        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3041
3042        let t_uuid = Uuid::new_v4();
3043        assert!(server_txn
3044            .internal_create(vec![entry_init!(
3045                (Attribute::Class, EntryClass::Object.to_value()),
3046                (Attribute::Class, EntryClass::ExtensibleObject.to_value()),
3047                (Attribute::Uuid, Value::Uuid(t_uuid)),
3048                (
3049                    Attribute::SyncExternalId,
3050                    Value::new_iutf8("uid=testperson")
3051                )
3052            ),])
3053            .is_ok());
3054
3055        // Name doesn't exist
3056        let r1 = server_txn.sync_external_id_to_uuid("tobias");
3057        assert_eq!(r1, Ok(None));
3058        // Name doesn't exist (not syntax normalised)
3059        let r2 = server_txn.sync_external_id_to_uuid("tObIAs");
3060        assert_eq!(r2, Ok(None));
3061        // Name does exist
3062        let r3 = server_txn.sync_external_id_to_uuid("uid=testperson");
3063        assert_eq!(r3, Ok(Some(t_uuid)));
3064        // Name is not syntax normalised (but exists)
3065        let r4 = server_txn.sync_external_id_to_uuid("uId=TeStPeRsOn");
3066        assert_eq!(r4, Ok(Some(t_uuid)));
3067    }
3068
3069    #[qs_test]
3070    async fn test_uuid_to_spn(server: &QueryServer) {
3071        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3072
3073        let e1 = entry_init!(
3074            (Attribute::Class, EntryClass::Object.to_value()),
3075            (Attribute::Class, EntryClass::Person.to_value()),
3076            (Attribute::Class, EntryClass::Account.to_value()),
3077            (Attribute::Name, Value::new_iname("testperson1")),
3078            (
3079                Attribute::Uuid,
3080                Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
3081            ),
3082            (Attribute::Description, Value::new_utf8s("testperson1")),
3083            (Attribute::DisplayName, Value::new_utf8s("testperson1"))
3084        );
3085        let ce = CreateEvent::new_internal(vec![e1]);
3086        let cr = server_txn.create(&ce);
3087        assert!(cr.is_ok());
3088
3089        // Name doesn't exist
3090        let r1 = server_txn.uuid_to_spn(uuid!("bae3f507-e6c3-44ba-ad01-f8ff1083534a"));
3091        // There is nothing.
3092        assert_eq!(r1, Ok(None));
3093        // Name does exist
3094        let r3 = server_txn.uuid_to_spn(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"));
3095        println!("{r3:?}");
3096        assert_eq!(
3097            r3.unwrap().unwrap(),
3098            Value::new_spn_str("testperson1", "example.com")
3099        );
3100        // Name is not syntax normalised (but exists)
3101        let r4 = server_txn.uuid_to_spn(uuid!("CC8E95B4-C24F-4D68-BA54-8BED76F63930"));
3102        assert_eq!(
3103            r4.unwrap().unwrap(),
3104            Value::new_spn_str("testperson1", "example.com")
3105        );
3106    }
3107
3108    #[qs_test]
3109    async fn test_uuid_to_rdn(server: &QueryServer) {
3110        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3111
3112        let e1 = entry_init!(
3113            (Attribute::Class, EntryClass::Object.to_value()),
3114            (Attribute::Class, EntryClass::Person.to_value()),
3115            (Attribute::Class, EntryClass::Account.to_value()),
3116            (Attribute::Name, Value::new_iname("testperson1")),
3117            (
3118                Attribute::Uuid,
3119                Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
3120            ),
3121            (Attribute::Description, Value::new_utf8s("testperson")),
3122            (Attribute::DisplayName, Value::new_utf8s("testperson1"))
3123        );
3124        let ce = CreateEvent::new_internal(vec![e1]);
3125        let cr = server_txn.create(&ce);
3126        assert!(cr.is_ok());
3127
3128        // Name doesn't exist
3129        let r1 = server_txn.uuid_to_rdn(uuid!("bae3f507-e6c3-44ba-ad01-f8ff1083534a"));
3130        // There is nothing.
3131        assert_eq!(r1.unwrap(), "uuid=bae3f507-e6c3-44ba-ad01-f8ff1083534a");
3132        // Name does exist
3133        let r3 = server_txn.uuid_to_rdn(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"));
3134        println!("{r3:?}");
3135        assert_eq!(r3.unwrap(), "spn=testperson1@example.com");
3136        // Uuid is not syntax normalised (but exists)
3137        let r4 = server_txn.uuid_to_rdn(uuid!("CC8E95B4-C24F-4D68-BA54-8BED76F63930"));
3138        assert_eq!(r4.unwrap(), "spn=testperson1@example.com");
3139    }
3140
3141    #[qs_test]
3142    async fn test_clone_value(server: &QueryServer) {
3143        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3144        let e1 = entry_init!(
3145            (Attribute::Class, EntryClass::Object.to_value()),
3146            (Attribute::Class, EntryClass::Account.to_value()),
3147            (Attribute::Class, EntryClass::Person.to_value()),
3148            (Attribute::Name, Value::new_iname("testperson1")),
3149            (
3150                Attribute::Uuid,
3151                Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
3152            ),
3153            (Attribute::Description, Value::new_utf8s("testperson1")),
3154            (Attribute::DisplayName, Value::new_utf8s("testperson1"))
3155        );
3156        let ce = CreateEvent::new_internal(vec![e1]);
3157        let cr = server_txn.create(&ce);
3158        assert!(cr.is_ok());
3159
3160        // test attr not exist
3161        let r1 = server_txn.clone_value(&Attribute::from("tausau"), "naoeutnhaou");
3162
3163        assert!(r1.is_err());
3164
3165        // test attr not-normalised (error)
3166        // test attr not-reference
3167        let r2 = server_txn.clone_value(&Attribute::Custom("NaMe".into()), "NaMe");
3168
3169        assert!(r2.is_err());
3170
3171        // test attr reference
3172        let r3 = server_txn.clone_value(&Attribute::from("member"), "testperson1");
3173
3174        assert_eq!(
3175            r3,
3176            Ok(Value::Refer(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")))
3177        );
3178
3179        // test attr reference already resolved.
3180        let r4 = server_txn.clone_value(
3181            &Attribute::from("member"),
3182            "cc8e95b4-c24f-4d68-ba54-8bed76f63930",
3183        );
3184
3185        debug!("{:?}", r4);
3186        assert_eq!(
3187            r4,
3188            Ok(Value::Refer(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")))
3189        );
3190    }
3191
3192    #[qs_test]
3193    async fn test_dynamic_schema_class(server: &QueryServer) {
3194        let e1 = entry_init!(
3195            (Attribute::Class, EntryClass::Object.to_value()),
3196            (Attribute::Class, EntryClass::TestClass.to_value()),
3197            (Attribute::Name, Value::new_iname("testobj1")),
3198            (
3199                Attribute::Uuid,
3200                Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
3201            )
3202        );
3203
3204        // Class definition
3205        let e_cd = entry_init!(
3206            (Attribute::Class, EntryClass::Object.to_value()),
3207            (Attribute::Class, EntryClass::ClassType.to_value()),
3208            (Attribute::ClassName, EntryClass::TestClass.to_value()),
3209            (
3210                Attribute::Uuid,
3211                Value::Uuid(uuid!("cfcae205-31c3-484b-8ced-667d1709c5e3"))
3212            ),
3213            (Attribute::Description, Value::new_utf8s("Test Class")),
3214            (Attribute::May, Value::from(Attribute::Name))
3215        );
3216        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3217        // Add a new class.
3218        let ce_class = CreateEvent::new_internal(vec![e_cd.clone()]);
3219        assert!(server_txn.create(&ce_class).is_ok());
3220        // Trying to add it now should fail.
3221        let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
3222        assert!(server_txn.create(&ce_fail).is_err());
3223
3224        // Commit
3225        server_txn.commit().expect("should not fail");
3226
3227        // Start a new write
3228        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3229        // Add the class to an object
3230        // should work
3231        let ce_work = CreateEvent::new_internal(vec![e1.clone()]);
3232        assert!(server_txn.create(&ce_work).is_ok());
3233
3234        // Commit
3235        server_txn.commit().expect("should not fail");
3236
3237        // Start a new write
3238        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3239        // delete the class
3240        let de_class = DeleteEvent::new_internal_invalid(filter!(f_eq(
3241            Attribute::ClassName,
3242            EntryClass::TestClass.into()
3243        )));
3244        assert!(server_txn.delete(&de_class).is_ok());
3245        // Commit
3246        server_txn.commit().expect("should not fail");
3247
3248        // Start a new write
3249        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3250        // Trying to add now should fail
3251        let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
3252        assert!(server_txn.create(&ce_fail).is_err());
3253        // Search our entry
3254        let testobj1 = server_txn
3255            .internal_search_uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
3256            .expect("failed");
3257        assert!(testobj1.attribute_equality(Attribute::Class, &EntryClass::TestClass.into()));
3258
3259        // Should still be good
3260        server_txn.commit().expect("should not fail");
3261        // Commit.
3262    }
3263
3264    #[qs_test]
3265    async fn test_dynamic_schema_attr(server: &QueryServer) {
3266        let e1 = entry_init!(
3267            (Attribute::Class, EntryClass::Object.to_value()),
3268            (Attribute::Class, EntryClass::ExtensibleObject.to_value()),
3269            (Attribute::Name, Value::new_iname("testobj1")),
3270            (
3271                Attribute::Uuid,
3272                Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
3273            ),
3274            (Attribute::TestAttr, Value::new_utf8s("test"))
3275        );
3276
3277        // Attribute definition
3278        let e_ad = entry_init!(
3279            (Attribute::Class, EntryClass::Object.to_value()),
3280            (Attribute::Class, EntryClass::AttributeType.to_value()),
3281            (
3282                Attribute::Uuid,
3283                Value::Uuid(uuid!("cfcae205-31c3-484b-8ced-667d1709c5e3"))
3284            ),
3285            (Attribute::AttributeName, Value::from(Attribute::TestAttr)),
3286            (Attribute::Description, Value::new_utf8s("Test Attribute")),
3287            (Attribute::MultiValue, Value::new_bool(false)),
3288            (Attribute::Unique, Value::new_bool(false)),
3289            (
3290                Attribute::Syntax,
3291                Value::new_syntaxs("UTF8STRING").expect("syntax")
3292            )
3293        );
3294
3295        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3296        // Add a new attribute.
3297        let ce_attr = CreateEvent::new_internal(vec![e_ad.clone()]);
3298        assert!(server_txn.create(&ce_attr).is_ok());
3299        // Trying to add it now should fail. (use extensible object)
3300        let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
3301        assert!(server_txn.create(&ce_fail).is_err());
3302
3303        // Commit
3304        server_txn.commit().expect("should not fail");
3305
3306        // Start a new write
3307        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3308        // Add the attr to an object
3309        // should work
3310        let ce_work = CreateEvent::new_internal(vec![e1.clone()]);
3311        assert!(server_txn.create(&ce_work).is_ok());
3312
3313        // Commit
3314        server_txn.commit().expect("should not fail");
3315
3316        // Start a new write
3317        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3318        // delete the attr
3319        let de_attr = DeleteEvent::new_internal_invalid(filter!(f_eq(
3320            Attribute::AttributeName,
3321            PartialValue::from(Attribute::TestAttr)
3322        )));
3323        assert!(server_txn.delete(&de_attr).is_ok());
3324        // Commit
3325        server_txn.commit().expect("should not fail");
3326
3327        // Start a new write
3328        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3329        // Trying to add now should fail
3330        let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
3331        assert!(server_txn.create(&ce_fail).is_err());
3332        // Search our attribute - should FAIL
3333        let filt = filter!(f_eq(Attribute::TestAttr, PartialValue::new_utf8s("test")));
3334        assert!(server_txn.internal_search(filt).is_err());
3335        // Search the entry - the attribute will still be present
3336        // even if we can't search on it.
3337        let testobj1 = server_txn
3338            .internal_search_uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
3339            .expect("failed");
3340        assert!(testobj1.attribute_equality(Attribute::TestAttr, &PartialValue::new_utf8s("test")));
3341
3342        server_txn.commit().expect("should not fail");
3343        // Commit.
3344    }
3345
3346    #[qs_test]
3347    async fn test_scim_entry_structure(server: &QueryServer) {
3348        let mut read_txn = server.read().await.unwrap();
3349
3350        // Query entry (A builtin one ?)
3351        let entry = read_txn
3352            .internal_search_uuid(UUID_IDM_PEOPLE_SELF_NAME_WRITE)
3353            .unwrap();
3354
3355        // Convert entry into scim
3356        let reduced = entry.as_ref().clone().into_reduced();
3357        let scim_entry = reduced.to_scim_kanidm(&mut read_txn).unwrap();
3358
3359        // Assert scim entry attributes are as expected
3360        assert_eq!(scim_entry.header.id, UUID_IDM_PEOPLE_SELF_NAME_WRITE);
3361        let name_scim = scim_entry.attrs.get(&Attribute::Name).unwrap();
3362        match name_scim {
3363            ScimValueKanidm::String(name) => {
3364                assert_eq!(name.clone(), "idm_people_self_name_write")
3365            }
3366            _ => {
3367                panic!("expected String, actual {name_scim:?}");
3368            }
3369        }
3370
3371        // such as returning a new struct type for `members` attributes or `managed_by`
3372        let entry_managed_by_scim = scim_entry.attrs.get(&Attribute::EntryManagedBy).unwrap();
3373        match entry_managed_by_scim {
3374            ScimValueKanidm::EntryReferences(managed_by) => {
3375                assert_eq!(
3376                    managed_by.first().unwrap().clone(),
3377                    ScimReference {
3378                        uuid: UUID_IDM_ADMINS,
3379                        value: "idm_admins@example.com".to_string()
3380                    }
3381                )
3382            }
3383            _ => {
3384                panic!("expected EntryReference, actual {entry_managed_by_scim:?}");
3385            }
3386        }
3387
3388        let members_scim = scim_entry.attrs.get(&Attribute::Member).unwrap();
3389        match members_scim {
3390            ScimValueKanidm::EntryReferences(members) => {
3391                assert_eq!(
3392                    members.first().unwrap().clone(),
3393                    ScimReference {
3394                        uuid: UUID_IDM_ALL_PERSONS,
3395                        value: "idm_all_persons@example.com".to_string()
3396                    }
3397                )
3398            }
3399            _ => {
3400                panic!("expected EntryReferences, actual {members_scim:?}");
3401            }
3402        }
3403    }
3404
3405    #[qs_test]
3406    async fn test_scim_effective_access_query(server: &QueryServer) {
3407        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3408
3409        let group_uuid = Uuid::new_v4();
3410        let e1 = entry_init!(
3411            (Attribute::Class, EntryClass::Object.to_value()),
3412            (Attribute::Class, EntryClass::Group.to_value()),
3413            (Attribute::Name, Value::new_iname("testgroup")),
3414            (Attribute::Uuid, Value::Uuid(group_uuid))
3415        );
3416
3417        assert!(server_txn.internal_create(vec![e1]).is_ok());
3418        assert!(server_txn.commit().is_ok());
3419
3420        // Now read that entry.
3421
3422        let mut server_txn = server.read().await.unwrap();
3423
3424        let idm_admin_entry = server_txn.internal_search_uuid(UUID_IDM_ADMIN).unwrap();
3425        let idm_admin_ident = Identity::from_impersonate_entry_readwrite(idm_admin_entry);
3426
3427        let query = ScimEntryGetQuery {
3428            ext_access_check: true,
3429            ..Default::default()
3430        };
3431
3432        let scim_entry = server_txn
3433            .scim_entry_id_get_ext(group_uuid, EntryClass::Group, query, idm_admin_ident)
3434            .unwrap();
3435
3436        let ext_access_check = scim_entry.ext_access_check.unwrap();
3437
3438        trace!(?ext_access_check);
3439
3440        assert!(ext_access_check.delete);
3441        assert!(ext_access_check.search.check(&Attribute::DirectMemberOf));
3442        assert!(ext_access_check.search.check(&Attribute::MemberOf));
3443        assert!(ext_access_check.search.check(&Attribute::Name));
3444        assert!(ext_access_check.modify_present.check(&Attribute::Name));
3445        assert!(ext_access_check.modify_remove.check(&Attribute::Name));
3446    }
3447
3448    #[qs_test]
3449    async fn test_scim_basic_search_ext_query(server: &QueryServer) {
3450        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3451
3452        let group_uuid = Uuid::new_v4();
3453        let e1 = entry_init!(
3454            (Attribute::Class, EntryClass::Object.to_value()),
3455            (Attribute::Class, EntryClass::Group.to_value()),
3456            (Attribute::Name, Value::new_iname("testgroup")),
3457            (Attribute::Uuid, Value::Uuid(group_uuid))
3458        );
3459
3460        assert!(server_txn.internal_create(vec![e1]).is_ok());
3461        assert!(server_txn.commit().is_ok());
3462
3463        // Now read that entry.
3464        let mut server_txn = server.read().await.unwrap();
3465
3466        let idm_admin_entry = server_txn.internal_search_uuid(UUID_IDM_ADMIN).unwrap();
3467        let idm_admin_ident = Identity::from_impersonate_entry_readwrite(idm_admin_entry);
3468
3469        let filter = ScimFilter::And(
3470            Box::new(ScimFilter::Equal(
3471                Attribute::Class.into(),
3472                EntryClass::Group.into(),
3473            )),
3474            Box::new(ScimFilter::Equal(
3475                Attribute::Uuid.into(),
3476                JsonValue::String(group_uuid.to_string()),
3477            )),
3478        );
3479
3480        let base: ScimListResponse = server_txn
3481            .scim_search_ext(idm_admin_ident, filter, ScimEntryGetQuery::default())
3482            .unwrap();
3483
3484        assert_eq!(base.resources.len(), 1);
3485        assert_eq!(base.total_results, 1);
3486        // Pagination not requested,
3487        assert_eq!(base.items_per_page, None);
3488        assert_eq!(base.start_index, None);
3489        assert_eq!(base.resources[0].header.id, group_uuid);
3490    }
3491
3492    #[qs_test]
3493    async fn test_scim_basic_search_ext_query_with_sort(server: &QueryServer) {
3494        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3495
3496        for i in (1..4).rev() {
3497            let e1 = entry_init!(
3498                (Attribute::Class, EntryClass::Object.to_value()),
3499                (Attribute::Class, EntryClass::Group.to_value()),
3500                (
3501                    Attribute::Name,
3502                    Value::new_iname(format!("testgroup{i}").as_str())
3503                )
3504            );
3505            assert!(server_txn.internal_create(vec![e1]).is_ok());
3506        }
3507
3508        assert!(server_txn.commit().is_ok());
3509
3510        // Now read that entry.
3511        let mut server_txn = server.read().await.unwrap();
3512
3513        let idm_admin_entry = server_txn.internal_search_uuid(UUID_IDM_ADMIN).unwrap();
3514        let idm_admin_ident = Identity::from_impersonate_entry_readwrite(idm_admin_entry);
3515
3516        let filter = ScimFilter::And(
3517            Box::new(ScimFilter::Equal(
3518                Attribute::Class.into(),
3519                EntryClass::Group.into(),
3520            )),
3521            Box::new(ScimFilter::StartsWith(
3522                Attribute::Name.into(),
3523                JsonValue::String("testgroup".into()),
3524            )),
3525        );
3526
3527        let base: ScimListResponse = server_txn
3528            .scim_search_ext(
3529                idm_admin_ident.clone(),
3530                filter.clone(),
3531                ScimEntryGetQuery {
3532                    sort_by: Some(Attribute::Name),
3533                    ..Default::default()
3534                },
3535            )
3536            .unwrap();
3537
3538        assert_eq!(base.resources.len(), 3);
3539        assert_eq!(base.total_results, 3);
3540        // Pagination not requested,
3541        assert_eq!(base.items_per_page, None);
3542        assert_eq!(base.start_index, None);
3543
3544        let Some(ScimValueKanidm::String(testgroup_name_0)) =
3545            base.resources[0].attrs.get(&Attribute::Name)
3546        else {
3547            panic!("Invalid data in attribute.");
3548        };
3549        let Some(ScimValueKanidm::String(testgroup_name_1)) =
3550            base.resources[1].attrs.get(&Attribute::Name)
3551        else {
3552            panic!("Invalid data in attribute.");
3553        };
3554        let Some(ScimValueKanidm::String(testgroup_name_2)) =
3555            base.resources[2].attrs.get(&Attribute::Name)
3556        else {
3557            panic!("Invalid data in attribute.");
3558        };
3559
3560        assert!(testgroup_name_0 < testgroup_name_1);
3561        assert!(testgroup_name_0 < testgroup_name_2);
3562        assert!(testgroup_name_1 < testgroup_name_2);
3563
3564        // ================
3565        // Test pagination.
3566        let base: ScimListResponse = server_txn
3567            .scim_search_ext(
3568                idm_admin_ident.clone(),
3569                filter.clone(),
3570                ScimEntryGetQuery {
3571                    count: NonZeroU64::new(1),
3572                    ..Default::default()
3573                },
3574            )
3575            .unwrap();
3576
3577        assert_eq!(base.resources.len(), 1);
3578        assert_eq!(base.total_results, 3);
3579        // Pagination not requested,
3580        assert_eq!(base.items_per_page, NonZeroU64::new(1));
3581        assert_eq!(base.start_index, NonZeroU64::new(1));
3582
3583        let Some(ScimValueKanidm::String(testgroup_name_0)) =
3584            base.resources[0].attrs.get(&Attribute::Name)
3585        else {
3586            panic!("Invalid data in attribute.");
3587        };
3588        // DB has reverse order
3589        assert_eq!(testgroup_name_0, "testgroup3");
3590
3591        // ================
3592        // Test pagination + sort
3593        let base: ScimListResponse = server_txn
3594            .scim_search_ext(
3595                idm_admin_ident,
3596                filter.clone(),
3597                ScimEntryGetQuery {
3598                    sort_by: Some(Attribute::Name),
3599                    count: NonZeroU64::new(2),
3600                    start_index: NonZeroU64::new(2),
3601                    ..Default::default()
3602                },
3603            )
3604            .unwrap();
3605
3606        assert_eq!(base.resources.len(), 2);
3607        assert_eq!(base.total_results, 3);
3608        assert_eq!(base.items_per_page, NonZeroU64::new(2));
3609        assert_eq!(base.start_index, NonZeroU64::new(2));
3610
3611        let Some(ScimValueKanidm::String(testgroup_name_0)) =
3612            base.resources[0].attrs.get(&Attribute::Name)
3613        else {
3614            panic!("Invalid data in attribute.");
3615        };
3616        let Some(ScimValueKanidm::String(testgroup_name_1)) =
3617            base.resources[1].attrs.get(&Attribute::Name)
3618        else {
3619            panic!("Invalid data in attribute.");
3620        };
3621        // Sorted, note we skipped entry "testgroup 1" using pagination.
3622        assert_eq!(testgroup_name_0, "testgroup2");
3623        assert_eq!(testgroup_name_1, "testgroup3");
3624    }
3625}