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 client::ScimFilter,
38 server::{ScimListResponse, ScimOAuth2ClaimMap, ScimOAuth2ScopeMap, ScimReference},
39 JsonValue, ScimEntryGetQuery,
40};
41use std::collections::BTreeSet;
42use std::str::FromStr;
43use std::sync::Arc;
44use tokio::sync::{Semaphore, SemaphorePermit};
45use tracing::trace;
46
47pub(crate) mod access;
48pub mod batch_modify;
49pub mod create;
50pub mod delete;
51pub mod identity;
52pub(crate) mod keys;
53pub(crate) mod migrations;
54pub mod modify;
55pub(crate) mod recycle;
56pub mod scim;
57
58const RESOLVE_FILTER_CACHE_MAX: usize = 256;
59const RESOLVE_FILTER_CACHE_LOCAL: usize = 8;
60
61#[derive(Debug, Clone, Copy, PartialOrd, PartialEq, Eq)]
62pub(crate) enum ServerPhase {
63 Bootstrap,
64 SchemaReady,
65 DomainInfoReady,
66 Running,
67}
68
69#[derive(Debug, Clone, PartialEq, Eq)]
72pub struct DomainInfo {
73 pub(crate) d_uuid: Uuid,
74 pub(crate) d_name: String,
75 pub(crate) d_display: String,
76 pub(crate) d_vers: DomainVersion,
77 pub(crate) d_patch_level: u32,
78 pub(crate) d_devel_taint: bool,
79 pub(crate) d_ldap_allow_unix_pw_bind: bool,
80 pub(crate) d_allow_easter_eggs: bool,
81 d_image: Option<ImageValue>,
83}
84
85impl DomainInfo {
86 pub fn name(&self) -> &str {
87 self.d_name.as_str()
88 }
89
90 pub fn display_name(&self) -> &str {
91 self.d_display.as_str()
92 }
93
94 pub fn devel_taint(&self) -> bool {
95 self.d_devel_taint
96 }
97
98 pub fn image(&self) -> Option<&ImageValue> {
99 self.d_image.as_ref()
100 }
101
102 pub fn has_custom_image(&self) -> bool {
103 self.d_image.is_some()
104 }
105
106 pub fn allow_easter_eggs(&self) -> bool {
107 self.d_allow_easter_eggs
108 }
109
110 #[cfg(feature = "test")]
111 pub fn new_test() -> CowCell<Self> {
112 concread::cowcell::CowCell::new(Self {
113 d_uuid: Uuid::new_v4(),
114 d_name: "test domain".to_string(),
115 d_display: "Test Domain".to_string(),
116 d_vers: 1,
117 d_patch_level: 0,
118 d_devel_taint: false,
119 d_ldap_allow_unix_pw_bind: false,
120 d_allow_easter_eggs: false,
121 d_image: None,
122 })
123 }
124}
125
126#[derive(Debug, Clone, PartialEq, Eq, Default)]
127pub struct SystemConfig {
128 pub(crate) denied_names: HashSet<String>,
129 pub(crate) pw_badlist: HashSet<String>,
130}
131
132#[derive(Clone)]
133pub struct QueryServer {
134 phase: Arc<CowCell<ServerPhase>>,
135 pub(crate) d_info: Arc<CowCell<DomainInfo>>,
136 system_config: Arc<CowCell<SystemConfig>>,
137 be: Backend,
138 schema: Arc<Schema>,
139 accesscontrols: Arc<AccessControls>,
140 db_tickets: Arc<Semaphore>,
141 read_tickets: Arc<Semaphore>,
142 write_ticket: Arc<Semaphore>,
143 resolve_filter_cache: Arc<ResolveFilterCache>,
144 dyngroup_cache: Arc<CowCell<DynGroupCache>>,
145 cid_max: Arc<CowCell<Cid>>,
146 key_providers: Arc<KeyProviders>,
147}
148
149pub struct QueryServerReadTransaction<'a> {
150 be_txn: BackendReadTransaction<'a>,
151 pub(crate) d_info: CowCellReadTxn<DomainInfo>,
154 system_config: CowCellReadTxn<SystemConfig>,
155 schema: SchemaReadTransaction,
156 accesscontrols: AccessControlsReadTransaction<'a>,
157 key_providers: KeyProvidersReadTransaction,
158 _db_ticket: SemaphorePermit<'a>,
159 _read_ticket: SemaphorePermit<'a>,
160 resolve_filter_cache: ResolveFilterCacheReadTxn<'a>,
161 trim_cid: Cid,
164}
165
166unsafe impl Sync for QueryServerReadTransaction<'_> {}
167
168unsafe impl Send for QueryServerReadTransaction<'_> {}
169
170bitflags::bitflags! {
171 #[derive(Copy, Clone, Debug)]
172 pub struct ChangeFlag: u32 {
173 const SCHEMA = 0b0000_0001;
174 const ACP = 0b0000_0010;
175 const OAUTH2 = 0b0000_0100;
176 const DOMAIN = 0b0000_1000;
177 const SYSTEM_CONFIG = 0b0001_0000;
178 const SYNC_AGREEMENT = 0b0010_0000;
179 const KEY_MATERIAL = 0b0100_0000;
180 const APPLICATION = 0b1000_0000;
181 }
182}
183
184pub struct QueryServerWriteTransaction<'a> {
185 committed: bool,
186 phase: CowCellWriteTxn<'a, ServerPhase>,
187 d_info: CowCellWriteTxn<'a, DomainInfo>,
188 system_config: CowCellWriteTxn<'a, SystemConfig>,
189 curtime: Duration,
190 cid: CowCellWriteTxn<'a, Cid>,
191 trim_cid: Cid,
192 pub(crate) be_txn: BackendWriteTransaction<'a>,
193 pub(crate) schema: SchemaWriteTransaction<'a>,
194 accesscontrols: AccessControlsWriteTransaction<'a>,
195 key_providers: KeyProvidersWriteTransaction<'a>,
196 pub(super) changed_flags: ChangeFlag,
200
201 pub(super) changed_uuid: HashSet<Uuid>,
203 _db_ticket: SemaphorePermit<'a>,
204 _write_ticket: SemaphorePermit<'a>,
205 resolve_filter_cache_clear: bool,
206 resolve_filter_cache_write: ARCacheWriteTxn<
207 'a,
208 (IdentityId, Arc<Filter<FilterValid>>),
209 Arc<Filter<FilterValidResolved>>,
210 (),
211 >,
212 resolve_filter_cache: ARCacheReadTxn<
213 'a,
214 (IdentityId, Arc<Filter<FilterValid>>),
215 Arc<Filter<FilterValidResolved>>,
216 (),
217 >,
218 dyngroup_cache: CowCellWriteTxn<'a, DynGroupCache>,
219}
220
221impl QueryServerWriteTransaction<'_> {
222 pub(crate) fn trim_cid(&self) -> &Cid {
223 &self.trim_cid
224 }
225}
226
227pub trait QueryServerTransaction<'a> {
237 type BackendTransactionType: BackendTransaction;
238 fn get_be_txn(&mut self) -> &mut Self::BackendTransactionType;
239
240 type SchemaTransactionType: SchemaTransaction;
241 fn get_schema<'b>(&self) -> &'b Self::SchemaTransactionType;
242
243 type AccessControlsTransactionType: AccessControlsTransaction<'a>;
244 fn get_accesscontrols(&self) -> &Self::AccessControlsTransactionType;
245
246 type KeyProvidersTransactionType: KeyProvidersTransaction;
247 fn get_key_providers(&self) -> &Self::KeyProvidersTransactionType;
248
249 fn pw_badlist(&self) -> &HashSet<String>;
250
251 fn denied_names(&self) -> &HashSet<String>;
252
253 fn get_domain_version(&self) -> DomainVersion;
254
255 fn get_domain_patch_level(&self) -> u32;
256
257 fn get_domain_development_taint(&self) -> bool;
258
259 fn get_domain_uuid(&self) -> Uuid;
260
261 fn get_domain_name(&self) -> &str;
262
263 fn get_domain_display_name(&self) -> &str;
264
265 fn get_domain_image_value(&self) -> Option<ImageValue>;
266
267 fn get_resolve_filter_cache(&mut self) -> Option<&mut ResolveFilterCacheReadTxn<'a>>;
268
269 fn get_resolve_filter_cache_and_be_txn(
273 &mut self,
274 ) -> (
275 &mut Self::BackendTransactionType,
276 Option<&mut ResolveFilterCacheReadTxn<'a>>,
277 );
278
279 #[instrument(level = "debug", skip_all)]
289 fn search_ext(
290 &mut self,
291 se: &SearchEvent,
292 ) -> Result<Vec<EntryReducedCommitted>, OperationError> {
293 let entries = self.search(se)?;
299
300 let access = self.get_accesscontrols();
301 access
302 .search_filter_entry_attributes(se, entries)
303 .map_err(|e| {
304 admin_error!(?e, "Failed to filter entry attributes");
306 e
307 })
308 }
310
311 #[instrument(level = "debug", skip_all)]
312 fn search(
313 &mut self,
314 se: &SearchEvent,
315 ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
316 if se.ident.is_internal() {
317 trace!(internal_filter = ?se.filter, "search");
318 } else {
319 security_info!(initiator = %se.ident, "search");
320 admin_debug!(external_filter = ?se.filter, "search");
321 }
322
323 let (be_txn, resolve_filter_cache) = self.get_resolve_filter_cache_and_be_txn();
333
334 let idxmeta = be_txn.get_idxmeta_ref();
335
336 trace!(resolve_filter_cache = %resolve_filter_cache.is_some());
337
338 let vfr = se
340 .filter
341 .resolve(&se.ident, Some(idxmeta), resolve_filter_cache)
342 .map_err(|e| {
343 admin_error!(?e, "search filter resolve failure");
344 e
345 })?;
346
347 let lims = se.ident.limits();
348
349 let res = self.get_be_txn().search(lims, &vfr).map_err(|e| {
354 admin_error!(?e, "backend failure");
355 OperationError::Backend
356 })?;
357
358 let access = self.get_accesscontrols();
364 access.search_filter_entries(se, res).map_err(|e| {
365 admin_error!(?e, "Unable to access filter entries");
366 e
367 })
368 }
369
370 #[instrument(level = "debug", skip_all)]
371 fn exists(&mut self, ee: &ExistsEvent) -> Result<bool, OperationError> {
372 let (be_txn, resolve_filter_cache) = self.get_resolve_filter_cache_and_be_txn();
373 let idxmeta = be_txn.get_idxmeta_ref();
374
375 let vfr = ee
376 .filter
377 .resolve(&ee.ident, Some(idxmeta), resolve_filter_cache)
378 .map_err(|e| {
379 admin_error!(?e, "Failed to resolve filter");
380 e
381 })?;
382
383 let lims = ee.ident.limits();
384
385 if ee.ident.is_internal() {
386 be_txn.exists(lims, &vfr).map_err(|e| {
389 admin_error!(?e, "backend failure");
390 OperationError::Backend
391 })
392 } else {
393 let res = self.get_be_txn().search(lims, &vfr).map_err(|e| {
396 admin_error!(?e, "backend failure");
397 OperationError::Backend
398 })?;
399
400 let access = self.get_accesscontrols();
407 access
408 .filter_entries(&ee.ident, &ee.filter_orig, res)
409 .map_err(|e| {
410 admin_error!(?e, "Unable to access filter entries");
411 e
412 })
413 .map(|entries| !entries.is_empty())
414 }
415 }
416
417 fn name_to_uuid(&mut self, name: &str) -> Result<Uuid, OperationError> {
418 let work = EXTRACT_VAL_DN
428 .captures(name)
429 .and_then(|caps| caps.name("val"))
430 .map(|v| v.as_str().to_lowercase())
431 .ok_or(OperationError::InvalidValueState)?;
432
433 Uuid::parse_str(&work).or_else(|_| {
435 self.get_be_txn()
436 .name2uuid(&work)?
437 .ok_or(OperationError::NoMatchingEntries)
438 })
439 }
440
441 fn sync_external_id_to_uuid(
443 &mut self,
444 external_id: &str,
445 ) -> Result<Option<Uuid>, OperationError> {
446 Uuid::parse_str(external_id).map(Some).or_else(|_| {
448 let lname = external_id.to_lowercase();
449 self.get_be_txn().externalid2uuid(lname.as_str())
450 })
451 }
452
453 fn uuid_to_spn(&mut self, uuid: Uuid) -> Result<Option<Value>, OperationError> {
454 let r = self.get_be_txn().uuid2spn(uuid)?;
455
456 if let Some(ref n) = r {
457 debug_assert!(n.is_spn() || n.is_iname());
460 }
461
462 Ok(r)
463 }
464
465 fn uuid_to_rdn(&mut self, uuid: Uuid) -> Result<String, OperationError> {
466 self.get_be_txn()
468 .uuid2rdn(uuid)
469 .map(|v| v.unwrap_or_else(|| format!("uuid={}", uuid.as_hyphenated())))
470 }
471
472 #[instrument(level = "debug", skip_all)]
474 fn internal_exists(&mut self, filter: Filter<FilterInvalid>) -> Result<bool, OperationError> {
475 let f_valid = filter
477 .validate(self.get_schema())
478 .map_err(OperationError::SchemaViolation)?;
479 let ee = ExistsEvent::new_internal(f_valid);
481 self.exists(&ee)
483 }
484
485 #[instrument(level = "debug", skip_all)]
486 fn internal_search(
487 &mut self,
488 filter: Filter<FilterInvalid>,
489 ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
490 let f_valid = filter
491 .validate(self.get_schema())
492 .map_err(OperationError::SchemaViolation)?;
493 let se = SearchEvent::new_internal(f_valid);
494 self.search(&se)
495 }
496
497 #[instrument(level = "debug", skip_all)]
498 fn impersonate_search_valid(
499 &mut self,
500 f_valid: Filter<FilterValid>,
501 f_intent_valid: Filter<FilterValid>,
502 event: &Identity,
503 ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
504 let se = SearchEvent::new_impersonate(event, f_valid, f_intent_valid);
505 self.search(&se)
506 }
507
508 fn impersonate_search_ext_valid(
510 &mut self,
511 f_valid: Filter<FilterValid>,
512 f_intent_valid: Filter<FilterValid>,
513 event: &Identity,
514 ) -> Result<Vec<Entry<EntryReduced, EntryCommitted>>, OperationError> {
515 let se = SearchEvent::new_impersonate(event, f_valid, f_intent_valid);
516 self.search_ext(&se)
517 }
518
519 fn impersonate_search(
521 &mut self,
522 filter: Filter<FilterInvalid>,
523 filter_intent: Filter<FilterInvalid>,
524 event: &Identity,
525 ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
526 let f_valid = filter
527 .validate(self.get_schema())
528 .map_err(OperationError::SchemaViolation)?;
529 let f_intent_valid = filter_intent
530 .validate(self.get_schema())
531 .map_err(OperationError::SchemaViolation)?;
532 self.impersonate_search_valid(f_valid, f_intent_valid, event)
533 }
534
535 #[instrument(level = "debug", skip_all)]
536 fn impersonate_search_ext(
537 &mut self,
538 filter: Filter<FilterInvalid>,
539 filter_intent: Filter<FilterInvalid>,
540 event: &Identity,
541 ) -> Result<Vec<Entry<EntryReduced, EntryCommitted>>, OperationError> {
542 let f_valid = filter
543 .validate(self.get_schema())
544 .map_err(OperationError::SchemaViolation)?;
545 let f_intent_valid = filter_intent
546 .validate(self.get_schema())
547 .map_err(OperationError::SchemaViolation)?;
548 self.impersonate_search_ext_valid(f_valid, f_intent_valid, event)
549 }
550
551 #[instrument(level = "debug", skip_all)]
554 fn internal_search_uuid(
555 &mut self,
556 uuid: Uuid,
557 ) -> Result<Arc<EntrySealedCommitted>, OperationError> {
558 let filter = filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
559 let f_valid = filter.validate(self.get_schema()).map_err(|e| {
560 error!(?e, "Filter Validate - SchemaViolation");
561 OperationError::SchemaViolation(e)
562 })?;
563 let se = SearchEvent::new_internal(f_valid);
564
565 let mut vs = self.search(&se)?;
566 match vs.pop() {
567 Some(entry) if vs.is_empty() => Ok(entry),
568 _ => Err(OperationError::NoMatchingEntries),
569 }
570 }
571
572 #[instrument(level = "debug", skip_all)]
575 fn internal_search_all_uuid(
576 &mut self,
577 uuid: Uuid,
578 ) -> Result<Arc<EntrySealedCommitted>, OperationError> {
579 let filter = filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
580 let f_valid = filter.validate(self.get_schema()).map_err(|e| {
581 error!(?e, "Filter Validate - SchemaViolation");
582 OperationError::SchemaViolation(e)
583 })?;
584 let se = SearchEvent::new_internal(f_valid);
585
586 let mut vs = self.search(&se)?;
587 match vs.pop() {
588 Some(entry) if vs.is_empty() => Ok(entry),
589 _ => Err(OperationError::NoMatchingEntries),
590 }
591 }
592
593 #[instrument(level = "debug", skip_all)]
595 fn internal_search_conflict_uuid(
596 &mut self,
597 uuid: Uuid,
598 ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
599 let filter = filter_all!(f_and(vec![
600 f_eq(Attribute::SourceUuid, PartialValue::Uuid(uuid)),
601 f_eq(Attribute::Class, EntryClass::Conflict.into())
602 ]));
603 let f_valid = filter.validate(self.get_schema()).map_err(|e| {
604 error!(?e, "Filter Validate - SchemaViolation");
605 OperationError::SchemaViolation(e)
606 })?;
607 let se = SearchEvent::new_internal(f_valid);
608
609 self.search(&se)
610 }
611
612 #[instrument(level = "debug", skip_all)]
613 fn impersonate_search_ext_uuid(
614 &mut self,
615 uuid: Uuid,
616 event: &Identity,
617 ) -> Result<Entry<EntryReduced, EntryCommitted>, OperationError> {
618 let filter_intent = filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
619 let filter = filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
620
621 let mut vs = self.impersonate_search_ext(filter, filter_intent, event)?;
622 match vs.pop() {
623 Some(entry) if vs.is_empty() => Ok(entry),
624 _ => {
625 if vs.is_empty() {
626 Err(OperationError::NoMatchingEntries)
627 } else {
628 Err(OperationError::UniqueConstraintViolation)
630 }
631 }
632 }
633 }
634
635 #[instrument(level = "debug", skip_all)]
636 fn impersonate_search_uuid(
637 &mut self,
638 uuid: Uuid,
639 event: &Identity,
640 ) -> Result<Arc<EntrySealedCommitted>, OperationError> {
641 let filter_intent = filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
642 let filter = filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
643
644 let mut vs = self.impersonate_search(filter, filter_intent, event)?;
645 match vs.pop() {
646 Some(entry) if vs.is_empty() => Ok(entry),
647 _ => Err(OperationError::NoMatchingEntries),
648 }
649 }
650
651 fn clone_value(&mut self, attr: &Attribute, value: &str) -> Result<Value, OperationError> {
654 let schema = self.get_schema();
655
656 match schema.get_attributes().get(attr) {
661 Some(schema_a) => {
662 match schema_a.syntax {
663 SyntaxType::Utf8String => Ok(Value::new_utf8(value.to_string())),
664 SyntaxType::Utf8StringInsensitive => Ok(Value::new_iutf8(value)),
665 SyntaxType::Utf8StringIname => Ok(Value::new_iname(value)),
666 SyntaxType::Boolean => Value::new_bools(value)
667 .ok_or_else(|| OperationError::InvalidAttribute("Invalid boolean syntax".to_string())),
668 SyntaxType::SyntaxId => Value::new_syntaxs(value)
669 .ok_or_else(|| OperationError::InvalidAttribute("Invalid Syntax syntax".to_string())),
670 SyntaxType::IndexId => Value::new_indexes(value)
671 .ok_or_else(|| OperationError::InvalidAttribute("Invalid Index syntax".to_string())),
672 SyntaxType::CredentialType => CredentialType::try_from(value)
673 .map(Value::CredentialType)
674 .map_err(|()| OperationError::InvalidAttribute("Invalid CredentialType syntax".to_string())),
675 SyntaxType::Uuid => {
676 let un = self
679 .name_to_uuid(value)
680 .unwrap_or(UUID_DOES_NOT_EXIST);
681 Ok(Value::Uuid(un))
682 }
683 SyntaxType::ReferenceUuid => {
684 let un = self
685 .name_to_uuid(value)
686 .unwrap_or(UUID_DOES_NOT_EXIST);
687 Ok(Value::Refer(un))
688 }
689 SyntaxType::JsonFilter => Value::new_json_filter_s(value)
690 .ok_or_else(|| OperationError::InvalidAttribute("Invalid Filter syntax".to_string())),
691 SyntaxType::Image => Value::new_image(value),
692
693 SyntaxType::Credential => Err(OperationError::InvalidAttribute("Credentials can not be supplied through modification - please use the IDM api".to_string())),
694 SyntaxType::SecretUtf8String => Err(OperationError::InvalidAttribute("Radius secrets can not be supplied through modification - please use the IDM api".to_string())),
695 SyntaxType::SshKey => Err(OperationError::InvalidAttribute("SSH public keys can not be supplied through modification - please use the IDM api".to_string())),
696 SyntaxType::SecurityPrincipalName => Err(OperationError::InvalidAttribute("SPNs are generated and not able to be set.".to_string())),
697 SyntaxType::Uint32 => Value::new_uint32_str(value)
698 .ok_or_else(|| OperationError::InvalidAttribute("Invalid uint32 syntax".to_string())),
699 SyntaxType::Cid => Err(OperationError::InvalidAttribute("CIDs are generated and not able to be set.".to_string())),
700 SyntaxType::NsUniqueId => Value::new_nsuniqueid_s(value)
701 .ok_or_else(|| OperationError::InvalidAttribute("Invalid NsUniqueId syntax".to_string())),
702 SyntaxType::DateTime => Value::new_datetime_s(value)
703 .ok_or_else(|| OperationError::InvalidAttribute("Invalid DateTime (rfc3339) syntax".to_string())),
704 SyntaxType::EmailAddress => Value::new_email_address_s(value)
705 .ok_or_else(|| OperationError::InvalidAttribute("Invalid Email Address syntax".to_string())),
706 SyntaxType::Url => Value::new_url_s(value)
707 .ok_or_else(|| OperationError::InvalidAttribute("Invalid Url (whatwg/url) syntax".to_string())),
708 SyntaxType::OauthScope => Value::new_oauthscope(value)
709 .ok_or_else(|| OperationError::InvalidAttribute("Invalid Oauth Scope syntax".to_string())),
710 SyntaxType::WebauthnAttestationCaList => Value::new_webauthn_attestation_ca_list(value)
711 .ok_or_else(|| OperationError::InvalidAttribute("Invalid Webauthn Attestation CA List".to_string())),
712 SyntaxType::OauthScopeMap => Err(OperationError::InvalidAttribute("Oauth Scope Maps can not be supplied through modification - please use the IDM api".to_string())),
713 SyntaxType::OauthClaimMap => Err(OperationError::InvalidAttribute("Oauth Claim Maps can not be supplied through modification - please use the IDM api".to_string())),
714 SyntaxType::PrivateBinary => Err(OperationError::InvalidAttribute("Private Binary Values can not be supplied through modification".to_string())),
715 SyntaxType::IntentToken => Err(OperationError::InvalidAttribute("Intent Token Values can not be supplied through modification".to_string())),
716 SyntaxType::Passkey => Err(OperationError::InvalidAttribute("Passkey Values can not be supplied through modification".to_string())),
717 SyntaxType::AttestedPasskey => Err(OperationError::InvalidAttribute("AttestedPasskey Values can not be supplied through modification".to_string())),
718 SyntaxType::Session => Err(OperationError::InvalidAttribute("Session Values can not be supplied through modification".to_string())),
719 SyntaxType::ApiToken => Err(OperationError::InvalidAttribute("ApiToken Values can not be supplied through modification".to_string())),
720 SyntaxType::JwsKeyEs256 => Err(OperationError::InvalidAttribute("JwsKeyEs256 Values can not be supplied through modification".to_string())),
721 SyntaxType::JwsKeyRs256 => Err(OperationError::InvalidAttribute("JwsKeyRs256 Values can not be supplied through modification".to_string())),
722 SyntaxType::Oauth2Session => Err(OperationError::InvalidAttribute("Oauth2Session Values can not be supplied through modification".to_string())),
723 SyntaxType::UiHint => UiHint::from_str(value)
724 .map(Value::UiHint)
725 .map_err(|()| OperationError::InvalidAttribute("Invalid uihint syntax".to_string())),
726 SyntaxType::TotpSecret => Err(OperationError::InvalidAttribute("TotpSecret Values can not be supplied through modification".to_string())),
727 SyntaxType::AuditLogString => Err(OperationError::InvalidAttribute("Audit logs are generated and not able to be set.".to_string())),
728 SyntaxType::EcKeyPrivate => Err(OperationError::InvalidAttribute("Ec keys are generated and not able to be set.".to_string())),
729 SyntaxType::KeyInternal => Err(OperationError::InvalidAttribute("Internal keys are generated and not able to be set.".to_string())),
730 SyntaxType::HexString => Value::new_hex_string_s(value)
731 .ok_or_else(|| OperationError::InvalidAttribute("Invalid hex string syntax".to_string())),
732 SyntaxType::Certificate => Value::new_certificate_s(value)
733 .ok_or_else(|| OperationError::InvalidAttribute("Invalid x509 certificate syntax".to_string())),
734 SyntaxType::ApplicationPassword => Err(OperationError::InvalidAttribute("ApplicationPassword values can not be supplied through modification".to_string())),
735 }
736 }
737 None => {
738 Err(OperationError::InvalidAttributeName(attr.to_string()))
741 }
742 }
743 }
744
745 fn clone_partialvalue(
746 &mut self,
747 attr: &Attribute,
748 value: &str,
749 ) -> Result<PartialValue, OperationError> {
750 let schema = self.get_schema();
751
752 match schema.get_attributes().get(attr) {
754 Some(schema_a) => {
755 match schema_a.syntax {
756 SyntaxType::Utf8String | SyntaxType::TotpSecret => {
757 Ok(PartialValue::new_utf8(value.to_string()))
758 }
759 SyntaxType::Utf8StringInsensitive
760 | SyntaxType::JwsKeyEs256
761 | SyntaxType::JwsKeyRs256 => Ok(PartialValue::new_iutf8(value)),
762 SyntaxType::Utf8StringIname => Ok(PartialValue::new_iname(value)),
763 SyntaxType::Boolean => PartialValue::new_bools(value).ok_or_else(|| {
764 OperationError::InvalidAttribute("Invalid boolean syntax".to_string())
765 }),
766 SyntaxType::SyntaxId => PartialValue::new_syntaxs(value).ok_or_else(|| {
767 OperationError::InvalidAttribute("Invalid Syntax syntax".to_string())
768 }),
769 SyntaxType::IndexId => PartialValue::new_indexes(value).ok_or_else(|| {
770 OperationError::InvalidAttribute("Invalid Index syntax".to_string())
771 }),
772 SyntaxType::CredentialType => CredentialType::try_from(value)
773 .map(PartialValue::CredentialType)
774 .map_err(|()| {
775 OperationError::InvalidAttribute(
776 "Invalid credentialtype syntax".to_string(),
777 )
778 }),
779 SyntaxType::Uuid => {
780 let un = self.name_to_uuid(value).unwrap_or(UUID_DOES_NOT_EXIST);
781 Ok(PartialValue::Uuid(un))
782 }
783 SyntaxType::ReferenceUuid
787 | SyntaxType::OauthScopeMap
788 | SyntaxType::Session
789 | SyntaxType::ApiToken
790 | SyntaxType::Oauth2Session
791 | SyntaxType::ApplicationPassword => {
792 let un = self.name_to_uuid(value).unwrap_or(UUID_DOES_NOT_EXIST);
793 Ok(PartialValue::Refer(un))
794 }
795 SyntaxType::OauthClaimMap => self
796 .name_to_uuid(value)
797 .map(PartialValue::Refer)
798 .or_else(|_| Ok(PartialValue::new_iutf8(value))),
799
800 SyntaxType::JsonFilter => {
801 PartialValue::new_json_filter_s(value).ok_or_else(|| {
802 OperationError::InvalidAttribute("Invalid Filter syntax".to_string())
803 })
804 }
805 SyntaxType::Credential => Ok(PartialValue::new_credential_tag(value)),
806 SyntaxType::SecretUtf8String => Ok(PartialValue::new_secret_str()),
807 SyntaxType::SshKey => Ok(PartialValue::new_sshkey_tag_s(value)),
808 SyntaxType::SecurityPrincipalName => {
809 PartialValue::new_spn_s(value).ok_or_else(|| {
810 OperationError::InvalidAttribute("Invalid spn syntax".to_string())
811 })
812 }
813 SyntaxType::Uint32 => PartialValue::new_uint32_str(value).ok_or_else(|| {
814 OperationError::InvalidAttribute("Invalid uint32 syntax".to_string())
815 }),
816 SyntaxType::Cid => PartialValue::new_cid_s(value).ok_or_else(|| {
817 OperationError::InvalidAttribute("Invalid cid syntax".to_string())
818 }),
819 SyntaxType::NsUniqueId => Ok(PartialValue::new_nsuniqueid_s(value)),
820 SyntaxType::DateTime => PartialValue::new_datetime_s(value).ok_or_else(|| {
821 OperationError::InvalidAttribute(
822 "Invalid DateTime (rfc3339) syntax".to_string(),
823 )
824 }),
825 SyntaxType::EmailAddress => Ok(PartialValue::new_email_address_s(value)),
826 SyntaxType::Url => PartialValue::new_url_s(value).ok_or_else(|| {
827 OperationError::InvalidAttribute(
828 "Invalid Url (whatwg/url) syntax".to_string(),
829 )
830 }),
831 SyntaxType::OauthScope => Ok(PartialValue::new_oauthscope(value)),
832 SyntaxType::PrivateBinary => Ok(PartialValue::PrivateBinary),
833 SyntaxType::IntentToken => PartialValue::new_intenttoken_s(value.to_string())
834 .ok_or_else(|| {
835 OperationError::InvalidAttribute(
836 "Invalid Intent Token ID (uuid) syntax".to_string(),
837 )
838 }),
839 SyntaxType::Passkey => PartialValue::new_passkey_s(value).ok_or_else(|| {
840 OperationError::InvalidAttribute("Invalid Passkey UUID syntax".to_string())
841 }),
842 SyntaxType::AttestedPasskey => PartialValue::new_attested_passkey_s(value)
843 .ok_or_else(|| {
844 OperationError::InvalidAttribute(
845 "Invalid AttestedPasskey UUID syntax".to_string(),
846 )
847 }),
848 SyntaxType::UiHint => UiHint::from_str(value)
849 .map(PartialValue::UiHint)
850 .map_err(|()| {
851 OperationError::InvalidAttribute("Invalid uihint syntax".to_string())
852 }),
853 SyntaxType::AuditLogString => Ok(PartialValue::new_utf8s(value)),
854 SyntaxType::EcKeyPrivate => Ok(PartialValue::SecretValue),
855 SyntaxType::Image => Ok(PartialValue::new_utf8s(value)),
856 SyntaxType::WebauthnAttestationCaList => Err(OperationError::InvalidAttribute(
857 "Invalid - unable to query attestation CA list".to_string(),
858 )),
859 SyntaxType::HexString | SyntaxType::KeyInternal | SyntaxType::Certificate => {
860 PartialValue::new_hex_string_s(value).ok_or_else(|| {
861 OperationError::InvalidAttribute(
862 "Invalid syntax, expected hex string".to_string(),
863 )
864 })
865 }
866 }
867 }
868 None => {
869 Err(OperationError::InvalidAttributeName(attr.to_string()))
872 }
873 }
874 }
875
876 fn resolve_scim_interim(
877 &mut self,
878 scim_value_intermediate: ScimValueIntermediate,
879 ) -> Result<Option<ScimValueKanidm>, OperationError> {
880 match scim_value_intermediate {
881 ScimValueIntermediate::References(uuids) => {
882 let scim_references = uuids
883 .into_iter()
884 .map(|uuid| {
885 self.uuid_to_spn(uuid)
886 .and_then(|maybe_value| {
887 maybe_value.ok_or(OperationError::InvalidValueState)
888 })
889 .map(|value| ScimReference {
890 uuid,
891 value: value.to_proto_string_clone(),
892 })
893 })
894 .collect::<Result<Vec<_>, _>>()?;
895 Ok(Some(ScimValueKanidm::EntryReferences(scim_references)))
896 }
897 ScimValueIntermediate::Oauth2ClaimMap(unresolved_maps) => {
898 let scim_claim_maps = unresolved_maps
899 .into_iter()
900 .map(
901 |UnresolvedScimValueOauth2ClaimMap {
902 group_uuid,
903 claim,
904 join_char,
905 values,
906 }| {
907 self.uuid_to_spn(group_uuid)
908 .and_then(|maybe_value| {
909 maybe_value.ok_or(OperationError::InvalidValueState)
910 })
911 .map(|value| ScimOAuth2ClaimMap {
912 group: value.to_proto_string_clone(),
913 group_uuid,
914 claim,
915 join_char,
916 values,
917 })
918 },
919 )
920 .collect::<Result<Vec<_>, _>>()?;
921
922 Ok(Some(ScimValueKanidm::OAuth2ClaimMap(scim_claim_maps)))
923 }
924
925 ScimValueIntermediate::Oauth2ScopeMap(unresolved_maps) => {
926 let scim_claim_maps = unresolved_maps
927 .into_iter()
928 .map(|UnresolvedScimValueOauth2ScopeMap { group_uuid, scopes }| {
929 self.uuid_to_spn(group_uuid)
930 .and_then(|maybe_value| {
931 maybe_value.ok_or(OperationError::InvalidValueState)
932 })
933 .map(|value| ScimOAuth2ScopeMap {
934 group: value.to_proto_string_clone(),
935 group_uuid,
936 scopes,
937 })
938 })
939 .collect::<Result<Vec<_>, _>>()?;
940
941 Ok(Some(ScimValueKanidm::OAuth2ScopeMap(scim_claim_maps)))
942 }
943 }
944 }
945
946 fn resolve_scim_json_get(
947 &mut self,
948 attr: &Attribute,
949 value: &JsonValue,
950 ) -> Result<PartialValue, OperationError> {
951 let schema = self.get_schema();
952 let Some(schema_a) = schema.get_attributes().get(attr) else {
954 return Err(OperationError::InvalidAttributeName(attr.to_string()));
957 };
958
959 match schema_a.syntax {
960 SyntaxType::Utf8String => {
961 let JsonValue::String(value) = value else {
962 return Err(OperationError::InvalidAttribute(attr.to_string()));
963 };
964 Ok(PartialValue::Utf8(value.to_string()))
965 }
966 SyntaxType::Utf8StringInsensitive => {
967 let JsonValue::String(value) = value else {
968 return Err(OperationError::InvalidAttribute(attr.to_string()));
969 };
970 Ok(PartialValue::new_iutf8(value))
971 }
972 SyntaxType::Utf8StringIname => {
973 let JsonValue::String(value) = value else {
974 return Err(OperationError::InvalidAttribute(attr.to_string()));
975 };
976 Ok(PartialValue::new_iname(value))
977 }
978 SyntaxType::Uuid => {
979 let JsonValue::String(value) = value else {
980 return Err(OperationError::InvalidAttribute(attr.to_string()));
981 };
982
983 let un = self.name_to_uuid(value).unwrap_or(UUID_DOES_NOT_EXIST);
984 Ok(PartialValue::Uuid(un))
985 }
986 SyntaxType::ReferenceUuid
987 | SyntaxType::OauthScopeMap
988 | SyntaxType::Session
989 | SyntaxType::ApiToken
990 | SyntaxType::Oauth2Session
991 | SyntaxType::ApplicationPassword => {
992 let JsonValue::String(value) = value else {
993 return Err(OperationError::InvalidAttribute(attr.to_string()));
994 };
995
996 let un = self.name_to_uuid(value).unwrap_or(UUID_DOES_NOT_EXIST);
997 Ok(PartialValue::Refer(un))
998 }
999
1000 _ => Err(OperationError::InvalidAttribute(attr.to_string())),
1001 }
1002 }
1003
1004 fn resolve_valueset_intermediate(
1005 &mut self,
1006 vs_inter: ValueSetIntermediate,
1007 ) -> Result<ValueSet, OperationError> {
1008 match vs_inter {
1009 ValueSetIntermediate::References {
1010 mut resolved,
1011 unresolved,
1012 } => {
1013 for value in unresolved {
1014 let un = self.name_to_uuid(value.as_str()).unwrap_or_else(|_| {
1015 warn!(
1016 ?value,
1017 "Value can not be resolved to a uuid - assuming it does not exist."
1018 );
1019 UUID_DOES_NOT_EXIST
1020 });
1021
1022 resolved.insert(un);
1023 }
1024
1025 let vs = ValueSetRefer::from_set(resolved);
1026 Ok(vs)
1027 }
1028
1029 ValueSetIntermediate::Oauth2ClaimMap {
1030 mut resolved,
1031 unresolved,
1032 } => {
1033 resolved.extend(unresolved.into_iter().map(
1034 |UnresolvedValueSetOauth2ClaimMap {
1035 group_name,
1036 claim,
1037 join_char,
1038 claim_values,
1039 }| {
1040 let group_uuid =
1041 self.name_to_uuid(group_name.as_str()).unwrap_or_else(|_| {
1042 warn!(
1043 ?group_name,
1044 "Value can not be resolved to a uuid - assuming it does not exist."
1045 );
1046 UUID_DOES_NOT_EXIST
1047 });
1048
1049 ResolvedValueSetOauth2ClaimMap {
1050 group_uuid,
1051 claim,
1052 join_char,
1053 claim_values,
1054 }
1055 },
1056 ));
1057
1058 let vs = ValueSetOauthClaimMap::from_set(resolved);
1059 Ok(vs)
1060 }
1061
1062 ValueSetIntermediate::Oauth2ScopeMap {
1063 mut resolved,
1064 unresolved,
1065 } => {
1066 resolved.extend(unresolved.into_iter().map(
1067 |UnresolvedValueSetOauth2ScopeMap { group_name, scopes }| {
1068 let group_uuid =
1069 self.name_to_uuid(group_name.as_str()).unwrap_or_else(|_| {
1070 warn!(
1071 ?group_name,
1072 "Value can not be resolved to a uuid - assuming it does not exist."
1073 );
1074 UUID_DOES_NOT_EXIST
1075 });
1076
1077 ResolvedValueSetOauth2ScopeMap { group_uuid, scopes }
1078 },
1079 ));
1080
1081 let vs = ValueSetOauthScopeMap::from_set(resolved);
1082 Ok(vs)
1083 }
1084 }
1085 }
1086
1087 fn resolve_valueset(&mut self, value: &ValueSet) -> Result<Vec<String>, OperationError> {
1089 if let Some(r_set) = value.as_refer_set() {
1090 let v: Result<Vec<_>, _> = r_set
1091 .iter()
1092 .copied()
1093 .map(|ur| {
1094 let nv = self.uuid_to_spn(ur)?;
1095 match nv {
1096 Some(v) => Ok(v.to_proto_string_clone()),
1097 None => Ok(uuid_to_proto_string(ur)),
1098 }
1099 })
1100 .collect();
1101 v
1102 } else if let Some(r_map) = value.as_oauthscopemap() {
1103 let v: Result<Vec<_>, _> = r_map
1104 .iter()
1105 .map(|(u, m)| {
1106 let nv = self.uuid_to_spn(*u)?;
1107 let u = match nv {
1108 Some(v) => v.to_proto_string_clone(),
1109 None => uuid_to_proto_string(*u),
1110 };
1111 Ok(format!("{u}: {m:?}"))
1112 })
1113 .collect();
1114 v
1115 } else if let Some(r_map) = value.as_oauthclaim_map() {
1116 let mut v = Vec::with_capacity(0);
1117 for (claim_name, mapping) in r_map.iter() {
1118 for (group_ref, claims) in mapping.values() {
1119 let join_char = mapping.join().to_str();
1120
1121 let nv = self.uuid_to_spn(*group_ref)?;
1122 let resolved_id = match nv {
1123 Some(v) => v.to_proto_string_clone(),
1124 None => uuid_to_proto_string(*group_ref),
1125 };
1126
1127 let joined = str_concat!(claims, ",");
1128
1129 v.push(format!("{claim_name}:{resolved_id}:{join_char}:{joined:?}"))
1130 }
1131 }
1132 Ok(v)
1133 } else {
1134 let v: Vec<_> = value.to_proto_string_clone_iter().collect();
1135 Ok(v)
1136 }
1137 }
1138
1139 fn resolve_valueset_ldap(
1140 &mut self,
1141 value: &ValueSet,
1142 basedn: &str,
1143 ) -> Result<Vec<Vec<u8>>, OperationError> {
1144 if let Some(r_set) = value.as_refer_set() {
1145 let v: Result<Vec<_>, _> = r_set
1146 .iter()
1147 .copied()
1148 .map(|ur| {
1149 let rdn = self.uuid_to_rdn(ur)?;
1150 Ok(format!("{rdn},{basedn}").into_bytes())
1151 })
1152 .collect();
1153 v
1154 } else if let Some(key_iter) = value.as_sshpubkey_string_iter() {
1157 let v: Vec<_> = key_iter.map(|s| s.into_bytes()).collect();
1158 Ok(v)
1159 } else {
1160 let v: Vec<_> = value
1161 .to_proto_string_clone_iter()
1162 .map(|s| s.into_bytes())
1163 .collect();
1164 Ok(v)
1165 }
1166 }
1167
1168 fn get_db_domain(&mut self) -> Result<Arc<EntrySealedCommitted>, OperationError> {
1169 self.internal_search_uuid(UUID_DOMAIN_INFO)
1170 }
1171
1172 fn get_domain_key_object_handle(&self) -> Result<Arc<KeyObject>, OperationError> {
1173 self.get_key_providers()
1174 .get_key_object_handle(UUID_DOMAIN_INFO)
1175 .ok_or(OperationError::KP0031KeyObjectNotFound)
1176 }
1177
1178 fn get_domain_es256_private_key(&mut self) -> Result<Vec<u8>, OperationError> {
1179 self.internal_search_uuid(UUID_DOMAIN_INFO)
1180 .and_then(|e| {
1181 e.get_ava_single_private_binary(Attribute::Es256PrivateKeyDer)
1182 .map(|s| s.to_vec())
1183 .ok_or(OperationError::InvalidEntryState)
1184 })
1185 .map_err(|e| {
1186 admin_error!(?e, "Error getting domain es256 key");
1187 e
1188 })
1189 }
1190
1191 fn get_domain_ldap_allow_unix_pw_bind(&mut self) -> Result<bool, OperationError> {
1192 self.internal_search_uuid(UUID_DOMAIN_INFO).map(|entry| {
1193 entry
1194 .get_ava_single_bool(Attribute::LdapAllowUnixPwBind)
1195 .unwrap_or(true)
1196 })
1197 }
1198
1199 fn get_sc_password_badlist(&mut self) -> Result<HashSet<String>, OperationError> {
1202 self.internal_search_uuid(UUID_SYSTEM_CONFIG)
1203 .map(|e| match e.get_ava_iter_iutf8(Attribute::BadlistPassword) {
1204 Some(vs_str_iter) => vs_str_iter.map(str::to_string).collect::<HashSet<_>>(),
1205 None => HashSet::default(),
1206 })
1207 .map_err(|e| {
1208 error!(
1209 ?e,
1210 "Failed to retrieve password badlist from system configuration"
1211 );
1212 e
1213 })
1214 }
1215
1216 fn get_sc_denied_names(&mut self) -> Result<HashSet<String>, OperationError> {
1219 self.internal_search_uuid(UUID_SYSTEM_CONFIG)
1220 .map(|e| match e.get_ava_iter_iname(Attribute::DeniedName) {
1221 Some(vs_str_iter) => vs_str_iter.map(str::to_string).collect::<HashSet<_>>(),
1222 None => HashSet::default(),
1223 })
1224 .map_err(|e| {
1225 error!(
1226 ?e,
1227 "Failed to retrieve denied names from system configuration"
1228 );
1229 e
1230 })
1231 }
1232
1233 fn get_oauth2rs_set(&mut self) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
1234 self.internal_search(filter!(f_eq(
1235 Attribute::Class,
1236 EntryClass::OAuth2ResourceServer.into(),
1237 )))
1238 }
1239
1240 fn get_applications_set(&mut self) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
1241 self.internal_search(filter!(f_eq(
1242 Attribute::Class,
1243 EntryClass::Application.into(),
1244 )))
1245 }
1246
1247 #[instrument(level = "debug", skip_all)]
1248 fn consumer_get_state(&mut self) -> Result<ReplRuvRange, OperationError> {
1249 let domain_uuid = self.get_domain_uuid();
1274
1275 let ruv_snapshot = self.get_be_txn().get_ruv();
1278
1279 ruv_snapshot
1281 .current_ruv_range()
1282 .map(|ranges| ReplRuvRange::V1 {
1283 domain_uuid,
1284 ranges,
1285 })
1286 }
1287}
1288
1289impl<'a> QueryServerTransaction<'a> for QueryServerReadTransaction<'a> {
1293 type AccessControlsTransactionType = AccessControlsReadTransaction<'a>;
1294 type BackendTransactionType = BackendReadTransaction<'a>;
1295 type SchemaTransactionType = SchemaReadTransaction;
1296 type KeyProvidersTransactionType = KeyProvidersReadTransaction;
1297
1298 fn get_be_txn(&mut self) -> &mut BackendReadTransaction<'a> {
1299 &mut self.be_txn
1300 }
1301
1302 fn get_schema<'b>(&self) -> &'b SchemaReadTransaction {
1303 unsafe {
1307 let s = (&self.schema) as *const _;
1308 &*s
1309 }
1310 }
1311
1312 fn get_accesscontrols(&self) -> &AccessControlsReadTransaction<'a> {
1313 &self.accesscontrols
1314 }
1315
1316 fn get_key_providers(&self) -> &KeyProvidersReadTransaction {
1317 &self.key_providers
1318 }
1319
1320 fn get_resolve_filter_cache(&mut self) -> Option<&mut ResolveFilterCacheReadTxn<'a>> {
1321 Some(&mut self.resolve_filter_cache)
1322 }
1323
1324 fn get_resolve_filter_cache_and_be_txn(
1325 &mut self,
1326 ) -> (
1327 &mut BackendReadTransaction<'a>,
1328 Option<&mut ResolveFilterCacheReadTxn<'a>>,
1329 ) {
1330 (&mut self.be_txn, Some(&mut self.resolve_filter_cache))
1331 }
1332
1333 fn pw_badlist(&self) -> &HashSet<String> {
1334 &self.system_config.pw_badlist
1335 }
1336
1337 fn denied_names(&self) -> &HashSet<String> {
1338 &self.system_config.denied_names
1339 }
1340
1341 fn get_domain_version(&self) -> DomainVersion {
1342 self.d_info.d_vers
1343 }
1344
1345 fn get_domain_patch_level(&self) -> u32 {
1346 self.d_info.d_patch_level
1347 }
1348
1349 fn get_domain_development_taint(&self) -> bool {
1350 self.d_info.d_devel_taint
1351 }
1352
1353 fn get_domain_uuid(&self) -> Uuid {
1354 self.d_info.d_uuid
1355 }
1356
1357 fn get_domain_name(&self) -> &str {
1358 &self.d_info.d_name
1359 }
1360
1361 fn get_domain_display_name(&self) -> &str {
1362 &self.d_info.d_display
1363 }
1364
1365 fn get_domain_image_value(&self) -> Option<ImageValue> {
1366 self.d_info.d_image.clone()
1367 }
1368}
1369
1370impl QueryServerReadTransaction<'_> {
1371 pub(crate) fn trim_cid(&self) -> &Cid {
1372 &self.trim_cid
1373 }
1374
1375 pub fn domain_info(&mut self) -> Result<ProtoDomainInfo, OperationError> {
1377 let d_info = &self.d_info;
1378
1379 Ok(ProtoDomainInfo {
1380 name: d_info.d_name.clone(),
1381 displayname: d_info.d_display.clone(),
1382 uuid: d_info.d_uuid,
1383 level: d_info.d_vers,
1384 })
1385 }
1386
1387 pub(crate) fn verify(&mut self) -> Vec<Result<(), ConsistencyError>> {
1391 let be_errs = self.get_be_txn().verify();
1395
1396 if !be_errs.is_empty() {
1397 return be_errs;
1398 }
1399
1400 let sc_errs = self.get_schema().validate();
1402
1403 if !sc_errs.is_empty() {
1404 return sc_errs;
1405 }
1406
1407 let idx_errs = self.get_be_txn().verify_indexes();
1411
1412 if !idx_errs.is_empty() {
1413 return idx_errs;
1414 }
1415
1416 let mut results = Vec::with_capacity(0);
1419
1420 let schema = self.get_schema();
1423
1424 let filt_all = filter!(f_pres(Attribute::Class));
1425 let all_entries = match self.internal_search(filt_all) {
1426 Ok(a) => a,
1427 Err(_e) => return vec![Err(ConsistencyError::QueryServerSearchFailure)],
1428 };
1429
1430 for e in all_entries {
1431 e.verify(schema, &mut results)
1432 }
1433
1434 self.get_be_txn().verify_ruv(&mut results);
1436
1437 Plugins::run_verify(self, &mut results);
1443 results
1446 }
1447
1448 #[instrument(level = "debug", skip_all)]
1449 pub fn scim_entry_id_get_ext(
1450 &mut self,
1451 uuid: Uuid,
1452 class: EntryClass,
1453 query: ScimEntryGetQuery,
1454 ident: Identity,
1455 ) -> Result<ScimEntryKanidm, OperationError> {
1456 let filter_intent = filter!(f_and!([
1457 f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)),
1458 f_eq(Attribute::Class, class.into())
1459 ]));
1460
1461 let f_intent_valid = filter_intent
1462 .validate(self.get_schema())
1463 .map_err(OperationError::SchemaViolation)?;
1464
1465 let f_valid = f_intent_valid.clone().into_ignore_hidden();
1466
1467 let r_attrs = query
1468 .attributes
1469 .map(|attr_set| attr_set.into_iter().collect());
1470
1471 let se = SearchEvent {
1472 ident,
1473 filter: f_valid,
1474 filter_orig: f_intent_valid,
1475 attrs: r_attrs,
1476 effective_access_check: query.ext_access_check,
1477 };
1478
1479 let mut vs = self.search_ext(&se)?;
1480 match vs.pop() {
1481 Some(entry) if vs.is_empty() => entry.to_scim_kanidm(self),
1482 _ => {
1483 if vs.is_empty() {
1484 Err(OperationError::NoMatchingEntries)
1485 } else {
1486 Err(OperationError::UniqueConstraintViolation)
1488 }
1489 }
1490 }
1491 }
1492
1493 #[instrument(level = "debug", skip_all)]
1494 pub fn scim_search_ext(
1495 &mut self,
1496 ident: Identity,
1497 filter: &ScimFilter,
1498 query: ScimEntryGetQuery,
1499 ) -> Result<ScimListResponse, OperationError> {
1500 let filter_intent = Filter::from_scim_ro(&ident, filter, self)?;
1501
1502 let f_intent_valid = filter_intent
1503 .validate(self.get_schema())
1504 .map_err(OperationError::SchemaViolation)?;
1505
1506 let f_valid = f_intent_valid.clone().into_ignore_hidden();
1507
1508 let r_attrs = query
1509 .attributes
1510 .map(|attr_set| attr_set.into_iter().collect());
1511
1512 let se = SearchEvent {
1513 ident,
1514 filter: f_valid,
1515 filter_orig: f_intent_valid,
1516 attrs: r_attrs,
1517 effective_access_check: query.ext_access_check,
1518 };
1519
1520 let vs = self.search_ext(&se)?;
1521
1522 let resources = vs
1523 .into_iter()
1524 .map(|entry| entry.to_scim_kanidm(self))
1525 .collect::<Result<Vec<_>, _>>()?;
1526
1527 Ok(ScimListResponse {
1528 schemas: Vec::with_capacity(0),
1529 total_results: resources.len() as u64,
1530 resources,
1531 })
1532 }
1533}
1534
1535impl<'a> QueryServerTransaction<'a> for QueryServerWriteTransaction<'a> {
1536 type AccessControlsTransactionType = AccessControlsWriteTransaction<'a>;
1537 type BackendTransactionType = BackendWriteTransaction<'a>;
1538 type SchemaTransactionType = SchemaWriteTransaction<'a>;
1539 type KeyProvidersTransactionType = KeyProvidersWriteTransaction<'a>;
1540
1541 fn get_be_txn(&mut self) -> &mut BackendWriteTransaction<'a> {
1542 &mut self.be_txn
1543 }
1544
1545 fn get_schema<'b>(&self) -> &'b SchemaWriteTransaction<'a> {
1546 unsafe {
1550 let s = (&self.schema) as *const _;
1551 &*s
1552 }
1553 }
1554
1555 fn get_accesscontrols(&self) -> &AccessControlsWriteTransaction<'a> {
1556 &self.accesscontrols
1557 }
1558
1559 fn get_key_providers(&self) -> &KeyProvidersWriteTransaction<'a> {
1560 &self.key_providers
1561 }
1562
1563 fn get_resolve_filter_cache(&mut self) -> Option<&mut ResolveFilterCacheReadTxn<'a>> {
1564 if self.resolve_filter_cache_clear || *self.phase < ServerPhase::SchemaReady {
1565 None
1566 } else {
1567 Some(&mut self.resolve_filter_cache)
1568 }
1569 }
1570
1571 fn get_resolve_filter_cache_and_be_txn(
1572 &mut self,
1573 ) -> (
1574 &mut BackendWriteTransaction<'a>,
1575 Option<&mut ResolveFilterCacheReadTxn<'a>>,
1576 ) {
1577 if self.resolve_filter_cache_clear || *self.phase < ServerPhase::SchemaReady {
1578 (&mut self.be_txn, None)
1579 } else {
1580 (&mut self.be_txn, Some(&mut self.resolve_filter_cache))
1581 }
1582 }
1583
1584 fn pw_badlist(&self) -> &HashSet<String> {
1585 &self.system_config.pw_badlist
1586 }
1587
1588 fn denied_names(&self) -> &HashSet<String> {
1589 &self.system_config.denied_names
1590 }
1591
1592 fn get_domain_version(&self) -> DomainVersion {
1593 self.d_info.d_vers
1594 }
1595
1596 fn get_domain_patch_level(&self) -> u32 {
1597 self.d_info.d_patch_level
1598 }
1599
1600 fn get_domain_development_taint(&self) -> bool {
1601 self.d_info.d_devel_taint
1602 }
1603
1604 fn get_domain_uuid(&self) -> Uuid {
1605 self.d_info.d_uuid
1606 }
1607
1608 fn get_domain_name(&self) -> &str {
1610 &self.d_info.d_name
1611 }
1612
1613 fn get_domain_display_name(&self) -> &str {
1614 &self.d_info.d_display
1615 }
1616
1617 fn get_domain_image_value(&self) -> Option<ImageValue> {
1618 self.d_info.d_image.clone()
1619 }
1620}
1621
1622impl QueryServer {
1623 pub fn new(
1624 be: Backend,
1625 schema: Schema,
1626 domain_name: String,
1627 curtime: Duration,
1628 ) -> Result<Self, OperationError> {
1629 let (s_uuid, d_uuid, ts_max) = {
1630 let mut wr = be.write()?;
1631 let s_uuid = wr.get_db_s_uuid()?;
1632 let d_uuid = wr.get_db_d_uuid()?;
1633 let ts_max = wr.get_db_ts_max(curtime)?;
1634 wr.commit()?;
1635 (s_uuid, d_uuid, ts_max)
1636 };
1637
1638 let pool_size = be.get_pool_size();
1639
1640 debug!("Server UUID -> {:?}", s_uuid);
1641 debug!("Domain UUID -> {:?}", d_uuid);
1642 debug!("Domain Name -> {:?}", domain_name);
1643
1644 let d_info = Arc::new(CowCell::new(DomainInfo {
1645 d_uuid,
1646 d_vers: DOMAIN_LEVEL_0,
1649 d_patch_level: 0,
1650 d_name: domain_name.clone(),
1651 d_display: domain_name,
1654 d_devel_taint: option_env!("KANIDM_PRE_RELEASE").is_some(),
1656 d_ldap_allow_unix_pw_bind: false,
1657 d_allow_easter_eggs: false,
1658 d_image: None,
1659 }));
1660
1661 let cid = Cid::new_lamport(s_uuid, curtime, &ts_max);
1662 let cid_max = Arc::new(CowCell::new(cid));
1663
1664 let system_config = Arc::new(CowCell::new(SystemConfig::default()));
1666
1667 let dyngroup_cache = Arc::new(CowCell::new(DynGroupCache::default()));
1668
1669 let phase = Arc::new(CowCell::new(ServerPhase::Bootstrap));
1670
1671 let resolve_filter_cache = Arc::new(
1672 ARCacheBuilder::new()
1673 .set_size(RESOLVE_FILTER_CACHE_MAX, RESOLVE_FILTER_CACHE_LOCAL)
1674 .set_reader_quiesce(true)
1675 .build()
1676 .ok_or_else(|| {
1677 error!("Failed to build filter resolve cache");
1678 OperationError::DB0003FilterResolveCacheBuild
1679 })?,
1680 );
1681
1682 let key_providers = Arc::new(KeyProviders::default());
1683
1684 debug_assert!(pool_size > 0);
1687 let read_ticket_pool = std::cmp::max(pool_size - 1, 1);
1688
1689 Ok(QueryServer {
1690 phase,
1691 d_info,
1692 system_config,
1693 be,
1694 schema: Arc::new(schema),
1695 accesscontrols: Arc::new(AccessControls::default()),
1696 db_tickets: Arc::new(Semaphore::new(pool_size as usize)),
1697 read_tickets: Arc::new(Semaphore::new(read_ticket_pool as usize)),
1698 write_ticket: Arc::new(Semaphore::new(1)),
1699 resolve_filter_cache,
1700 dyngroup_cache,
1701 cid_max,
1702 key_providers,
1703 })
1704 }
1705
1706 pub fn try_quiesce(&self) {
1707 self.be.try_quiesce();
1708 self.accesscontrols.try_quiesce();
1709 self.resolve_filter_cache.try_quiesce();
1710 }
1711
1712 #[instrument(level = "debug", skip_all)]
1713 async fn read_acquire_ticket(&self) -> Option<(SemaphorePermit<'_>, SemaphorePermit<'_>)> {
1714 let read_ticket = if cfg!(test) {
1718 self.read_tickets
1719 .try_acquire()
1720 .inspect_err(|err| {
1721 error!(?err, "Unable to acquire read ticket!");
1722 })
1723 .ok()?
1724 } else {
1725 let fut = tokio::time::timeout(
1726 Duration::from_millis(DB_LOCK_ACQUIRE_TIMEOUT_MILLIS),
1727 self.read_tickets.acquire(),
1728 );
1729
1730 match fut.await {
1731 Ok(Ok(ticket)) => ticket,
1732 Ok(Err(_)) => {
1733 error!("Failed to acquire read ticket, may be poisoned.");
1734 return None;
1735 }
1736 Err(_) => {
1737 error!("Failed to acquire read ticket, server is overloaded.");
1738 return None;
1739 }
1740 }
1741 };
1742
1743 let db_ticket = if cfg!(test) {
1748 self.db_tickets
1749 .try_acquire()
1750 .inspect_err(|err| {
1751 error!(?err, "Unable to acquire database ticket!");
1752 })
1753 .ok()?
1754 } else {
1755 self.db_tickets
1756 .acquire()
1757 .await
1758 .inspect_err(|err| {
1759 error!(?err, "Unable to acquire database ticket!");
1760 })
1761 .ok()?
1762 };
1763
1764 Some((read_ticket, db_ticket))
1765 }
1766
1767 pub async fn read(&self) -> Result<QueryServerReadTransaction<'_>, OperationError> {
1768 let (read_ticket, db_ticket) = self
1769 .read_acquire_ticket()
1770 .await
1771 .ok_or(OperationError::DatabaseLockAcquisitionTimeout)?;
1772 let schema = self.schema.read();
1776
1777 let cid_max = self.cid_max.read();
1778 let trim_cid = cid_max.sub_secs(CHANGELOG_MAX_AGE)?;
1779
1780 let be_txn = self.be.read()?;
1781
1782 Ok(QueryServerReadTransaction {
1783 be_txn,
1784 schema,
1785 d_info: self.d_info.read(),
1786 system_config: self.system_config.read(),
1787 accesscontrols: self.accesscontrols.read(),
1788 key_providers: self.key_providers.read(),
1789 _db_ticket: db_ticket,
1790 _read_ticket: read_ticket,
1791 resolve_filter_cache: self.resolve_filter_cache.read(),
1792 trim_cid,
1793 })
1794 }
1795
1796 #[instrument(level = "debug", skip_all)]
1797 async fn write_acquire_ticket(&self) -> Option<(SemaphorePermit<'_>, SemaphorePermit<'_>)> {
1798 let write_ticket = if cfg!(test) {
1800 self.write_ticket
1801 .try_acquire()
1802 .inspect_err(|err| {
1803 error!(?err, "Unable to acquire write ticket!");
1804 })
1805 .ok()?
1806 } else {
1807 let fut = tokio::time::timeout(
1808 Duration::from_millis(DB_LOCK_ACQUIRE_TIMEOUT_MILLIS),
1809 self.write_ticket.acquire(),
1810 );
1811
1812 match fut.await {
1813 Ok(Ok(ticket)) => ticket,
1814 Ok(Err(_)) => {
1815 error!("Failed to acquire write ticket, may be poisoned.");
1816 return None;
1817 }
1818 Err(_) => {
1819 error!("Failed to acquire write ticket, server is overloaded.");
1820 return None;
1821 }
1822 }
1823 };
1824
1825 let db_ticket = if cfg!(test) {
1829 self.db_tickets
1830 .try_acquire()
1831 .inspect_err(|err| {
1832 error!(?err, "Unable to acquire write db_ticket!");
1833 })
1834 .ok()?
1835 } else {
1836 self.db_tickets
1837 .acquire()
1838 .await
1839 .inspect_err(|err| {
1840 error!(?err, "Unable to acquire write db_ticket!");
1841 })
1842 .ok()?
1843 };
1844
1845 Some((write_ticket, db_ticket))
1846 }
1847
1848 pub async fn write(
1849 &self,
1850 curtime: Duration,
1851 ) -> Result<QueryServerWriteTransaction<'_>, OperationError> {
1852 let (write_ticket, db_ticket) = self
1853 .write_acquire_ticket()
1854 .await
1855 .ok_or(OperationError::DatabaseLockAcquisitionTimeout)?;
1856
1857 let be_txn = self.be.write()?;
1862
1863 let schema_write = self.schema.write();
1864 let d_info = self.d_info.write();
1865 let system_config = self.system_config.write();
1866 let phase = self.phase.write();
1867
1868 let mut cid = self.cid_max.write();
1869 *cid = Cid::new_lamport(cid.s_uuid, curtime, &cid.ts);
1871
1872 let trim_cid = cid.sub_secs(CHANGELOG_MAX_AGE)?;
1873
1874 Ok(QueryServerWriteTransaction {
1875 committed: false,
1882 phase,
1883 d_info,
1884 system_config,
1885 curtime,
1886 cid,
1887 trim_cid,
1888 be_txn,
1889 schema: schema_write,
1890 accesscontrols: self.accesscontrols.write(),
1891 changed_flags: ChangeFlag::empty(),
1892 changed_uuid: HashSet::new(),
1893 _db_ticket: db_ticket,
1894 _write_ticket: write_ticket,
1895 resolve_filter_cache: self.resolve_filter_cache.read(),
1896 resolve_filter_cache_clear: false,
1897 resolve_filter_cache_write: self.resolve_filter_cache.write(),
1898 dyngroup_cache: self.dyngroup_cache.write(),
1899 key_providers: self.key_providers.write(),
1900 })
1901 }
1902
1903 #[cfg(any(test, debug_assertions))]
1904 pub async fn clear_cache(&self) -> Result<(), OperationError> {
1905 let ct = duration_from_epoch_now();
1906 let mut w_txn = self.write(ct).await?;
1907 w_txn.clear_cache()?;
1908 w_txn.commit()
1909 }
1910
1911 pub async fn verify(&self) -> Vec<Result<(), ConsistencyError>> {
1912 let current_time = duration_from_epoch_now();
1913 if self
1918 .write(current_time)
1919 .await
1920 .and_then(|mut txn| {
1921 txn.force_schema_reload();
1922 txn.commit()
1923 })
1924 .is_err()
1925 {
1926 return vec![Err(ConsistencyError::Unknown)];
1927 };
1928
1929 match self.read().await {
1930 Ok(mut r_txn) => r_txn.verify(),
1931 Err(_) => vec![Err(ConsistencyError::Unknown)],
1932 }
1933 }
1934}
1935
1936impl<'a> QueryServerWriteTransaction<'a> {
1937 pub(crate) fn get_server_uuid(&self) -> Uuid {
1938 self.cid.s_uuid
1940 }
1941
1942 pub(crate) fn reset_server_uuid(&mut self) -> Result<(), OperationError> {
1943 let s_uuid = self.be_txn.reset_db_s_uuid().map_err(|err| {
1944 error!(?err, "Failed to reset server replication uuid");
1945 err
1946 })?;
1947
1948 debug!(?s_uuid, "reset server replication uuid");
1949
1950 self.cid.s_uuid = s_uuid;
1951
1952 Ok(())
1953 }
1954
1955 pub(crate) fn get_curtime(&self) -> Duration {
1956 self.curtime
1957 }
1958
1959 pub(crate) fn get_cid(&self) -> &Cid {
1960 &self.cid
1961 }
1962
1963 pub(crate) fn get_key_providers_mut(&mut self) -> &mut KeyProvidersWriteTransaction<'a> {
1964 &mut self.key_providers
1965 }
1966
1967 pub(crate) fn get_dyngroup_cache(&mut self) -> &mut DynGroupCache {
1968 &mut self.dyngroup_cache
1969 }
1970
1971 pub fn domain_raise(&mut self, level: u32) -> Result<(), OperationError> {
1972 if level > DOMAIN_MAX_LEVEL {
1973 return Err(OperationError::MG0002RaiseDomainLevelExceedsMaximum);
1974 }
1975
1976 let modl = ModifyList::new_purge_and_set(Attribute::Version, Value::Uint32(level));
1977 let udi = PVUUID_DOMAIN_INFO.clone();
1978 let filt = filter_all!(f_eq(Attribute::Uuid, udi));
1979 self.internal_modify(&filt, &modl)
1980 }
1981
1982 pub fn domain_remigrate(&mut self, level: u32) -> Result<(), OperationError> {
1983 let mut_d_info = self.d_info.get_mut();
1984
1985 if level > mut_d_info.d_vers {
1986 return Ok(());
1988 } else if level < DOMAIN_MIN_REMIGRATION_LEVEL {
1989 return Err(OperationError::MG0001InvalidReMigrationLevel);
1990 };
1991
1992 info!(
1993 "Prepare to re-migrate from {} -> {}",
1994 level, mut_d_info.d_vers
1995 );
1996 mut_d_info.d_vers = level;
1997 self.changed_flags.insert(ChangeFlag::DOMAIN);
1998
1999 Ok(())
2000 }
2001
2002 #[instrument(level = "debug", skip_all)]
2003 pub(crate) fn reload_schema(&mut self) -> Result<(), OperationError> {
2004 let filt = filter!(f_eq(Attribute::Class, EntryClass::AttributeType.into()));
2007 let res = self.internal_search(filt).map_err(|e| {
2008 admin_error!("reload schema internal search failed {:?}", e);
2009 e
2010 })?;
2011 let attributetypes: Result<Vec<_>, _> =
2013 res.iter().map(|e| SchemaAttribute::try_from(e)).collect();
2014
2015 let attributetypes = attributetypes.map_err(|e| {
2016 admin_error!("reload schema attributetypes {:?}", e);
2017 e
2018 })?;
2019
2020 self.schema.update_attributes(attributetypes).map_err(|e| {
2021 admin_error!("reload schema update attributetypes {:?}", e);
2022 e
2023 })?;
2024
2025 let filt = filter!(f_eq(Attribute::Class, EntryClass::ClassType.into()));
2027 let res = self.internal_search(filt).map_err(|e| {
2028 admin_error!("reload schema internal search failed {:?}", e);
2029 e
2030 })?;
2031 let classtypes: Result<Vec<_>, _> = res.iter().map(|e| SchemaClass::try_from(e)).collect();
2033 let classtypes = classtypes.map_err(|e| {
2034 admin_error!("reload schema classtypes {:?}", e);
2035 e
2036 })?;
2037
2038 self.schema.update_classes(classtypes).map_err(|e| {
2039 admin_error!("reload schema update classtypes {:?}", e);
2040 e
2041 })?;
2042
2043 let valid_r = self.schema.validate();
2045
2046 if valid_r.is_empty() {
2048 trace!("Reloading idxmeta ...");
2050 self.be_txn
2051 .update_idxmeta(self.schema.reload_idxmeta())
2052 .map_err(|e| {
2053 admin_error!("reload schema update idxmeta {:?}", e);
2054 e
2055 })
2056 } else {
2057 admin_error!("Schema reload failed -> {:?}", valid_r);
2059 Err(OperationError::ConsistencyError(
2060 valid_r.into_iter().filter_map(|v| v.err()).collect(),
2061 ))
2062 }?;
2063
2064 self.resolve_filter_cache_clear = true;
2067
2068 DynGroup::reload(self)?;
2071
2072 Ok(())
2073 }
2074
2075 #[instrument(level = "debug", skip_all)]
2076 fn reload_accesscontrols(&mut self) -> Result<(), OperationError> {
2077 trace!("ACP reload started ...");
2085
2086 let filt = filter!(f_eq(Attribute::Class, EntryClass::SyncAccount.into()));
2089
2090 let res = self.internal_search(filt).map_err(|e| {
2091 admin_error!(
2092 err = ?e,
2093 "reload accesscontrols internal search failed",
2094 );
2095 e
2096 })?;
2097
2098 let sync_agreement_map: HashMap<Uuid, BTreeSet<Attribute>> = res
2099 .iter()
2100 .filter_map(|e| {
2101 e.get_ava_as_iutf8(Attribute::SyncYieldAuthority)
2102 .map(|set| {
2103 let set: BTreeSet<_> =
2104 set.iter().map(|s| Attribute::from(s.as_str())).collect();
2105 (e.get_uuid(), set)
2106 })
2107 })
2108 .collect();
2109
2110 self.accesscontrols
2111 .update_sync_agreements(sync_agreement_map);
2112
2113 let filt = filter!(f_and!([
2115 f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2116 f_eq(Attribute::Class, EntryClass::AccessControlSearch.into()),
2117 f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2118 ]));
2119
2120 let res = self.internal_search(filt).map_err(|e| {
2121 admin_error!(
2122 err = ?e,
2123 "reload accesscontrols internal search failed",
2124 );
2125 e
2126 })?;
2127 let search_acps: Result<Vec<_>, _> = res
2128 .iter()
2129 .map(|e| AccessControlSearch::try_from(self, e))
2130 .collect();
2131
2132 let search_acps = search_acps.map_err(|e| {
2133 admin_error!(err = ?e, "Unable to parse search accesscontrols");
2134 e
2135 })?;
2136
2137 self.accesscontrols
2138 .update_search(search_acps)
2139 .map_err(|e| {
2140 admin_error!(err = ?e, "Failed to update search accesscontrols");
2141 e
2142 })?;
2143 let filt = filter!(f_and!([
2145 f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2146 f_eq(Attribute::Class, EntryClass::AccessControlCreate.into()),
2147 f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2148 ]));
2149
2150 let res = self.internal_search(filt).map_err(|e| {
2151 admin_error!(
2152 err = ?e,
2153 "reload accesscontrols internal search failed"
2154 );
2155 e
2156 })?;
2157 let create_acps: Result<Vec<_>, _> = res
2158 .iter()
2159 .map(|e| AccessControlCreate::try_from(self, e))
2160 .collect();
2161
2162 let create_acps = create_acps.map_err(|e| {
2163 admin_error!(err = ?e, "Unable to parse create accesscontrols");
2164 e
2165 })?;
2166
2167 self.accesscontrols
2168 .update_create(create_acps)
2169 .map_err(|e| {
2170 admin_error!(err = ?e, "Failed to update create accesscontrols");
2171 e
2172 })?;
2173 let filt = filter!(f_and!([
2175 f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2176 f_eq(Attribute::Class, EntryClass::AccessControlModify.into()),
2177 f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2178 ]));
2179
2180 let res = self.internal_search(filt).map_err(|e| {
2181 admin_error!("reload accesscontrols internal search failed {:?}", e);
2182 e
2183 })?;
2184 let modify_acps: Result<Vec<_>, _> = res
2185 .iter()
2186 .map(|e| AccessControlModify::try_from(self, e))
2187 .collect();
2188
2189 let modify_acps = modify_acps.map_err(|e| {
2190 admin_error!("Unable to parse modify accesscontrols {:?}", e);
2191 e
2192 })?;
2193
2194 self.accesscontrols
2195 .update_modify(modify_acps)
2196 .map_err(|e| {
2197 admin_error!("Failed to update modify accesscontrols {:?}", e);
2198 e
2199 })?;
2200 let filt = filter!(f_and!([
2202 f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2203 f_eq(Attribute::Class, EntryClass::AccessControlDelete.into()),
2204 f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2205 ]));
2206
2207 let res = self.internal_search(filt).map_err(|e| {
2208 admin_error!("reload accesscontrols internal search failed {:?}", e);
2209 e
2210 })?;
2211 let delete_acps: Result<Vec<_>, _> = res
2212 .iter()
2213 .map(|e| AccessControlDelete::try_from(self, e))
2214 .collect();
2215
2216 let delete_acps = delete_acps.map_err(|e| {
2217 admin_error!("Unable to parse delete accesscontrols {:?}", e);
2218 e
2219 })?;
2220
2221 self.accesscontrols.update_delete(delete_acps).map_err(|e| {
2222 admin_error!("Failed to update delete accesscontrols {:?}", e);
2223 e
2224 })
2225 }
2226
2227 #[instrument(level = "debug", skip_all)]
2228 pub(crate) fn reload_key_material(&mut self) -> Result<(), OperationError> {
2229 let filt = filter!(f_eq(Attribute::Class, EntryClass::KeyProvider.into()));
2230
2231 let res = self.internal_search(filt).map_err(|e| {
2232 admin_error!(
2233 err = ?e,
2234 "reload key providers internal search failed",
2235 );
2236 e
2237 })?;
2238
2239 let providers = res
2242 .iter()
2243 .map(|e| KeyProvider::try_from(e).and_then(|kp| kp.test().map(|()| kp)))
2244 .collect::<Result<Vec<_>, _>>()?;
2245
2246 self.key_providers.update_providers(providers)?;
2247
2248 let filt = filter!(f_eq(Attribute::Class, EntryClass::KeyObject.into()));
2249
2250 let res = self.internal_search(filt).map_err(|e| {
2251 admin_error!(
2252 err = ?e,
2253 "reload key objects internal search failed",
2254 );
2255 e
2256 })?;
2257
2258 res.iter()
2259 .try_for_each(|entry| self.key_providers.load_key_object(entry.as_ref()))
2260 }
2261
2262 #[instrument(level = "debug", skip_all)]
2263 pub(crate) fn reload_system_config(&mut self) -> Result<(), OperationError> {
2264 let denied_names = self.get_sc_denied_names()?;
2265 let pw_badlist = self.get_sc_password_badlist()?;
2266
2267 let mut_system_config = self.system_config.get_mut();
2268 mut_system_config.denied_names = denied_names;
2269 mut_system_config.pw_badlist = pw_badlist;
2270 Ok(())
2271 }
2272
2273 #[instrument(level = "debug", skip_all)]
2275 pub(crate) fn reload_domain_info_version(&mut self) -> Result<(), OperationError> {
2276 let domain_info = self.internal_search_uuid(UUID_DOMAIN_INFO).map_err(|err| {
2277 error!(?err, "Error getting domain info");
2278 err
2279 })?;
2280
2281 let domain_info_version = domain_info
2282 .get_ava_single_uint32(Attribute::Version)
2283 .ok_or_else(|| {
2284 error!("domain info missing attribute version");
2285 OperationError::InvalidEntryState
2286 })?;
2287
2288 let domain_info_patch_level = domain_info
2289 .get_ava_single_uint32(Attribute::PatchLevel)
2290 .unwrap_or(0);
2291
2292 let current_devel_flag = option_env!("KANIDM_PRE_RELEASE").is_some();
2296 let domain_info_devel_taint = current_devel_flag
2297 || domain_info
2298 .get_ava_single_bool(Attribute::DomainDevelopmentTaint)
2299 .unwrap_or_default();
2300
2301 let domain_allow_easter_eggs = domain_info
2302 .get_ava_single_bool(Attribute::DomainAllowEasterEggs)
2303 .unwrap_or(option_env!("KANIDM_PRE_RELEASE").is_some());
2305
2306 let mut_d_info = self.d_info.get_mut();
2310 let previous_version = mut_d_info.d_vers;
2311 let previous_patch_level = mut_d_info.d_patch_level;
2312 mut_d_info.d_vers = domain_info_version;
2313 mut_d_info.d_patch_level = domain_info_patch_level;
2314 mut_d_info.d_devel_taint = domain_info_devel_taint;
2315 mut_d_info.d_allow_easter_eggs = domain_allow_easter_eggs;
2316
2317 if (previous_version == domain_info_version
2320 && previous_patch_level == domain_info_patch_level)
2321 || *self.phase < ServerPhase::DomainInfoReady
2322 {
2323 return Ok(());
2324 }
2325
2326 debug!(domain_previous_version = ?previous_version, domain_target_version = ?domain_info_version);
2327 debug!(domain_previous_patch_level = ?previous_patch_level, domain_target_patch_level = ?domain_info_patch_level);
2328
2329 if previous_version == DOMAIN_LEVEL_0 {
2333 debug!(
2334 "Server was just brought up, skipping migrations as we are already at target level"
2335 );
2336 return Ok(());
2337 }
2338
2339 if previous_version < DOMAIN_MIN_REMIGRATION_LEVEL {
2340 error!("UNABLE TO PROCEED. You are attempting a Skip update which is NOT SUPPORTED. You must upgrade one-version of Kanidm at a time.");
2341 error!("For more see: https://kanidm.github.io/kanidm/stable/support.html#upgrade-policy and https://kanidm.github.io/kanidm/stable/server_updates.html");
2342 error!(domain_previous_version = ?previous_version, domain_target_version = ?domain_info_version);
2343 error!(domain_previous_patch_level = ?previous_patch_level, domain_target_patch_level = ?domain_info_patch_level);
2344 return Err(OperationError::MG0008SkipUpgradeAttempted);
2345 }
2346
2347 if previous_version <= DOMAIN_LEVEL_8 && domain_info_version >= DOMAIN_LEVEL_9 {
2348 self.migrate_domain_8_to_9()?;
2350 }
2351
2352 if previous_patch_level < PATCH_LEVEL_2
2353 && domain_info_patch_level >= PATCH_LEVEL_2
2354 && domain_info_version == DOMAIN_LEVEL_9
2355 {
2356 self.migrate_domain_patch_level_2()?;
2357 }
2358
2359 if previous_version <= DOMAIN_LEVEL_9 && domain_info_version >= DOMAIN_LEVEL_10 {
2360 self.migrate_domain_9_to_10()?;
2362 }
2363
2364 if previous_version <= DOMAIN_LEVEL_10 && domain_info_version >= DOMAIN_LEVEL_11 {
2365 self.migrate_domain_10_to_11()?;
2367 }
2368
2369 if previous_version <= DOMAIN_LEVEL_11 && domain_info_version >= DOMAIN_LEVEL_12 {
2370 self.migrate_domain_11_to_12()?;
2372 }
2373
2374 debug_assert!(domain_info_version <= DOMAIN_MAX_LEVEL);
2378
2379 Ok(())
2380 }
2381
2382 #[instrument(level = "debug", skip_all)]
2384 pub(crate) fn reload_domain_info(&mut self) -> Result<(), OperationError> {
2385 let domain_entry = self.get_db_domain()?;
2386
2387 let domain_name = domain_entry
2388 .get_ava_single_iname(Attribute::DomainName)
2389 .map(str::to_string)
2390 .ok_or(OperationError::InvalidEntryState)?;
2391
2392 let display_name = domain_entry
2393 .get_ava_single_utf8(Attribute::DomainDisplayName)
2394 .map(str::to_string)
2395 .unwrap_or_else(|| format!("Kanidm {domain_name}"));
2396
2397 let domain_ldap_allow_unix_pw_bind = domain_entry
2398 .get_ava_single_bool(Attribute::LdapAllowUnixPwBind)
2399 .unwrap_or(true);
2400
2401 let domain_image = domain_entry.get_ava_single_image(Attribute::Image);
2402
2403 let domain_uuid = self.be_txn.get_db_d_uuid()?;
2404
2405 let mut_d_info = self.d_info.get_mut();
2406 mut_d_info.d_ldap_allow_unix_pw_bind = domain_ldap_allow_unix_pw_bind;
2407 if mut_d_info.d_uuid != domain_uuid {
2408 admin_warn!(
2409 "Using domain uuid from the database {} - was {} in memory",
2410 domain_name,
2411 mut_d_info.d_name,
2412 );
2413 mut_d_info.d_uuid = domain_uuid;
2414 }
2415 if mut_d_info.d_name != domain_name {
2416 admin_warn!(
2417 "Using domain name from the database {} - was {} in memory",
2418 domain_name,
2419 mut_d_info.d_name,
2420 );
2421 admin_warn!(
2422 "If you think this is an error, see https://kanidm.github.io/kanidm/master/domain_rename.html"
2423 );
2424 mut_d_info.d_name = domain_name;
2425 }
2426 mut_d_info.d_display = display_name;
2427 mut_d_info.d_image = domain_image;
2428 Ok(())
2429 }
2430
2431 pub fn set_domain_display_name(&mut self, new_domain_name: &str) -> Result<(), OperationError> {
2435 let modl = ModifyList::new_purge_and_set(
2436 Attribute::DomainDisplayName,
2437 Value::new_utf8(new_domain_name.to_string()),
2438 );
2439 let udi = PVUUID_DOMAIN_INFO.clone();
2440 let filt = filter_all!(f_eq(Attribute::Uuid, udi));
2441 self.internal_modify(&filt, &modl)
2442 }
2443
2444 pub fn danger_domain_rename(&mut self, new_domain_name: &str) -> Result<(), OperationError> {
2457 let modl =
2458 ModifyList::new_purge_and_set(Attribute::DomainName, Value::new_iname(new_domain_name));
2459 let udi = PVUUID_DOMAIN_INFO.clone();
2460 let filt = filter_all!(f_eq(Attribute::Uuid, udi));
2461 self.internal_modify(&filt, &modl)
2462 }
2463
2464 pub fn reindex(&mut self, immediate: bool) -> Result<(), OperationError> {
2465 self.be_txn.reindex(immediate)
2469 }
2470
2471 fn force_schema_reload(&mut self) {
2472 self.changed_flags.insert(ChangeFlag::SCHEMA);
2473 }
2474
2475 fn force_domain_reload(&mut self) {
2476 self.changed_flags.insert(ChangeFlag::DOMAIN);
2477 }
2478
2479 pub(crate) fn upgrade_reindex(&mut self, v: i64) -> Result<(), OperationError> {
2480 self.be_txn.upgrade_reindex(v)
2481 }
2482
2483 #[inline]
2484 pub(crate) fn get_changed_app(&self) -> bool {
2485 self.changed_flags.contains(ChangeFlag::APPLICATION)
2486 }
2487
2488 #[inline]
2489 pub(crate) fn get_changed_oauth2(&self) -> bool {
2490 self.changed_flags.contains(ChangeFlag::OAUTH2)
2491 }
2492
2493 #[inline]
2494 pub(crate) fn clear_changed_oauth2(&mut self) {
2495 self.changed_flags.remove(ChangeFlag::OAUTH2)
2496 }
2497
2498 pub(crate) fn set_phase_bootstrap(&mut self) {
2501 *self.phase = ServerPhase::Bootstrap;
2502 }
2503
2504 pub(crate) fn set_phase(&mut self, phase: ServerPhase) {
2506 if phase > *self.phase {
2508 *self.phase = phase
2509 }
2510 }
2511
2512 pub(crate) fn get_phase(&mut self) -> ServerPhase {
2513 *self.phase
2514 }
2515
2516 pub(crate) fn reload(&mut self) -> Result<(), OperationError> {
2517 if self.changed_flags.contains(ChangeFlag::DOMAIN) {
2520 self.reload_domain_info_version()?;
2521 }
2522
2523 if self.changed_flags.contains(ChangeFlag::SCHEMA) {
2528 self.reload_schema()?;
2529
2530 if *self.phase >= ServerPhase::Running {
2534 self.reindex(false)?;
2535 self.reload_schema()?;
2536 }
2537 }
2538
2539 if self
2542 .changed_flags
2543 .intersects(ChangeFlag::SCHEMA | ChangeFlag::KEY_MATERIAL)
2544 {
2545 self.reload_key_material()?;
2546 }
2547
2548 if self
2556 .changed_flags
2557 .intersects(ChangeFlag::SCHEMA | ChangeFlag::ACP | ChangeFlag::SYNC_AGREEMENT)
2558 {
2559 self.reload_accesscontrols()?;
2560 } else {
2561 }
2566
2567 if self.changed_flags.contains(ChangeFlag::SYSTEM_CONFIG) {
2568 self.reload_system_config()?;
2569 }
2570
2571 if self.changed_flags.contains(ChangeFlag::DOMAIN) {
2572 self.reload_domain_info()?;
2573 }
2574
2575 self.changed_flags.remove(
2577 ChangeFlag::DOMAIN
2578 | ChangeFlag::SCHEMA
2579 | ChangeFlag::SYSTEM_CONFIG
2580 | ChangeFlag::ACP
2581 | ChangeFlag::SYNC_AGREEMENT
2582 | ChangeFlag::KEY_MATERIAL,
2583 );
2584
2585 Ok(())
2586 }
2587
2588 #[cfg(any(test, debug_assertions))]
2589 #[instrument(level = "debug", skip_all)]
2590 pub fn clear_cache(&mut self) -> Result<(), OperationError> {
2591 self.be_txn.clear_cache()
2592 }
2593
2594 #[instrument(level = "info", name="qswt_commit" skip_all)]
2595 pub fn commit(mut self) -> Result<(), OperationError> {
2596 self.reload()?;
2597
2598 let QueryServerWriteTransaction {
2600 committed,
2601 phase,
2602 d_info,
2603 system_config,
2604 mut be_txn,
2605 schema,
2606 accesscontrols,
2607 cid,
2608 dyngroup_cache,
2609 key_providers,
2610 _db_ticket,
2612 _write_ticket,
2613 curtime: _,
2615 trim_cid: _,
2616 changed_flags,
2617 changed_uuid: _,
2618 resolve_filter_cache: _,
2619 resolve_filter_cache_clear,
2620 mut resolve_filter_cache_write,
2621 } = self;
2622 debug_assert!(!committed);
2623
2624 trace!(
2626 changed = ?changed_flags.iter_names().collect::<Vec<_>>(),
2627 );
2628
2629 be_txn.set_db_ts_max(cid.ts)?;
2632 cid.commit();
2633
2634 if resolve_filter_cache_clear {
2636 resolve_filter_cache_write.clear();
2637 }
2638 resolve_filter_cache_write.commit();
2639
2640 schema
2644 .commit()
2645 .map(|_| d_info.commit())
2646 .map(|_| system_config.commit())
2647 .map(|_| phase.commit())
2648 .map(|_| dyngroup_cache.commit())
2649 .and_then(|_| key_providers.commit())
2650 .and_then(|_| accesscontrols.commit())
2651 .and_then(|_| be_txn.commit())
2652 }
2653
2654 pub(crate) fn get_txn_cid(&self) -> &Cid {
2655 &self.cid
2656 }
2657}
2658
2659#[cfg(test)]
2660mod tests {
2661 use crate::prelude::*;
2662 use kanidm_proto::scim_v1::{
2663 client::ScimFilter,
2664 server::{ScimListResponse, ScimReference},
2665 JsonValue, ScimEntryGetQuery,
2666 };
2667
2668 #[qs_test]
2669 async fn test_name_to_uuid(server: &QueryServer) {
2670 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2671
2672 let t_uuid = Uuid::new_v4();
2673 assert!(server_txn
2674 .internal_create(vec![entry_init!(
2675 (Attribute::Class, EntryClass::Object.to_value()),
2676 (Attribute::Class, EntryClass::Account.to_value()),
2677 (Attribute::Class, EntryClass::Person.to_value()),
2678 (Attribute::Name, Value::new_iname("testperson1")),
2679 (Attribute::Uuid, Value::Uuid(t_uuid)),
2680 (Attribute::Description, Value::new_utf8s("testperson1")),
2681 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2682 ),])
2683 .is_ok());
2684
2685 let r1 = server_txn.name_to_uuid("testpers");
2687 assert!(r1.is_err());
2688 let r2 = server_txn.name_to_uuid("tEsTpErS");
2690 assert!(r2.is_err());
2691 let r3 = server_txn.name_to_uuid("testperson1");
2693 assert_eq!(r3, Ok(t_uuid));
2694 let r4 = server_txn.name_to_uuid("tEsTpErSoN1");
2696 assert_eq!(r4, Ok(t_uuid));
2697 let r5 = server_txn.name_to_uuid("name=testperson1");
2699 assert_eq!(r5, Ok(t_uuid));
2700 let r6 = server_txn.name_to_uuid("name=testperson1,o=example");
2702 assert_eq!(r6, Ok(t_uuid));
2703 }
2704
2705 #[qs_test]
2706 async fn test_external_id_to_uuid(server: &QueryServer) {
2707 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2708
2709 let t_uuid = Uuid::new_v4();
2710 assert!(server_txn
2711 .internal_create(vec![entry_init!(
2712 (Attribute::Class, EntryClass::Object.to_value()),
2713 (Attribute::Class, EntryClass::ExtensibleObject.to_value()),
2714 (Attribute::Uuid, Value::Uuid(t_uuid)),
2715 (
2716 Attribute::SyncExternalId,
2717 Value::new_iutf8("uid=testperson")
2718 )
2719 ),])
2720 .is_ok());
2721
2722 let r1 = server_txn.sync_external_id_to_uuid("tobias");
2724 assert_eq!(r1, Ok(None));
2725 let r2 = server_txn.sync_external_id_to_uuid("tObIAs");
2727 assert_eq!(r2, Ok(None));
2728 let r3 = server_txn.sync_external_id_to_uuid("uid=testperson");
2730 assert_eq!(r3, Ok(Some(t_uuid)));
2731 let r4 = server_txn.sync_external_id_to_uuid("uId=TeStPeRsOn");
2733 assert_eq!(r4, Ok(Some(t_uuid)));
2734 }
2735
2736 #[qs_test]
2737 async fn test_uuid_to_spn(server: &QueryServer) {
2738 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2739
2740 let e1 = entry_init!(
2741 (Attribute::Class, EntryClass::Object.to_value()),
2742 (Attribute::Class, EntryClass::Person.to_value()),
2743 (Attribute::Class, EntryClass::Account.to_value()),
2744 (Attribute::Name, Value::new_iname("testperson1")),
2745 (
2746 Attribute::Uuid,
2747 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2748 ),
2749 (Attribute::Description, Value::new_utf8s("testperson1")),
2750 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2751 );
2752 let ce = CreateEvent::new_internal(vec![e1]);
2753 let cr = server_txn.create(&ce);
2754 assert!(cr.is_ok());
2755
2756 let r1 = server_txn.uuid_to_spn(uuid!("bae3f507-e6c3-44ba-ad01-f8ff1083534a"));
2758 assert_eq!(r1, Ok(None));
2760 let r3 = server_txn.uuid_to_spn(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"));
2762 println!("{r3:?}");
2763 assert_eq!(
2764 r3.unwrap().unwrap(),
2765 Value::new_spn_str("testperson1", "example.com")
2766 );
2767 let r4 = server_txn.uuid_to_spn(uuid!("CC8E95B4-C24F-4D68-BA54-8BED76F63930"));
2769 assert_eq!(
2770 r4.unwrap().unwrap(),
2771 Value::new_spn_str("testperson1", "example.com")
2772 );
2773 }
2774
2775 #[qs_test]
2776 async fn test_uuid_to_rdn(server: &QueryServer) {
2777 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2778
2779 let e1 = entry_init!(
2780 (Attribute::Class, EntryClass::Object.to_value()),
2781 (Attribute::Class, EntryClass::Person.to_value()),
2782 (Attribute::Class, EntryClass::Account.to_value()),
2783 (Attribute::Name, Value::new_iname("testperson1")),
2784 (
2785 Attribute::Uuid,
2786 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2787 ),
2788 (Attribute::Description, Value::new_utf8s("testperson")),
2789 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2790 );
2791 let ce = CreateEvent::new_internal(vec![e1]);
2792 let cr = server_txn.create(&ce);
2793 assert!(cr.is_ok());
2794
2795 let r1 = server_txn.uuid_to_rdn(uuid!("bae3f507-e6c3-44ba-ad01-f8ff1083534a"));
2797 assert_eq!(r1.unwrap(), "uuid=bae3f507-e6c3-44ba-ad01-f8ff1083534a");
2799 let r3 = server_txn.uuid_to_rdn(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"));
2801 println!("{r3:?}");
2802 assert_eq!(r3.unwrap(), "spn=testperson1@example.com");
2803 let r4 = server_txn.uuid_to_rdn(uuid!("CC8E95B4-C24F-4D68-BA54-8BED76F63930"));
2805 assert_eq!(r4.unwrap(), "spn=testperson1@example.com");
2806 }
2807
2808 #[qs_test]
2809 async fn test_clone_value(server: &QueryServer) {
2810 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2811 let e1 = entry_init!(
2812 (Attribute::Class, EntryClass::Object.to_value()),
2813 (Attribute::Class, EntryClass::Account.to_value()),
2814 (Attribute::Class, EntryClass::Person.to_value()),
2815 (Attribute::Name, Value::new_iname("testperson1")),
2816 (
2817 Attribute::Uuid,
2818 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2819 ),
2820 (Attribute::Description, Value::new_utf8s("testperson1")),
2821 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2822 );
2823 let ce = CreateEvent::new_internal(vec![e1]);
2824 let cr = server_txn.create(&ce);
2825 assert!(cr.is_ok());
2826
2827 let r1 = server_txn.clone_value(&Attribute::from("tausau"), "naoeutnhaou");
2829
2830 assert!(r1.is_err());
2831
2832 let r2 = server_txn.clone_value(&Attribute::Custom("NaMe".into()), "NaMe");
2835
2836 assert!(r2.is_err());
2837
2838 let r3 = server_txn.clone_value(&Attribute::from("member"), "testperson1");
2840
2841 assert_eq!(
2842 r3,
2843 Ok(Value::Refer(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")))
2844 );
2845
2846 let r4 = server_txn.clone_value(
2848 &Attribute::from("member"),
2849 "cc8e95b4-c24f-4d68-ba54-8bed76f63930",
2850 );
2851
2852 debug!("{:?}", r4);
2853 assert_eq!(
2854 r4,
2855 Ok(Value::Refer(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")))
2856 );
2857 }
2858
2859 #[qs_test]
2860 async fn test_dynamic_schema_class(server: &QueryServer) {
2861 let e1 = entry_init!(
2862 (Attribute::Class, EntryClass::Object.to_value()),
2863 (Attribute::Class, EntryClass::TestClass.to_value()),
2864 (Attribute::Name, Value::new_iname("testobj1")),
2865 (
2866 Attribute::Uuid,
2867 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2868 )
2869 );
2870
2871 let e_cd = entry_init!(
2873 (Attribute::Class, EntryClass::Object.to_value()),
2874 (Attribute::Class, EntryClass::ClassType.to_value()),
2875 (Attribute::ClassName, EntryClass::TestClass.to_value()),
2876 (
2877 Attribute::Uuid,
2878 Value::Uuid(uuid!("cfcae205-31c3-484b-8ced-667d1709c5e3"))
2879 ),
2880 (Attribute::Description, Value::new_utf8s("Test Class")),
2881 (Attribute::May, Value::from(Attribute::Name))
2882 );
2883 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2884 let ce_class = CreateEvent::new_internal(vec![e_cd.clone()]);
2886 assert!(server_txn.create(&ce_class).is_ok());
2887 let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
2889 assert!(server_txn.create(&ce_fail).is_err());
2890
2891 server_txn.commit().expect("should not fail");
2893
2894 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2896 let ce_work = CreateEvent::new_internal(vec![e1.clone()]);
2899 assert!(server_txn.create(&ce_work).is_ok());
2900
2901 server_txn.commit().expect("should not fail");
2903
2904 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2906 let de_class = DeleteEvent::new_internal_invalid(filter!(f_eq(
2908 Attribute::ClassName,
2909 EntryClass::TestClass.into()
2910 )));
2911 assert!(server_txn.delete(&de_class).is_ok());
2912 server_txn.commit().expect("should not fail");
2914
2915 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2917 let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
2919 assert!(server_txn.create(&ce_fail).is_err());
2920 let testobj1 = server_txn
2922 .internal_search_uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2923 .expect("failed");
2924 assert!(testobj1.attribute_equality(Attribute::Class, &EntryClass::TestClass.into()));
2925
2926 server_txn.commit().expect("should not fail");
2928 }
2930
2931 #[qs_test]
2932 async fn test_dynamic_schema_attr(server: &QueryServer) {
2933 let e1 = entry_init!(
2934 (Attribute::Class, EntryClass::Object.to_value()),
2935 (Attribute::Class, EntryClass::ExtensibleObject.to_value()),
2936 (Attribute::Name, Value::new_iname("testobj1")),
2937 (
2938 Attribute::Uuid,
2939 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2940 ),
2941 (Attribute::TestAttr, Value::new_utf8s("test"))
2942 );
2943
2944 let e_ad = entry_init!(
2946 (Attribute::Class, EntryClass::Object.to_value()),
2947 (Attribute::Class, EntryClass::AttributeType.to_value()),
2948 (
2949 Attribute::Uuid,
2950 Value::Uuid(uuid!("cfcae205-31c3-484b-8ced-667d1709c5e3"))
2951 ),
2952 (Attribute::AttributeName, Value::from(Attribute::TestAttr)),
2953 (Attribute::Description, Value::new_utf8s("Test Attribute")),
2954 (Attribute::MultiValue, Value::new_bool(false)),
2955 (Attribute::Unique, Value::new_bool(false)),
2956 (
2957 Attribute::Syntax,
2958 Value::new_syntaxs("UTF8STRING").expect("syntax")
2959 )
2960 );
2961
2962 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2963 let ce_attr = CreateEvent::new_internal(vec![e_ad.clone()]);
2965 assert!(server_txn.create(&ce_attr).is_ok());
2966 let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
2968 assert!(server_txn.create(&ce_fail).is_err());
2969
2970 server_txn.commit().expect("should not fail");
2972
2973 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2975 let ce_work = CreateEvent::new_internal(vec![e1.clone()]);
2978 assert!(server_txn.create(&ce_work).is_ok());
2979
2980 server_txn.commit().expect("should not fail");
2982
2983 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2985 let de_attr = DeleteEvent::new_internal_invalid(filter!(f_eq(
2987 Attribute::AttributeName,
2988 PartialValue::from(Attribute::TestAttr)
2989 )));
2990 assert!(server_txn.delete(&de_attr).is_ok());
2991 server_txn.commit().expect("should not fail");
2993
2994 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2996 let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
2998 assert!(server_txn.create(&ce_fail).is_err());
2999 let filt = filter!(f_eq(Attribute::TestAttr, PartialValue::new_utf8s("test")));
3001 assert!(server_txn.internal_search(filt).is_err());
3002 let testobj1 = server_txn
3005 .internal_search_uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
3006 .expect("failed");
3007 assert!(testobj1.attribute_equality(Attribute::TestAttr, &PartialValue::new_utf8s("test")));
3008
3009 server_txn.commit().expect("should not fail");
3010 }
3012
3013 #[qs_test]
3014 async fn test_scim_entry_structure(server: &QueryServer) {
3015 let mut read_txn = server.read().await.unwrap();
3016
3017 let entry = read_txn
3019 .internal_search_uuid(UUID_IDM_PEOPLE_SELF_NAME_WRITE)
3020 .unwrap();
3021
3022 let reduced = entry.as_ref().clone().into_reduced();
3024 let scim_entry = reduced.to_scim_kanidm(&mut read_txn).unwrap();
3025
3026 assert_eq!(scim_entry.header.id, UUID_IDM_PEOPLE_SELF_NAME_WRITE);
3028 let name_scim = scim_entry.attrs.get(&Attribute::Name).unwrap();
3029 match name_scim {
3030 ScimValueKanidm::String(name) => {
3031 assert_eq!(name.clone(), "idm_people_self_name_write")
3032 }
3033 _ => {
3034 panic!("expected String, actual {name_scim:?}");
3035 }
3036 }
3037
3038 let entry_managed_by_scim = scim_entry.attrs.get(&Attribute::EntryManagedBy).unwrap();
3040 match entry_managed_by_scim {
3041 ScimValueKanidm::EntryReferences(managed_by) => {
3042 assert_eq!(
3043 managed_by.first().unwrap().clone(),
3044 ScimReference {
3045 uuid: UUID_IDM_ADMINS,
3046 value: "idm_admins@example.com".to_string()
3047 }
3048 )
3049 }
3050 _ => {
3051 panic!("expected EntryReference, actual {entry_managed_by_scim:?}");
3052 }
3053 }
3054
3055 let members_scim = scim_entry.attrs.get(&Attribute::Member).unwrap();
3056 match members_scim {
3057 ScimValueKanidm::EntryReferences(members) => {
3058 assert_eq!(
3059 members.first().unwrap().clone(),
3060 ScimReference {
3061 uuid: UUID_IDM_ALL_PERSONS,
3062 value: "idm_all_persons@example.com".to_string()
3063 }
3064 )
3065 }
3066 _ => {
3067 panic!("expected EntryReferences, actual {members_scim:?}");
3068 }
3069 }
3070 }
3071
3072 #[qs_test]
3073 async fn test_scim_effective_access_query(server: &QueryServer) {
3074 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3075
3076 let group_uuid = Uuid::new_v4();
3077 let e1 = entry_init!(
3078 (Attribute::Class, EntryClass::Object.to_value()),
3079 (Attribute::Class, EntryClass::Group.to_value()),
3080 (Attribute::Name, Value::new_iname("testgroup")),
3081 (Attribute::Uuid, Value::Uuid(group_uuid))
3082 );
3083
3084 assert!(server_txn.internal_create(vec![e1]).is_ok());
3085 assert!(server_txn.commit().is_ok());
3086
3087 let mut server_txn = server.read().await.unwrap();
3090
3091 let idm_admin_entry = server_txn.internal_search_uuid(UUID_IDM_ADMIN).unwrap();
3092 let idm_admin_ident = Identity::from_impersonate_entry_readwrite(idm_admin_entry);
3093
3094 let query = ScimEntryGetQuery {
3095 ext_access_check: true,
3096 ..Default::default()
3097 };
3098
3099 let scim_entry = server_txn
3100 .scim_entry_id_get_ext(group_uuid, EntryClass::Group, query, idm_admin_ident)
3101 .unwrap();
3102
3103 let ext_access_check = scim_entry.ext_access_check.unwrap();
3104
3105 trace!(?ext_access_check);
3106
3107 assert!(ext_access_check.delete);
3108 assert!(ext_access_check.search.check(&Attribute::DirectMemberOf));
3109 assert!(ext_access_check.search.check(&Attribute::MemberOf));
3110 assert!(ext_access_check.search.check(&Attribute::Name));
3111 assert!(ext_access_check.modify_present.check(&Attribute::Name));
3112 assert!(ext_access_check.modify_remove.check(&Attribute::Name));
3113 }
3114
3115 #[qs_test]
3116 async fn test_scim_basic_search_ext_query(server: &QueryServer) {
3117 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3118
3119 let group_uuid = Uuid::new_v4();
3120 let e1 = entry_init!(
3121 (Attribute::Class, EntryClass::Object.to_value()),
3122 (Attribute::Class, EntryClass::Group.to_value()),
3123 (Attribute::Name, Value::new_iname("testgroup")),
3124 (Attribute::Uuid, Value::Uuid(group_uuid))
3125 );
3126
3127 assert!(server_txn.internal_create(vec![e1]).is_ok());
3128 assert!(server_txn.commit().is_ok());
3129
3130 let mut server_txn = server.read().await.unwrap();
3132
3133 let idm_admin_entry = server_txn.internal_search_uuid(UUID_IDM_ADMIN).unwrap();
3134 let idm_admin_ident = Identity::from_impersonate_entry_readwrite(idm_admin_entry);
3135
3136 let filter = ScimFilter::And(
3137 Box::new(ScimFilter::Equal(
3138 Attribute::Class.into(),
3139 EntryClass::Group.into(),
3140 )),
3141 Box::new(ScimFilter::Equal(
3142 Attribute::Uuid.into(),
3143 JsonValue::String(group_uuid.to_string()),
3144 )),
3145 );
3146
3147 let base: ScimListResponse = server_txn
3148 .scim_search_ext(idm_admin_ident, &filter, ScimEntryGetQuery::default())
3149 .unwrap();
3150
3151 assert_eq!(base.resources.len(), 1);
3152 assert_eq!(base.total_results, 1);
3153 assert_eq!(base.resources[0].header.id, group_uuid);
3154 }
3155}