kanidmd_lib/server/
mod.rs

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