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