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