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