kanidmd_lib/server/
mod.rs

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