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