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