1use self::access::{
5 profiles::{
6 AccessControlCreate, AccessControlDelete, AccessControlModify, AccessControlSearch,
7 },
8 AccessControls, AccessControlsReadTransaction, AccessControlsTransaction,
9 AccessControlsWriteTransaction,
10};
11use self::keys::{
12 KeyObject, KeyProvider, KeyProviders, KeyProvidersReadTransaction, KeyProvidersTransaction,
13 KeyProvidersWriteTransaction,
14};
15use crate::be::{Backend, BackendReadTransaction, BackendTransaction, BackendWriteTransaction};
16use crate::filter::{
17 Filter, FilterInvalid, FilterValid, FilterValidResolved, ResolveFilterCache,
18 ResolveFilterCacheReadTxn,
19};
20use crate::plugins::dyngroup::{DynGroup, DynGroupCache};
21use crate::plugins::Plugins;
22use crate::prelude::*;
23use crate::repl::cid::Cid;
24use crate::repl::proto::ReplRuvRange;
25use crate::repl::ruv::ReplicationUpdateVectorTransaction;
26use crate::schema::{
27 Schema, SchemaAttribute, SchemaClass, SchemaReadTransaction, SchemaTransaction,
28 SchemaWriteTransaction,
29};
30use crate::value::{CredentialType, EXTRACT_VAL_DN};
31use crate::valueset::*;
32use concread::arcache::{ARCacheBuilder, ARCacheReadTxn, ARCacheWriteTxn};
33use concread::cowcell::*;
34use hashbrown::{HashMap, HashSet};
35use kanidm_proto::internal::{DomainInfo as ProtoDomainInfo, ImageValue, UiHint};
36use kanidm_proto::scim_v1::{
37 server::{ScimListResponse, ScimOAuth2ClaimMap, ScimOAuth2ScopeMap, ScimReference},
38 JsonValue, ScimEntryGetQuery, ScimFilter,
39};
40use std::collections::BTreeSet;
41use std::num::NonZeroU64;
42use std::str::FromStr;
43use std::sync::Arc;
44use time::OffsetDateTime;
45use tokio::sync::{Semaphore, SemaphorePermit};
46use tracing::trace;
47
48pub(crate) mod access;
49pub mod batch_modify;
50pub mod create;
51pub mod delete;
52pub mod identity;
53pub(crate) mod keys;
54pub(crate) mod migrations;
55pub mod modify;
56pub(crate) mod recycle;
57pub mod scim;
58
59const RESOLVE_FILTER_CACHE_MAX: usize = 256;
60const RESOLVE_FILTER_CACHE_LOCAL: usize = 8;
61
62#[derive(Debug, Clone, Copy, PartialOrd, PartialEq, Eq)]
63pub(crate) enum ServerPhase {
64 Bootstrap,
65 SchemaReady,
66 DomainInfoReady,
67 Running,
68}
69
70#[derive(Debug, Clone, PartialEq, Eq)]
73pub struct DomainInfo {
74 pub(crate) d_uuid: Uuid,
75 pub(crate) d_name: String,
76 pub(crate) d_display: String,
77 pub(crate) d_vers: DomainVersion,
78 pub(crate) d_patch_level: u32,
79 pub(crate) d_devel_taint: bool,
80 pub(crate) d_ldap_allow_unix_pw_bind: bool,
81 pub(crate) d_allow_easter_eggs: bool,
82 d_image: Option<ImageValue>,
84}
85
86impl DomainInfo {
87 pub fn name(&self) -> &str {
88 self.d_name.as_str()
89 }
90
91 pub fn display_name(&self) -> &str {
92 self.d_display.as_str()
93 }
94
95 pub fn devel_taint(&self) -> bool {
96 self.d_devel_taint
97 }
98
99 pub fn image(&self) -> Option<&ImageValue> {
100 self.d_image.as_ref()
101 }
102
103 pub fn has_custom_image(&self) -> bool {
104 self.d_image.is_some()
105 }
106
107 pub fn allow_easter_eggs(&self) -> bool {
108 self.d_allow_easter_eggs
109 }
110
111 #[cfg(feature = "test")]
112 pub fn new_test() -> CowCell<Self> {
113 concread::cowcell::CowCell::new(Self {
114 d_uuid: Uuid::new_v4(),
115 d_name: "test domain".to_string(),
116 d_display: "Test Domain".to_string(),
117 d_vers: 1,
118 d_patch_level: 0,
119 d_devel_taint: false,
120 d_ldap_allow_unix_pw_bind: false,
121 d_allow_easter_eggs: false,
122 d_image: None,
123 })
124 }
125}
126
127#[derive(Debug, Clone, PartialEq, Eq, Default)]
128pub struct SystemConfig {
129 pub(crate) denied_names: HashSet<String>,
130 pub(crate) pw_badlist: HashSet<String>,
131}
132
133#[derive(Clone)]
134pub struct QueryServer {
135 phase: Arc<CowCell<ServerPhase>>,
136 pub(crate) d_info: Arc<CowCell<DomainInfo>>,
137 system_config: Arc<CowCell<SystemConfig>>,
138 be: Backend,
139 schema: Arc<Schema>,
140 accesscontrols: Arc<AccessControls>,
141 db_tickets: Arc<Semaphore>,
142 read_tickets: Arc<Semaphore>,
143 write_ticket: Arc<Semaphore>,
144 resolve_filter_cache: Arc<ResolveFilterCache>,
145 dyngroup_cache: Arc<CowCell<DynGroupCache>>,
146 cid_max: Arc<CowCell<Cid>>,
147 key_providers: Arc<KeyProviders>,
148}
149
150pub struct QueryServerReadTransaction<'a> {
151 be_txn: BackendReadTransaction<'a>,
152 pub(crate) d_info: CowCellReadTxn<DomainInfo>,
155 system_config: CowCellReadTxn<SystemConfig>,
156 schema: SchemaReadTransaction,
157 accesscontrols: AccessControlsReadTransaction<'a>,
158 key_providers: KeyProvidersReadTransaction,
159 _db_ticket: SemaphorePermit<'a>,
160 _read_ticket: SemaphorePermit<'a>,
161 resolve_filter_cache: ResolveFilterCacheReadTxn<'a>,
162 trim_cid: Cid,
165}
166
167unsafe impl Sync for QueryServerReadTransaction<'_> {}
168
169unsafe impl Send for QueryServerReadTransaction<'_> {}
170
171bitflags::bitflags! {
172 #[derive(Copy, Clone, Debug)]
173 pub struct ChangeFlag: u32 {
174 const SCHEMA = 0b0000_0001;
175 const ACP = 0b0000_0010;
176 const OAUTH2 = 0b0000_0100;
177 const DOMAIN = 0b0000_1000;
178 const SYSTEM_CONFIG = 0b0001_0000;
179 const SYNC_AGREEMENT = 0b0010_0000;
180 const KEY_MATERIAL = 0b0100_0000;
181 const APPLICATION = 0b1000_0000;
182 }
183}
184
185pub struct QueryServerWriteTransaction<'a> {
186 committed: bool,
187 phase: CowCellWriteTxn<'a, ServerPhase>,
188 d_info: CowCellWriteTxn<'a, DomainInfo>,
189 system_config: CowCellWriteTxn<'a, SystemConfig>,
190 curtime: Duration,
191 cid: CowCellWriteTxn<'a, Cid>,
192 trim_cid: Cid,
193 pub(crate) be_txn: BackendWriteTransaction<'a>,
194 pub(crate) schema: SchemaWriteTransaction<'a>,
195 accesscontrols: AccessControlsWriteTransaction<'a>,
196 key_providers: KeyProvidersWriteTransaction<'a>,
197 pub(super) changed_flags: ChangeFlag,
201
202 pub(super) changed_uuid: HashSet<Uuid>,
204 _db_ticket: SemaphorePermit<'a>,
205 _write_ticket: SemaphorePermit<'a>,
206 resolve_filter_cache_clear: bool,
207 resolve_filter_cache_write: ARCacheWriteTxn<
208 'a,
209 (IdentityId, Arc<Filter<FilterValid>>),
210 Arc<Filter<FilterValidResolved>>,
211 (),
212 >,
213 resolve_filter_cache: ARCacheReadTxn<
214 'a,
215 (IdentityId, Arc<Filter<FilterValid>>),
216 Arc<Filter<FilterValidResolved>>,
217 (),
218 >,
219 dyngroup_cache: CowCellWriteTxn<'a, DynGroupCache>,
220}
221
222impl QueryServerWriteTransaction<'_> {
223 pub(crate) fn trim_cid(&self) -> &Cid {
224 &self.trim_cid
225 }
226}
227
228pub trait QueryServerTransaction<'a> {
238 type BackendTransactionType: BackendTransaction;
239 fn get_be_txn(&mut self) -> &mut Self::BackendTransactionType;
240
241 type SchemaTransactionType: SchemaTransaction;
242 fn get_schema<'b>(&self) -> &'b Self::SchemaTransactionType;
243
244 type AccessControlsTransactionType: AccessControlsTransaction<'a>;
245 fn get_accesscontrols(&self) -> &Self::AccessControlsTransactionType;
246
247 type KeyProvidersTransactionType: KeyProvidersTransaction;
248 fn get_key_providers(&self) -> &Self::KeyProvidersTransactionType;
249
250 fn pw_badlist(&self) -> &HashSet<String>;
251
252 fn denied_names(&self) -> &HashSet<String>;
253
254 fn get_domain_version(&self) -> DomainVersion;
255
256 fn get_domain_patch_level(&self) -> u32;
257
258 fn get_domain_development_taint(&self) -> bool;
259
260 fn get_domain_uuid(&self) -> Uuid;
261
262 fn get_domain_name(&self) -> &str;
263
264 fn get_domain_display_name(&self) -> &str;
265
266 fn get_domain_image_value(&self) -> Option<ImageValue>;
267
268 fn get_resolve_filter_cache(&mut self) -> Option<&mut ResolveFilterCacheReadTxn<'a>>;
269
270 fn get_resolve_filter_cache_and_be_txn(
274 &mut self,
275 ) -> (
276 &mut Self::BackendTransactionType,
277 Option<&mut ResolveFilterCacheReadTxn<'a>>,
278 );
279
280 #[instrument(level = "debug", skip_all)]
290 fn search_ext(
291 &mut self,
292 se: &SearchEvent,
293 ) -> Result<Vec<EntryReducedCommitted>, OperationError> {
294 let entries = self.search(se)?;
300
301 let access = self.get_accesscontrols();
302 access
303 .search_filter_entry_attributes(se, entries)
304 .map_err(|e| {
305 admin_error!(?e, "Failed to filter entry attributes");
307 e
308 })
309 }
311
312 #[instrument(level = "debug", skip_all)]
313 fn search(
314 &mut self,
315 se: &SearchEvent,
316 ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
317 if se.ident.is_internal() {
318 trace!(internal_filter = ?se.filter, "search");
319 } else {
320 security_info!(initiator = %se.ident, "search");
321 admin_debug!(external_filter = ?se.filter, "search");
322 }
323
324 let (be_txn, resolve_filter_cache) = self.get_resolve_filter_cache_and_be_txn();
334
335 let idxmeta = be_txn.get_idxmeta_ref();
336
337 trace!(resolve_filter_cache = %resolve_filter_cache.is_some());
338
339 let vfr = se
341 .filter
342 .resolve(&se.ident, Some(idxmeta), resolve_filter_cache)
343 .map_err(|e| {
344 admin_error!(?e, "search filter resolve failure");
345 e
346 })?;
347
348 let lims = se.ident.limits();
349
350 let res = self.get_be_txn().search(lims, &vfr).map_err(|e| {
355 admin_error!(?e, "backend failure");
356 OperationError::Backend
357 })?;
358
359 let access = self.get_accesscontrols();
365 access.search_filter_entries(se, res).map_err(|e| {
366 admin_error!(?e, "Unable to access filter entries");
367 e
368 })
369 }
370
371 #[instrument(level = "debug", skip_all)]
372 fn exists(&mut self, ee: &ExistsEvent) -> Result<bool, OperationError> {
373 let (be_txn, resolve_filter_cache) = self.get_resolve_filter_cache_and_be_txn();
374 let idxmeta = be_txn.get_idxmeta_ref();
375
376 let vfr = ee
377 .filter
378 .resolve(&ee.ident, Some(idxmeta), resolve_filter_cache)
379 .map_err(|e| {
380 admin_error!(?e, "Failed to resolve filter");
381 e
382 })?;
383
384 let lims = ee.ident.limits();
385
386 if ee.ident.is_internal() {
387 be_txn.exists(lims, &vfr).map_err(|e| {
390 admin_error!(?e, "backend failure");
391 OperationError::Backend
392 })
393 } else {
394 let res = self.get_be_txn().search(lims, &vfr).map_err(|e| {
397 admin_error!(?e, "backend failure");
398 OperationError::Backend
399 })?;
400
401 let access = self.get_accesscontrols();
408 access
409 .filter_entries(&ee.ident, &ee.filter_orig, res)
410 .map_err(|e| {
411 admin_error!(?e, "Unable to access filter entries");
412 e
413 })
414 .map(|entries| !entries.is_empty())
415 }
416 }
417
418 fn name_to_uuid(&mut self, name: &str) -> Result<Uuid, OperationError> {
419 let work = EXTRACT_VAL_DN
429 .captures(name)
430 .and_then(|caps| caps.name("val"))
431 .map(|v| v.as_str().to_lowercase())
432 .ok_or(OperationError::InvalidValueState)?;
433
434 Uuid::parse_str(&work).or_else(|_| {
436 self.get_be_txn()
437 .name2uuid(&work)?
438 .ok_or(OperationError::NoMatchingEntries)
439 })
440 }
441
442 fn sync_external_id_to_uuid(
444 &mut self,
445 external_id: &str,
446 ) -> Result<Option<Uuid>, OperationError> {
447 Uuid::parse_str(external_id).map(Some).or_else(|_| {
449 let lname = external_id.to_lowercase();
450 self.get_be_txn().externalid2uuid(lname.as_str())
451 })
452 }
453
454 fn uuid_to_spn(&mut self, uuid: Uuid) -> Result<Option<Value>, OperationError> {
455 let r = self.get_be_txn().uuid2spn(uuid)?;
456
457 if let Some(ref n) = r {
458 debug_assert!(n.is_spn() || n.is_iname());
461 }
462
463 Ok(r)
464 }
465
466 fn uuid_to_rdn(&mut self, uuid: Uuid) -> Result<String, OperationError> {
467 self.get_be_txn()
469 .uuid2rdn(uuid)
470 .map(|v| v.unwrap_or_else(|| format!("uuid={}", uuid.as_hyphenated())))
471 }
472
473 #[instrument(level = "debug", skip_all)]
475 fn internal_exists(&mut self, filter: &Filter<FilterInvalid>) -> Result<bool, OperationError> {
476 let f_valid = filter
478 .validate(self.get_schema())
479 .map_err(OperationError::SchemaViolation)?;
480 let ee = ExistsEvent::new_internal(f_valid);
482 self.exists(&ee)
484 }
485
486 #[instrument(level = "debug", skip_all)]
487 fn internal_exists_uuid(&mut self, uuid: Uuid) -> Result<bool, OperationError> {
488 let filter = filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
489 self.internal_exists(&filter)
490 }
491
492 #[instrument(level = "debug", skip_all)]
493 fn internal_search(
494 &mut self,
495 filter: Filter<FilterInvalid>,
496 ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
497 let f_valid = filter
498 .validate(self.get_schema())
499 .map_err(OperationError::SchemaViolation)?;
500 let se = SearchEvent::new_internal(f_valid);
501 self.search(&se)
502 }
503
504 #[instrument(level = "debug", skip_all)]
505 fn impersonate_search_valid(
506 &mut self,
507 f_valid: Filter<FilterValid>,
508 f_intent_valid: Filter<FilterValid>,
509 event: &Identity,
510 ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
511 let se = SearchEvent::new_impersonate(event, f_valid, f_intent_valid);
512 self.search(&se)
513 }
514
515 fn impersonate_search_ext_valid(
517 &mut self,
518 f_valid: Filter<FilterValid>,
519 f_intent_valid: Filter<FilterValid>,
520 event: &Identity,
521 ) -> Result<Vec<Entry<EntryReduced, EntryCommitted>>, OperationError> {
522 let se = SearchEvent::new_impersonate(event, f_valid, f_intent_valid);
523 self.search_ext(&se)
524 }
525
526 fn impersonate_search(
528 &mut self,
529 filter: Filter<FilterInvalid>,
530 filter_intent: Filter<FilterInvalid>,
531 event: &Identity,
532 ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
533 let f_valid = filter
534 .validate(self.get_schema())
535 .map_err(OperationError::SchemaViolation)?;
536 let f_intent_valid = filter_intent
537 .validate(self.get_schema())
538 .map_err(OperationError::SchemaViolation)?;
539 self.impersonate_search_valid(f_valid, f_intent_valid, event)
540 }
541
542 #[instrument(level = "debug", skip_all)]
543 fn impersonate_search_ext(
544 &mut self,
545 filter: Filter<FilterInvalid>,
546 filter_intent: Filter<FilterInvalid>,
547 event: &Identity,
548 ) -> Result<Vec<Entry<EntryReduced, EntryCommitted>>, OperationError> {
549 let f_valid = filter
550 .validate(self.get_schema())
551 .map_err(OperationError::SchemaViolation)?;
552 let f_intent_valid = filter_intent
553 .validate(self.get_schema())
554 .map_err(OperationError::SchemaViolation)?;
555 self.impersonate_search_ext_valid(f_valid, f_intent_valid, event)
556 }
557
558 #[instrument(level = "debug", skip_all)]
561 fn internal_search_uuid(
562 &mut self,
563 uuid: Uuid,
564 ) -> Result<Arc<EntrySealedCommitted>, OperationError> {
565 let filter = filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
566 let f_valid = filter.validate(self.get_schema()).map_err(|e| {
567 error!(?e, "Filter Validate - SchemaViolation");
568 OperationError::SchemaViolation(e)
569 })?;
570 let se = SearchEvent::new_internal(f_valid);
571
572 let mut vs = self.search(&se)?;
573 match vs.pop() {
574 Some(entry) if vs.is_empty() => Ok(entry),
575 _ => Err(OperationError::NoMatchingEntries),
576 }
577 }
578
579 #[instrument(level = "debug", skip_all)]
582 fn internal_search_all_uuid(
583 &mut self,
584 uuid: Uuid,
585 ) -> Result<Arc<EntrySealedCommitted>, OperationError> {
586 let filter = filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
587 let f_valid = filter.validate(self.get_schema()).map_err(|e| {
588 error!(?e, "Filter Validate - SchemaViolation");
589 OperationError::SchemaViolation(e)
590 })?;
591 let se = SearchEvent::new_internal(f_valid);
592
593 let mut vs = self.search(&se)?;
594 match vs.pop() {
595 Some(entry) if vs.is_empty() => Ok(entry),
596 _ => Err(OperationError::NoMatchingEntries),
597 }
598 }
599
600 #[instrument(level = "debug", skip_all)]
602 fn internal_search_conflict_uuid(
603 &mut self,
604 uuid: Uuid,
605 ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
606 let filter = filter_all!(f_and(vec![
607 f_eq(Attribute::SourceUuid, PartialValue::Uuid(uuid)),
608 f_eq(Attribute::Class, EntryClass::Conflict.into())
609 ]));
610 let f_valid = filter.validate(self.get_schema()).map_err(|e| {
611 error!(?e, "Filter Validate - SchemaViolation");
612 OperationError::SchemaViolation(e)
613 })?;
614 let se = SearchEvent::new_internal(f_valid);
615
616 self.search(&se)
617 }
618
619 #[instrument(level = "debug", skip_all)]
620 fn impersonate_search_ext_uuid(
621 &mut self,
622 uuid: Uuid,
623 event: &Identity,
624 ) -> Result<Entry<EntryReduced, EntryCommitted>, OperationError> {
625 let filter_intent = filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
626 let filter = filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
627
628 let mut vs = self.impersonate_search_ext(filter, filter_intent, event)?;
629 match vs.pop() {
630 Some(entry) if vs.is_empty() => Ok(entry),
631 _ => {
632 if vs.is_empty() {
633 Err(OperationError::NoMatchingEntries)
634 } else {
635 Err(OperationError::UniqueConstraintViolation)
637 }
638 }
639 }
640 }
641
642 #[instrument(level = "debug", skip_all)]
643 fn impersonate_search_uuid(
644 &mut self,
645 uuid: Uuid,
646 event: &Identity,
647 ) -> Result<Arc<EntrySealedCommitted>, OperationError> {
648 let filter_intent = filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
649 let filter = filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
650
651 let mut vs = self.impersonate_search(filter, filter_intent, event)?;
652 match vs.pop() {
653 Some(entry) if vs.is_empty() => Ok(entry),
654 _ => Err(OperationError::NoMatchingEntries),
655 }
656 }
657
658 fn clone_value(&mut self, attr: &Attribute, value: &str) -> Result<Value, OperationError> {
661 let schema = self.get_schema();
662
663 match schema.get_attributes().get(attr) {
668 Some(schema_a) => {
669 match schema_a.syntax {
670 SyntaxType::Utf8String => Ok(Value::new_utf8(value.to_string())),
671 SyntaxType::Utf8StringInsensitive => Ok(Value::new_iutf8(value)),
672 SyntaxType::Utf8StringIname => Ok(Value::new_iname(value)),
673 SyntaxType::Boolean => Value::new_bools(value)
674 .ok_or_else(|| OperationError::InvalidAttribute("Invalid boolean syntax".to_string())),
675 SyntaxType::SyntaxId => Value::new_syntaxs(value)
676 .ok_or_else(|| OperationError::InvalidAttribute("Invalid Syntax syntax".to_string())),
677 SyntaxType::IndexId => Value::new_indexes(value)
678 .ok_or_else(|| OperationError::InvalidAttribute("Invalid Index syntax".to_string())),
679 SyntaxType::CredentialType => CredentialType::try_from(value)
680 .map(Value::CredentialType)
681 .map_err(|()| OperationError::InvalidAttribute("Invalid CredentialType syntax".to_string())),
682 SyntaxType::Uuid => {
683 let un = self
686 .name_to_uuid(value)
687 .unwrap_or(UUID_DOES_NOT_EXIST);
688 Ok(Value::Uuid(un))
689 }
690 SyntaxType::ReferenceUuid => {
691 let un = self
692 .name_to_uuid(value)
693 .unwrap_or(UUID_DOES_NOT_EXIST);
694 Ok(Value::Refer(un))
695 }
696 SyntaxType::JsonFilter => Value::new_json_filter_s(value)
697 .ok_or_else(|| OperationError::InvalidAttribute("Invalid Filter syntax".to_string())),
698 SyntaxType::Image => Value::new_image(value),
699
700 SyntaxType::Credential => Err(OperationError::InvalidAttribute("Credentials can not be supplied through modification - please use the IDM api".to_string())),
701 SyntaxType::SecretUtf8String => Err(OperationError::InvalidAttribute("Radius secrets can not be supplied through modification - please use the IDM api".to_string())),
702 SyntaxType::SshKey => Err(OperationError::InvalidAttribute("SSH public keys can not be supplied through modification - please use the IDM api".to_string())),
703 SyntaxType::SecurityPrincipalName => Err(OperationError::InvalidAttribute("SPNs are generated and not able to be set.".to_string())),
704 SyntaxType::Uint32 => Value::new_uint32_str(value)
705 .ok_or_else(|| OperationError::InvalidAttribute("Invalid uint32 syntax".to_string())),
706 SyntaxType::Cid => Err(OperationError::InvalidAttribute("CIDs are generated and not able to be set.".to_string())),
707 SyntaxType::NsUniqueId => Value::new_nsuniqueid_s(value)
708 .ok_or_else(|| OperationError::InvalidAttribute("Invalid NsUniqueId syntax".to_string())),
709 SyntaxType::DateTime => Value::new_datetime_s(value)
710 .ok_or_else(|| OperationError::InvalidAttribute("Invalid DateTime (rfc3339) syntax".to_string())),
711 SyntaxType::EmailAddress => Value::new_email_address_s(value)
712 .ok_or_else(|| OperationError::InvalidAttribute("Invalid Email Address syntax".to_string())),
713 SyntaxType::Url => Value::new_url_s(value)
714 .ok_or_else(|| OperationError::InvalidAttribute("Invalid Url (whatwg/url) syntax".to_string())),
715 SyntaxType::OauthScope => Value::new_oauthscope(value)
716 .ok_or_else(|| OperationError::InvalidAttribute("Invalid Oauth Scope syntax".to_string())),
717 SyntaxType::WebauthnAttestationCaList => Value::new_webauthn_attestation_ca_list(value)
718 .ok_or_else(|| OperationError::InvalidAttribute("Invalid Webauthn Attestation CA List".to_string())),
719 SyntaxType::OauthScopeMap => Err(OperationError::InvalidAttribute("Oauth Scope Maps can not be supplied through modification - please use the IDM api".to_string())),
720 SyntaxType::OauthClaimMap => Err(OperationError::InvalidAttribute("Oauth Claim Maps can not be supplied through modification - please use the IDM api".to_string())),
721 SyntaxType::PrivateBinary => Err(OperationError::InvalidAttribute("Private Binary Values can not be supplied through modification".to_string())),
722 SyntaxType::IntentToken => Err(OperationError::InvalidAttribute("Intent Token Values can not be supplied through modification".to_string())),
723 SyntaxType::Passkey => Err(OperationError::InvalidAttribute("Passkey Values can not be supplied through modification".to_string())),
724 SyntaxType::AttestedPasskey => Err(OperationError::InvalidAttribute("AttestedPasskey Values can not be supplied through modification".to_string())),
725 SyntaxType::Session => Err(OperationError::InvalidAttribute("Session Values can not be supplied through modification".to_string())),
726 SyntaxType::ApiToken => Err(OperationError::InvalidAttribute("ApiToken Values can not be supplied through modification".to_string())),
727 SyntaxType::JwsKeyEs256 => Err(OperationError::InvalidAttribute("JwsKeyEs256 Values can not be supplied through modification".to_string())),
728 SyntaxType::JwsKeyRs256 => Err(OperationError::InvalidAttribute("JwsKeyRs256 Values can not be supplied through modification".to_string())),
729 SyntaxType::Oauth2Session => Err(OperationError::InvalidAttribute("Oauth2Session Values can not be supplied through modification".to_string())),
730 SyntaxType::UiHint => UiHint::from_str(value)
731 .map(Value::UiHint)
732 .map_err(|()| OperationError::InvalidAttribute("Invalid uihint syntax".to_string())),
733 SyntaxType::TotpSecret => Err(OperationError::InvalidAttribute("TotpSecret Values can not be supplied through modification".to_string())),
734 SyntaxType::AuditLogString => Err(OperationError::InvalidAttribute("Audit logs are generated and not able to be set.".to_string())),
735 SyntaxType::EcKeyPrivate => Err(OperationError::InvalidAttribute("Ec keys are generated and not able to be set.".to_string())),
736 SyntaxType::KeyInternal => Err(OperationError::InvalidAttribute("Internal keys are generated and not able to be set.".to_string())),
737 SyntaxType::HexString => Value::new_hex_string_s(value)
738 .ok_or_else(|| OperationError::InvalidAttribute("Invalid hex string syntax".to_string())),
739 SyntaxType::Certificate => Value::new_certificate_s(value)
740 .ok_or_else(|| OperationError::InvalidAttribute("Invalid x509 certificate syntax".to_string())),
741 SyntaxType::ApplicationPassword => Err(OperationError::InvalidAttribute("ApplicationPassword values can not be supplied through modification".to_string())),
742 SyntaxType::Json => Err(OperationError::InvalidAttribute("Json values can not be supplied through modification".to_string())),
743 SyntaxType::Message => Err(OperationError::InvalidAttribute("Message values can not be supplied through modification".to_string())),
744 }
745 }
746 None => {
747 Err(OperationError::InvalidAttributeName(attr.to_string()))
750 }
751 }
752 }
753
754 fn clone_partialvalue(
755 &mut self,
756 attr: &Attribute,
757 value: &str,
758 ) -> Result<PartialValue, OperationError> {
759 let schema = self.get_schema();
760
761 match schema.get_attributes().get(attr) {
763 Some(schema_a) => {
764 match schema_a.syntax {
765 SyntaxType::Utf8String | SyntaxType::TotpSecret => {
766 Ok(PartialValue::new_utf8(value.to_string()))
767 }
768 SyntaxType::Utf8StringInsensitive
769 | SyntaxType::JwsKeyEs256
770 | SyntaxType::JwsKeyRs256 => Ok(PartialValue::new_iutf8(value)),
771 SyntaxType::Utf8StringIname => Ok(PartialValue::new_iname(value)),
772 SyntaxType::Boolean => PartialValue::new_bools(value).ok_or_else(|| {
773 OperationError::InvalidAttribute("Invalid boolean syntax".to_string())
774 }),
775 SyntaxType::SyntaxId => PartialValue::new_syntaxs(value).ok_or_else(|| {
776 OperationError::InvalidAttribute("Invalid Syntax syntax".to_string())
777 }),
778 SyntaxType::IndexId => PartialValue::new_indexes(value).ok_or_else(|| {
779 OperationError::InvalidAttribute("Invalid Index syntax".to_string())
780 }),
781 SyntaxType::CredentialType => CredentialType::try_from(value)
782 .map(PartialValue::CredentialType)
783 .map_err(|()| {
784 OperationError::InvalidAttribute(
785 "Invalid credentialtype syntax".to_string(),
786 )
787 }),
788 SyntaxType::Uuid => {
789 let un = self.name_to_uuid(value).unwrap_or(UUID_DOES_NOT_EXIST);
790 Ok(PartialValue::Uuid(un))
791 }
792 SyntaxType::ReferenceUuid
796 | SyntaxType::OauthScopeMap
797 | SyntaxType::Session
798 | SyntaxType::ApiToken
799 | SyntaxType::Oauth2Session
800 | SyntaxType::ApplicationPassword => {
801 let un = self.name_to_uuid(value).unwrap_or(UUID_DOES_NOT_EXIST);
802 Ok(PartialValue::Refer(un))
803 }
804 SyntaxType::OauthClaimMap => self
805 .name_to_uuid(value)
806 .map(PartialValue::Refer)
807 .or_else(|_| Ok(PartialValue::new_iutf8(value))),
808
809 SyntaxType::JsonFilter => {
810 PartialValue::new_json_filter_s(value).ok_or_else(|| {
811 OperationError::InvalidAttribute("Invalid Filter syntax".to_string())
812 })
813 }
814 SyntaxType::Credential => Ok(PartialValue::new_credential_tag(value)),
815 SyntaxType::SecretUtf8String => Ok(PartialValue::new_secret_str()),
816 SyntaxType::SshKey => Ok(PartialValue::new_sshkey_tag_s(value)),
817 SyntaxType::SecurityPrincipalName => {
818 PartialValue::new_spn_s(value).ok_or_else(|| {
819 OperationError::InvalidAttribute("Invalid spn syntax".to_string())
820 })
821 }
822 SyntaxType::Uint32 => PartialValue::new_uint32_str(value).ok_or_else(|| {
823 OperationError::InvalidAttribute("Invalid uint32 syntax".to_string())
824 }),
825 SyntaxType::Cid => PartialValue::new_cid_s(value).ok_or_else(|| {
826 OperationError::InvalidAttribute("Invalid cid syntax".to_string())
827 }),
828 SyntaxType::NsUniqueId => Ok(PartialValue::new_nsuniqueid_s(value)),
829 SyntaxType::DateTime => PartialValue::new_datetime_s(value).ok_or_else(|| {
830 OperationError::InvalidAttribute(
831 "Invalid DateTime (rfc3339) syntax".to_string(),
832 )
833 }),
834 SyntaxType::EmailAddress => Ok(PartialValue::new_email_address_s(value)),
835 SyntaxType::Url => PartialValue::new_url_s(value).ok_or_else(|| {
836 OperationError::InvalidAttribute(
837 "Invalid Url (whatwg/url) syntax".to_string(),
838 )
839 }),
840 SyntaxType::OauthScope => Ok(PartialValue::new_oauthscope(value)),
841 SyntaxType::PrivateBinary => Ok(PartialValue::PrivateBinary),
842 SyntaxType::IntentToken => PartialValue::new_intenttoken_s(value.to_string())
843 .ok_or_else(|| {
844 OperationError::InvalidAttribute(
845 "Invalid Intent Token ID (uuid) syntax".to_string(),
846 )
847 }),
848 SyntaxType::Passkey => PartialValue::new_passkey_s(value).ok_or_else(|| {
849 OperationError::InvalidAttribute("Invalid Passkey UUID syntax".to_string())
850 }),
851 SyntaxType::AttestedPasskey => PartialValue::new_attested_passkey_s(value)
852 .ok_or_else(|| {
853 OperationError::InvalidAttribute(
854 "Invalid AttestedPasskey UUID syntax".to_string(),
855 )
856 }),
857 SyntaxType::UiHint => UiHint::from_str(value)
858 .map(PartialValue::UiHint)
859 .map_err(|()| {
860 OperationError::InvalidAttribute("Invalid uihint syntax".to_string())
861 }),
862 SyntaxType::AuditLogString => Ok(PartialValue::new_utf8s(value)),
863 SyntaxType::EcKeyPrivate => Ok(PartialValue::SecretValue),
864 SyntaxType::Image => Ok(PartialValue::new_utf8s(value)),
865 SyntaxType::WebauthnAttestationCaList => Err(OperationError::InvalidAttribute(
866 "Invalid - unable to query attestation CA list".to_string(),
867 )),
868 SyntaxType::HexString | SyntaxType::KeyInternal | SyntaxType::Certificate => {
869 PartialValue::new_hex_string_s(value).ok_or_else(|| {
870 OperationError::InvalidAttribute(
871 "Invalid syntax, expected hex string".to_string(),
872 )
873 })
874 }
875 SyntaxType::Json => Err(OperationError::InvalidAttribute(
876 "Json values can not be validated by this interface".to_string(),
877 )),
878 SyntaxType::Message => Err(OperationError::InvalidAttribute(
879 "Message values can not be validated by this interface".to_string(),
880 )),
881 }
882 }
883 None => {
884 Err(OperationError::InvalidAttributeName(attr.to_string()))
887 }
888 }
889 }
890
891 fn resolve_scim_interim(
892 &mut self,
893 scim_value_intermediate: ScimValueIntermediate,
894 ) -> Result<Option<ScimValueKanidm>, OperationError> {
895 match scim_value_intermediate {
896 ScimValueIntermediate::References(uuids) => {
897 let scim_references = uuids
898 .into_iter()
899 .map(|uuid| {
900 self.uuid_to_spn(uuid)
901 .and_then(|maybe_value| {
902 maybe_value.ok_or(OperationError::InvalidValueState)
903 })
904 .map(|value| ScimReference {
905 uuid,
906 value: value.to_proto_string_clone(),
907 })
908 })
909 .collect::<Result<Vec<_>, _>>()?;
910 Ok(Some(ScimValueKanidm::EntryReferences(scim_references)))
911 }
912 ScimValueIntermediate::Oauth2ClaimMap(unresolved_maps) => {
913 let scim_claim_maps = unresolved_maps
914 .into_iter()
915 .map(
916 |UnresolvedScimValueOauth2ClaimMap {
917 group_uuid,
918 claim,
919 join_char,
920 values,
921 }| {
922 self.uuid_to_spn(group_uuid)
923 .and_then(|maybe_value| {
924 maybe_value.ok_or(OperationError::InvalidValueState)
925 })
926 .map(|value| ScimOAuth2ClaimMap {
927 group: value.to_proto_string_clone(),
928 group_uuid,
929 claim,
930 join_char,
931 values,
932 })
933 },
934 )
935 .collect::<Result<Vec<_>, _>>()?;
936
937 Ok(Some(ScimValueKanidm::OAuth2ClaimMap(scim_claim_maps)))
938 }
939
940 ScimValueIntermediate::Oauth2ScopeMap(unresolved_maps) => {
941 let scim_claim_maps = unresolved_maps
942 .into_iter()
943 .map(|UnresolvedScimValueOauth2ScopeMap { group_uuid, scopes }| {
944 self.uuid_to_spn(group_uuid)
945 .and_then(|maybe_value| {
946 maybe_value.ok_or(OperationError::InvalidValueState)
947 })
948 .map(|value| ScimOAuth2ScopeMap {
949 group: value.to_proto_string_clone(),
950 group_uuid,
951 scopes,
952 })
953 })
954 .collect::<Result<Vec<_>, _>>()?;
955
956 Ok(Some(ScimValueKanidm::OAuth2ScopeMap(scim_claim_maps)))
957 }
958 }
959 }
960
961 fn resolve_scim_json_get(
962 &mut self,
963 attr: &Attribute,
964 value: &JsonValue,
965 ) -> Result<PartialValue, OperationError> {
966 let schema = self.get_schema();
967 let Some(schema_a) = schema.get_attributes().get(attr) else {
969 return Err(OperationError::InvalidAttributeName(attr.to_string()));
972 };
973
974 debug!(schema_syntax = ?schema_a.syntax, ?value);
975
976 match schema_a.syntax {
977 SyntaxType::Utf8String => {
978 let JsonValue::String(value) = value else {
979 return Err(OperationError::InvalidAttribute(attr.to_string()));
980 };
981 Ok(PartialValue::Utf8(value.to_string()))
982 }
983 SyntaxType::Utf8StringInsensitive => {
984 let JsonValue::String(value) = value else {
985 return Err(OperationError::InvalidAttribute(attr.to_string()));
986 };
987 Ok(PartialValue::new_iutf8(value))
988 }
989 SyntaxType::Utf8StringIname => {
990 let JsonValue::String(value) = value else {
991 return Err(OperationError::InvalidAttribute(attr.to_string()));
992 };
993 Ok(PartialValue::new_iname(value))
994 }
995 SyntaxType::Uuid => {
996 let JsonValue::String(value) = value else {
997 return Err(OperationError::InvalidAttribute(attr.to_string()));
998 };
999
1000 let un = self.name_to_uuid(value).unwrap_or(UUID_DOES_NOT_EXIST);
1001 Ok(PartialValue::Uuid(un))
1002 }
1003 SyntaxType::Boolean => {
1004 let JsonValue::Bool(value) = value else {
1005 return Err(OperationError::InvalidAttribute(attr.to_string()));
1006 };
1007 Ok(PartialValue::Bool(*value))
1008 }
1009 SyntaxType::SyntaxId => {
1010 let JsonValue::String(value) = value else {
1011 return Err(OperationError::InvalidAttribute(attr.to_string()));
1012 };
1013 let Ok(value) = SyntaxType::try_from(value.as_str()) else {
1014 return Err(OperationError::InvalidAttribute(attr.to_string()));
1015 };
1016 Ok(PartialValue::Syntax(value))
1017 }
1018 SyntaxType::ReferenceUuid
1019 | SyntaxType::OauthScopeMap
1020 | SyntaxType::Session
1021 | SyntaxType::ApiToken
1022 | SyntaxType::Oauth2Session
1023 | SyntaxType::ApplicationPassword => {
1024 let JsonValue::String(value) = value else {
1025 return Err(OperationError::InvalidAttribute(attr.to_string()));
1026 };
1027
1028 let un = self.name_to_uuid(value).unwrap_or(UUID_DOES_NOT_EXIST);
1029 Ok(PartialValue::Refer(un))
1030 }
1031
1032 _ => Err(OperationError::InvalidAttribute(attr.to_string())),
1033 }
1034 }
1035
1036 fn resolve_valueset_intermediate(
1037 &mut self,
1038 vs_inter: ValueSetIntermediate,
1039 ) -> Result<ValueSet, OperationError> {
1040 match vs_inter {
1041 ValueSetIntermediate::References {
1042 mut resolved,
1043 unresolved,
1044 } => {
1045 for value in unresolved {
1046 let un = self.name_to_uuid(value.as_str()).unwrap_or_else(|_| {
1047 warn!(
1048 ?value,
1049 "Value can not be resolved to a uuid - assuming it does not exist."
1050 );
1051 UUID_DOES_NOT_EXIST
1052 });
1053
1054 resolved.insert(un);
1055 }
1056
1057 let vs = ValueSetRefer::from_set(resolved);
1058 Ok(vs)
1059 }
1060
1061 ValueSetIntermediate::Oauth2ClaimMap {
1062 mut resolved,
1063 unresolved,
1064 } => {
1065 resolved.extend(unresolved.into_iter().map(
1066 |UnresolvedValueSetOauth2ClaimMap {
1067 group_name,
1068 claim,
1069 join_char,
1070 claim_values,
1071 }| {
1072 let group_uuid =
1073 self.name_to_uuid(group_name.as_str()).unwrap_or_else(|_| {
1074 warn!(
1075 ?group_name,
1076 "Value can not be resolved to a uuid - assuming it does not exist."
1077 );
1078 UUID_DOES_NOT_EXIST
1079 });
1080
1081 ResolvedValueSetOauth2ClaimMap {
1082 group_uuid,
1083 claim,
1084 join_char,
1085 claim_values,
1086 }
1087 },
1088 ));
1089
1090 let vs = ValueSetOauthClaimMap::from_set(resolved);
1091 Ok(vs)
1092 }
1093
1094 ValueSetIntermediate::Oauth2ScopeMap {
1095 mut resolved,
1096 unresolved,
1097 } => {
1098 resolved.extend(unresolved.into_iter().map(
1099 |UnresolvedValueSetOauth2ScopeMap { group_name, scopes }| {
1100 let group_uuid =
1101 self.name_to_uuid(group_name.as_str()).unwrap_or_else(|_| {
1102 warn!(
1103 ?group_name,
1104 "Value can not be resolved to a uuid - assuming it does not exist."
1105 );
1106 UUID_DOES_NOT_EXIST
1107 });
1108
1109 ResolvedValueSetOauth2ScopeMap { group_uuid, scopes }
1110 },
1111 ));
1112
1113 let vs = ValueSetOauthScopeMap::from_set(resolved);
1114 Ok(vs)
1115 }
1116 }
1117 }
1118
1119 fn resolve_valueset(&mut self, value: &ValueSet) -> Result<Vec<String>, OperationError> {
1121 if let Some(r_set) = value.as_refer_set() {
1122 let v: Result<Vec<_>, _> = r_set
1123 .iter()
1124 .copied()
1125 .map(|ur| {
1126 let nv = self.uuid_to_spn(ur)?;
1127 match nv {
1128 Some(v) => Ok(v.to_proto_string_clone()),
1129 None => Ok(uuid_to_proto_string(ur)),
1130 }
1131 })
1132 .collect();
1133 v
1134 } else if let Some(r_map) = value.as_oauthscopemap() {
1135 let v: Result<Vec<_>, _> = r_map
1136 .iter()
1137 .map(|(u, m)| {
1138 let nv = self.uuid_to_spn(*u)?;
1139 let u = match nv {
1140 Some(v) => v.to_proto_string_clone(),
1141 None => uuid_to_proto_string(*u),
1142 };
1143 Ok(format!("{u}: {m:?}"))
1144 })
1145 .collect();
1146 v
1147 } else if let Some(r_map) = value.as_oauthclaim_map() {
1148 let mut v = Vec::with_capacity(0);
1149 for (claim_name, mapping) in r_map.iter() {
1150 for (group_ref, claims) in mapping.values() {
1151 let join_char = mapping.join().to_str();
1152
1153 let nv = self.uuid_to_spn(*group_ref)?;
1154 let resolved_id = match nv {
1155 Some(v) => v.to_proto_string_clone(),
1156 None => uuid_to_proto_string(*group_ref),
1157 };
1158
1159 let joined = str_concat!(claims, ",");
1160
1161 v.push(format!("{claim_name}:{resolved_id}:{join_char}:{joined:?}"))
1162 }
1163 }
1164 Ok(v)
1165 } else {
1166 let v: Vec<_> = value.to_proto_string_clone_iter().collect();
1167 Ok(v)
1168 }
1169 }
1170
1171 fn resolve_valueset_ldap(
1172 &mut self,
1173 value: &ValueSet,
1174 basedn: &str,
1175 ) -> Result<Vec<Vec<u8>>, OperationError> {
1176 if let Some(r_set) = value.as_refer_set() {
1177 let v: Result<Vec<_>, _> = r_set
1178 .iter()
1179 .copied()
1180 .map(|ur| {
1181 let rdn = self.uuid_to_rdn(ur)?;
1182 Ok(format!("{rdn},{basedn}").into_bytes())
1183 })
1184 .collect();
1185 v
1186 } else if let Some(key_iter) = value.as_sshpubkey_string_iter() {
1189 let v: Vec<_> = key_iter.map(|s| s.into_bytes()).collect();
1190 Ok(v)
1191 } else {
1192 let v: Vec<_> = value
1193 .to_proto_string_clone_iter()
1194 .map(|s| s.into_bytes())
1195 .collect();
1196 Ok(v)
1197 }
1198 }
1199
1200 fn get_db_domain(&mut self) -> Result<Arc<EntrySealedCommitted>, OperationError> {
1201 self.internal_search_uuid(UUID_DOMAIN_INFO)
1202 }
1203
1204 fn get_domain_key_object_handle(&self) -> Result<Arc<KeyObject>, OperationError> {
1205 self.get_key_providers()
1206 .get_key_object_handle(UUID_DOMAIN_INFO)
1207 .ok_or(OperationError::KP0031KeyObjectNotFound)
1208 }
1209
1210 fn get_domain_es256_private_key(&mut self) -> Result<Vec<u8>, OperationError> {
1211 self.internal_search_uuid(UUID_DOMAIN_INFO)
1212 .and_then(|e| {
1213 e.get_ava_single_private_binary(Attribute::Es256PrivateKeyDer)
1214 .map(|s| s.to_vec())
1215 .ok_or(OperationError::InvalidEntryState)
1216 })
1217 .map_err(|e| {
1218 admin_error!(?e, "Error getting domain es256 key");
1219 e
1220 })
1221 }
1222
1223 fn get_domain_ldap_allow_unix_pw_bind(&mut self) -> Result<bool, OperationError> {
1224 self.internal_search_uuid(UUID_DOMAIN_INFO).map(|entry| {
1225 entry
1226 .get_ava_single_bool(Attribute::LdapAllowUnixPwBind)
1227 .unwrap_or(true)
1228 })
1229 }
1230
1231 fn get_sc_password_badlist(&mut self) -> Result<HashSet<String>, OperationError> {
1234 self.internal_search_uuid(UUID_SYSTEM_CONFIG)
1235 .map(|e| match e.get_ava_iter_iutf8(Attribute::BadlistPassword) {
1236 Some(vs_str_iter) => vs_str_iter.map(str::to_string).collect::<HashSet<_>>(),
1237 None => HashSet::default(),
1238 })
1239 .map_err(|e| {
1240 error!(
1241 ?e,
1242 "Failed to retrieve password badlist from system configuration"
1243 );
1244 e
1245 })
1246 }
1247
1248 fn get_sc_denied_names(&mut self) -> Result<HashSet<String>, OperationError> {
1251 self.internal_search_uuid(UUID_SYSTEM_CONFIG)
1252 .map(|e| match e.get_ava_iter_iname(Attribute::DeniedName) {
1253 Some(vs_str_iter) => vs_str_iter.map(str::to_string).collect::<HashSet<_>>(),
1254 None => HashSet::default(),
1255 })
1256 .map_err(|e| {
1257 error!(
1258 ?e,
1259 "Failed to retrieve denied names from system configuration"
1260 );
1261 e
1262 })
1263 }
1264
1265 fn get_oauth2rs_set(&mut self) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
1266 self.internal_search(filter!(f_eq(
1267 Attribute::Class,
1268 EntryClass::OAuth2ResourceServer.into(),
1269 )))
1270 }
1271
1272 fn get_applications_set(&mut self) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
1273 self.internal_search(filter!(f_eq(
1274 Attribute::Class,
1275 EntryClass::Application.into(),
1276 )))
1277 }
1278
1279 #[instrument(level = "debug", skip_all)]
1280 fn consumer_get_state(&mut self) -> Result<ReplRuvRange, OperationError> {
1281 let domain_uuid = self.get_domain_uuid();
1306
1307 let ruv_snapshot = self.get_be_txn().get_ruv();
1310
1311 ruv_snapshot
1313 .current_ruv_range()
1314 .map(|ranges| ReplRuvRange::V1 {
1315 domain_uuid,
1316 ranges,
1317 })
1318 }
1319}
1320
1321impl<'a> QueryServerTransaction<'a> for QueryServerReadTransaction<'a> {
1325 type AccessControlsTransactionType = AccessControlsReadTransaction<'a>;
1326 type BackendTransactionType = BackendReadTransaction<'a>;
1327 type SchemaTransactionType = SchemaReadTransaction;
1328 type KeyProvidersTransactionType = KeyProvidersReadTransaction;
1329
1330 fn get_be_txn(&mut self) -> &mut BackendReadTransaction<'a> {
1331 &mut self.be_txn
1332 }
1333
1334 fn get_schema<'b>(&self) -> &'b SchemaReadTransaction {
1335 unsafe {
1339 let s = (&self.schema) as *const _;
1340 &*s
1341 }
1342 }
1343
1344 fn get_accesscontrols(&self) -> &AccessControlsReadTransaction<'a> {
1345 &self.accesscontrols
1346 }
1347
1348 fn get_key_providers(&self) -> &KeyProvidersReadTransaction {
1349 &self.key_providers
1350 }
1351
1352 fn get_resolve_filter_cache(&mut self) -> Option<&mut ResolveFilterCacheReadTxn<'a>> {
1353 Some(&mut self.resolve_filter_cache)
1354 }
1355
1356 fn get_resolve_filter_cache_and_be_txn(
1357 &mut self,
1358 ) -> (
1359 &mut BackendReadTransaction<'a>,
1360 Option<&mut ResolveFilterCacheReadTxn<'a>>,
1361 ) {
1362 (&mut self.be_txn, Some(&mut self.resolve_filter_cache))
1363 }
1364
1365 fn pw_badlist(&self) -> &HashSet<String> {
1366 &self.system_config.pw_badlist
1367 }
1368
1369 fn denied_names(&self) -> &HashSet<String> {
1370 &self.system_config.denied_names
1371 }
1372
1373 fn get_domain_version(&self) -> DomainVersion {
1374 self.d_info.d_vers
1375 }
1376
1377 fn get_domain_patch_level(&self) -> u32 {
1378 self.d_info.d_patch_level
1379 }
1380
1381 fn get_domain_development_taint(&self) -> bool {
1382 self.d_info.d_devel_taint
1383 }
1384
1385 fn get_domain_uuid(&self) -> Uuid {
1386 self.d_info.d_uuid
1387 }
1388
1389 fn get_domain_name(&self) -> &str {
1390 &self.d_info.d_name
1391 }
1392
1393 fn get_domain_display_name(&self) -> &str {
1394 &self.d_info.d_display
1395 }
1396
1397 fn get_domain_image_value(&self) -> Option<ImageValue> {
1398 self.d_info.d_image.clone()
1399 }
1400}
1401
1402impl QueryServerReadTransaction<'_> {
1403 pub(crate) fn trim_cid(&self) -> &Cid {
1404 &self.trim_cid
1405 }
1406
1407 pub fn domain_info(&mut self) -> Result<ProtoDomainInfo, OperationError> {
1409 let d_info = &self.d_info;
1410
1411 Ok(ProtoDomainInfo {
1412 name: d_info.d_name.clone(),
1413 displayname: d_info.d_display.clone(),
1414 uuid: d_info.d_uuid,
1415 level: d_info.d_vers,
1416 })
1417 }
1418
1419 pub(crate) fn verify(&mut self) -> Vec<Result<(), ConsistencyError>> {
1423 let be_errs = self.get_be_txn().verify();
1427
1428 if !be_errs.is_empty() {
1429 return be_errs;
1430 }
1431
1432 let sc_errs = self.get_schema().validate();
1434
1435 if !sc_errs.is_empty() {
1436 return sc_errs;
1437 }
1438
1439 let idx_errs = self.get_be_txn().verify_indexes();
1443
1444 if !idx_errs.is_empty() {
1445 return idx_errs;
1446 }
1447
1448 let mut results = Vec::with_capacity(0);
1451
1452 let schema = self.get_schema();
1455
1456 let filt_all = filter!(f_pres(Attribute::Class));
1457 let all_entries = match self.internal_search(filt_all) {
1458 Ok(a) => a,
1459 Err(_e) => return vec![Err(ConsistencyError::QueryServerSearchFailure)],
1460 };
1461
1462 for e in all_entries {
1463 e.verify(schema, &mut results)
1464 }
1465
1466 self.get_be_txn().verify_ruv(&mut results);
1468
1469 Plugins::run_verify(self, &mut results);
1475 results
1478 }
1479
1480 #[instrument(level = "debug", skip_all)]
1481 pub fn scim_entry_id_get_ext(
1482 &mut self,
1483 uuid: Uuid,
1484 class: EntryClass,
1485 query: ScimEntryGetQuery,
1486 ident: Identity,
1487 ) -> Result<ScimEntryKanidm, OperationError> {
1488 let filter_intent = filter!(f_and!([
1489 f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)),
1490 f_eq(Attribute::Class, class.into())
1491 ]));
1492
1493 let f_intent_valid = filter_intent
1494 .validate(self.get_schema())
1495 .map_err(OperationError::SchemaViolation)?;
1496
1497 let f_valid = f_intent_valid.clone().into_ignore_hidden();
1498
1499 let r_attrs = query
1500 .attributes
1501 .map(|attr_set| attr_set.into_iter().collect());
1502
1503 let se = SearchEvent {
1504 ident,
1505 filter: f_valid,
1506 filter_orig: f_intent_valid,
1507 attrs: r_attrs,
1508 effective_access_check: query.ext_access_check,
1509 };
1510
1511 let mut vs = self.search_ext(&se)?;
1512 match vs.pop() {
1513 Some(entry) if vs.is_empty() => entry.to_scim_kanidm(self),
1514 _ => {
1515 if vs.is_empty() {
1516 Err(OperationError::NoMatchingEntries)
1517 } else {
1518 Err(OperationError::UniqueConstraintViolation)
1520 }
1521 }
1522 }
1523 }
1524
1525 #[instrument(level = "debug", skip_all)]
1526 pub fn scim_search_ext(
1527 &mut self,
1528 ident: Identity,
1529 filter: ScimFilter,
1530 query: ScimEntryGetQuery,
1531 ) -> Result<ScimListResponse, OperationError> {
1532 let filter = if let Some(ref user_filter) = query.filter {
1533 ScimFilter::And(Box::new(filter), Box::new(user_filter.clone()))
1534 } else {
1535 filter
1536 };
1537
1538 let filter_intent = Filter::from_scim_ro(&ident, &filter, self)?;
1539
1540 self.scim_search_filter_ext(ident, &filter_intent, query)
1541 }
1542
1543 pub fn scim_search_filter_ext(
1544 &mut self,
1545 ident: Identity,
1546 filter_intent: &Filter<FilterInvalid>,
1547 query: ScimEntryGetQuery,
1548 ) -> Result<ScimListResponse, OperationError> {
1549 let f_intent_valid = filter_intent
1550 .validate(self.get_schema())
1551 .map_err(OperationError::SchemaViolation)?;
1552
1553 let f_valid = f_intent_valid.clone().into_ignore_hidden();
1554
1555 let r_attrs = query
1556 .attributes
1557 .map(|attr_set| attr_set.into_iter().collect());
1558
1559 let se = SearchEvent {
1560 ident,
1561 filter: f_valid,
1562 filter_orig: f_intent_valid,
1563 attrs: r_attrs,
1564 effective_access_check: query.ext_access_check,
1565 };
1566
1567 let mut result_set = self.search_ext(&se)?;
1568
1569 let total_results = result_set.len() as u64;
1571
1572 if let Some(sort_attr) = query.sort_by {
1578 result_set.sort_unstable_by(|entry_left, entry_right| {
1579 let left = entry_left.get_ava_set(&sort_attr);
1580 let right = entry_right.get_ava_set(&sort_attr);
1581 match (left, right) {
1582 (Some(left), Some(right)) => left.cmp(right),
1583 (Some(_), None) => std::cmp::Ordering::Less,
1584 (None, Some(_)) => std::cmp::Ordering::Greater,
1585 (None, None) => std::cmp::Ordering::Equal,
1586 }
1587 });
1588 }
1589
1590 let (items_per_page, start_index, paginated_result_set) = if let Some(count) = query.count {
1592 let count: u64 = count.get();
1593 let start_index: u64 = query
1596 .start_index
1597 .map(|non_zero_index|
1598 non_zero_index.get() - 1)
1600 .unwrap_or_default();
1601
1602 if start_index as usize > result_set.len() {
1604 return Err(OperationError::SC0029PaginationOutOfBounds);
1607 }
1608
1609 let mut result_set = result_set.split_off(start_index as usize);
1610 result_set.truncate(count as usize);
1611
1612 (
1613 NonZeroU64::new(count),
1614 NonZeroU64::new(start_index + 1),
1615 result_set,
1616 )
1617 } else {
1618 (None, None, result_set)
1620 };
1621
1622 let resources = paginated_result_set
1623 .into_iter()
1624 .map(|entry| entry.to_scim_kanidm(self))
1625 .collect::<Result<Vec<_>, _>>()?;
1626
1627 Ok(ScimListResponse {
1628 schemas: Vec::with_capacity(0),
1630 total_results,
1631 items_per_page,
1632 start_index,
1633 resources,
1634 })
1635 }
1636
1637 #[instrument(level = "debug", skip_all)]
1638 pub fn scim_search_message_ready_ext(
1639 &mut self,
1640 ident: Identity,
1641 curtime: Duration,
1642 ) -> Result<ScimListResponse, OperationError> {
1643 let curtime_odt = OffsetDateTime::UNIX_EPOCH + curtime;
1644
1645 let filter_intent = filter_all!(f_and(vec![
1646 f_eq(Attribute::Class, EntryClass::OutboundMessage.into()),
1647 f_lt(Attribute::SendAfter, PartialValue::DateTime(curtime_odt)),
1648 f_andnot(f_pres(Attribute::SentAt))
1649 ]));
1650
1651 let query = ScimEntryGetQuery::default();
1652
1653 self.scim_search_filter_ext(ident, &filter_intent, query)
1654 }
1655}
1656
1657impl<'a> QueryServerTransaction<'a> for QueryServerWriteTransaction<'a> {
1658 type AccessControlsTransactionType = AccessControlsWriteTransaction<'a>;
1659 type BackendTransactionType = BackendWriteTransaction<'a>;
1660 type SchemaTransactionType = SchemaWriteTransaction<'a>;
1661 type KeyProvidersTransactionType = KeyProvidersWriteTransaction<'a>;
1662
1663 fn get_be_txn(&mut self) -> &mut BackendWriteTransaction<'a> {
1664 &mut self.be_txn
1665 }
1666
1667 fn get_schema<'b>(&self) -> &'b SchemaWriteTransaction<'a> {
1668 unsafe {
1672 let s = (&self.schema) as *const _;
1673 &*s
1674 }
1675 }
1676
1677 fn get_accesscontrols(&self) -> &AccessControlsWriteTransaction<'a> {
1678 &self.accesscontrols
1679 }
1680
1681 fn get_key_providers(&self) -> &KeyProvidersWriteTransaction<'a> {
1682 &self.key_providers
1683 }
1684
1685 fn get_resolve_filter_cache(&mut self) -> Option<&mut ResolveFilterCacheReadTxn<'a>> {
1686 if self.resolve_filter_cache_clear || *self.phase < ServerPhase::SchemaReady {
1687 None
1688 } else {
1689 Some(&mut self.resolve_filter_cache)
1690 }
1691 }
1692
1693 fn get_resolve_filter_cache_and_be_txn(
1694 &mut self,
1695 ) -> (
1696 &mut BackendWriteTransaction<'a>,
1697 Option<&mut ResolveFilterCacheReadTxn<'a>>,
1698 ) {
1699 if self.resolve_filter_cache_clear || *self.phase < ServerPhase::SchemaReady {
1700 (&mut self.be_txn, None)
1701 } else {
1702 (&mut self.be_txn, Some(&mut self.resolve_filter_cache))
1703 }
1704 }
1705
1706 fn pw_badlist(&self) -> &HashSet<String> {
1707 &self.system_config.pw_badlist
1708 }
1709
1710 fn denied_names(&self) -> &HashSet<String> {
1711 &self.system_config.denied_names
1712 }
1713
1714 fn get_domain_version(&self) -> DomainVersion {
1715 self.d_info.d_vers
1716 }
1717
1718 fn get_domain_patch_level(&self) -> u32 {
1719 self.d_info.d_patch_level
1720 }
1721
1722 fn get_domain_development_taint(&self) -> bool {
1723 self.d_info.d_devel_taint
1724 }
1725
1726 fn get_domain_uuid(&self) -> Uuid {
1727 self.d_info.d_uuid
1728 }
1729
1730 fn get_domain_name(&self) -> &str {
1732 &self.d_info.d_name
1733 }
1734
1735 fn get_domain_display_name(&self) -> &str {
1736 &self.d_info.d_display
1737 }
1738
1739 fn get_domain_image_value(&self) -> Option<ImageValue> {
1740 self.d_info.d_image.clone()
1741 }
1742}
1743
1744impl QueryServer {
1745 pub fn new(
1746 be: Backend,
1747 schema: Schema,
1748 domain_name: String,
1749 curtime: Duration,
1750 ) -> Result<Self, OperationError> {
1751 let (s_uuid, d_uuid, ts_max) = {
1752 let mut wr = be.write()?;
1753 let s_uuid = wr.get_db_s_uuid()?;
1754 let d_uuid = wr.get_db_d_uuid()?;
1755 let ts_max = wr.get_db_ts_max(curtime)?;
1756 wr.commit()?;
1757 (s_uuid, d_uuid, ts_max)
1758 };
1759
1760 let pool_size = be.get_pool_size();
1761
1762 debug!("Server UUID -> {:?}", s_uuid);
1763 debug!("Domain UUID -> {:?}", d_uuid);
1764 debug!("Domain Name -> {:?}", domain_name);
1765
1766 let d_info = Arc::new(CowCell::new(DomainInfo {
1767 d_uuid,
1768 d_vers: DOMAIN_LEVEL_0,
1771 d_patch_level: 0,
1772 d_name: domain_name.clone(),
1773 d_display: domain_name,
1776 d_devel_taint: option_env!("KANIDM_PRE_RELEASE").is_some(),
1778 d_ldap_allow_unix_pw_bind: false,
1779 d_allow_easter_eggs: false,
1780 d_image: None,
1781 }));
1782
1783 let cid = Cid::new_lamport(s_uuid, curtime, &ts_max);
1784 let cid_max = Arc::new(CowCell::new(cid));
1785
1786 let system_config = Arc::new(CowCell::new(SystemConfig::default()));
1788
1789 let dyngroup_cache = Arc::new(CowCell::new(DynGroupCache::default()));
1790
1791 let phase = Arc::new(CowCell::new(ServerPhase::Bootstrap));
1792
1793 let resolve_filter_cache = Arc::new(
1794 ARCacheBuilder::new()
1795 .set_size(RESOLVE_FILTER_CACHE_MAX, RESOLVE_FILTER_CACHE_LOCAL)
1796 .set_reader_quiesce(true)
1797 .build()
1798 .ok_or_else(|| {
1799 error!("Failed to build filter resolve cache");
1800 OperationError::DB0003FilterResolveCacheBuild
1801 })?,
1802 );
1803
1804 let key_providers = Arc::new(KeyProviders::default());
1805
1806 debug_assert!(pool_size > 0);
1809 let read_ticket_pool = std::cmp::max(pool_size - 1, 1);
1810
1811 Ok(QueryServer {
1812 phase,
1813 d_info,
1814 system_config,
1815 be,
1816 schema: Arc::new(schema),
1817 accesscontrols: Arc::new(AccessControls::default()),
1818 db_tickets: Arc::new(Semaphore::new(pool_size as usize)),
1819 read_tickets: Arc::new(Semaphore::new(read_ticket_pool as usize)),
1820 write_ticket: Arc::new(Semaphore::new(1)),
1821 resolve_filter_cache,
1822 dyngroup_cache,
1823 cid_max,
1824 key_providers,
1825 })
1826 }
1827
1828 pub fn try_quiesce(&self) {
1829 self.be.try_quiesce();
1830 self.accesscontrols.try_quiesce();
1831 self.resolve_filter_cache.try_quiesce();
1832 }
1833
1834 #[instrument(level = "debug", skip_all)]
1835 async fn read_acquire_ticket(&self) -> Option<(SemaphorePermit<'_>, SemaphorePermit<'_>)> {
1836 let read_ticket = if cfg!(test) {
1840 self.read_tickets
1841 .try_acquire()
1842 .inspect_err(|err| {
1843 error!(?err, "Unable to acquire read ticket!");
1844 })
1845 .ok()?
1846 } else {
1847 let fut = tokio::time::timeout(
1848 Duration::from_millis(DB_LOCK_ACQUIRE_TIMEOUT_MILLIS),
1849 self.read_tickets.acquire(),
1850 );
1851
1852 match fut.await {
1853 Ok(Ok(ticket)) => ticket,
1854 Ok(Err(_)) => {
1855 error!("Failed to acquire read ticket, may be poisoned.");
1856 return None;
1857 }
1858 Err(_) => {
1859 error!("Failed to acquire read ticket, server is overloaded.");
1860 return None;
1861 }
1862 }
1863 };
1864
1865 let db_ticket = if cfg!(test) {
1870 self.db_tickets
1871 .try_acquire()
1872 .inspect_err(|err| {
1873 error!(?err, "Unable to acquire database ticket!");
1874 })
1875 .ok()?
1876 } else {
1877 self.db_tickets
1878 .acquire()
1879 .await
1880 .inspect_err(|err| {
1881 error!(?err, "Unable to acquire database ticket!");
1882 })
1883 .ok()?
1884 };
1885
1886 Some((read_ticket, db_ticket))
1887 }
1888
1889 pub async fn read(&self) -> Result<QueryServerReadTransaction<'_>, OperationError> {
1890 let (read_ticket, db_ticket) = self
1891 .read_acquire_ticket()
1892 .await
1893 .ok_or(OperationError::DatabaseLockAcquisitionTimeout)?;
1894 let schema = self.schema.read();
1898
1899 let cid_max = self.cid_max.read();
1900 let trim_cid = cid_max.sub_secs(CHANGELOG_MAX_AGE)?;
1901
1902 let be_txn = self.be.read()?;
1903
1904 Ok(QueryServerReadTransaction {
1905 be_txn,
1906 schema,
1907 d_info: self.d_info.read(),
1908 system_config: self.system_config.read(),
1909 accesscontrols: self.accesscontrols.read(),
1910 key_providers: self.key_providers.read(),
1911 _db_ticket: db_ticket,
1912 _read_ticket: read_ticket,
1913 resolve_filter_cache: self.resolve_filter_cache.read(),
1914 trim_cid,
1915 })
1916 }
1917
1918 #[instrument(level = "debug", skip_all)]
1919 async fn write_acquire_ticket(&self) -> Option<(SemaphorePermit<'_>, SemaphorePermit<'_>)> {
1920 let write_ticket = if cfg!(test) {
1922 self.write_ticket
1923 .try_acquire()
1924 .inspect_err(|err| {
1925 error!(?err, "Unable to acquire write ticket!");
1926 })
1927 .ok()?
1928 } else {
1929 let fut = tokio::time::timeout(
1930 Duration::from_millis(DB_LOCK_ACQUIRE_TIMEOUT_MILLIS),
1931 self.write_ticket.acquire(),
1932 );
1933
1934 match fut.await {
1935 Ok(Ok(ticket)) => ticket,
1936 Ok(Err(_)) => {
1937 error!("Failed to acquire write ticket, may be poisoned.");
1938 return None;
1939 }
1940 Err(_) => {
1941 error!("Failed to acquire write ticket, server is overloaded.");
1942 return None;
1943 }
1944 }
1945 };
1946
1947 let db_ticket = if cfg!(test) {
1951 self.db_tickets
1952 .try_acquire()
1953 .inspect_err(|err| {
1954 error!(?err, "Unable to acquire write db_ticket!");
1955 })
1956 .ok()?
1957 } else {
1958 self.db_tickets
1959 .acquire()
1960 .await
1961 .inspect_err(|err| {
1962 error!(?err, "Unable to acquire write db_ticket!");
1963 })
1964 .ok()?
1965 };
1966
1967 Some((write_ticket, db_ticket))
1968 }
1969
1970 pub async fn write(
1971 &self,
1972 curtime: Duration,
1973 ) -> Result<QueryServerWriteTransaction<'_>, OperationError> {
1974 let (write_ticket, db_ticket) = self
1975 .write_acquire_ticket()
1976 .await
1977 .ok_or(OperationError::DatabaseLockAcquisitionTimeout)?;
1978
1979 let be_txn = self.be.write()?;
1984
1985 let schema_write = self.schema.write();
1986 let d_info = self.d_info.write();
1987 let system_config = self.system_config.write();
1988 let phase = self.phase.write();
1989
1990 let mut cid = self.cid_max.write();
1991 *cid = Cid::new_lamport(cid.s_uuid, curtime, &cid.ts);
1993
1994 let trim_cid = cid.sub_secs(CHANGELOG_MAX_AGE)?;
1995
1996 Ok(QueryServerWriteTransaction {
1997 committed: false,
2004 phase,
2005 d_info,
2006 system_config,
2007 curtime,
2008 cid,
2009 trim_cid,
2010 be_txn,
2011 schema: schema_write,
2012 accesscontrols: self.accesscontrols.write(),
2013 changed_flags: ChangeFlag::empty(),
2014 changed_uuid: HashSet::new(),
2015 _db_ticket: db_ticket,
2016 _write_ticket: write_ticket,
2017 resolve_filter_cache: self.resolve_filter_cache.read(),
2018 resolve_filter_cache_clear: false,
2019 resolve_filter_cache_write: self.resolve_filter_cache.write(),
2020 dyngroup_cache: self.dyngroup_cache.write(),
2021 key_providers: self.key_providers.write(),
2022 })
2023 }
2024
2025 #[cfg(any(test, debug_assertions))]
2026 pub async fn clear_cache(&self) -> Result<(), OperationError> {
2027 let ct = duration_from_epoch_now();
2028 let mut w_txn = self.write(ct).await?;
2029 w_txn.clear_cache()?;
2030 w_txn.commit()
2031 }
2032
2033 pub async fn verify(&self) -> Vec<Result<(), ConsistencyError>> {
2034 let current_time = duration_from_epoch_now();
2035 if self
2040 .write(current_time)
2041 .await
2042 .and_then(|mut txn| {
2043 txn.force_schema_reload();
2044 txn.commit()
2045 })
2046 .is_err()
2047 {
2048 return vec![Err(ConsistencyError::Unknown)];
2049 };
2050
2051 match self.read().await {
2052 Ok(mut r_txn) => r_txn.verify(),
2053 Err(_) => vec![Err(ConsistencyError::Unknown)],
2054 }
2055 }
2056}
2057
2058impl<'a> QueryServerWriteTransaction<'a> {
2059 pub(crate) fn get_server_uuid(&self) -> Uuid {
2060 self.cid.s_uuid
2062 }
2063
2064 pub(crate) fn reset_server_uuid(&mut self) -> Result<(), OperationError> {
2065 let s_uuid = self.be_txn.reset_db_s_uuid().map_err(|err| {
2066 error!(?err, "Failed to reset server replication uuid");
2067 err
2068 })?;
2069
2070 debug!(?s_uuid, "reset server replication uuid");
2071
2072 self.cid.s_uuid = s_uuid;
2073
2074 Ok(())
2075 }
2076
2077 pub(crate) fn get_curtime(&self) -> Duration {
2078 self.curtime
2079 }
2080
2081 pub(crate) fn get_curtime_odt(&self) -> OffsetDateTime {
2082 OffsetDateTime::UNIX_EPOCH + self.curtime
2083 }
2084
2085 pub(crate) fn get_cid(&self) -> &Cid {
2086 &self.cid
2087 }
2088
2089 pub(crate) fn get_key_providers_mut(&mut self) -> &mut KeyProvidersWriteTransaction<'a> {
2090 &mut self.key_providers
2091 }
2092
2093 pub(crate) fn get_dyngroup_cache(&mut self) -> &mut DynGroupCache {
2094 &mut self.dyngroup_cache
2095 }
2096
2097 pub fn domain_raise(&mut self, level: u32) -> Result<(), OperationError> {
2098 if level > DOMAIN_MAX_LEVEL {
2099 return Err(OperationError::MG0002RaiseDomainLevelExceedsMaximum);
2100 }
2101
2102 let modl = ModifyList::new_purge_and_set(Attribute::Version, Value::Uint32(level));
2103 let udi = PVUUID_DOMAIN_INFO.clone();
2104 let filt = filter_all!(f_eq(Attribute::Uuid, udi));
2105 self.internal_modify(&filt, &modl)
2106 }
2107
2108 pub fn domain_remigrate(&mut self, level: u32) -> Result<(), OperationError> {
2109 let mut_d_info = self.d_info.get_mut();
2110
2111 if level > mut_d_info.d_vers {
2112 return Ok(());
2114 } else if level < DOMAIN_MIN_REMIGRATION_LEVEL {
2115 return Err(OperationError::MG0001InvalidReMigrationLevel);
2116 };
2117
2118 info!(
2119 "Prepare to re-migrate from {} -> {}",
2120 level, mut_d_info.d_vers
2121 );
2122 mut_d_info.d_vers = level;
2123 self.changed_flags.insert(ChangeFlag::DOMAIN);
2124
2125 Ok(())
2126 }
2127
2128 #[instrument(level = "debug", skip_all)]
2129 pub(crate) fn reload_schema(&mut self) -> Result<(), OperationError> {
2130 let filt = filter!(f_eq(Attribute::Class, EntryClass::AttributeType.into()));
2133 let res = self.internal_search(filt).map_err(|e| {
2134 admin_error!("reload schema internal search failed {:?}", e);
2135 e
2136 })?;
2137 let attributetypes: Result<Vec<_>, _> =
2139 res.iter().map(|e| SchemaAttribute::try_from(e)).collect();
2140
2141 let attributetypes = attributetypes.map_err(|e| {
2142 admin_error!("reload schema attributetypes {:?}", e);
2143 e
2144 })?;
2145
2146 self.schema.update_attributes(attributetypes).map_err(|e| {
2147 admin_error!("reload schema update attributetypes {:?}", e);
2148 e
2149 })?;
2150
2151 let filt = filter!(f_eq(Attribute::Class, EntryClass::ClassType.into()));
2153 let res = self.internal_search(filt).map_err(|e| {
2154 admin_error!("reload schema internal search failed {:?}", e);
2155 e
2156 })?;
2157 let classtypes: Result<Vec<_>, _> = res.iter().map(|e| SchemaClass::try_from(e)).collect();
2159 let classtypes = classtypes.map_err(|e| {
2160 admin_error!("reload schema classtypes {:?}", e);
2161 e
2162 })?;
2163
2164 self.schema.update_classes(classtypes).map_err(|e| {
2165 admin_error!("reload schema update classtypes {:?}", e);
2166 e
2167 })?;
2168
2169 let valid_r = self.schema.validate();
2171
2172 if valid_r.is_empty() {
2174 trace!("Reloading idxmeta ...");
2176 self.be_txn
2177 .update_idxmeta(self.schema.reload_idxmeta())
2178 .map_err(|e| {
2179 admin_error!("reload schema update idxmeta {:?}", e);
2180 e
2181 })
2182 } else {
2183 admin_error!("Schema reload failed -> {:?}", valid_r);
2185 Err(OperationError::ConsistencyError(
2186 valid_r.into_iter().filter_map(|v| v.err()).collect(),
2187 ))
2188 }?;
2189
2190 self.resolve_filter_cache_clear = true;
2193
2194 DynGroup::reload(self)?;
2197
2198 Ok(())
2199 }
2200
2201 #[instrument(level = "debug", skip_all)]
2202 fn reload_accesscontrols(&mut self) -> Result<(), OperationError> {
2203 trace!("ACP reload started ...");
2211
2212 let filt = filter!(f_eq(Attribute::Class, EntryClass::SyncAccount.into()));
2215
2216 let res = self.internal_search(filt).map_err(|e| {
2217 admin_error!(
2218 err = ?e,
2219 "reload accesscontrols internal search failed",
2220 );
2221 e
2222 })?;
2223
2224 let sync_agreement_map: HashMap<Uuid, BTreeSet<Attribute>> = res
2225 .iter()
2226 .filter_map(|e| {
2227 e.get_ava_as_iutf8(Attribute::SyncYieldAuthority)
2228 .map(|set| {
2229 let set: BTreeSet<_> =
2230 set.iter().map(|s| Attribute::from(s.as_str())).collect();
2231 (e.get_uuid(), set)
2232 })
2233 })
2234 .collect();
2235
2236 self.accesscontrols
2237 .update_sync_agreements(sync_agreement_map);
2238
2239 let filt = filter!(f_and!([
2241 f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2242 f_eq(Attribute::Class, EntryClass::AccessControlSearch.into()),
2243 f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2244 ]));
2245
2246 let res = self.internal_search(filt).map_err(|e| {
2247 admin_error!(
2248 err = ?e,
2249 "reload accesscontrols internal search failed",
2250 );
2251 e
2252 })?;
2253 let search_acps: Result<Vec<_>, _> = res
2254 .iter()
2255 .map(|e| AccessControlSearch::try_from(self, e))
2256 .collect();
2257
2258 let search_acps = search_acps.map_err(|e| {
2259 admin_error!(err = ?e, "Unable to parse search accesscontrols");
2260 e
2261 })?;
2262
2263 self.accesscontrols
2264 .update_search(search_acps)
2265 .map_err(|e| {
2266 admin_error!(err = ?e, "Failed to update search accesscontrols");
2267 e
2268 })?;
2269 let filt = filter!(f_and!([
2271 f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2272 f_eq(Attribute::Class, EntryClass::AccessControlCreate.into()),
2273 f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2274 ]));
2275
2276 let res = self.internal_search(filt).map_err(|e| {
2277 admin_error!(
2278 err = ?e,
2279 "reload accesscontrols internal search failed"
2280 );
2281 e
2282 })?;
2283 let create_acps: Result<Vec<_>, _> = res
2284 .iter()
2285 .map(|e| AccessControlCreate::try_from(self, e))
2286 .collect();
2287
2288 let create_acps = create_acps.map_err(|e| {
2289 admin_error!(err = ?e, "Unable to parse create accesscontrols");
2290 e
2291 })?;
2292
2293 self.accesscontrols
2294 .update_create(create_acps)
2295 .map_err(|e| {
2296 admin_error!(err = ?e, "Failed to update create accesscontrols");
2297 e
2298 })?;
2299 let filt = filter!(f_and!([
2301 f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2302 f_eq(Attribute::Class, EntryClass::AccessControlModify.into()),
2303 f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2304 ]));
2305
2306 let res = self.internal_search(filt).map_err(|e| {
2307 admin_error!("reload accesscontrols internal search failed {:?}", e);
2308 e
2309 })?;
2310 let modify_acps: Result<Vec<_>, _> = res
2311 .iter()
2312 .map(|e| AccessControlModify::try_from(self, e))
2313 .collect();
2314
2315 let modify_acps = modify_acps.map_err(|e| {
2316 admin_error!("Unable to parse modify accesscontrols {:?}", e);
2317 e
2318 })?;
2319
2320 self.accesscontrols
2321 .update_modify(modify_acps)
2322 .map_err(|e| {
2323 admin_error!("Failed to update modify accesscontrols {:?}", e);
2324 e
2325 })?;
2326 let filt = filter!(f_and!([
2328 f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2329 f_eq(Attribute::Class, EntryClass::AccessControlDelete.into()),
2330 f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2331 ]));
2332
2333 let res = self.internal_search(filt).map_err(|e| {
2334 admin_error!("reload accesscontrols internal search failed {:?}", e);
2335 e
2336 })?;
2337 let delete_acps: Result<Vec<_>, _> = res
2338 .iter()
2339 .map(|e| AccessControlDelete::try_from(self, e))
2340 .collect();
2341
2342 let delete_acps = delete_acps.map_err(|e| {
2343 admin_error!("Unable to parse delete accesscontrols {:?}", e);
2344 e
2345 })?;
2346
2347 self.accesscontrols.update_delete(delete_acps).map_err(|e| {
2348 admin_error!("Failed to update delete accesscontrols {:?}", e);
2349 e
2350 })
2351 }
2352
2353 #[instrument(level = "debug", skip_all)]
2354 pub(crate) fn reload_key_material(&mut self) -> Result<(), OperationError> {
2355 let filt = filter!(f_eq(Attribute::Class, EntryClass::KeyProvider.into()));
2356
2357 let res = self.internal_search(filt).map_err(|e| {
2358 admin_error!(
2359 err = ?e,
2360 "reload key providers internal search failed",
2361 );
2362 e
2363 })?;
2364
2365 let providers = res
2368 .iter()
2369 .map(|e| KeyProvider::try_from(e).and_then(|kp| kp.test().map(|()| kp)))
2370 .collect::<Result<Vec<_>, _>>()?;
2371
2372 self.key_providers.update_providers(providers)?;
2373
2374 let filt = filter!(f_eq(Attribute::Class, EntryClass::KeyObject.into()));
2375
2376 let res = self.internal_search(filt).map_err(|e| {
2377 admin_error!(
2378 err = ?e,
2379 "reload key objects internal search failed",
2380 );
2381 e
2382 })?;
2383
2384 res.iter()
2385 .try_for_each(|entry| self.key_providers.load_key_object(entry.as_ref()))
2386 }
2387
2388 #[instrument(level = "debug", skip_all)]
2389 pub(crate) fn reload_system_config(&mut self) -> Result<(), OperationError> {
2390 let denied_names = self.get_sc_denied_names()?;
2391 let pw_badlist = self.get_sc_password_badlist()?;
2392
2393 let mut_system_config = self.system_config.get_mut();
2394 mut_system_config.denied_names = denied_names;
2395 mut_system_config.pw_badlist = pw_badlist;
2396 Ok(())
2397 }
2398
2399 #[instrument(level = "debug", skip_all)]
2401 pub(crate) fn reload_domain_info_version(&mut self) -> Result<(), OperationError> {
2402 let domain_info = self.internal_search_uuid(UUID_DOMAIN_INFO).map_err(|err| {
2403 error!(?err, "Error getting domain info");
2404 err
2405 })?;
2406
2407 let domain_info_version = domain_info
2408 .get_ava_single_uint32(Attribute::Version)
2409 .ok_or_else(|| {
2410 error!("domain info missing attribute version");
2411 OperationError::InvalidEntryState
2412 })?;
2413
2414 let domain_info_patch_level = domain_info
2415 .get_ava_single_uint32(Attribute::PatchLevel)
2416 .unwrap_or(0);
2417
2418 let current_devel_flag = option_env!("KANIDM_PRE_RELEASE").is_some();
2422 let domain_info_devel_taint = current_devel_flag
2423 || domain_info
2424 .get_ava_single_bool(Attribute::DomainDevelopmentTaint)
2425 .unwrap_or_default();
2426
2427 let domain_allow_easter_eggs = domain_info
2428 .get_ava_single_bool(Attribute::DomainAllowEasterEggs)
2429 .unwrap_or(option_env!("KANIDM_PRE_RELEASE").is_some());
2431
2432 let mut_d_info = self.d_info.get_mut();
2436 let previous_version = mut_d_info.d_vers;
2437 let previous_patch_level = mut_d_info.d_patch_level;
2438 mut_d_info.d_vers = domain_info_version;
2439 mut_d_info.d_patch_level = domain_info_patch_level;
2440 mut_d_info.d_devel_taint = domain_info_devel_taint;
2441 mut_d_info.d_allow_easter_eggs = domain_allow_easter_eggs;
2442
2443 if (previous_version == domain_info_version
2446 && previous_patch_level == domain_info_patch_level)
2447 || *self.phase < ServerPhase::DomainInfoReady
2448 {
2449 return Ok(());
2450 }
2451
2452 debug!(domain_previous_version = ?previous_version, domain_target_version = ?domain_info_version);
2453 debug!(domain_previous_patch_level = ?previous_patch_level, domain_target_patch_level = ?domain_info_patch_level);
2454
2455 if previous_version == DOMAIN_LEVEL_0 {
2459 debug!(
2460 "Server was just brought up, skipping migrations as we are already at target level"
2461 );
2462 return Ok(());
2463 }
2464
2465 if previous_version < DOMAIN_MIN_REMIGRATION_LEVEL {
2466 error!("UNABLE TO PROCEED. You are attempting a Skip update which is NOT SUPPORTED. You must upgrade one-version of Kanidm at a time.");
2467 error!("For more see: https://kanidm.github.io/kanidm/stable/support.html#upgrade-policy and https://kanidm.github.io/kanidm/stable/server_updates.html");
2468 error!(domain_previous_version = ?previous_version, domain_target_version = ?domain_info_version);
2469 error!(domain_previous_patch_level = ?previous_patch_level, domain_target_patch_level = ?domain_info_patch_level);
2470 return Err(OperationError::MG0008SkipUpgradeAttempted);
2471 }
2472
2473 if previous_version <= DOMAIN_LEVEL_8 && domain_info_version >= DOMAIN_LEVEL_9 {
2474 self.migrate_domain_8_to_9()?;
2476 }
2477
2478 if previous_patch_level < PATCH_LEVEL_2
2479 && domain_info_patch_level >= PATCH_LEVEL_2
2480 && domain_info_version == DOMAIN_LEVEL_9
2481 {
2482 self.migrate_domain_patch_level_2()?;
2483 }
2484
2485 if previous_version <= DOMAIN_LEVEL_9 && domain_info_version >= DOMAIN_LEVEL_10 {
2486 self.migrate_domain_9_to_10()?;
2488 }
2489
2490 if previous_version <= DOMAIN_LEVEL_10 && domain_info_version >= DOMAIN_LEVEL_11 {
2491 self.migrate_domain_10_to_11()?;
2493 }
2494
2495 if previous_version <= DOMAIN_LEVEL_11 && domain_info_version >= DOMAIN_LEVEL_12 {
2496 self.migrate_domain_11_to_12()?;
2498 }
2499
2500 if previous_version <= DOMAIN_LEVEL_12 && domain_info_version >= DOMAIN_LEVEL_13 {
2501 self.migrate_domain_12_to_13()?;
2503 }
2504
2505 debug_assert!(domain_info_version <= DOMAIN_MAX_LEVEL);
2509
2510 Ok(())
2511 }
2512
2513 #[instrument(level = "debug", skip_all)]
2515 pub(crate) fn reload_domain_info(&mut self) -> Result<(), OperationError> {
2516 let domain_entry = self.get_db_domain()?;
2517
2518 let domain_name = domain_entry
2519 .get_ava_single_iname(Attribute::DomainName)
2520 .map(str::to_string)
2521 .ok_or(OperationError::InvalidEntryState)?;
2522
2523 let display_name = domain_entry
2524 .get_ava_single_utf8(Attribute::DomainDisplayName)
2525 .map(str::to_string)
2526 .unwrap_or_else(|| format!("Kanidm {domain_name}"));
2527
2528 let domain_ldap_allow_unix_pw_bind = domain_entry
2529 .get_ava_single_bool(Attribute::LdapAllowUnixPwBind)
2530 .unwrap_or(true);
2531
2532 let domain_image = domain_entry.get_ava_single_image(Attribute::Image);
2533
2534 let domain_uuid = self.be_txn.get_db_d_uuid()?;
2535
2536 let mut_d_info = self.d_info.get_mut();
2537 mut_d_info.d_ldap_allow_unix_pw_bind = domain_ldap_allow_unix_pw_bind;
2538 if mut_d_info.d_uuid != domain_uuid {
2539 admin_warn!(
2540 "Using domain uuid from the database {} - was {} in memory",
2541 domain_name,
2542 mut_d_info.d_name,
2543 );
2544 mut_d_info.d_uuid = domain_uuid;
2545 }
2546 if mut_d_info.d_name != domain_name {
2547 admin_warn!(
2548 "Using domain name from the database {} - was {} in memory",
2549 domain_name,
2550 mut_d_info.d_name,
2551 );
2552 admin_warn!(
2553 "If you think this is an error, see https://kanidm.github.io/kanidm/master/domain_rename.html"
2554 );
2555 mut_d_info.d_name = domain_name;
2556 }
2557 mut_d_info.d_display = display_name;
2558 mut_d_info.d_image = domain_image;
2559 Ok(())
2560 }
2561
2562 pub fn set_domain_display_name(&mut self, new_domain_name: &str) -> Result<(), OperationError> {
2566 let modl = ModifyList::new_purge_and_set(
2567 Attribute::DomainDisplayName,
2568 Value::new_utf8(new_domain_name.to_string()),
2569 );
2570 let udi = PVUUID_DOMAIN_INFO.clone();
2571 let filt = filter_all!(f_eq(Attribute::Uuid, udi));
2572 self.internal_modify(&filt, &modl)
2573 }
2574
2575 pub fn danger_domain_rename(&mut self, new_domain_name: &str) -> Result<(), OperationError> {
2588 let modl =
2589 ModifyList::new_purge_and_set(Attribute::DomainName, Value::new_iname(new_domain_name));
2590 let udi = PVUUID_DOMAIN_INFO.clone();
2591 let filt = filter_all!(f_eq(Attribute::Uuid, udi));
2592 self.internal_modify(&filt, &modl)
2593 }
2594
2595 pub fn reindex(&mut self, immediate: bool) -> Result<(), OperationError> {
2596 self.be_txn.reindex(immediate)
2600 }
2601
2602 fn force_schema_reload(&mut self) {
2603 self.changed_flags.insert(ChangeFlag::SCHEMA);
2604 }
2605
2606 fn force_domain_reload(&mut self) {
2607 self.changed_flags.insert(ChangeFlag::DOMAIN);
2608 }
2609
2610 pub(crate) fn upgrade_reindex(&mut self, v: i64) -> Result<(), OperationError> {
2611 self.be_txn.upgrade_reindex(v)
2612 }
2613
2614 #[inline]
2615 pub(crate) fn get_changed_app(&self) -> bool {
2616 self.changed_flags.contains(ChangeFlag::APPLICATION)
2617 }
2618
2619 #[inline]
2620 pub(crate) fn get_changed_oauth2(&self) -> bool {
2621 self.changed_flags.contains(ChangeFlag::OAUTH2)
2622 }
2623
2624 #[inline]
2625 pub(crate) fn clear_changed_oauth2(&mut self) {
2626 self.changed_flags.remove(ChangeFlag::OAUTH2)
2627 }
2628
2629 pub(crate) fn set_phase_bootstrap(&mut self) {
2632 *self.phase = ServerPhase::Bootstrap;
2633 }
2634
2635 pub(crate) fn set_phase(&mut self, phase: ServerPhase) {
2637 if phase > *self.phase {
2639 *self.phase = phase
2640 }
2641 }
2642
2643 pub(crate) fn get_phase(&mut self) -> ServerPhase {
2644 *self.phase
2645 }
2646
2647 pub(crate) fn reload(&mut self) -> Result<(), OperationError> {
2648 if self.changed_flags.contains(ChangeFlag::DOMAIN) {
2651 self.reload_domain_info_version()?;
2652 }
2653
2654 if self.changed_flags.contains(ChangeFlag::SCHEMA) {
2659 self.reload_schema()?;
2660
2661 if *self.phase >= ServerPhase::Running {
2665 self.reindex(false)?;
2666 self.reload_schema()?;
2667 }
2668 }
2669
2670 if self
2673 .changed_flags
2674 .intersects(ChangeFlag::SCHEMA | ChangeFlag::KEY_MATERIAL)
2675 {
2676 self.reload_key_material()?;
2677 }
2678
2679 if self
2687 .changed_flags
2688 .intersects(ChangeFlag::SCHEMA | ChangeFlag::ACP | ChangeFlag::SYNC_AGREEMENT)
2689 {
2690 self.reload_accesscontrols()?;
2691 } else {
2692 }
2697
2698 if self.changed_flags.contains(ChangeFlag::SYSTEM_CONFIG) {
2699 self.reload_system_config()?;
2700 }
2701
2702 if self.changed_flags.contains(ChangeFlag::DOMAIN) {
2703 self.reload_domain_info()?;
2704 }
2705
2706 self.changed_flags.remove(
2708 ChangeFlag::DOMAIN
2709 | ChangeFlag::SCHEMA
2710 | ChangeFlag::SYSTEM_CONFIG
2711 | ChangeFlag::ACP
2712 | ChangeFlag::SYNC_AGREEMENT
2713 | ChangeFlag::KEY_MATERIAL,
2714 );
2715
2716 Ok(())
2717 }
2718
2719 #[cfg(any(test, debug_assertions))]
2720 #[instrument(level = "debug", skip_all)]
2721 pub fn clear_cache(&mut self) -> Result<(), OperationError> {
2722 self.be_txn.clear_cache()
2723 }
2724
2725 #[instrument(level = "debug", name="qswt_commit" skip_all)]
2726 pub fn commit(mut self) -> Result<(), OperationError> {
2727 self.reload()?;
2728
2729 let QueryServerWriteTransaction {
2731 committed,
2732 phase,
2733 d_info,
2734 system_config,
2735 mut be_txn,
2736 schema,
2737 accesscontrols,
2738 cid,
2739 dyngroup_cache,
2740 key_providers,
2741 _db_ticket,
2743 _write_ticket,
2744 curtime: _,
2746 trim_cid: _,
2747 changed_flags,
2748 changed_uuid: _,
2749 resolve_filter_cache: _,
2750 resolve_filter_cache_clear,
2751 mut resolve_filter_cache_write,
2752 } = self;
2753 debug_assert!(!committed);
2754
2755 trace!(
2757 changed = ?changed_flags.iter_names().collect::<Vec<_>>(),
2758 );
2759
2760 be_txn.set_db_ts_max(cid.ts)?;
2763 cid.commit();
2764
2765 if resolve_filter_cache_clear {
2767 resolve_filter_cache_write.clear();
2768 }
2769 resolve_filter_cache_write.commit();
2770
2771 schema
2775 .commit()
2776 .map(|_| d_info.commit())
2777 .map(|_| system_config.commit())
2778 .map(|_| phase.commit())
2779 .map(|_| dyngroup_cache.commit())
2780 .and_then(|_| key_providers.commit())
2781 .and_then(|_| accesscontrols.commit())
2782 .and_then(|_| be_txn.commit())
2783 }
2784
2785 pub(crate) fn get_txn_cid(&self) -> &Cid {
2786 &self.cid
2787 }
2788}
2789
2790#[cfg(test)]
2791mod tests {
2792 use crate::prelude::*;
2793 use kanidm_proto::scim_v1::{
2794 server::{ScimListResponse, ScimReference},
2795 JsonValue, ScimEntryGetQuery, ScimFilter,
2796 };
2797 use std::num::NonZeroU64;
2798
2799 #[qs_test]
2800 async fn test_name_to_uuid(server: &QueryServer) {
2801 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2802
2803 let t_uuid = Uuid::new_v4();
2804 assert!(server_txn
2805 .internal_create(vec![entry_init!(
2806 (Attribute::Class, EntryClass::Object.to_value()),
2807 (Attribute::Class, EntryClass::Account.to_value()),
2808 (Attribute::Class, EntryClass::Person.to_value()),
2809 (Attribute::Name, Value::new_iname("testperson1")),
2810 (Attribute::Uuid, Value::Uuid(t_uuid)),
2811 (Attribute::Description, Value::new_utf8s("testperson1")),
2812 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2813 ),])
2814 .is_ok());
2815
2816 let r1 = server_txn.name_to_uuid("testpers");
2818 assert!(r1.is_err());
2819 let r2 = server_txn.name_to_uuid("tEsTpErS");
2821 assert!(r2.is_err());
2822 let r3 = server_txn.name_to_uuid("testperson1");
2824 assert_eq!(r3, Ok(t_uuid));
2825 let r4 = server_txn.name_to_uuid("tEsTpErSoN1");
2827 assert_eq!(r4, Ok(t_uuid));
2828 let r5 = server_txn.name_to_uuid("name=testperson1");
2830 assert_eq!(r5, Ok(t_uuid));
2831 let r6 = server_txn.name_to_uuid("name=testperson1,o=example");
2833 assert_eq!(r6, Ok(t_uuid));
2834 }
2835
2836 #[qs_test]
2837 async fn test_external_id_to_uuid(server: &QueryServer) {
2838 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2839
2840 let t_uuid = Uuid::new_v4();
2841 assert!(server_txn
2842 .internal_create(vec![entry_init!(
2843 (Attribute::Class, EntryClass::Object.to_value()),
2844 (Attribute::Class, EntryClass::ExtensibleObject.to_value()),
2845 (Attribute::Uuid, Value::Uuid(t_uuid)),
2846 (
2847 Attribute::SyncExternalId,
2848 Value::new_iutf8("uid=testperson")
2849 )
2850 ),])
2851 .is_ok());
2852
2853 let r1 = server_txn.sync_external_id_to_uuid("tobias");
2855 assert_eq!(r1, Ok(None));
2856 let r2 = server_txn.sync_external_id_to_uuid("tObIAs");
2858 assert_eq!(r2, Ok(None));
2859 let r3 = server_txn.sync_external_id_to_uuid("uid=testperson");
2861 assert_eq!(r3, Ok(Some(t_uuid)));
2862 let r4 = server_txn.sync_external_id_to_uuid("uId=TeStPeRsOn");
2864 assert_eq!(r4, Ok(Some(t_uuid)));
2865 }
2866
2867 #[qs_test]
2868 async fn test_uuid_to_spn(server: &QueryServer) {
2869 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2870
2871 let e1 = entry_init!(
2872 (Attribute::Class, EntryClass::Object.to_value()),
2873 (Attribute::Class, EntryClass::Person.to_value()),
2874 (Attribute::Class, EntryClass::Account.to_value()),
2875 (Attribute::Name, Value::new_iname("testperson1")),
2876 (
2877 Attribute::Uuid,
2878 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2879 ),
2880 (Attribute::Description, Value::new_utf8s("testperson1")),
2881 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2882 );
2883 let ce = CreateEvent::new_internal(vec![e1]);
2884 let cr = server_txn.create(&ce);
2885 assert!(cr.is_ok());
2886
2887 let r1 = server_txn.uuid_to_spn(uuid!("bae3f507-e6c3-44ba-ad01-f8ff1083534a"));
2889 assert_eq!(r1, Ok(None));
2891 let r3 = server_txn.uuid_to_spn(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"));
2893 println!("{r3:?}");
2894 assert_eq!(
2895 r3.unwrap().unwrap(),
2896 Value::new_spn_str("testperson1", "example.com")
2897 );
2898 let r4 = server_txn.uuid_to_spn(uuid!("CC8E95B4-C24F-4D68-BA54-8BED76F63930"));
2900 assert_eq!(
2901 r4.unwrap().unwrap(),
2902 Value::new_spn_str("testperson1", "example.com")
2903 );
2904 }
2905
2906 #[qs_test]
2907 async fn test_uuid_to_rdn(server: &QueryServer) {
2908 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2909
2910 let e1 = entry_init!(
2911 (Attribute::Class, EntryClass::Object.to_value()),
2912 (Attribute::Class, EntryClass::Person.to_value()),
2913 (Attribute::Class, EntryClass::Account.to_value()),
2914 (Attribute::Name, Value::new_iname("testperson1")),
2915 (
2916 Attribute::Uuid,
2917 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2918 ),
2919 (Attribute::Description, Value::new_utf8s("testperson")),
2920 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2921 );
2922 let ce = CreateEvent::new_internal(vec![e1]);
2923 let cr = server_txn.create(&ce);
2924 assert!(cr.is_ok());
2925
2926 let r1 = server_txn.uuid_to_rdn(uuid!("bae3f507-e6c3-44ba-ad01-f8ff1083534a"));
2928 assert_eq!(r1.unwrap(), "uuid=bae3f507-e6c3-44ba-ad01-f8ff1083534a");
2930 let r3 = server_txn.uuid_to_rdn(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"));
2932 println!("{r3:?}");
2933 assert_eq!(r3.unwrap(), "spn=testperson1@example.com");
2934 let r4 = server_txn.uuid_to_rdn(uuid!("CC8E95B4-C24F-4D68-BA54-8BED76F63930"));
2936 assert_eq!(r4.unwrap(), "spn=testperson1@example.com");
2937 }
2938
2939 #[qs_test]
2940 async fn test_clone_value(server: &QueryServer) {
2941 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2942 let e1 = entry_init!(
2943 (Attribute::Class, EntryClass::Object.to_value()),
2944 (Attribute::Class, EntryClass::Account.to_value()),
2945 (Attribute::Class, EntryClass::Person.to_value()),
2946 (Attribute::Name, Value::new_iname("testperson1")),
2947 (
2948 Attribute::Uuid,
2949 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2950 ),
2951 (Attribute::Description, Value::new_utf8s("testperson1")),
2952 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2953 );
2954 let ce = CreateEvent::new_internal(vec![e1]);
2955 let cr = server_txn.create(&ce);
2956 assert!(cr.is_ok());
2957
2958 let r1 = server_txn.clone_value(&Attribute::from("tausau"), "naoeutnhaou");
2960
2961 assert!(r1.is_err());
2962
2963 let r2 = server_txn.clone_value(&Attribute::Custom("NaMe".into()), "NaMe");
2966
2967 assert!(r2.is_err());
2968
2969 let r3 = server_txn.clone_value(&Attribute::from("member"), "testperson1");
2971
2972 assert_eq!(
2973 r3,
2974 Ok(Value::Refer(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")))
2975 );
2976
2977 let r4 = server_txn.clone_value(
2979 &Attribute::from("member"),
2980 "cc8e95b4-c24f-4d68-ba54-8bed76f63930",
2981 );
2982
2983 debug!("{:?}", r4);
2984 assert_eq!(
2985 r4,
2986 Ok(Value::Refer(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")))
2987 );
2988 }
2989
2990 #[qs_test]
2991 async fn test_dynamic_schema_class(server: &QueryServer) {
2992 let e1 = entry_init!(
2993 (Attribute::Class, EntryClass::Object.to_value()),
2994 (Attribute::Class, EntryClass::TestClass.to_value()),
2995 (Attribute::Name, Value::new_iname("testobj1")),
2996 (
2997 Attribute::Uuid,
2998 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2999 )
3000 );
3001
3002 let e_cd = entry_init!(
3004 (Attribute::Class, EntryClass::Object.to_value()),
3005 (Attribute::Class, EntryClass::ClassType.to_value()),
3006 (Attribute::ClassName, EntryClass::TestClass.to_value()),
3007 (
3008 Attribute::Uuid,
3009 Value::Uuid(uuid!("cfcae205-31c3-484b-8ced-667d1709c5e3"))
3010 ),
3011 (Attribute::Description, Value::new_utf8s("Test Class")),
3012 (Attribute::May, Value::from(Attribute::Name))
3013 );
3014 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3015 let ce_class = CreateEvent::new_internal(vec![e_cd.clone()]);
3017 assert!(server_txn.create(&ce_class).is_ok());
3018 let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
3020 assert!(server_txn.create(&ce_fail).is_err());
3021
3022 server_txn.commit().expect("should not fail");
3024
3025 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3027 let ce_work = CreateEvent::new_internal(vec![e1.clone()]);
3030 assert!(server_txn.create(&ce_work).is_ok());
3031
3032 server_txn.commit().expect("should not fail");
3034
3035 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3037 let de_class = DeleteEvent::new_internal_invalid(filter!(f_eq(
3039 Attribute::ClassName,
3040 EntryClass::TestClass.into()
3041 )));
3042 assert!(server_txn.delete(&de_class).is_ok());
3043 server_txn.commit().expect("should not fail");
3045
3046 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3048 let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
3050 assert!(server_txn.create(&ce_fail).is_err());
3051 let testobj1 = server_txn
3053 .internal_search_uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
3054 .expect("failed");
3055 assert!(testobj1.attribute_equality(Attribute::Class, &EntryClass::TestClass.into()));
3056
3057 server_txn.commit().expect("should not fail");
3059 }
3061
3062 #[qs_test]
3063 async fn test_dynamic_schema_attr(server: &QueryServer) {
3064 let e1 = entry_init!(
3065 (Attribute::Class, EntryClass::Object.to_value()),
3066 (Attribute::Class, EntryClass::ExtensibleObject.to_value()),
3067 (Attribute::Name, Value::new_iname("testobj1")),
3068 (
3069 Attribute::Uuid,
3070 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
3071 ),
3072 (Attribute::TestAttr, Value::new_utf8s("test"))
3073 );
3074
3075 let e_ad = entry_init!(
3077 (Attribute::Class, EntryClass::Object.to_value()),
3078 (Attribute::Class, EntryClass::AttributeType.to_value()),
3079 (
3080 Attribute::Uuid,
3081 Value::Uuid(uuid!("cfcae205-31c3-484b-8ced-667d1709c5e3"))
3082 ),
3083 (Attribute::AttributeName, Value::from(Attribute::TestAttr)),
3084 (Attribute::Description, Value::new_utf8s("Test Attribute")),
3085 (Attribute::MultiValue, Value::new_bool(false)),
3086 (Attribute::Unique, Value::new_bool(false)),
3087 (
3088 Attribute::Syntax,
3089 Value::new_syntaxs("UTF8STRING").expect("syntax")
3090 )
3091 );
3092
3093 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3094 let ce_attr = CreateEvent::new_internal(vec![e_ad.clone()]);
3096 assert!(server_txn.create(&ce_attr).is_ok());
3097 let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
3099 assert!(server_txn.create(&ce_fail).is_err());
3100
3101 server_txn.commit().expect("should not fail");
3103
3104 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3106 let ce_work = CreateEvent::new_internal(vec![e1.clone()]);
3109 assert!(server_txn.create(&ce_work).is_ok());
3110
3111 server_txn.commit().expect("should not fail");
3113
3114 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3116 let de_attr = DeleteEvent::new_internal_invalid(filter!(f_eq(
3118 Attribute::AttributeName,
3119 PartialValue::from(Attribute::TestAttr)
3120 )));
3121 assert!(server_txn.delete(&de_attr).is_ok());
3122 server_txn.commit().expect("should not fail");
3124
3125 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3127 let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
3129 assert!(server_txn.create(&ce_fail).is_err());
3130 let filt = filter!(f_eq(Attribute::TestAttr, PartialValue::new_utf8s("test")));
3132 assert!(server_txn.internal_search(filt).is_err());
3133 let testobj1 = server_txn
3136 .internal_search_uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
3137 .expect("failed");
3138 assert!(testobj1.attribute_equality(Attribute::TestAttr, &PartialValue::new_utf8s("test")));
3139
3140 server_txn.commit().expect("should not fail");
3141 }
3143
3144 #[qs_test]
3145 async fn test_scim_entry_structure(server: &QueryServer) {
3146 let mut read_txn = server.read().await.unwrap();
3147
3148 let entry = read_txn
3150 .internal_search_uuid(UUID_IDM_PEOPLE_SELF_NAME_WRITE)
3151 .unwrap();
3152
3153 let reduced = entry.as_ref().clone().into_reduced();
3155 let scim_entry = reduced.to_scim_kanidm(&mut read_txn).unwrap();
3156
3157 assert_eq!(scim_entry.header.id, UUID_IDM_PEOPLE_SELF_NAME_WRITE);
3159 let name_scim = scim_entry.attrs.get(&Attribute::Name).unwrap();
3160 match name_scim {
3161 ScimValueKanidm::String(name) => {
3162 assert_eq!(name.clone(), "idm_people_self_name_write")
3163 }
3164 _ => {
3165 panic!("expected String, actual {name_scim:?}");
3166 }
3167 }
3168
3169 let entry_managed_by_scim = scim_entry.attrs.get(&Attribute::EntryManagedBy).unwrap();
3171 match entry_managed_by_scim {
3172 ScimValueKanidm::EntryReferences(managed_by) => {
3173 assert_eq!(
3174 managed_by.first().unwrap().clone(),
3175 ScimReference {
3176 uuid: UUID_IDM_ADMINS,
3177 value: "idm_admins@example.com".to_string()
3178 }
3179 )
3180 }
3181 _ => {
3182 panic!("expected EntryReference, actual {entry_managed_by_scim:?}");
3183 }
3184 }
3185
3186 let members_scim = scim_entry.attrs.get(&Attribute::Member).unwrap();
3187 match members_scim {
3188 ScimValueKanidm::EntryReferences(members) => {
3189 assert_eq!(
3190 members.first().unwrap().clone(),
3191 ScimReference {
3192 uuid: UUID_IDM_ALL_PERSONS,
3193 value: "idm_all_persons@example.com".to_string()
3194 }
3195 )
3196 }
3197 _ => {
3198 panic!("expected EntryReferences, actual {members_scim:?}");
3199 }
3200 }
3201 }
3202
3203 #[qs_test]
3204 async fn test_scim_effective_access_query(server: &QueryServer) {
3205 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3206
3207 let group_uuid = Uuid::new_v4();
3208 let e1 = entry_init!(
3209 (Attribute::Class, EntryClass::Object.to_value()),
3210 (Attribute::Class, EntryClass::Group.to_value()),
3211 (Attribute::Name, Value::new_iname("testgroup")),
3212 (Attribute::Uuid, Value::Uuid(group_uuid))
3213 );
3214
3215 assert!(server_txn.internal_create(vec![e1]).is_ok());
3216 assert!(server_txn.commit().is_ok());
3217
3218 let mut server_txn = server.read().await.unwrap();
3221
3222 let idm_admin_entry = server_txn.internal_search_uuid(UUID_IDM_ADMIN).unwrap();
3223 let idm_admin_ident = Identity::from_impersonate_entry_readwrite(idm_admin_entry);
3224
3225 let query = ScimEntryGetQuery {
3226 ext_access_check: true,
3227 ..Default::default()
3228 };
3229
3230 let scim_entry = server_txn
3231 .scim_entry_id_get_ext(group_uuid, EntryClass::Group, query, idm_admin_ident)
3232 .unwrap();
3233
3234 let ext_access_check = scim_entry.ext_access_check.unwrap();
3235
3236 trace!(?ext_access_check);
3237
3238 assert!(ext_access_check.delete);
3239 assert!(ext_access_check.search.check(&Attribute::DirectMemberOf));
3240 assert!(ext_access_check.search.check(&Attribute::MemberOf));
3241 assert!(ext_access_check.search.check(&Attribute::Name));
3242 assert!(ext_access_check.modify_present.check(&Attribute::Name));
3243 assert!(ext_access_check.modify_remove.check(&Attribute::Name));
3244 }
3245
3246 #[qs_test]
3247 async fn test_scim_basic_search_ext_query(server: &QueryServer) {
3248 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3249
3250 let group_uuid = Uuid::new_v4();
3251 let e1 = entry_init!(
3252 (Attribute::Class, EntryClass::Object.to_value()),
3253 (Attribute::Class, EntryClass::Group.to_value()),
3254 (Attribute::Name, Value::new_iname("testgroup")),
3255 (Attribute::Uuid, Value::Uuid(group_uuid))
3256 );
3257
3258 assert!(server_txn.internal_create(vec![e1]).is_ok());
3259 assert!(server_txn.commit().is_ok());
3260
3261 let mut server_txn = server.read().await.unwrap();
3263
3264 let idm_admin_entry = server_txn.internal_search_uuid(UUID_IDM_ADMIN).unwrap();
3265 let idm_admin_ident = Identity::from_impersonate_entry_readwrite(idm_admin_entry);
3266
3267 let filter = ScimFilter::And(
3268 Box::new(ScimFilter::Equal(
3269 Attribute::Class.into(),
3270 EntryClass::Group.into(),
3271 )),
3272 Box::new(ScimFilter::Equal(
3273 Attribute::Uuid.into(),
3274 JsonValue::String(group_uuid.to_string()),
3275 )),
3276 );
3277
3278 let base: ScimListResponse = server_txn
3279 .scim_search_ext(idm_admin_ident, filter, ScimEntryGetQuery::default())
3280 .unwrap();
3281
3282 assert_eq!(base.resources.len(), 1);
3283 assert_eq!(base.total_results, 1);
3284 assert_eq!(base.items_per_page, None);
3286 assert_eq!(base.start_index, None);
3287 assert_eq!(base.resources[0].header.id, group_uuid);
3288 }
3289
3290 #[qs_test]
3291 async fn test_scim_basic_search_ext_query_with_sort(server: &QueryServer) {
3292 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3293
3294 for i in (1..4).rev() {
3295 let e1 = entry_init!(
3296 (Attribute::Class, EntryClass::Object.to_value()),
3297 (Attribute::Class, EntryClass::Group.to_value()),
3298 (
3299 Attribute::Name,
3300 Value::new_iname(format!("testgroup{i}").as_str())
3301 )
3302 );
3303 assert!(server_txn.internal_create(vec![e1]).is_ok());
3304 }
3305
3306 assert!(server_txn.commit().is_ok());
3307
3308 let mut server_txn = server.read().await.unwrap();
3310
3311 let idm_admin_entry = server_txn.internal_search_uuid(UUID_IDM_ADMIN).unwrap();
3312 let idm_admin_ident = Identity::from_impersonate_entry_readwrite(idm_admin_entry);
3313
3314 let filter = ScimFilter::And(
3315 Box::new(ScimFilter::Equal(
3316 Attribute::Class.into(),
3317 EntryClass::Group.into(),
3318 )),
3319 Box::new(ScimFilter::StartsWith(
3320 Attribute::Name.into(),
3321 JsonValue::String("testgroup".into()),
3322 )),
3323 );
3324
3325 let base: ScimListResponse = server_txn
3326 .scim_search_ext(
3327 idm_admin_ident.clone(),
3328 filter.clone(),
3329 ScimEntryGetQuery {
3330 sort_by: Some(Attribute::Name),
3331 ..Default::default()
3332 },
3333 )
3334 .unwrap();
3335
3336 assert_eq!(base.resources.len(), 3);
3337 assert_eq!(base.total_results, 3);
3338 assert_eq!(base.items_per_page, None);
3340 assert_eq!(base.start_index, None);
3341
3342 let Some(ScimValueKanidm::String(testgroup_name_0)) =
3343 base.resources[0].attrs.get(&Attribute::Name)
3344 else {
3345 panic!("Invalid data in attribute.");
3346 };
3347 let Some(ScimValueKanidm::String(testgroup_name_1)) =
3348 base.resources[1].attrs.get(&Attribute::Name)
3349 else {
3350 panic!("Invalid data in attribute.");
3351 };
3352 let Some(ScimValueKanidm::String(testgroup_name_2)) =
3353 base.resources[2].attrs.get(&Attribute::Name)
3354 else {
3355 panic!("Invalid data in attribute.");
3356 };
3357
3358 assert!(testgroup_name_0 < testgroup_name_1);
3359 assert!(testgroup_name_0 < testgroup_name_2);
3360 assert!(testgroup_name_1 < testgroup_name_2);
3361
3362 let base: ScimListResponse = server_txn
3365 .scim_search_ext(
3366 idm_admin_ident.clone(),
3367 filter.clone(),
3368 ScimEntryGetQuery {
3369 count: NonZeroU64::new(1),
3370 ..Default::default()
3371 },
3372 )
3373 .unwrap();
3374
3375 assert_eq!(base.resources.len(), 1);
3376 assert_eq!(base.total_results, 3);
3377 assert_eq!(base.items_per_page, NonZeroU64::new(1));
3379 assert_eq!(base.start_index, NonZeroU64::new(1));
3380
3381 let Some(ScimValueKanidm::String(testgroup_name_0)) =
3382 base.resources[0].attrs.get(&Attribute::Name)
3383 else {
3384 panic!("Invalid data in attribute.");
3385 };
3386 assert_eq!(testgroup_name_0, "testgroup3");
3388
3389 let base: ScimListResponse = server_txn
3392 .scim_search_ext(
3393 idm_admin_ident,
3394 filter.clone(),
3395 ScimEntryGetQuery {
3396 sort_by: Some(Attribute::Name),
3397 count: NonZeroU64::new(2),
3398 start_index: NonZeroU64::new(2),
3399 ..Default::default()
3400 },
3401 )
3402 .unwrap();
3403
3404 assert_eq!(base.resources.len(), 2);
3405 assert_eq!(base.total_results, 3);
3406 assert_eq!(base.items_per_page, NonZeroU64::new(2));
3407 assert_eq!(base.start_index, NonZeroU64::new(2));
3408
3409 let Some(ScimValueKanidm::String(testgroup_name_0)) =
3410 base.resources[0].attrs.get(&Attribute::Name)
3411 else {
3412 panic!("Invalid data in attribute.");
3413 };
3414 let Some(ScimValueKanidm::String(testgroup_name_1)) =
3415 base.resources[1].attrs.get(&Attribute::Name)
3416 else {
3417 panic!("Invalid data in attribute.");
3418 };
3419 assert_eq!(testgroup_name_0, "testgroup2");
3421 assert_eq!(testgroup_name_1, "testgroup3");
3422 }
3423}