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        // The schema is now valid, so we load this up
1546
1547        //  * Indexing (req be + sch )
1548        let idx_errs = self.get_be_txn().verify_indexes();
1549
1550        if !idx_errs.is_empty() {
1551            return idx_errs;
1552        }
1553
1554        // If anything error to this point we can't trust the verifications below. From
1555        // here we can just amass results.
1556        let mut results = Vec::with_capacity(0);
1557
1558        // Verify all our entries. Weird flex I know, but it's needed for verifying
1559        // the entry changelogs are consistent to their entries.
1560        let schema = self.get_schema();
1561
1562        let filt_all = filter!(f_pres(Attribute::Class));
1563        let all_entries = match self.internal_search(filt_all) {
1564            Ok(a) => a,
1565            Err(_e) => return vec![Err(ConsistencyError::QueryServerSearchFailure)],
1566        };
1567
1568        for e in all_entries {
1569            e.verify(schema, &mut results)
1570        }
1571
1572        // Verify the RUV to the entry changelogs now.
1573        self.get_be_txn().verify_ruv(&mut results);
1574
1575        // Ok entries passed, lets move on to the content.
1576        // Most of our checks are in the plugins, so we let them
1577        // do their job.
1578
1579        // Now, call the plugins verification system.
1580        Plugins::run_verify(self, &mut results);
1581        // Finished
1582
1583        results
1584    }
1585
1586    #[instrument(level = "debug", skip_all)]
1587    pub fn scim_entry_id_get_ext(
1588        &mut self,
1589        uuid: Uuid,
1590        class: EntryClass,
1591        query: ScimEntryGetQuery,
1592        ident: Identity,
1593    ) -> Result<ScimEntryKanidm, OperationError> {
1594        let filter_intent = filter!(f_and!([
1595            f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)),
1596            f_eq(Attribute::Class, class.into())
1597        ]));
1598
1599        let f_intent_valid = filter_intent
1600            .validate(self.get_schema())
1601            .map_err(OperationError::SchemaViolation)?;
1602
1603        let f_valid = f_intent_valid.clone().into_ignore_hidden();
1604
1605        let r_attrs = query
1606            .attributes
1607            .map(|attr_set| attr_set.into_iter().collect());
1608
1609        let se = SearchEvent {
1610            ident,
1611            filter: f_valid,
1612            filter_orig: f_intent_valid,
1613            attrs: r_attrs,
1614            effective_access_check: query.ext_access_check,
1615        };
1616
1617        let mut vs = self.search_ext(&se)?;
1618        match vs.pop() {
1619            Some(entry) if vs.is_empty() => entry.to_scim_kanidm(self),
1620            _ => {
1621                if vs.is_empty() {
1622                    Err(OperationError::NoMatchingEntries)
1623                } else {
1624                    // Multiple entries matched, should not be possible!
1625                    Err(OperationError::UniqueConstraintViolation)
1626                }
1627            }
1628        }
1629    }
1630
1631    #[instrument(level = "debug", skip_all)]
1632    pub fn scim_search_ext(
1633        &mut self,
1634        ident: Identity,
1635        filter: ScimFilter,
1636        query: ScimEntryGetQuery,
1637    ) -> Result<Vec<ScimEntryKanidm>, OperationError> {
1638        let filter_intent = Filter::from_scim_ro(&ident, &filter, self)?;
1639
1640        let f_intent_valid = filter_intent
1641            .validate(self.get_schema())
1642            .map_err(OperationError::SchemaViolation)?;
1643
1644        let f_valid = f_intent_valid.clone().into_ignore_hidden();
1645
1646        let r_attrs = query
1647            .attributes
1648            .map(|attr_set| attr_set.into_iter().collect());
1649
1650        let se = SearchEvent {
1651            ident,
1652            filter: f_valid,
1653            filter_orig: f_intent_valid,
1654            attrs: r_attrs,
1655            effective_access_check: query.ext_access_check,
1656        };
1657
1658        let vs = self.search_ext(&se)?;
1659
1660        vs.into_iter()
1661            .map(|entry| entry.to_scim_kanidm(self))
1662            .collect()
1663    }
1664}
1665
1666impl<'a> QueryServerTransaction<'a> for QueryServerWriteTransaction<'a> {
1667    type AccessControlsTransactionType = AccessControlsWriteTransaction<'a>;
1668    type BackendTransactionType = BackendWriteTransaction<'a>;
1669    type SchemaTransactionType = SchemaWriteTransaction<'a>;
1670    type KeyProvidersTransactionType = KeyProvidersWriteTransaction<'a>;
1671
1672    fn get_be_txn(&mut self) -> &mut BackendWriteTransaction<'a> {
1673        &mut self.be_txn
1674    }
1675
1676    fn get_schema<'b>(&self) -> &'b SchemaWriteTransaction<'a> {
1677        // Strip the lifetime here. Schema is a sub-component of the transaction and is
1678        // *never* changed excepting in a write TXN, so we want to allow the schema to
1679        // be borrowed while the rest of the read txn is under a mut.
1680        unsafe {
1681            let s = (&self.schema) as *const _;
1682            &*s
1683        }
1684    }
1685
1686    fn get_accesscontrols(&self) -> &AccessControlsWriteTransaction<'a> {
1687        &self.accesscontrols
1688    }
1689
1690    fn get_key_providers(&self) -> &KeyProvidersWriteTransaction<'a> {
1691        &self.key_providers
1692    }
1693
1694    fn get_resolve_filter_cache(&mut self) -> Option<&mut ResolveFilterCacheReadTxn<'a>> {
1695        if self.resolve_filter_cache_clear || *self.phase < ServerPhase::SchemaReady {
1696            None
1697        } else {
1698            Some(&mut self.resolve_filter_cache)
1699        }
1700    }
1701
1702    fn get_resolve_filter_cache_and_be_txn(
1703        &mut self,
1704    ) -> (
1705        &mut BackendWriteTransaction<'a>,
1706        Option<&mut ResolveFilterCacheReadTxn<'a>>,
1707    ) {
1708        if self.resolve_filter_cache_clear || *self.phase < ServerPhase::SchemaReady {
1709            (&mut self.be_txn, None)
1710        } else {
1711            (&mut self.be_txn, Some(&mut self.resolve_filter_cache))
1712        }
1713    }
1714
1715    fn pw_badlist(&self) -> &HashSet<String> {
1716        &self.system_config.pw_badlist
1717    }
1718
1719    fn denied_names(&self) -> &HashSet<String> {
1720        &self.system_config.denied_names
1721    }
1722
1723    fn get_domain_version(&self) -> DomainVersion {
1724        self.d_info.d_vers
1725    }
1726
1727    fn get_domain_patch_level(&self) -> u32 {
1728        self.d_info.d_patch_level
1729    }
1730
1731    fn get_domain_development_taint(&self) -> bool {
1732        self.d_info.d_devel_taint
1733    }
1734
1735    fn get_domain_uuid(&self) -> Uuid {
1736        self.d_info.d_uuid
1737    }
1738
1739    /// Gets the in-memory domain_name element
1740    fn get_domain_name(&self) -> &str {
1741        &self.d_info.d_name
1742    }
1743
1744    fn get_domain_display_name(&self) -> &str {
1745        &self.d_info.d_display
1746    }
1747
1748    fn get_domain_image_value(&self) -> Option<ImageValue> {
1749        self.d_info.d_image.clone()
1750    }
1751}
1752
1753impl QueryServer {
1754    pub fn new(
1755        be: Backend,
1756        schema: Schema,
1757        domain_name: String,
1758        curtime: Duration,
1759    ) -> Result<Self, OperationError> {
1760        let (s_uuid, d_uuid, ts_max) = {
1761            let mut wr = be.write()?;
1762            let s_uuid = wr.get_db_s_uuid()?;
1763            let d_uuid = wr.get_db_d_uuid()?;
1764            let ts_max = wr.get_db_ts_max(curtime)?;
1765            wr.commit()?;
1766            (s_uuid, d_uuid, ts_max)
1767        };
1768
1769        let pool_size = be.get_pool_size();
1770
1771        debug!("Server UUID -> {:?}", s_uuid);
1772        debug!("Domain UUID -> {:?}", d_uuid);
1773        debug!("Domain Name -> {:?}", domain_name);
1774
1775        let d_info = Arc::new(CowCell::new(DomainInfo {
1776            d_uuid,
1777            // Start with our level as zero.
1778            // This will be reloaded from the DB shortly :)
1779            d_vers: DOMAIN_LEVEL_0,
1780            d_patch_level: 0,
1781            d_name: domain_name.clone(),
1782            // we set the domain_display_name to the configuration file's domain_name
1783            // here because the database is not started, so we cannot pull it from there.
1784            d_display: domain_name,
1785            // Automatically derive our current taint mode based on the PRERELEASE setting.
1786            d_devel_taint: option_env!("KANIDM_PRE_RELEASE").is_some(),
1787            d_ldap_allow_unix_pw_bind: false,
1788            d_allow_easter_eggs: false,
1789            d_image: None,
1790        }));
1791
1792        let cid = Cid::new_lamport(s_uuid, curtime, &ts_max);
1793        let cid_max = Arc::new(CowCell::new(cid));
1794
1795        // These default to empty, but they'll be populated shortly.
1796        let system_config = Arc::new(CowCell::new(SystemConfig::default()));
1797
1798        let dyngroup_cache = Arc::new(CowCell::new(DynGroupCache::default()));
1799
1800        let phase = Arc::new(CowCell::new(ServerPhase::Bootstrap));
1801
1802        let resolve_filter_cache = Arc::new(
1803            ARCacheBuilder::new()
1804                .set_size(RESOLVE_FILTER_CACHE_MAX, RESOLVE_FILTER_CACHE_LOCAL)
1805                .set_reader_quiesce(true)
1806                .build()
1807                .ok_or_else(|| {
1808                    error!("Failed to build filter resolve cache");
1809                    OperationError::DB0003FilterResolveCacheBuild
1810                })?,
1811        );
1812
1813        let key_providers = Arc::new(KeyProviders::default());
1814
1815        // These needs to be pool_size minus one to always leave a DB ticket
1816        // for a writer. But it also needs to be at least one :)
1817        debug_assert!(pool_size > 0);
1818        let read_ticket_pool = std::cmp::max(pool_size - 1, 1);
1819
1820        Ok(QueryServer {
1821            phase,
1822            d_info,
1823            system_config,
1824            be,
1825            schema: Arc::new(schema),
1826            accesscontrols: Arc::new(AccessControls::default()),
1827            db_tickets: Arc::new(Semaphore::new(pool_size as usize)),
1828            read_tickets: Arc::new(Semaphore::new(read_ticket_pool as usize)),
1829            write_ticket: Arc::new(Semaphore::new(1)),
1830            resolve_filter_cache,
1831            dyngroup_cache,
1832            cid_max,
1833            key_providers,
1834        })
1835    }
1836
1837    pub fn try_quiesce(&self) {
1838        self.be.try_quiesce();
1839        self.accesscontrols.try_quiesce();
1840        self.resolve_filter_cache.try_quiesce();
1841    }
1842
1843    #[instrument(level = "debug", skip_all)]
1844    async fn read_acquire_ticket(&self) -> Option<(SemaphorePermit<'_>, SemaphorePermit<'_>)> {
1845        // Get a read ticket. Basically this forces us to queue with other readers, while preventing
1846        // us from competing with writers on the db tickets. This tilts us to write prioritising
1847        // on db operations by always making sure a writer can get a db ticket.
1848        let read_ticket = if cfg!(test) {
1849            self.read_tickets
1850                .try_acquire()
1851                .inspect_err(|err| {
1852                    error!(?err, "Unable to acquire read ticket!");
1853                })
1854                .ok()?
1855        } else {
1856            let fut = tokio::time::timeout(
1857                Duration::from_millis(DB_LOCK_ACQUIRE_TIMEOUT_MILLIS),
1858                self.read_tickets.acquire(),
1859            );
1860
1861            match fut.await {
1862                Ok(Ok(ticket)) => ticket,
1863                Ok(Err(_)) => {
1864                    error!("Failed to acquire read ticket, may be poisoned.");
1865                    return None;
1866                }
1867                Err(_) => {
1868                    error!("Failed to acquire read ticket, server is overloaded.");
1869                    return None;
1870                }
1871            }
1872        };
1873
1874        // We need to ensure a db conn will be available. At this point either a db ticket
1875        // *must* be available because pool_size >= 2 and the only other holders are write
1876        // and read ticket holders, OR pool_size == 1, and we are waiting on the writer to now
1877        // complete.
1878        let db_ticket = if cfg!(test) {
1879            self.db_tickets
1880                .try_acquire()
1881                .inspect_err(|err| {
1882                    error!(?err, "Unable to acquire database ticket!");
1883                })
1884                .ok()?
1885        } else {
1886            self.db_tickets
1887                .acquire()
1888                .await
1889                .inspect_err(|err| {
1890                    error!(?err, "Unable to acquire database ticket!");
1891                })
1892                .ok()?
1893        };
1894
1895        Some((read_ticket, db_ticket))
1896    }
1897
1898    pub async fn read(&self) -> Result<QueryServerReadTransaction<'_>, OperationError> {
1899        let (read_ticket, db_ticket) = self
1900            .read_acquire_ticket()
1901            .await
1902            .ok_or(OperationError::DatabaseLockAcquisitionTimeout)?;
1903        // Point of no return - we now have a DB thread AND the read ticket, we MUST complete
1904        // as soon as possible! The following locks and elements below are SYNCHRONOUS but
1905        // will never be contented at this point, and will always progress.
1906        let schema = self.schema.read();
1907
1908        let cid_max = self.cid_max.read();
1909        let trim_cid = cid_max.sub_secs(CHANGELOG_MAX_AGE)?;
1910
1911        let be_txn = self.be.read()?;
1912
1913        Ok(QueryServerReadTransaction {
1914            be_txn,
1915            schema,
1916            d_info: self.d_info.read(),
1917            system_config: self.system_config.read(),
1918            accesscontrols: self.accesscontrols.read(),
1919            key_providers: self.key_providers.read(),
1920            _db_ticket: db_ticket,
1921            _read_ticket: read_ticket,
1922            resolve_filter_cache: self.resolve_filter_cache.read(),
1923            trim_cid,
1924        })
1925    }
1926
1927    #[instrument(level = "debug", skip_all)]
1928    async fn write_acquire_ticket(&self) -> Option<(SemaphorePermit<'_>, SemaphorePermit<'_>)> {
1929        // Guarantee we are the only writer on the thread pool
1930        let write_ticket = if cfg!(test) {
1931            self.write_ticket
1932                .try_acquire()
1933                .inspect_err(|err| {
1934                    error!(?err, "Unable to acquire write ticket!");
1935                })
1936                .ok()?
1937        } else {
1938            let fut = tokio::time::timeout(
1939                Duration::from_millis(DB_LOCK_ACQUIRE_TIMEOUT_MILLIS),
1940                self.write_ticket.acquire(),
1941            );
1942
1943            match fut.await {
1944                Ok(Ok(ticket)) => ticket,
1945                Ok(Err(_)) => {
1946                    error!("Failed to acquire write ticket, may be poisoned.");
1947                    return None;
1948                }
1949                Err(_) => {
1950                    error!("Failed to acquire write ticket, server is overloaded.");
1951                    return None;
1952                }
1953            }
1954        };
1955
1956        // We need to ensure a db conn will be available. At this point either a db ticket
1957        // *must* be available because pool_size >= 2 and the only other are readers, or
1958        // pool_size == 1 and we are waiting on a single reader to now complete
1959        let db_ticket = if cfg!(test) {
1960            self.db_tickets
1961                .try_acquire()
1962                .inspect_err(|err| {
1963                    error!(?err, "Unable to acquire write db_ticket!");
1964                })
1965                .ok()?
1966        } else {
1967            self.db_tickets
1968                .acquire()
1969                .await
1970                .inspect_err(|err| {
1971                    error!(?err, "Unable to acquire write db_ticket!");
1972                })
1973                .ok()?
1974        };
1975
1976        Some((write_ticket, db_ticket))
1977    }
1978
1979    pub async fn write(
1980        &self,
1981        curtime: Duration,
1982    ) -> Result<QueryServerWriteTransaction<'_>, OperationError> {
1983        let (write_ticket, db_ticket) = self
1984            .write_acquire_ticket()
1985            .await
1986            .ok_or(OperationError::DatabaseLockAcquisitionTimeout)?;
1987
1988        // Point of no return - we now have a DB thread AND the write ticket, we MUST complete
1989        // as soon as possible! The following locks and elements below are SYNCHRONOUS but
1990        // will never be contented at this point, and will always progress.
1991
1992        let be_txn = self.be.write()?;
1993
1994        let schema_write = self.schema.write();
1995        let d_info = self.d_info.write();
1996        let system_config = self.system_config.write();
1997        let phase = self.phase.write();
1998
1999        let mut cid = self.cid_max.write();
2000        // Update the cid now.
2001        *cid = Cid::new_lamport(cid.s_uuid, curtime, &cid.ts);
2002
2003        let trim_cid = cid.sub_secs(CHANGELOG_MAX_AGE)?;
2004
2005        Ok(QueryServerWriteTransaction {
2006            // I think this is *not* needed, because commit is mut self which should
2007            // take ownership of the value, and cause the commit to "only be run
2008            // once".
2009            //
2010            // The committed flag is however used for abort-specific code in drop
2011            // which today I don't think we have ... yet.
2012            committed: false,
2013            phase,
2014            d_info,
2015            system_config,
2016            curtime,
2017            cid,
2018            trim_cid,
2019            be_txn,
2020            schema: schema_write,
2021            accesscontrols: self.accesscontrols.write(),
2022            changed_flags: ChangeFlag::empty(),
2023            changed_uuid: HashSet::new(),
2024            _db_ticket: db_ticket,
2025            _write_ticket: write_ticket,
2026            resolve_filter_cache: self.resolve_filter_cache.read(),
2027            resolve_filter_cache_clear: false,
2028            resolve_filter_cache_write: self.resolve_filter_cache.write(),
2029            dyngroup_cache: self.dyngroup_cache.write(),
2030            key_providers: self.key_providers.write(),
2031        })
2032    }
2033
2034    #[cfg(any(test, debug_assertions))]
2035    pub async fn clear_cache(&self) -> Result<(), OperationError> {
2036        let ct = duration_from_epoch_now();
2037        let mut w_txn = self.write(ct).await?;
2038        w_txn.clear_cache()?;
2039        w_txn.commit()
2040    }
2041
2042    pub async fn verify(&self) -> Vec<Result<(), ConsistencyError>> {
2043        let current_time = duration_from_epoch_now();
2044        // Before we can proceed, command the QS to load schema in full.
2045        // IMPORTANT: While we take a write txn, this does no writes to the
2046        // actual db, it's only so we can write to the in memory schema
2047        // structures.
2048        if self
2049            .write(current_time)
2050            .await
2051            .and_then(|mut txn| {
2052                txn.force_schema_reload();
2053                txn.commit()
2054            })
2055            .is_err()
2056        {
2057            return vec![Err(ConsistencyError::Unknown)];
2058        };
2059
2060        match self.read().await {
2061            Ok(mut r_txn) => r_txn.verify(),
2062            Err(_) => vec![Err(ConsistencyError::Unknown)],
2063        }
2064    }
2065}
2066
2067impl<'a> QueryServerWriteTransaction<'a> {
2068    pub(crate) fn get_server_uuid(&self) -> Uuid {
2069        // Cid has our server id within
2070        self.cid.s_uuid
2071    }
2072
2073    pub(crate) fn reset_server_uuid(&mut self) -> Result<(), OperationError> {
2074        let s_uuid = self.be_txn.reset_db_s_uuid().map_err(|err| {
2075            error!(?err, "Failed to reset server replication uuid");
2076            err
2077        })?;
2078
2079        debug!(?s_uuid, "reset server replication uuid");
2080
2081        self.cid.s_uuid = s_uuid;
2082
2083        Ok(())
2084    }
2085
2086    pub(crate) fn get_curtime(&self) -> Duration {
2087        self.curtime
2088    }
2089
2090    pub(crate) fn get_cid(&self) -> &Cid {
2091        &self.cid
2092    }
2093
2094    pub(crate) fn get_key_providers_mut(&mut self) -> &mut KeyProvidersWriteTransaction<'a> {
2095        &mut self.key_providers
2096    }
2097
2098    pub(crate) fn get_dyngroup_cache(&mut self) -> &mut DynGroupCache {
2099        &mut self.dyngroup_cache
2100    }
2101
2102    pub fn domain_raise(&mut self, level: u32) -> Result<(), OperationError> {
2103        if level > DOMAIN_MAX_LEVEL {
2104            return Err(OperationError::MG0002RaiseDomainLevelExceedsMaximum);
2105        }
2106
2107        let modl = ModifyList::new_purge_and_set(Attribute::Version, Value::Uint32(level));
2108        let udi = PVUUID_DOMAIN_INFO.clone();
2109        let filt = filter_all!(f_eq(Attribute::Uuid, udi));
2110        self.internal_modify(&filt, &modl)
2111    }
2112
2113    pub fn domain_remigrate(&mut self, level: u32) -> Result<(), OperationError> {
2114        let mut_d_info = self.d_info.get_mut();
2115
2116        if level > mut_d_info.d_vers {
2117            // Nothing to do.
2118            return Ok(());
2119        } else if level < DOMAIN_MIN_REMIGRATION_LEVEL {
2120            return Err(OperationError::MG0001InvalidReMigrationLevel);
2121        };
2122
2123        info!(
2124            "Prepare to re-migrate from {} -> {}",
2125            level, mut_d_info.d_vers
2126        );
2127        mut_d_info.d_vers = level;
2128        self.changed_flags.insert(ChangeFlag::DOMAIN);
2129
2130        Ok(())
2131    }
2132
2133    #[instrument(level = "debug", skip_all)]
2134    pub(crate) fn reload_schema(&mut self) -> Result<(), OperationError> {
2135        // supply entries to the writable schema to reload from.
2136        // find all attributes.
2137        let filt = filter!(f_eq(Attribute::Class, EntryClass::AttributeType.into()));
2138        let res = self.internal_search(filt).map_err(|e| {
2139            admin_error!("reload schema internal search failed {:?}", e);
2140            e
2141        })?;
2142        // load them.
2143        let attributetypes: Result<Vec<_>, _> =
2144            res.iter().map(|e| SchemaAttribute::try_from(e)).collect();
2145
2146        let attributetypes = attributetypes.map_err(|e| {
2147            admin_error!("reload schema attributetypes {:?}", e);
2148            e
2149        })?;
2150
2151        self.schema.update_attributes(attributetypes).map_err(|e| {
2152            admin_error!("reload schema update attributetypes {:?}", e);
2153            e
2154        })?;
2155
2156        // find all classes
2157        let filt = filter!(f_eq(Attribute::Class, EntryClass::ClassType.into()));
2158        let res = self.internal_search(filt).map_err(|e| {
2159            admin_error!("reload schema internal search failed {:?}", e);
2160            e
2161        })?;
2162        // load them.
2163        let classtypes: Result<Vec<_>, _> = res.iter().map(|e| SchemaClass::try_from(e)).collect();
2164        let classtypes = classtypes.map_err(|e| {
2165            admin_error!("reload schema classtypes {:?}", e);
2166            e
2167        })?;
2168
2169        self.schema.update_classes(classtypes).map_err(|e| {
2170            admin_error!("reload schema update classtypes {:?}", e);
2171            e
2172        })?;
2173
2174        // validate.
2175        let valid_r = self.schema.validate();
2176
2177        // Translate the result.
2178        if valid_r.is_empty() {
2179            // Now use this to reload the backend idxmeta
2180            trace!("Reloading idxmeta ...");
2181            self.be_txn
2182                .update_idxmeta(self.schema.reload_idxmeta())
2183                .map_err(|e| {
2184                    admin_error!("reload schema update idxmeta {:?}", e);
2185                    e
2186                })
2187        } else {
2188            // Log the failures?
2189            admin_error!("Schema reload failed -> {:?}", valid_r);
2190            Err(OperationError::ConsistencyError(
2191                valid_r.into_iter().filter_map(|v| v.err()).collect(),
2192            ))
2193        }?;
2194
2195        // Since we reloaded the schema, we need to reload the filter cache since it
2196        // may have incorrect or outdated information about indexes now.
2197        self.resolve_filter_cache_clear = true;
2198
2199        // Trigger reloads on services that require post-schema reloads.
2200        // Mainly this is plugins.
2201        DynGroup::reload(self)?;
2202
2203        Ok(())
2204    }
2205
2206    #[instrument(level = "debug", skip_all)]
2207    fn reload_accesscontrols(&mut self) -> Result<(), OperationError> {
2208        // supply entries to the writable access controls to reload from.
2209        // This has to be done in FOUR passes - one for each type!
2210        //
2211        // Note, we have to do the search, parse, then submit here, because of the
2212        // requirement to have the write query server reference in the parse stage - this
2213        // would cause a rust double-borrow if we had AccessControls to try to handle
2214        // the entry lists themself.
2215        trace!("ACP reload started ...");
2216
2217        // Update the set of sync agreements
2218
2219        let filt = filter!(f_eq(Attribute::Class, EntryClass::SyncAccount.into()));
2220
2221        let res = self.internal_search(filt).map_err(|e| {
2222            admin_error!(
2223                err = ?e,
2224                "reload accesscontrols internal search failed",
2225            );
2226            e
2227        })?;
2228
2229        let sync_agreement_map: HashMap<Uuid, BTreeSet<Attribute>> = res
2230            .iter()
2231            .filter_map(|e| {
2232                e.get_ava_as_iutf8(Attribute::SyncYieldAuthority)
2233                    .map(|set| {
2234                        let set: BTreeSet<_> =
2235                            set.iter().map(|s| Attribute::from(s.as_str())).collect();
2236                        (e.get_uuid(), set)
2237                    })
2238            })
2239            .collect();
2240
2241        self.accesscontrols
2242            .update_sync_agreements(sync_agreement_map);
2243
2244        // Update search
2245        let filt = filter!(f_and!([
2246            f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2247            f_eq(Attribute::Class, EntryClass::AccessControlSearch.into()),
2248            f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2249        ]));
2250
2251        let res = self.internal_search(filt).map_err(|e| {
2252            admin_error!(
2253                err = ?e,
2254                "reload accesscontrols internal search failed",
2255            );
2256            e
2257        })?;
2258        let search_acps: Result<Vec<_>, _> = res
2259            .iter()
2260            .map(|e| AccessControlSearch::try_from(self, e))
2261            .collect();
2262
2263        let search_acps = search_acps.map_err(|e| {
2264            admin_error!(err = ?e, "Unable to parse search accesscontrols");
2265            e
2266        })?;
2267
2268        self.accesscontrols
2269            .update_search(search_acps)
2270            .map_err(|e| {
2271                admin_error!(err = ?e, "Failed to update search accesscontrols");
2272                e
2273            })?;
2274        // Update create
2275        let filt = filter!(f_and!([
2276            f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2277            f_eq(Attribute::Class, EntryClass::AccessControlCreate.into()),
2278            f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2279        ]));
2280
2281        let res = self.internal_search(filt).map_err(|e| {
2282            admin_error!(
2283                err = ?e,
2284                "reload accesscontrols internal search failed"
2285            );
2286            e
2287        })?;
2288        let create_acps: Result<Vec<_>, _> = res
2289            .iter()
2290            .map(|e| AccessControlCreate::try_from(self, e))
2291            .collect();
2292
2293        let create_acps = create_acps.map_err(|e| {
2294            admin_error!(err = ?e, "Unable to parse create accesscontrols");
2295            e
2296        })?;
2297
2298        self.accesscontrols
2299            .update_create(create_acps)
2300            .map_err(|e| {
2301                admin_error!(err = ?e, "Failed to update create accesscontrols");
2302                e
2303            })?;
2304        // Update modify
2305        let filt = filter!(f_and!([
2306            f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2307            f_eq(Attribute::Class, EntryClass::AccessControlModify.into()),
2308            f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2309        ]));
2310
2311        let res = self.internal_search(filt).map_err(|e| {
2312            admin_error!("reload accesscontrols internal search failed {:?}", e);
2313            e
2314        })?;
2315        let modify_acps: Result<Vec<_>, _> = res
2316            .iter()
2317            .map(|e| AccessControlModify::try_from(self, e))
2318            .collect();
2319
2320        let modify_acps = modify_acps.map_err(|e| {
2321            admin_error!("Unable to parse modify accesscontrols {:?}", e);
2322            e
2323        })?;
2324
2325        self.accesscontrols
2326            .update_modify(modify_acps)
2327            .map_err(|e| {
2328                admin_error!("Failed to update modify accesscontrols {:?}", e);
2329                e
2330            })?;
2331        // Update delete
2332        let filt = filter!(f_and!([
2333            f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2334            f_eq(Attribute::Class, EntryClass::AccessControlDelete.into()),
2335            f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2336        ]));
2337
2338        let res = self.internal_search(filt).map_err(|e| {
2339            admin_error!("reload accesscontrols internal search failed {:?}", e);
2340            e
2341        })?;
2342        let delete_acps: Result<Vec<_>, _> = res
2343            .iter()
2344            .map(|e| AccessControlDelete::try_from(self, e))
2345            .collect();
2346
2347        let delete_acps = delete_acps.map_err(|e| {
2348            admin_error!("Unable to parse delete accesscontrols {:?}", e);
2349            e
2350        })?;
2351
2352        self.accesscontrols.update_delete(delete_acps).map_err(|e| {
2353            admin_error!("Failed to update delete accesscontrols {:?}", e);
2354            e
2355        })
2356    }
2357
2358    #[instrument(level = "debug", skip_all)]
2359    pub(crate) fn reload_key_material(&mut self) -> Result<(), OperationError> {
2360        let filt = filter!(f_eq(Attribute::Class, EntryClass::KeyProvider.into()));
2361
2362        let res = self.internal_search(filt).map_err(|e| {
2363            admin_error!(
2364                err = ?e,
2365                "reload key providers internal search failed",
2366            );
2367            e
2368        })?;
2369
2370        // FUTURE: During this reload we may need to access the PIN or other data
2371        // to access the provider.
2372        let providers = res
2373            .iter()
2374            .map(|e| KeyProvider::try_from(e).and_then(|kp| kp.test().map(|()| kp)))
2375            .collect::<Result<Vec<_>, _>>()?;
2376
2377        self.key_providers.update_providers(providers)?;
2378
2379        let filt = filter!(f_eq(Attribute::Class, EntryClass::KeyObject.into()));
2380
2381        let res = self.internal_search(filt).map_err(|e| {
2382            admin_error!(
2383                err = ?e,
2384                "reload key objects internal search failed",
2385            );
2386            e
2387        })?;
2388
2389        res.iter()
2390            .try_for_each(|entry| self.key_providers.load_key_object(entry.as_ref()))
2391    }
2392
2393    #[instrument(level = "debug", skip_all)]
2394    pub(crate) fn reload_system_config(&mut self) -> Result<(), OperationError> {
2395        let denied_names = self.get_sc_denied_names()?;
2396        let pw_badlist = self.get_sc_password_badlist()?;
2397
2398        let mut_system_config = self.system_config.get_mut();
2399        mut_system_config.denied_names = denied_names;
2400        mut_system_config.pw_badlist = pw_badlist;
2401        Ok(())
2402    }
2403
2404    /// Pulls the domain name from the database and updates the DomainInfo data in memory
2405    #[instrument(level = "debug", skip_all)]
2406    pub(crate) fn reload_domain_info_version(&mut self) -> Result<(), OperationError> {
2407        let domain_info = self.internal_search_uuid(UUID_DOMAIN_INFO).map_err(|err| {
2408            error!(?err, "Error getting domain info");
2409            err
2410        })?;
2411
2412        let domain_info_version = domain_info
2413            .get_ava_single_uint32(Attribute::Version)
2414            .ok_or_else(|| {
2415                error!("domain info missing attribute version");
2416                OperationError::InvalidEntryState
2417            })?;
2418
2419        let domain_info_patch_level = domain_info
2420            .get_ava_single_uint32(Attribute::PatchLevel)
2421            .unwrap_or(0);
2422
2423        // If we have moved from stable to dev, this triggers the taint. If we
2424        // are moving from dev to stable, the db will be true triggering the
2425        // taint flag. If we are stable to stable this will be false.
2426        let current_devel_flag = option_env!("KANIDM_PRE_RELEASE").is_some();
2427        let domain_info_devel_taint = current_devel_flag
2428            || domain_info
2429                .get_ava_single_bool(Attribute::DomainDevelopmentTaint)
2430                .unwrap_or_default();
2431
2432        let domain_allow_easter_eggs = domain_info
2433            .get_ava_single_bool(Attribute::DomainAllowEasterEggs)
2434            // This defaults to false for release versions, and true in development
2435            .unwrap_or(option_env!("KANIDM_PRE_RELEASE").is_some());
2436
2437        // We have to set the domain version here so that features which check for it
2438        // will now see it's been increased. This also prevents recursion during reloads
2439        // inside of a domain migration.
2440        let mut_d_info = self.d_info.get_mut();
2441        let previous_version = mut_d_info.d_vers;
2442        let previous_patch_level = mut_d_info.d_patch_level;
2443        mut_d_info.d_vers = domain_info_version;
2444        mut_d_info.d_patch_level = domain_info_patch_level;
2445        mut_d_info.d_devel_taint = domain_info_devel_taint;
2446        mut_d_info.d_allow_easter_eggs = domain_allow_easter_eggs;
2447
2448        // We must both be at the correct domain version *and* the correct patch level. If we are
2449        // not, then we only proceed to migrate *if* our server boot phase is correct.
2450        if (previous_version == domain_info_version
2451            && previous_patch_level == domain_info_patch_level)
2452            || *self.phase < ServerPhase::DomainInfoReady
2453        {
2454            return Ok(());
2455        }
2456
2457        debug!(domain_previous_version = ?previous_version, domain_target_version = ?domain_info_version);
2458        debug!(domain_previous_patch_level = ?previous_patch_level, domain_target_patch_level = ?domain_info_patch_level);
2459
2460        // We have to check for DL0 since that's the initialisation level. If we are at DL0 then
2461        // the server was just brought up and there are no other actions to take since we are
2462        // now at TGT level.
2463        if previous_version == DOMAIN_LEVEL_0 {
2464            debug!(
2465                "Server was just brought up, skipping migrations as we are already at target level"
2466            );
2467            return Ok(());
2468        }
2469
2470        if previous_version < DOMAIN_MIN_REMIGRATION_LEVEL {
2471            error!("UNABLE TO PROCEED. You are attempting a Skip update which is NOT SUPPORTED. You must upgrade one-version of Kanidm at a time.");
2472            error!("For more see: https://kanidm.github.io/kanidm/stable/support.html#upgrade-policy and https://kanidm.github.io/kanidm/stable/server_updates.html");
2473            error!(domain_previous_version = ?previous_version, domain_target_version = ?domain_info_version);
2474            error!(domain_previous_patch_level = ?previous_patch_level, domain_target_patch_level = ?domain_info_patch_level);
2475            return Err(OperationError::MG0008SkipUpgradeAttempted);
2476        }
2477
2478        if previous_version <= DOMAIN_LEVEL_8 && domain_info_version >= DOMAIN_LEVEL_9 {
2479            // 1.4 -> 1.5
2480            self.migrate_domain_8_to_9()?;
2481        }
2482
2483        if previous_patch_level < PATCH_LEVEL_2
2484            && domain_info_patch_level >= PATCH_LEVEL_2
2485            && domain_info_version == DOMAIN_LEVEL_9
2486        {
2487            self.migrate_domain_patch_level_2()?;
2488        }
2489
2490        if previous_version <= DOMAIN_LEVEL_9 && domain_info_version >= DOMAIN_LEVEL_10 {
2491            // 1.5 -> 1.6
2492            self.migrate_domain_9_to_10()?;
2493        }
2494
2495        if previous_version <= DOMAIN_LEVEL_10 && domain_info_version >= DOMAIN_LEVEL_11 {
2496            // 1.6 -> 1.7
2497            self.migrate_domain_10_to_11()?;
2498        }
2499
2500        // This is here to catch when we increase domain levels but didn't create the migration
2501        // hooks. If this fails it probably means you need to add another migration hook
2502        // in the above.
2503        debug_assert!(domain_info_version <= DOMAIN_MAX_LEVEL);
2504
2505        Ok(())
2506    }
2507
2508    /// Pulls the domain name from the database and updates the DomainInfo data in memory
2509    #[instrument(level = "debug", skip_all)]
2510    pub(crate) fn reload_domain_info(&mut self) -> Result<(), OperationError> {
2511        let domain_entry = self.get_db_domain()?;
2512
2513        let domain_name = domain_entry
2514            .get_ava_single_iname(Attribute::DomainName)
2515            .map(str::to_string)
2516            .ok_or(OperationError::InvalidEntryState)?;
2517
2518        let display_name = domain_entry
2519            .get_ava_single_utf8(Attribute::DomainDisplayName)
2520            .map(str::to_string)
2521            .unwrap_or_else(|| format!("Kanidm {}", domain_name));
2522
2523        let domain_ldap_allow_unix_pw_bind = domain_entry
2524            .get_ava_single_bool(Attribute::LdapAllowUnixPwBind)
2525            .unwrap_or(true);
2526
2527        let domain_image = domain_entry.get_ava_single_image(Attribute::Image);
2528
2529        let domain_uuid = self.be_txn.get_db_d_uuid()?;
2530
2531        let mut_d_info = self.d_info.get_mut();
2532        mut_d_info.d_ldap_allow_unix_pw_bind = domain_ldap_allow_unix_pw_bind;
2533        if mut_d_info.d_uuid != domain_uuid {
2534            admin_warn!(
2535                "Using domain uuid from the database {} - was {} in memory",
2536                domain_name,
2537                mut_d_info.d_name,
2538            );
2539            mut_d_info.d_uuid = domain_uuid;
2540        }
2541        if mut_d_info.d_name != domain_name {
2542            admin_warn!(
2543                "Using domain name from the database {} - was {} in memory",
2544                domain_name,
2545                mut_d_info.d_name,
2546            );
2547            admin_warn!(
2548                    "If you think this is an error, see https://kanidm.github.io/kanidm/master/domain_rename.html"
2549                );
2550            mut_d_info.d_name = domain_name;
2551        }
2552        mut_d_info.d_display = display_name;
2553        mut_d_info.d_image = domain_image;
2554        Ok(())
2555    }
2556
2557    /// Initiate a domain display name change process. This isn't particularly scary
2558    /// because it's just a wibbly human-facing thing, not used for secure
2559    /// activities (yet)
2560    pub fn set_domain_display_name(&mut self, new_domain_name: &str) -> Result<(), OperationError> {
2561        let modl = ModifyList::new_purge_and_set(
2562            Attribute::DomainDisplayName,
2563            Value::new_utf8(new_domain_name.to_string()),
2564        );
2565        let udi = PVUUID_DOMAIN_INFO.clone();
2566        let filt = filter_all!(f_eq(Attribute::Uuid, udi));
2567        self.internal_modify(&filt, &modl)
2568    }
2569
2570    /// Initiate a domain rename process. This is generally an internal function but it's
2571    /// exposed to the cli for admins to be able to initiate the process.
2572    ///
2573    /// # Safety
2574    /// This is UNSAFE because while it may change the domain name, it doesn't update
2575    /// the running configured version of the domain name that is resident to the
2576    /// query server.
2577    ///
2578    /// Currently it's only used to test what happens if we rename the domain and how
2579    /// that impacts spns, but in the future we may need to reconsider how this is
2580    /// approached, especially if we have a domain re-name replicated to us. It could
2581    /// be that we end up needing to have this as a cow cell or similar?
2582    pub fn danger_domain_rename(&mut self, new_domain_name: &str) -> Result<(), OperationError> {
2583        let modl =
2584            ModifyList::new_purge_and_set(Attribute::DomainName, Value::new_iname(new_domain_name));
2585        let udi = PVUUID_DOMAIN_INFO.clone();
2586        let filt = filter_all!(f_eq(Attribute::Uuid, udi));
2587        self.internal_modify(&filt, &modl)
2588    }
2589
2590    pub fn reindex(&mut self, immediate: bool) -> Result<(), OperationError> {
2591        // initiate a be reindex here. This could have been from first run checking
2592        // the versions, or it could just be from the cli where an admin needs to do an
2593        // indexing.
2594        self.be_txn.reindex(immediate)
2595    }
2596
2597    fn force_schema_reload(&mut self) {
2598        self.changed_flags.insert(ChangeFlag::SCHEMA);
2599    }
2600
2601    fn force_domain_reload(&mut self) {
2602        self.changed_flags.insert(ChangeFlag::DOMAIN);
2603    }
2604
2605    pub(crate) fn upgrade_reindex(&mut self, v: i64) -> Result<(), OperationError> {
2606        self.be_txn.upgrade_reindex(v)
2607    }
2608
2609    #[inline]
2610    pub(crate) fn get_changed_app(&self) -> bool {
2611        self.changed_flags.contains(ChangeFlag::APPLICATION)
2612    }
2613
2614    #[inline]
2615    pub(crate) fn get_changed_oauth2(&self) -> bool {
2616        self.changed_flags.contains(ChangeFlag::OAUTH2)
2617    }
2618
2619    #[inline]
2620    pub(crate) fn clear_changed_oauth2(&mut self) {
2621        self.changed_flags.remove(ChangeFlag::OAUTH2)
2622    }
2623
2624    /// Indicate that we are about to re-bootstrap this server. You should ONLY
2625    /// call this during a replication refresh!!!
2626    pub(crate) fn set_phase_bootstrap(&mut self) {
2627        *self.phase = ServerPhase::Bootstrap;
2628    }
2629
2630    /// Raise the currently running server phase.
2631    pub(crate) fn set_phase(&mut self, phase: ServerPhase) {
2632        // Phase changes are one way
2633        if phase > *self.phase {
2634            *self.phase = phase
2635        }
2636    }
2637
2638    pub(crate) fn get_phase(&mut self) -> ServerPhase {
2639        *self.phase
2640    }
2641
2642    pub(crate) fn reload(&mut self) -> Result<(), OperationError> {
2643        // First, check if the domain version has changed. This can trigger
2644        // changes to schema, access controls and more.
2645        if self.changed_flags.contains(ChangeFlag::DOMAIN) {
2646            self.reload_domain_info_version()?;
2647        }
2648
2649        // This could be faster if we cache the set of classes changed
2650        // in an operation so we can check if we need to do the reload or not
2651        //
2652        // Reload the schema from qs.
2653        if self.changed_flags.contains(ChangeFlag::SCHEMA) {
2654            self.reload_schema()?;
2655
2656            // If the server is in a late phase of start up or is
2657            // operational, then a reindex may be required. After the reindex, the schema
2658            // must also be reloaded so that slope optimisation indexes are loaded correctly.
2659            if *self.phase >= ServerPhase::Running {
2660                self.reindex(false)?;
2661                self.reload_schema()?;
2662            }
2663        }
2664
2665        // We need to reload cryptographic providers before anything else so that
2666        // sync agreements and the domain can access their key material.
2667        if self
2668            .changed_flags
2669            .intersects(ChangeFlag::SCHEMA | ChangeFlag::KEY_MATERIAL)
2670        {
2671            self.reload_key_material()?;
2672        }
2673
2674        // Determine if we need to update access control profiles
2675        // based on any modifications that have occurred.
2676        // IF SCHEMA CHANGED WE MUST ALSO RELOAD!!! IE if schema had an attr removed
2677        // that we rely on we MUST fail this here!!
2678        //
2679        // Also note that changing sync agreements triggers an acp reload since
2680        // access controls need to be aware of these agreements.
2681        if self
2682            .changed_flags
2683            .intersects(ChangeFlag::SCHEMA | ChangeFlag::ACP | ChangeFlag::SYNC_AGREEMENT)
2684        {
2685            self.reload_accesscontrols()?;
2686        } else {
2687            // On a reload the cache is dropped, otherwise we tell accesscontrols
2688            // to drop anything related that was changed.
2689            // self.accesscontrols
2690            //    .invalidate_related_cache(self.changed_uuid.into_inner().as_slice())
2691        }
2692
2693        if self.changed_flags.contains(ChangeFlag::SYSTEM_CONFIG) {
2694            self.reload_system_config()?;
2695        }
2696
2697        if self.changed_flags.contains(ChangeFlag::DOMAIN) {
2698            self.reload_domain_info()?;
2699        }
2700
2701        // Clear flags
2702        self.changed_flags.remove(
2703            ChangeFlag::DOMAIN
2704                | ChangeFlag::SCHEMA
2705                | ChangeFlag::SYSTEM_CONFIG
2706                | ChangeFlag::ACP
2707                | ChangeFlag::SYNC_AGREEMENT
2708                | ChangeFlag::KEY_MATERIAL,
2709        );
2710
2711        Ok(())
2712    }
2713
2714    #[cfg(any(test, debug_assertions))]
2715    #[instrument(level = "debug", skip_all)]
2716    pub fn clear_cache(&mut self) -> Result<(), OperationError> {
2717        self.be_txn.clear_cache()
2718    }
2719
2720    #[instrument(level = "info", name="qswt_commit" skip_all)]
2721    pub fn commit(mut self) -> Result<(), OperationError> {
2722        self.reload()?;
2723
2724        // Now destructure the transaction ready to reset it.
2725        let QueryServerWriteTransaction {
2726            committed,
2727            phase,
2728            d_info,
2729            system_config,
2730            mut be_txn,
2731            schema,
2732            accesscontrols,
2733            cid,
2734            dyngroup_cache,
2735            key_providers,
2736            // Hold these for a bit more ...
2737            _db_ticket,
2738            _write_ticket,
2739            // Ignore values that don't need a commit.
2740            curtime: _,
2741            trim_cid: _,
2742            changed_flags,
2743            changed_uuid: _,
2744            resolve_filter_cache: _,
2745            resolve_filter_cache_clear,
2746            mut resolve_filter_cache_write,
2747        } = self;
2748        debug_assert!(!committed);
2749
2750        // Should have been cleared by any reloads.
2751        trace!(
2752            changed = ?changed_flags.iter_names().collect::<Vec<_>>(),
2753        );
2754
2755        // Write the cid to the db. If this fails, we can't assume replication
2756        // will be stable, so return if it fails.
2757        be_txn.set_db_ts_max(cid.ts)?;
2758        cid.commit();
2759
2760        // We don't care if this passes/fails, committing this is fine.
2761        if resolve_filter_cache_clear {
2762            resolve_filter_cache_write.clear();
2763        }
2764        resolve_filter_cache_write.commit();
2765
2766        // Point of no return - everything has been validated and reloaded.
2767        //
2768        // = Lets commit =
2769        schema
2770            .commit()
2771            .map(|_| d_info.commit())
2772            .map(|_| system_config.commit())
2773            .map(|_| phase.commit())
2774            .map(|_| dyngroup_cache.commit())
2775            .and_then(|_| key_providers.commit())
2776            .and_then(|_| accesscontrols.commit())
2777            .and_then(|_| be_txn.commit())
2778    }
2779
2780    pub(crate) fn get_txn_cid(&self) -> &Cid {
2781        &self.cid
2782    }
2783}
2784
2785#[cfg(test)]
2786mod tests {
2787    use crate::prelude::*;
2788    use kanidm_proto::scim_v1::client::ScimFilter;
2789    use kanidm_proto::scim_v1::server::ScimReference;
2790    use kanidm_proto::scim_v1::JsonValue;
2791    use kanidm_proto::scim_v1::ScimEntryGetQuery;
2792
2793    #[qs_test]
2794    async fn test_name_to_uuid(server: &QueryServer) {
2795        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2796
2797        let t_uuid = Uuid::new_v4();
2798        assert!(server_txn
2799            .internal_create(vec![entry_init!(
2800                (Attribute::Class, EntryClass::Object.to_value()),
2801                (Attribute::Class, EntryClass::Account.to_value()),
2802                (Attribute::Class, EntryClass::Person.to_value()),
2803                (Attribute::Name, Value::new_iname("testperson1")),
2804                (Attribute::Uuid, Value::Uuid(t_uuid)),
2805                (Attribute::Description, Value::new_utf8s("testperson1")),
2806                (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2807            ),])
2808            .is_ok());
2809
2810        // Name doesn't exist
2811        let r1 = server_txn.name_to_uuid("testpers");
2812        assert!(r1.is_err());
2813        // Name doesn't exist (not syntax normalised)
2814        let r2 = server_txn.name_to_uuid("tEsTpErS");
2815        assert!(r2.is_err());
2816        // Name does exist
2817        let r3 = server_txn.name_to_uuid("testperson1");
2818        assert_eq!(r3, Ok(t_uuid));
2819        // Name is not syntax normalised (but exists)
2820        let r4 = server_txn.name_to_uuid("tEsTpErSoN1");
2821        assert_eq!(r4, Ok(t_uuid));
2822        // Name is an rdn
2823        let r5 = server_txn.name_to_uuid("name=testperson1");
2824        assert_eq!(r5, Ok(t_uuid));
2825        // Name is a dn
2826        let r6 = server_txn.name_to_uuid("name=testperson1,o=example");
2827        assert_eq!(r6, Ok(t_uuid));
2828    }
2829
2830    #[qs_test]
2831    async fn test_external_id_to_uuid(server: &QueryServer) {
2832        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2833
2834        let t_uuid = Uuid::new_v4();
2835        assert!(server_txn
2836            .internal_create(vec![entry_init!(
2837                (Attribute::Class, EntryClass::Object.to_value()),
2838                (Attribute::Class, EntryClass::ExtensibleObject.to_value()),
2839                (Attribute::Uuid, Value::Uuid(t_uuid)),
2840                (
2841                    Attribute::SyncExternalId,
2842                    Value::new_iutf8("uid=testperson")
2843                )
2844            ),])
2845            .is_ok());
2846
2847        // Name doesn't exist
2848        let r1 = server_txn.sync_external_id_to_uuid("tobias");
2849        assert_eq!(r1, Ok(None));
2850        // Name doesn't exist (not syntax normalised)
2851        let r2 = server_txn.sync_external_id_to_uuid("tObIAs");
2852        assert_eq!(r2, Ok(None));
2853        // Name does exist
2854        let r3 = server_txn.sync_external_id_to_uuid("uid=testperson");
2855        assert_eq!(r3, Ok(Some(t_uuid)));
2856        // Name is not syntax normalised (but exists)
2857        let r4 = server_txn.sync_external_id_to_uuid("uId=TeStPeRsOn");
2858        assert_eq!(r4, Ok(Some(t_uuid)));
2859    }
2860
2861    #[qs_test]
2862    async fn test_uuid_to_spn(server: &QueryServer) {
2863        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2864
2865        let e1 = entry_init!(
2866            (Attribute::Class, EntryClass::Object.to_value()),
2867            (Attribute::Class, EntryClass::Person.to_value()),
2868            (Attribute::Class, EntryClass::Account.to_value()),
2869            (Attribute::Name, Value::new_iname("testperson1")),
2870            (
2871                Attribute::Uuid,
2872                Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2873            ),
2874            (Attribute::Description, Value::new_utf8s("testperson1")),
2875            (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2876        );
2877        let ce = CreateEvent::new_internal(vec![e1]);
2878        let cr = server_txn.create(&ce);
2879        assert!(cr.is_ok());
2880
2881        // Name doesn't exist
2882        let r1 = server_txn.uuid_to_spn(uuid!("bae3f507-e6c3-44ba-ad01-f8ff1083534a"));
2883        // There is nothing.
2884        assert_eq!(r1, Ok(None));
2885        // Name does exist
2886        let r3 = server_txn.uuid_to_spn(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"));
2887        println!("{r3:?}");
2888        assert_eq!(
2889            r3.unwrap().unwrap(),
2890            Value::new_spn_str("testperson1", "example.com")
2891        );
2892        // Name is not syntax normalised (but exists)
2893        let r4 = server_txn.uuid_to_spn(uuid!("CC8E95B4-C24F-4D68-BA54-8BED76F63930"));
2894        assert_eq!(
2895            r4.unwrap().unwrap(),
2896            Value::new_spn_str("testperson1", "example.com")
2897        );
2898    }
2899
2900    #[qs_test]
2901    async fn test_uuid_to_rdn(server: &QueryServer) {
2902        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2903
2904        let e1 = entry_init!(
2905            (Attribute::Class, EntryClass::Object.to_value()),
2906            (Attribute::Class, EntryClass::Person.to_value()),
2907            (Attribute::Class, EntryClass::Account.to_value()),
2908            (Attribute::Name, Value::new_iname("testperson1")),
2909            (
2910                Attribute::Uuid,
2911                Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2912            ),
2913            (Attribute::Description, Value::new_utf8s("testperson")),
2914            (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2915        );
2916        let ce = CreateEvent::new_internal(vec![e1]);
2917        let cr = server_txn.create(&ce);
2918        assert!(cr.is_ok());
2919
2920        // Name doesn't exist
2921        let r1 = server_txn.uuid_to_rdn(uuid!("bae3f507-e6c3-44ba-ad01-f8ff1083534a"));
2922        // There is nothing.
2923        assert_eq!(r1.unwrap(), "uuid=bae3f507-e6c3-44ba-ad01-f8ff1083534a");
2924        // Name does exist
2925        let r3 = server_txn.uuid_to_rdn(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"));
2926        println!("{r3:?}");
2927        assert_eq!(r3.unwrap(), "spn=testperson1@example.com");
2928        // Uuid is not syntax normalised (but exists)
2929        let r4 = server_txn.uuid_to_rdn(uuid!("CC8E95B4-C24F-4D68-BA54-8BED76F63930"));
2930        assert_eq!(r4.unwrap(), "spn=testperson1@example.com");
2931    }
2932
2933    #[qs_test]
2934    async fn test_clone_value(server: &QueryServer) {
2935        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2936        let e1 = entry_init!(
2937            (Attribute::Class, EntryClass::Object.to_value()),
2938            (Attribute::Class, EntryClass::Account.to_value()),
2939            (Attribute::Class, EntryClass::Person.to_value()),
2940            (Attribute::Name, Value::new_iname("testperson1")),
2941            (
2942                Attribute::Uuid,
2943                Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2944            ),
2945            (Attribute::Description, Value::new_utf8s("testperson1")),
2946            (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2947        );
2948        let ce = CreateEvent::new_internal(vec![e1]);
2949        let cr = server_txn.create(&ce);
2950        assert!(cr.is_ok());
2951
2952        // test attr not exist
2953        let r1 = server_txn.clone_value(&Attribute::from("tausau"), "naoeutnhaou");
2954
2955        assert!(r1.is_err());
2956
2957        // test attr not-normalised (error)
2958        // test attr not-reference
2959        let r2 = server_txn.clone_value(&Attribute::Custom("NaMe".into()), "NaMe");
2960
2961        assert!(r2.is_err());
2962
2963        // test attr reference
2964        let r3 = server_txn.clone_value(&Attribute::from("member"), "testperson1");
2965
2966        assert_eq!(
2967            r3,
2968            Ok(Value::Refer(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")))
2969        );
2970
2971        // test attr reference already resolved.
2972        let r4 = server_txn.clone_value(
2973            &Attribute::from("member"),
2974            "cc8e95b4-c24f-4d68-ba54-8bed76f63930",
2975        );
2976
2977        debug!("{:?}", r4);
2978        assert_eq!(
2979            r4,
2980            Ok(Value::Refer(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")))
2981        );
2982    }
2983
2984    #[qs_test]
2985    async fn test_dynamic_schema_class(server: &QueryServer) {
2986        let e1 = entry_init!(
2987            (Attribute::Class, EntryClass::Object.to_value()),
2988            (Attribute::Class, EntryClass::TestClass.to_value()),
2989            (Attribute::Name, Value::new_iname("testobj1")),
2990            (
2991                Attribute::Uuid,
2992                Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2993            )
2994        );
2995
2996        // Class definition
2997        let e_cd = entry_init!(
2998            (Attribute::Class, EntryClass::Object.to_value()),
2999            (Attribute::Class, EntryClass::ClassType.to_value()),
3000            (Attribute::ClassName, EntryClass::TestClass.to_value()),
3001            (
3002                Attribute::Uuid,
3003                Value::Uuid(uuid!("cfcae205-31c3-484b-8ced-667d1709c5e3"))
3004            ),
3005            (Attribute::Description, Value::new_utf8s("Test Class")),
3006            (Attribute::May, Value::from(Attribute::Name))
3007        );
3008        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3009        // Add a new class.
3010        let ce_class = CreateEvent::new_internal(vec![e_cd.clone()]);
3011        assert!(server_txn.create(&ce_class).is_ok());
3012        // Trying to add it now should fail.
3013        let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
3014        assert!(server_txn.create(&ce_fail).is_err());
3015
3016        // Commit
3017        server_txn.commit().expect("should not fail");
3018
3019        // Start a new write
3020        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3021        // Add the class to an object
3022        // should work
3023        let ce_work = CreateEvent::new_internal(vec![e1.clone()]);
3024        assert!(server_txn.create(&ce_work).is_ok());
3025
3026        // Commit
3027        server_txn.commit().expect("should not fail");
3028
3029        // Start a new write
3030        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3031        // delete the class
3032        let de_class = DeleteEvent::new_internal_invalid(filter!(f_eq(
3033            Attribute::ClassName,
3034            EntryClass::TestClass.into()
3035        )));
3036        assert!(server_txn.delete(&de_class).is_ok());
3037        // Commit
3038        server_txn.commit().expect("should not fail");
3039
3040        // Start a new write
3041        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3042        // Trying to add now should fail
3043        let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
3044        assert!(server_txn.create(&ce_fail).is_err());
3045        // Search our entry
3046        let testobj1 = server_txn
3047            .internal_search_uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
3048            .expect("failed");
3049        assert!(testobj1.attribute_equality(Attribute::Class, &EntryClass::TestClass.into()));
3050
3051        // Should still be good
3052        server_txn.commit().expect("should not fail");
3053        // Commit.
3054    }
3055
3056    #[qs_test]
3057    async fn test_dynamic_schema_attr(server: &QueryServer) {
3058        let e1 = entry_init!(
3059            (Attribute::Class, EntryClass::Object.to_value()),
3060            (Attribute::Class, EntryClass::ExtensibleObject.to_value()),
3061            (Attribute::Name, Value::new_iname("testobj1")),
3062            (
3063                Attribute::Uuid,
3064                Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
3065            ),
3066            (Attribute::TestAttr, Value::new_utf8s("test"))
3067        );
3068
3069        // Attribute definition
3070        let e_ad = entry_init!(
3071            (Attribute::Class, EntryClass::Object.to_value()),
3072            (Attribute::Class, EntryClass::AttributeType.to_value()),
3073            (
3074                Attribute::Uuid,
3075                Value::Uuid(uuid!("cfcae205-31c3-484b-8ced-667d1709c5e3"))
3076            ),
3077            (Attribute::AttributeName, Value::from(Attribute::TestAttr)),
3078            (Attribute::Description, Value::new_utf8s("Test Attribute")),
3079            (Attribute::MultiValue, Value::new_bool(false)),
3080            (Attribute::Unique, Value::new_bool(false)),
3081            (
3082                Attribute::Syntax,
3083                Value::new_syntaxs("UTF8STRING").expect("syntax")
3084            )
3085        );
3086
3087        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3088        // Add a new attribute.
3089        let ce_attr = CreateEvent::new_internal(vec![e_ad.clone()]);
3090        assert!(server_txn.create(&ce_attr).is_ok());
3091        // Trying to add it now should fail. (use extensible object)
3092        let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
3093        assert!(server_txn.create(&ce_fail).is_err());
3094
3095        // Commit
3096        server_txn.commit().expect("should not fail");
3097
3098        // Start a new write
3099        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3100        // Add the attr to an object
3101        // should work
3102        let ce_work = CreateEvent::new_internal(vec![e1.clone()]);
3103        assert!(server_txn.create(&ce_work).is_ok());
3104
3105        // Commit
3106        server_txn.commit().expect("should not fail");
3107
3108        // Start a new write
3109        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3110        // delete the attr
3111        let de_attr = DeleteEvent::new_internal_invalid(filter!(f_eq(
3112            Attribute::AttributeName,
3113            PartialValue::from(Attribute::TestAttr)
3114        )));
3115        assert!(server_txn.delete(&de_attr).is_ok());
3116        // Commit
3117        server_txn.commit().expect("should not fail");
3118
3119        // Start a new write
3120        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3121        // Trying to add now should fail
3122        let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
3123        assert!(server_txn.create(&ce_fail).is_err());
3124        // Search our attribute - should FAIL
3125        let filt = filter!(f_eq(Attribute::TestAttr, PartialValue::new_utf8s("test")));
3126        assert!(server_txn.internal_search(filt).is_err());
3127        // Search the entry - the attribute will still be present
3128        // even if we can't search on it.
3129        let testobj1 = server_txn
3130            .internal_search_uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
3131            .expect("failed");
3132        assert!(testobj1.attribute_equality(Attribute::TestAttr, &PartialValue::new_utf8s("test")));
3133
3134        server_txn.commit().expect("should not fail");
3135        // Commit.
3136    }
3137
3138    #[qs_test]
3139    async fn test_scim_entry_structure(server: &QueryServer) {
3140        let mut read_txn = server.read().await.unwrap();
3141
3142        // Query entry (A builtin one ?)
3143        let entry = read_txn
3144            .internal_search_uuid(UUID_IDM_PEOPLE_SELF_NAME_WRITE)
3145            .unwrap();
3146
3147        // Convert entry into scim
3148        let reduced = entry.as_ref().clone().into_reduced();
3149        let scim_entry = reduced.to_scim_kanidm(&mut read_txn).unwrap();
3150
3151        // Assert scim entry attributes are as expected
3152        assert_eq!(scim_entry.header.id, UUID_IDM_PEOPLE_SELF_NAME_WRITE);
3153        let name_scim = scim_entry.attrs.get(&Attribute::Name).unwrap();
3154        match name_scim {
3155            ScimValueKanidm::String(name) => {
3156                assert_eq!(name.clone(), "idm_people_self_name_write")
3157            }
3158            _ => {
3159                panic!("expected String, actual {:?}", name_scim);
3160            }
3161        }
3162
3163        // such as returning a new struct type for `members` attributes or `managed_by`
3164        let entry_managed_by_scim = scim_entry.attrs.get(&Attribute::EntryManagedBy).unwrap();
3165        match entry_managed_by_scim {
3166            ScimValueKanidm::EntryReferences(managed_by) => {
3167                assert_eq!(
3168                    managed_by.first().unwrap().clone(),
3169                    ScimReference {
3170                        uuid: UUID_IDM_ADMINS,
3171                        value: "idm_admins@example.com".to_string()
3172                    }
3173                )
3174            }
3175            _ => {
3176                panic!(
3177                    "expected EntryReference, actual {:?}",
3178                    entry_managed_by_scim
3179                );
3180            }
3181        }
3182
3183        let members_scim = scim_entry.attrs.get(&Attribute::Member).unwrap();
3184        match members_scim {
3185            ScimValueKanidm::EntryReferences(members) => {
3186                assert_eq!(
3187                    members.first().unwrap().clone(),
3188                    ScimReference {
3189                        uuid: UUID_IDM_ALL_PERSONS,
3190                        value: "idm_all_persons@example.com".to_string()
3191                    }
3192                )
3193            }
3194            _ => {
3195                panic!("expected EntryReferences, actual {:?}", members_scim);
3196            }
3197        }
3198    }
3199
3200    #[qs_test]
3201    async fn test_scim_effective_access_query(server: &QueryServer) {
3202        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3203
3204        let group_uuid = Uuid::new_v4();
3205        let e1 = entry_init!(
3206            (Attribute::Class, EntryClass::Object.to_value()),
3207            (Attribute::Class, EntryClass::Group.to_value()),
3208            (Attribute::Name, Value::new_iname("testgroup")),
3209            (Attribute::Uuid, Value::Uuid(group_uuid))
3210        );
3211
3212        assert!(server_txn.internal_create(vec![e1]).is_ok());
3213        assert!(server_txn.commit().is_ok());
3214
3215        // Now read that entry.
3216
3217        let mut server_txn = server.read().await.unwrap();
3218
3219        let idm_admin_entry = server_txn.internal_search_uuid(UUID_IDM_ADMIN).unwrap();
3220        let idm_admin_ident = Identity::from_impersonate_entry_readwrite(idm_admin_entry);
3221
3222        let query = ScimEntryGetQuery {
3223            ext_access_check: true,
3224            ..Default::default()
3225        };
3226
3227        let scim_entry = server_txn
3228            .scim_entry_id_get_ext(group_uuid, EntryClass::Group, query, idm_admin_ident)
3229            .unwrap();
3230
3231        let ext_access_check = scim_entry.ext_access_check.unwrap();
3232
3233        trace!(?ext_access_check);
3234
3235        assert!(ext_access_check.delete);
3236        assert!(ext_access_check.search.check(&Attribute::DirectMemberOf));
3237        assert!(ext_access_check.search.check(&Attribute::MemberOf));
3238        assert!(ext_access_check.search.check(&Attribute::Name));
3239        assert!(ext_access_check.modify_present.check(&Attribute::Name));
3240        assert!(ext_access_check.modify_remove.check(&Attribute::Name));
3241    }
3242
3243    #[qs_test]
3244    async fn test_scim_basic_search_ext_query(server: &QueryServer) {
3245        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3246
3247        let group_uuid = Uuid::new_v4();
3248        let e1 = entry_init!(
3249            (Attribute::Class, EntryClass::Object.to_value()),
3250            (Attribute::Class, EntryClass::Group.to_value()),
3251            (Attribute::Name, Value::new_iname("testgroup")),
3252            (Attribute::Uuid, Value::Uuid(group_uuid))
3253        );
3254
3255        assert!(server_txn.internal_create(vec![e1]).is_ok());
3256        assert!(server_txn.commit().is_ok());
3257
3258        // Now read that entry.
3259        let mut server_txn = server.read().await.unwrap();
3260
3261        let idm_admin_entry = server_txn.internal_search_uuid(UUID_IDM_ADMIN).unwrap();
3262        let idm_admin_ident = Identity::from_impersonate_entry_readwrite(idm_admin_entry);
3263
3264        let filter = ScimFilter::And(
3265            Box::new(ScimFilter::Equal(
3266                Attribute::Class.into(),
3267                EntryClass::Group.into(),
3268            )),
3269            Box::new(ScimFilter::Equal(
3270                Attribute::Uuid.into(),
3271                JsonValue::String(group_uuid.to_string()),
3272            )),
3273        );
3274
3275        let base: Vec<ScimEntryKanidm> = server_txn
3276            .scim_search_ext(idm_admin_ident, filter, ScimEntryGetQuery::default())
3277            .unwrap();
3278
3279        assert_eq!(base.len(), 1);
3280        assert_eq!(base[0].header.id, group_uuid);
3281    }
3282}