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::client::ScimFilter;
37use kanidm_proto::scim_v1::server::ScimOAuth2ClaimMap;
38use kanidm_proto::scim_v1::server::ScimOAuth2ScopeMap;
39use kanidm_proto::scim_v1::server::ScimReference;
40use kanidm_proto::scim_v1::JsonValue;
41use kanidm_proto::scim_v1::ScimEntryGetQuery;
42use std::collections::BTreeSet;
43use std::str::FromStr;
44use std::sync::Arc;
45use tokio::sync::{Semaphore, SemaphorePermit};
46use tracing::trace;
47
48pub(crate) mod access;
49pub mod batch_modify;
50pub mod create;
51pub mod delete;
52pub mod identity;
53pub(crate) mod keys;
54pub(crate) mod migrations;
55pub mod modify;
56pub(crate) mod recycle;
57pub mod scim;
58
59const RESOLVE_FILTER_CACHE_MAX: usize = 256;
60const RESOLVE_FILTER_CACHE_LOCAL: usize = 8;
61
62#[derive(Debug, Clone, Copy, PartialOrd, PartialEq, Eq)]
63pub(crate) enum ServerPhase {
64 Bootstrap,
65 SchemaReady,
66 DomainInfoReady,
67 Running,
68}
69
70#[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_search(
488 &mut self,
489 filter: Filter<FilterInvalid>,
490 ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
491 let f_valid = filter
492 .validate(self.get_schema())
493 .map_err(OperationError::SchemaViolation)?;
494 let se = SearchEvent::new_internal(f_valid);
495 self.search(&se)
496 }
497
498 #[instrument(level = "debug", skip_all)]
499 fn impersonate_search_valid(
500 &mut self,
501 f_valid: Filter<FilterValid>,
502 f_intent_valid: Filter<FilterValid>,
503 event: &Identity,
504 ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
505 let se = SearchEvent::new_impersonate(event, f_valid, f_intent_valid);
506 self.search(&se)
507 }
508
509 fn impersonate_search_ext_valid(
511 &mut self,
512 f_valid: Filter<FilterValid>,
513 f_intent_valid: Filter<FilterValid>,
514 event: &Identity,
515 ) -> Result<Vec<Entry<EntryReduced, EntryCommitted>>, OperationError> {
516 let se = SearchEvent::new_impersonate(event, f_valid, f_intent_valid);
517 self.search_ext(&se)
518 }
519
520 fn impersonate_search(
522 &mut self,
523 filter: Filter<FilterInvalid>,
524 filter_intent: Filter<FilterInvalid>,
525 event: &Identity,
526 ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
527 let f_valid = filter
528 .validate(self.get_schema())
529 .map_err(OperationError::SchemaViolation)?;
530 let f_intent_valid = filter_intent
531 .validate(self.get_schema())
532 .map_err(OperationError::SchemaViolation)?;
533 self.impersonate_search_valid(f_valid, f_intent_valid, event)
534 }
535
536 #[instrument(level = "debug", skip_all)]
537 fn impersonate_search_ext(
538 &mut self,
539 filter: Filter<FilterInvalid>,
540 filter_intent: Filter<FilterInvalid>,
541 event: &Identity,
542 ) -> Result<Vec<Entry<EntryReduced, EntryCommitted>>, OperationError> {
543 let f_valid = filter
544 .validate(self.get_schema())
545 .map_err(OperationError::SchemaViolation)?;
546 let f_intent_valid = filter_intent
547 .validate(self.get_schema())
548 .map_err(OperationError::SchemaViolation)?;
549 self.impersonate_search_ext_valid(f_valid, f_intent_valid, event)
550 }
551
552 #[instrument(level = "debug", skip_all)]
555 fn internal_search_uuid(
556 &mut self,
557 uuid: Uuid,
558 ) -> Result<Arc<EntrySealedCommitted>, OperationError> {
559 let filter = filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
560 let f_valid = filter.validate(self.get_schema()).map_err(|e| {
561 error!(?e, "Filter Validate - SchemaViolation");
562 OperationError::SchemaViolation(e)
563 })?;
564 let se = SearchEvent::new_internal(f_valid);
565
566 let mut vs = self.search(&se)?;
567 match vs.pop() {
568 Some(entry) if vs.is_empty() => Ok(entry),
569 _ => Err(OperationError::NoMatchingEntries),
570 }
571 }
572
573 #[instrument(level = "debug", skip_all)]
576 fn internal_search_all_uuid(
577 &mut self,
578 uuid: Uuid,
579 ) -> Result<Arc<EntrySealedCommitted>, OperationError> {
580 let filter = filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
581 let f_valid = filter.validate(self.get_schema()).map_err(|e| {
582 error!(?e, "Filter Validate - SchemaViolation");
583 OperationError::SchemaViolation(e)
584 })?;
585 let se = SearchEvent::new_internal(f_valid);
586
587 let mut vs = self.search(&se)?;
588 match vs.pop() {
589 Some(entry) if vs.is_empty() => Ok(entry),
590 _ => Err(OperationError::NoMatchingEntries),
591 }
592 }
593
594 #[instrument(level = "debug", skip_all)]
596 fn internal_search_conflict_uuid(
597 &mut self,
598 uuid: Uuid,
599 ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
600 let filter = filter_all!(f_and(vec![
601 f_eq(Attribute::SourceUuid, PartialValue::Uuid(uuid)),
602 f_eq(Attribute::Class, EntryClass::Conflict.into())
603 ]));
604 let f_valid = filter.validate(self.get_schema()).map_err(|e| {
605 error!(?e, "Filter Validate - SchemaViolation");
606 OperationError::SchemaViolation(e)
607 })?;
608 let se = SearchEvent::new_internal(f_valid);
609
610 self.search(&se)
611 }
612
613 #[instrument(level = "debug", skip_all)]
614 fn impersonate_search_ext_uuid(
615 &mut self,
616 uuid: Uuid,
617 event: &Identity,
618 ) -> Result<Entry<EntryReduced, EntryCommitted>, OperationError> {
619 let filter_intent = filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
620 let filter = filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
621
622 let mut vs = self.impersonate_search_ext(filter, filter_intent, event)?;
623 match vs.pop() {
624 Some(entry) if vs.is_empty() => Ok(entry),
625 _ => {
626 if vs.is_empty() {
627 Err(OperationError::NoMatchingEntries)
628 } else {
629 Err(OperationError::UniqueConstraintViolation)
631 }
632 }
633 }
634 }
635
636 #[instrument(level = "debug", skip_all)]
637 fn impersonate_search_uuid(
638 &mut self,
639 uuid: Uuid,
640 event: &Identity,
641 ) -> Result<Arc<EntrySealedCommitted>, OperationError> {
642 let filter_intent = filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
643 let filter = filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)));
644
645 let mut vs = self.impersonate_search(filter, filter_intent, event)?;
646 match vs.pop() {
647 Some(entry) if vs.is_empty() => Ok(entry),
648 _ => Err(OperationError::NoMatchingEntries),
649 }
650 }
651
652 fn clone_value(&mut self, attr: &Attribute, value: &str) -> Result<Value, OperationError> {
655 let schema = self.get_schema();
656
657 match schema.get_attributes().get(attr) {
662 Some(schema_a) => {
663 match schema_a.syntax {
664 SyntaxType::Utf8String => Ok(Value::new_utf8(value.to_string())),
665 SyntaxType::Utf8StringInsensitive => Ok(Value::new_iutf8(value)),
666 SyntaxType::Utf8StringIname => Ok(Value::new_iname(value)),
667 SyntaxType::Boolean => Value::new_bools(value)
668 .ok_or_else(|| OperationError::InvalidAttribute("Invalid boolean syntax".to_string())),
669 SyntaxType::SyntaxId => Value::new_syntaxs(value)
670 .ok_or_else(|| OperationError::InvalidAttribute("Invalid Syntax syntax".to_string())),
671 SyntaxType::IndexId => Value::new_indexes(value)
672 .ok_or_else(|| OperationError::InvalidAttribute("Invalid Index syntax".to_string())),
673 SyntaxType::CredentialType => CredentialType::try_from(value)
674 .map(Value::CredentialType)
675 .map_err(|()| OperationError::InvalidAttribute("Invalid CredentialType syntax".to_string())),
676 SyntaxType::Uuid => {
677 let un = self
680 .name_to_uuid(value)
681 .unwrap_or(UUID_DOES_NOT_EXIST);
682 Ok(Value::Uuid(un))
683 }
684 SyntaxType::ReferenceUuid => {
685 let un = self
686 .name_to_uuid(value)
687 .unwrap_or(UUID_DOES_NOT_EXIST);
688 Ok(Value::Refer(un))
689 }
690 SyntaxType::JsonFilter => Value::new_json_filter_s(value)
691 .ok_or_else(|| OperationError::InvalidAttribute("Invalid Filter syntax".to_string())),
692 SyntaxType::Image => Value::new_image(value),
693
694 SyntaxType::Credential => Err(OperationError::InvalidAttribute("Credentials can not be supplied through modification - please use the IDM api".to_string())),
695 SyntaxType::SecretUtf8String => Err(OperationError::InvalidAttribute("Radius secrets can not be supplied through modification - please use the IDM api".to_string())),
696 SyntaxType::SshKey => Err(OperationError::InvalidAttribute("SSH public keys can not be supplied through modification - please use the IDM api".to_string())),
697 SyntaxType::SecurityPrincipalName => Err(OperationError::InvalidAttribute("SPNs are generated and not able to be set.".to_string())),
698 SyntaxType::Uint32 => Value::new_uint32_str(value)
699 .ok_or_else(|| OperationError::InvalidAttribute("Invalid uint32 syntax".to_string())),
700 SyntaxType::Cid => Err(OperationError::InvalidAttribute("CIDs are generated and not able to be set.".to_string())),
701 SyntaxType::NsUniqueId => Value::new_nsuniqueid_s(value)
702 .ok_or_else(|| OperationError::InvalidAttribute("Invalid NsUniqueId syntax".to_string())),
703 SyntaxType::DateTime => Value::new_datetime_s(value)
704 .ok_or_else(|| OperationError::InvalidAttribute("Invalid DateTime (rfc3339) syntax".to_string())),
705 SyntaxType::EmailAddress => Value::new_email_address_s(value)
706 .ok_or_else(|| OperationError::InvalidAttribute("Invalid Email Address syntax".to_string())),
707 SyntaxType::Url => Value::new_url_s(value)
708 .ok_or_else(|| OperationError::InvalidAttribute("Invalid Url (whatwg/url) syntax".to_string())),
709 SyntaxType::OauthScope => Value::new_oauthscope(value)
710 .ok_or_else(|| OperationError::InvalidAttribute("Invalid Oauth Scope syntax".to_string())),
711 SyntaxType::WebauthnAttestationCaList => Value::new_webauthn_attestation_ca_list(value)
712 .ok_or_else(|| OperationError::InvalidAttribute("Invalid Webauthn Attestation CA List".to_string())),
713 SyntaxType::OauthScopeMap => Err(OperationError::InvalidAttribute("Oauth Scope Maps can not be supplied through modification - please use the IDM api".to_string())),
714 SyntaxType::OauthClaimMap => Err(OperationError::InvalidAttribute("Oauth Claim Maps can not be supplied through modification - please use the IDM api".to_string())),
715 SyntaxType::PrivateBinary => Err(OperationError::InvalidAttribute("Private Binary Values can not be supplied through modification".to_string())),
716 SyntaxType::IntentToken => Err(OperationError::InvalidAttribute("Intent Token Values can not be supplied through modification".to_string())),
717 SyntaxType::Passkey => Err(OperationError::InvalidAttribute("Passkey Values can not be supplied through modification".to_string())),
718 SyntaxType::AttestedPasskey => Err(OperationError::InvalidAttribute("AttestedPasskey Values can not be supplied through modification".to_string())),
719 SyntaxType::Session => Err(OperationError::InvalidAttribute("Session Values can not be supplied through modification".to_string())),
720 SyntaxType::ApiToken => Err(OperationError::InvalidAttribute("ApiToken Values can not be supplied through modification".to_string())),
721 SyntaxType::JwsKeyEs256 => Err(OperationError::InvalidAttribute("JwsKeyEs256 Values can not be supplied through modification".to_string())),
722 SyntaxType::JwsKeyRs256 => Err(OperationError::InvalidAttribute("JwsKeyRs256 Values can not be supplied through modification".to_string())),
723 SyntaxType::Oauth2Session => Err(OperationError::InvalidAttribute("Oauth2Session Values can not be supplied through modification".to_string())),
724 SyntaxType::UiHint => UiHint::from_str(value)
725 .map(Value::UiHint)
726 .map_err(|()| OperationError::InvalidAttribute("Invalid uihint syntax".to_string())),
727 SyntaxType::TotpSecret => Err(OperationError::InvalidAttribute("TotpSecret Values can not be supplied through modification".to_string())),
728 SyntaxType::AuditLogString => Err(OperationError::InvalidAttribute("Audit logs are generated and not able to be set.".to_string())),
729 SyntaxType::EcKeyPrivate => Err(OperationError::InvalidAttribute("Ec keys are generated and not able to be set.".to_string())),
730 SyntaxType::KeyInternal => Err(OperationError::InvalidAttribute("Internal keys are generated and not able to be set.".to_string())),
731 SyntaxType::HexString => Value::new_hex_string_s(value)
732 .ok_or_else(|| OperationError::InvalidAttribute("Invalid hex string syntax".to_string())),
733 SyntaxType::Certificate => Value::new_certificate_s(value)
734 .ok_or_else(|| OperationError::InvalidAttribute("Invalid x509 certificate syntax".to_string())),
735 SyntaxType::ApplicationPassword => Err(OperationError::InvalidAttribute("ApplicationPassword values can not be supplied through modification".to_string())),
736 }
737 }
738 None => {
739 Err(OperationError::InvalidAttributeName(attr.to_string()))
742 }
743 }
744 }
745
746 fn clone_partialvalue(
747 &mut self,
748 attr: &Attribute,
749 value: &str,
750 ) -> Result<PartialValue, OperationError> {
751 let schema = self.get_schema();
752
753 match schema.get_attributes().get(attr) {
755 Some(schema_a) => {
756 match schema_a.syntax {
757 SyntaxType::Utf8String | SyntaxType::TotpSecret => {
758 Ok(PartialValue::new_utf8(value.to_string()))
759 }
760 SyntaxType::Utf8StringInsensitive
761 | SyntaxType::JwsKeyEs256
762 | SyntaxType::JwsKeyRs256 => Ok(PartialValue::new_iutf8(value)),
763 SyntaxType::Utf8StringIname => Ok(PartialValue::new_iname(value)),
764 SyntaxType::Boolean => PartialValue::new_bools(value).ok_or_else(|| {
765 OperationError::InvalidAttribute("Invalid boolean syntax".to_string())
766 }),
767 SyntaxType::SyntaxId => PartialValue::new_syntaxs(value).ok_or_else(|| {
768 OperationError::InvalidAttribute("Invalid Syntax syntax".to_string())
769 }),
770 SyntaxType::IndexId => PartialValue::new_indexes(value).ok_or_else(|| {
771 OperationError::InvalidAttribute("Invalid Index syntax".to_string())
772 }),
773 SyntaxType::CredentialType => CredentialType::try_from(value)
774 .map(PartialValue::CredentialType)
775 .map_err(|()| {
776 OperationError::InvalidAttribute(
777 "Invalid credentialtype syntax".to_string(),
778 )
779 }),
780 SyntaxType::Uuid => {
781 let un = self.name_to_uuid(value).unwrap_or(UUID_DOES_NOT_EXIST);
782 Ok(PartialValue::Uuid(un))
783 }
784 SyntaxType::ReferenceUuid
788 | SyntaxType::OauthScopeMap
789 | SyntaxType::Session
790 | SyntaxType::ApiToken
791 | SyntaxType::Oauth2Session
792 | SyntaxType::ApplicationPassword => {
793 let un = self.name_to_uuid(value).unwrap_or(UUID_DOES_NOT_EXIST);
794 Ok(PartialValue::Refer(un))
795 }
796 SyntaxType::OauthClaimMap => self
797 .name_to_uuid(value)
798 .map(PartialValue::Refer)
799 .or_else(|_| Ok(PartialValue::new_iutf8(value))),
800
801 SyntaxType::JsonFilter => {
802 PartialValue::new_json_filter_s(value).ok_or_else(|| {
803 OperationError::InvalidAttribute("Invalid Filter syntax".to_string())
804 })
805 }
806 SyntaxType::Credential => Ok(PartialValue::new_credential_tag(value)),
807 SyntaxType::SecretUtf8String => Ok(PartialValue::new_secret_str()),
808 SyntaxType::SshKey => Ok(PartialValue::new_sshkey_tag_s(value)),
809 SyntaxType::SecurityPrincipalName => {
810 PartialValue::new_spn_s(value).ok_or_else(|| {
811 OperationError::InvalidAttribute("Invalid spn syntax".to_string())
812 })
813 }
814 SyntaxType::Uint32 => PartialValue::new_uint32_str(value).ok_or_else(|| {
815 OperationError::InvalidAttribute("Invalid uint32 syntax".to_string())
816 }),
817 SyntaxType::Cid => PartialValue::new_cid_s(value).ok_or_else(|| {
818 OperationError::InvalidAttribute("Invalid cid syntax".to_string())
819 }),
820 SyntaxType::NsUniqueId => Ok(PartialValue::new_nsuniqueid_s(value)),
821 SyntaxType::DateTime => PartialValue::new_datetime_s(value).ok_or_else(|| {
822 OperationError::InvalidAttribute(
823 "Invalid DateTime (rfc3339) syntax".to_string(),
824 )
825 }),
826 SyntaxType::EmailAddress => Ok(PartialValue::new_email_address_s(value)),
827 SyntaxType::Url => PartialValue::new_url_s(value).ok_or_else(|| {
828 OperationError::InvalidAttribute(
829 "Invalid Url (whatwg/url) syntax".to_string(),
830 )
831 }),
832 SyntaxType::OauthScope => Ok(PartialValue::new_oauthscope(value)),
833 SyntaxType::PrivateBinary => Ok(PartialValue::PrivateBinary),
834 SyntaxType::IntentToken => PartialValue::new_intenttoken_s(value.to_string())
835 .ok_or_else(|| {
836 OperationError::InvalidAttribute(
837 "Invalid Intent Token ID (uuid) syntax".to_string(),
838 )
839 }),
840 SyntaxType::Passkey => PartialValue::new_passkey_s(value).ok_or_else(|| {
841 OperationError::InvalidAttribute("Invalid Passkey UUID syntax".to_string())
842 }),
843 SyntaxType::AttestedPasskey => PartialValue::new_attested_passkey_s(value)
844 .ok_or_else(|| {
845 OperationError::InvalidAttribute(
846 "Invalid AttestedPasskey UUID syntax".to_string(),
847 )
848 }),
849 SyntaxType::UiHint => UiHint::from_str(value)
850 .map(PartialValue::UiHint)
851 .map_err(|()| {
852 OperationError::InvalidAttribute("Invalid uihint syntax".to_string())
853 }),
854 SyntaxType::AuditLogString => Ok(PartialValue::new_utf8s(value)),
855 SyntaxType::EcKeyPrivate => Ok(PartialValue::SecretValue),
856 SyntaxType::Image => Ok(PartialValue::new_utf8s(value)),
857 SyntaxType::WebauthnAttestationCaList => Err(OperationError::InvalidAttribute(
858 "Invalid - unable to query attestation CA list".to_string(),
859 )),
860 SyntaxType::HexString | SyntaxType::KeyInternal | SyntaxType::Certificate => {
861 PartialValue::new_hex_string_s(value).ok_or_else(|| {
862 OperationError::InvalidAttribute(
863 "Invalid syntax, expected hex string".to_string(),
864 )
865 })
866 }
867 }
868 }
869 None => {
870 Err(OperationError::InvalidAttributeName(attr.to_string()))
873 }
874 }
875 }
876
877 fn resolve_scim_interim(
878 &mut self,
879 scim_value_intermediate: ScimValueIntermediate,
880 ) -> Result<Option<ScimValueKanidm>, OperationError> {
881 match scim_value_intermediate {
882 ScimValueIntermediate::References(uuids) => {
883 let scim_references = uuids
884 .into_iter()
885 .map(|uuid| {
886 self.uuid_to_spn(uuid)
887 .and_then(|maybe_value| {
888 maybe_value.ok_or(OperationError::InvalidValueState)
889 })
890 .map(|value| ScimReference {
891 uuid,
892 value: value.to_proto_string_clone(),
893 })
894 })
895 .collect::<Result<Vec<_>, _>>()?;
896 Ok(Some(ScimValueKanidm::EntryReferences(scim_references)))
897 }
898 ScimValueIntermediate::Oauth2ClaimMap(unresolved_maps) => {
899 let scim_claim_maps = unresolved_maps
900 .into_iter()
901 .map(
902 |UnresolvedScimValueOauth2ClaimMap {
903 group_uuid,
904 claim,
905 join_char,
906 values,
907 }| {
908 self.uuid_to_spn(group_uuid)
909 .and_then(|maybe_value| {
910 maybe_value.ok_or(OperationError::InvalidValueState)
911 })
912 .map(|value| ScimOAuth2ClaimMap {
913 group: value.to_proto_string_clone(),
914 group_uuid,
915 claim,
916 join_char,
917 values,
918 })
919 },
920 )
921 .collect::<Result<Vec<_>, _>>()?;
922
923 Ok(Some(ScimValueKanidm::OAuth2ClaimMap(scim_claim_maps)))
924 }
925
926 ScimValueIntermediate::Oauth2ScopeMap(unresolved_maps) => {
927 let scim_claim_maps = unresolved_maps
928 .into_iter()
929 .map(|UnresolvedScimValueOauth2ScopeMap { group_uuid, scopes }| {
930 self.uuid_to_spn(group_uuid)
931 .and_then(|maybe_value| {
932 maybe_value.ok_or(OperationError::InvalidValueState)
933 })
934 .map(|value| ScimOAuth2ScopeMap {
935 group: value.to_proto_string_clone(),
936 group_uuid,
937 scopes,
938 })
939 })
940 .collect::<Result<Vec<_>, _>>()?;
941
942 Ok(Some(ScimValueKanidm::OAuth2ScopeMap(scim_claim_maps)))
943 }
944 }
945 }
946
947 fn resolve_scim_json_get(
948 &mut self,
949 attr: &Attribute,
950 value: &JsonValue,
951 ) -> Result<PartialValue, OperationError> {
952 let schema = self.get_schema();
953 let Some(schema_a) = schema.get_attributes().get(attr) else {
955 return Err(OperationError::InvalidAttributeName(attr.to_string()));
958 };
959
960 match schema_a.syntax {
961 SyntaxType::Utf8String => {
962 let JsonValue::String(value) = value else {
963 return Err(OperationError::InvalidAttribute(attr.to_string()));
964 };
965 Ok(PartialValue::Utf8(value.to_string()))
966 }
967 SyntaxType::Utf8StringInsensitive => {
968 let JsonValue::String(value) = value else {
969 return Err(OperationError::InvalidAttribute(attr.to_string()));
970 };
971 Ok(PartialValue::new_iutf8(value))
972 }
973 SyntaxType::Utf8StringIname => {
974 let JsonValue::String(value) = value else {
975 return Err(OperationError::InvalidAttribute(attr.to_string()));
976 };
977 Ok(PartialValue::new_iname(value))
978 }
979 SyntaxType::Uuid => {
980 let JsonValue::String(value) = value else {
981 return Err(OperationError::InvalidAttribute(attr.to_string()));
982 };
983
984 let un = self.name_to_uuid(value).unwrap_or(UUID_DOES_NOT_EXIST);
985 Ok(PartialValue::Uuid(un))
986 }
987 SyntaxType::ReferenceUuid
988 | SyntaxType::OauthScopeMap
989 | SyntaxType::Session
990 | SyntaxType::ApiToken
991 | SyntaxType::Oauth2Session
992 | SyntaxType::ApplicationPassword => {
993 let JsonValue::String(value) = value else {
994 return Err(OperationError::InvalidAttribute(attr.to_string()));
995 };
996
997 let un = self.name_to_uuid(value).unwrap_or(UUID_DOES_NOT_EXIST);
998 Ok(PartialValue::Refer(un))
999 }
1000
1001 _ => Err(OperationError::InvalidAttribute(attr.to_string())),
1002 }
1003 }
1004
1005 fn resolve_valueset_intermediate(
1006 &mut self,
1007 vs_inter: ValueSetIntermediate,
1008 ) -> Result<ValueSet, OperationError> {
1009 match vs_inter {
1010 ValueSetIntermediate::References {
1011 mut resolved,
1012 unresolved,
1013 } => {
1014 for value in unresolved {
1015 let un = self.name_to_uuid(value.as_str()).unwrap_or_else(|_| {
1016 warn!(
1017 ?value,
1018 "Value can not be resolved to a uuid - assuming it does not exist."
1019 );
1020 UUID_DOES_NOT_EXIST
1021 });
1022
1023 resolved.insert(un);
1024 }
1025
1026 let vs = ValueSetRefer::from_set(resolved);
1027 Ok(vs)
1028 }
1029
1030 ValueSetIntermediate::Oauth2ClaimMap {
1031 mut resolved,
1032 unresolved,
1033 } => {
1034 resolved.extend(unresolved.into_iter().map(
1035 |UnresolvedValueSetOauth2ClaimMap {
1036 group_name,
1037 claim,
1038 join_char,
1039 claim_values,
1040 }| {
1041 let group_uuid =
1042 self.name_to_uuid(group_name.as_str()).unwrap_or_else(|_| {
1043 warn!(
1044 ?group_name,
1045 "Value can not be resolved to a uuid - assuming it does not exist."
1046 );
1047 UUID_DOES_NOT_EXIST
1048 });
1049
1050 ResolvedValueSetOauth2ClaimMap {
1051 group_uuid,
1052 claim,
1053 join_char,
1054 claim_values,
1055 }
1056 },
1057 ));
1058
1059 let vs = ValueSetOauthClaimMap::from_set(resolved);
1060 Ok(vs)
1061 }
1062
1063 ValueSetIntermediate::Oauth2ScopeMap {
1064 mut resolved,
1065 unresolved,
1066 } => {
1067 resolved.extend(unresolved.into_iter().map(
1068 |UnresolvedValueSetOauth2ScopeMap { group_name, scopes }| {
1069 let group_uuid =
1070 self.name_to_uuid(group_name.as_str()).unwrap_or_else(|_| {
1071 warn!(
1072 ?group_name,
1073 "Value can not be resolved to a uuid - assuming it does not exist."
1074 );
1075 UUID_DOES_NOT_EXIST
1076 });
1077
1078 ResolvedValueSetOauth2ScopeMap { group_uuid, scopes }
1079 },
1080 ));
1081
1082 let vs = ValueSetOauthScopeMap::from_set(resolved);
1083 Ok(vs)
1084 }
1085 }
1086 }
1087
1088 fn resolve_valueset(&mut self, value: &ValueSet) -> Result<Vec<String>, OperationError> {
1090 if let Some(r_set) = value.as_refer_set() {
1091 let v: Result<Vec<_>, _> = r_set
1092 .iter()
1093 .copied()
1094 .map(|ur| {
1095 let nv = self.uuid_to_spn(ur)?;
1096 match nv {
1097 Some(v) => Ok(v.to_proto_string_clone()),
1098 None => Ok(uuid_to_proto_string(ur)),
1099 }
1100 })
1101 .collect();
1102 v
1103 } else if let Some(r_map) = value.as_oauthscopemap() {
1104 let v: Result<Vec<_>, _> = r_map
1105 .iter()
1106 .map(|(u, m)| {
1107 let nv = self.uuid_to_spn(*u)?;
1108 let u = match nv {
1109 Some(v) => v.to_proto_string_clone(),
1110 None => uuid_to_proto_string(*u),
1111 };
1112 Ok(format!("{u}: {m:?}"))
1113 })
1114 .collect();
1115 v
1116 } else if let Some(r_map) = value.as_oauthclaim_map() {
1117 let mut v = Vec::with_capacity(0);
1118 for (claim_name, mapping) in r_map.iter() {
1119 for (group_ref, claims) in mapping.values() {
1120 let join_char = mapping.join().to_str();
1121
1122 let nv = self.uuid_to_spn(*group_ref)?;
1123 let resolved_id = match nv {
1124 Some(v) => v.to_proto_string_clone(),
1125 None => uuid_to_proto_string(*group_ref),
1126 };
1127
1128 let joined = str_concat!(claims, ",");
1129
1130 v.push(format!(
1131 "{}:{}:{}:{:?}",
1132 claim_name, resolved_id, join_char, joined
1133 ))
1134 }
1135 }
1136 Ok(v)
1137 } else {
1138 let v: Vec<_> = value.to_proto_string_clone_iter().collect();
1139 Ok(v)
1140 }
1141 }
1142
1143 fn resolve_valueset_ldap(
1144 &mut self,
1145 value: &ValueSet,
1146 basedn: &str,
1147 ) -> Result<Vec<Vec<u8>>, OperationError> {
1148 if let Some(r_set) = value.as_refer_set() {
1149 let v: Result<Vec<_>, _> = r_set
1150 .iter()
1151 .copied()
1152 .map(|ur| {
1153 let rdn = self.uuid_to_rdn(ur)?;
1154 Ok(format!("{rdn},{basedn}").into_bytes())
1155 })
1156 .collect();
1157 v
1158 } else if let Some(key_iter) = value.as_sshpubkey_string_iter() {
1161 let v: Vec<_> = key_iter.map(|s| s.into_bytes()).collect();
1162 Ok(v)
1163 } else {
1164 let v: Vec<_> = value
1165 .to_proto_string_clone_iter()
1166 .map(|s| s.into_bytes())
1167 .collect();
1168 Ok(v)
1169 }
1170 }
1171
1172 fn get_db_domain(&mut self) -> Result<Arc<EntrySealedCommitted>, OperationError> {
1173 self.internal_search_uuid(UUID_DOMAIN_INFO)
1174 }
1175
1176 fn get_domain_key_object_handle(&self) -> Result<Arc<KeyObject>, OperationError> {
1177 self.get_key_providers()
1178 .get_key_object_handle(UUID_DOMAIN_INFO)
1179 .ok_or(OperationError::KP0031KeyObjectNotFound)
1180 }
1181
1182 fn get_domain_es256_private_key(&mut self) -> Result<Vec<u8>, OperationError> {
1183 self.internal_search_uuid(UUID_DOMAIN_INFO)
1184 .and_then(|e| {
1185 e.get_ava_single_private_binary(Attribute::Es256PrivateKeyDer)
1186 .map(|s| s.to_vec())
1187 .ok_or(OperationError::InvalidEntryState)
1188 })
1189 .map_err(|e| {
1190 admin_error!(?e, "Error getting domain es256 key");
1191 e
1192 })
1193 }
1194
1195 fn get_domain_ldap_allow_unix_pw_bind(&mut self) -> Result<bool, OperationError> {
1196 self.internal_search_uuid(UUID_DOMAIN_INFO).map(|entry| {
1197 entry
1198 .get_ava_single_bool(Attribute::LdapAllowUnixPwBind)
1199 .unwrap_or(true)
1200 })
1201 }
1202
1203 fn get_sc_password_badlist(&mut self) -> Result<HashSet<String>, OperationError> {
1206 self.internal_search_uuid(UUID_SYSTEM_CONFIG)
1207 .map(|e| match e.get_ava_iter_iutf8(Attribute::BadlistPassword) {
1208 Some(vs_str_iter) => vs_str_iter.map(str::to_string).collect::<HashSet<_>>(),
1209 None => HashSet::default(),
1210 })
1211 .map_err(|e| {
1212 error!(
1213 ?e,
1214 "Failed to retrieve password badlist from system configuration"
1215 );
1216 e
1217 })
1218 }
1219
1220 fn get_sc_denied_names(&mut self) -> Result<HashSet<String>, OperationError> {
1223 self.internal_search_uuid(UUID_SYSTEM_CONFIG)
1224 .map(|e| match e.get_ava_iter_iname(Attribute::DeniedName) {
1225 Some(vs_str_iter) => vs_str_iter.map(str::to_string).collect::<HashSet<_>>(),
1226 None => HashSet::default(),
1227 })
1228 .map_err(|e| {
1229 error!(
1230 ?e,
1231 "Failed to retrieve denied names from system configuration"
1232 );
1233 e
1234 })
1235 }
1236
1237 fn get_oauth2rs_set(&mut self) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
1238 self.internal_search(filter!(f_eq(
1239 Attribute::Class,
1240 EntryClass::OAuth2ResourceServer.into(),
1241 )))
1242 }
1243
1244 fn get_applications_set(&mut self) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
1245 self.internal_search(filter!(f_eq(
1246 Attribute::Class,
1247 EntryClass::Application.into(),
1248 )))
1249 }
1250
1251 #[instrument(level = "debug", skip_all)]
1252 fn consumer_get_state(&mut self) -> Result<ReplRuvRange, OperationError> {
1253 let domain_uuid = self.get_domain_uuid();
1278
1279 let ruv_snapshot = self.get_be_txn().get_ruv();
1282
1283 ruv_snapshot
1285 .current_ruv_range()
1286 .map(|ranges| ReplRuvRange::V1 {
1287 domain_uuid,
1288 ranges,
1289 })
1290 }
1291}
1292
1293impl<'a> QueryServerTransaction<'a> for QueryServerReadTransaction<'a> {
1297 type AccessControlsTransactionType = AccessControlsReadTransaction<'a>;
1298 type BackendTransactionType = BackendReadTransaction<'a>;
1299 type SchemaTransactionType = SchemaReadTransaction;
1300 type KeyProvidersTransactionType = KeyProvidersReadTransaction;
1301
1302 fn get_be_txn(&mut self) -> &mut BackendReadTransaction<'a> {
1303 &mut self.be_txn
1304 }
1305
1306 fn get_schema<'b>(&self) -> &'b SchemaReadTransaction {
1307 unsafe {
1311 let s = (&self.schema) as *const _;
1312 &*s
1313 }
1314 }
1315
1316 fn get_accesscontrols(&self) -> &AccessControlsReadTransaction<'a> {
1317 &self.accesscontrols
1318 }
1319
1320 fn get_key_providers(&self) -> &KeyProvidersReadTransaction {
1321 &self.key_providers
1322 }
1323
1324 fn get_resolve_filter_cache(&mut self) -> Option<&mut ResolveFilterCacheReadTxn<'a>> {
1325 Some(&mut self.resolve_filter_cache)
1326 }
1327
1328 fn get_resolve_filter_cache_and_be_txn(
1329 &mut self,
1330 ) -> (
1331 &mut BackendReadTransaction<'a>,
1332 Option<&mut ResolveFilterCacheReadTxn<'a>>,
1333 ) {
1334 (&mut self.be_txn, Some(&mut self.resolve_filter_cache))
1335 }
1336
1337 fn pw_badlist(&self) -> &HashSet<String> {
1338 &self.system_config.pw_badlist
1339 }
1340
1341 fn denied_names(&self) -> &HashSet<String> {
1342 &self.system_config.denied_names
1343 }
1344
1345 fn get_domain_version(&self) -> DomainVersion {
1346 self.d_info.d_vers
1347 }
1348
1349 fn get_domain_patch_level(&self) -> u32 {
1350 self.d_info.d_patch_level
1351 }
1352
1353 fn get_domain_development_taint(&self) -> bool {
1354 self.d_info.d_devel_taint
1355 }
1356
1357 fn get_domain_uuid(&self) -> Uuid {
1358 self.d_info.d_uuid
1359 }
1360
1361 fn get_domain_name(&self) -> &str {
1362 &self.d_info.d_name
1363 }
1364
1365 fn get_domain_display_name(&self) -> &str {
1366 &self.d_info.d_display
1367 }
1368
1369 fn get_domain_image_value(&self) -> Option<ImageValue> {
1370 self.d_info.d_image.clone()
1371 }
1372}
1373
1374impl QueryServerReadTransaction<'_> {
1375 pub(crate) fn trim_cid(&self) -> &Cid {
1376 &self.trim_cid
1377 }
1378
1379 pub fn domain_info(&mut self) -> Result<ProtoDomainInfo, OperationError> {
1381 let d_info = &self.d_info;
1382
1383 Ok(ProtoDomainInfo {
1384 name: d_info.d_name.clone(),
1385 displayname: d_info.d_display.clone(),
1386 uuid: d_info.d_uuid,
1387 level: d_info.d_vers,
1388 })
1389 }
1390
1391 pub(crate) fn verify(&mut self) -> Vec<Result<(), ConsistencyError>> {
1395 let be_errs = self.get_be_txn().verify();
1399
1400 if !be_errs.is_empty() {
1401 return be_errs;
1402 }
1403
1404 let sc_errs = self.get_schema().validate();
1406
1407 if !sc_errs.is_empty() {
1408 return sc_errs;
1409 }
1410
1411 let idx_errs = self.get_be_txn().verify_indexes();
1415
1416 if !idx_errs.is_empty() {
1417 return idx_errs;
1418 }
1419
1420 let mut results = Vec::with_capacity(0);
1423
1424 let schema = self.get_schema();
1427
1428 let filt_all = filter!(f_pres(Attribute::Class));
1429 let all_entries = match self.internal_search(filt_all) {
1430 Ok(a) => a,
1431 Err(_e) => return vec![Err(ConsistencyError::QueryServerSearchFailure)],
1432 };
1433
1434 for e in all_entries {
1435 e.verify(schema, &mut results)
1436 }
1437
1438 self.get_be_txn().verify_ruv(&mut results);
1440
1441 Plugins::run_verify(self, &mut results);
1447 results
1450 }
1451
1452 #[instrument(level = "debug", skip_all)]
1453 pub fn scim_entry_id_get_ext(
1454 &mut self,
1455 uuid: Uuid,
1456 class: EntryClass,
1457 query: ScimEntryGetQuery,
1458 ident: Identity,
1459 ) -> Result<ScimEntryKanidm, OperationError> {
1460 let filter_intent = filter!(f_and!([
1461 f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)),
1462 f_eq(Attribute::Class, class.into())
1463 ]));
1464
1465 let f_intent_valid = filter_intent
1466 .validate(self.get_schema())
1467 .map_err(OperationError::SchemaViolation)?;
1468
1469 let f_valid = f_intent_valid.clone().into_ignore_hidden();
1470
1471 let r_attrs = query
1472 .attributes
1473 .map(|attr_set| attr_set.into_iter().collect());
1474
1475 let se = SearchEvent {
1476 ident,
1477 filter: f_valid,
1478 filter_orig: f_intent_valid,
1479 attrs: r_attrs,
1480 effective_access_check: query.ext_access_check,
1481 };
1482
1483 let mut vs = self.search_ext(&se)?;
1484 match vs.pop() {
1485 Some(entry) if vs.is_empty() => entry.to_scim_kanidm(self),
1486 _ => {
1487 if vs.is_empty() {
1488 Err(OperationError::NoMatchingEntries)
1489 } else {
1490 Err(OperationError::UniqueConstraintViolation)
1492 }
1493 }
1494 }
1495 }
1496
1497 #[instrument(level = "debug", skip_all)]
1498 pub fn scim_search_ext(
1499 &mut self,
1500 ident: Identity,
1501 filter: ScimFilter,
1502 query: ScimEntryGetQuery,
1503 ) -> Result<Vec<ScimEntryKanidm>, OperationError> {
1504 let filter_intent = Filter::from_scim_ro(&ident, &filter, self)?;
1505
1506 let f_intent_valid = filter_intent
1507 .validate(self.get_schema())
1508 .map_err(OperationError::SchemaViolation)?;
1509
1510 let f_valid = f_intent_valid.clone().into_ignore_hidden();
1511
1512 let r_attrs = query
1513 .attributes
1514 .map(|attr_set| attr_set.into_iter().collect());
1515
1516 let se = SearchEvent {
1517 ident,
1518 filter: f_valid,
1519 filter_orig: f_intent_valid,
1520 attrs: r_attrs,
1521 effective_access_check: query.ext_access_check,
1522 };
1523
1524 let vs = self.search_ext(&se)?;
1525
1526 vs.into_iter()
1527 .map(|entry| entry.to_scim_kanidm(self))
1528 .collect()
1529 }
1530}
1531
1532impl<'a> QueryServerTransaction<'a> for QueryServerWriteTransaction<'a> {
1533 type AccessControlsTransactionType = AccessControlsWriteTransaction<'a>;
1534 type BackendTransactionType = BackendWriteTransaction<'a>;
1535 type SchemaTransactionType = SchemaWriteTransaction<'a>;
1536 type KeyProvidersTransactionType = KeyProvidersWriteTransaction<'a>;
1537
1538 fn get_be_txn(&mut self) -> &mut BackendWriteTransaction<'a> {
1539 &mut self.be_txn
1540 }
1541
1542 fn get_schema<'b>(&self) -> &'b SchemaWriteTransaction<'a> {
1543 unsafe {
1547 let s = (&self.schema) as *const _;
1548 &*s
1549 }
1550 }
1551
1552 fn get_accesscontrols(&self) -> &AccessControlsWriteTransaction<'a> {
1553 &self.accesscontrols
1554 }
1555
1556 fn get_key_providers(&self) -> &KeyProvidersWriteTransaction<'a> {
1557 &self.key_providers
1558 }
1559
1560 fn get_resolve_filter_cache(&mut self) -> Option<&mut ResolveFilterCacheReadTxn<'a>> {
1561 if self.resolve_filter_cache_clear || *self.phase < ServerPhase::SchemaReady {
1562 None
1563 } else {
1564 Some(&mut self.resolve_filter_cache)
1565 }
1566 }
1567
1568 fn get_resolve_filter_cache_and_be_txn(
1569 &mut self,
1570 ) -> (
1571 &mut BackendWriteTransaction<'a>,
1572 Option<&mut ResolveFilterCacheReadTxn<'a>>,
1573 ) {
1574 if self.resolve_filter_cache_clear || *self.phase < ServerPhase::SchemaReady {
1575 (&mut self.be_txn, None)
1576 } else {
1577 (&mut self.be_txn, Some(&mut self.resolve_filter_cache))
1578 }
1579 }
1580
1581 fn pw_badlist(&self) -> &HashSet<String> {
1582 &self.system_config.pw_badlist
1583 }
1584
1585 fn denied_names(&self) -> &HashSet<String> {
1586 &self.system_config.denied_names
1587 }
1588
1589 fn get_domain_version(&self) -> DomainVersion {
1590 self.d_info.d_vers
1591 }
1592
1593 fn get_domain_patch_level(&self) -> u32 {
1594 self.d_info.d_patch_level
1595 }
1596
1597 fn get_domain_development_taint(&self) -> bool {
1598 self.d_info.d_devel_taint
1599 }
1600
1601 fn get_domain_uuid(&self) -> Uuid {
1602 self.d_info.d_uuid
1603 }
1604
1605 fn get_domain_name(&self) -> &str {
1607 &self.d_info.d_name
1608 }
1609
1610 fn get_domain_display_name(&self) -> &str {
1611 &self.d_info.d_display
1612 }
1613
1614 fn get_domain_image_value(&self) -> Option<ImageValue> {
1615 self.d_info.d_image.clone()
1616 }
1617}
1618
1619impl QueryServer {
1620 pub fn new(
1621 be: Backend,
1622 schema: Schema,
1623 domain_name: String,
1624 curtime: Duration,
1625 ) -> Result<Self, OperationError> {
1626 let (s_uuid, d_uuid, ts_max) = {
1627 let mut wr = be.write()?;
1628 let s_uuid = wr.get_db_s_uuid()?;
1629 let d_uuid = wr.get_db_d_uuid()?;
1630 let ts_max = wr.get_db_ts_max(curtime)?;
1631 wr.commit()?;
1632 (s_uuid, d_uuid, ts_max)
1633 };
1634
1635 let pool_size = be.get_pool_size();
1636
1637 debug!("Server UUID -> {:?}", s_uuid);
1638 debug!("Domain UUID -> {:?}", d_uuid);
1639 debug!("Domain Name -> {:?}", domain_name);
1640
1641 let d_info = Arc::new(CowCell::new(DomainInfo {
1642 d_uuid,
1643 d_vers: DOMAIN_LEVEL_0,
1646 d_patch_level: 0,
1647 d_name: domain_name.clone(),
1648 d_display: domain_name,
1651 d_devel_taint: option_env!("KANIDM_PRE_RELEASE").is_some(),
1653 d_ldap_allow_unix_pw_bind: false,
1654 d_allow_easter_eggs: false,
1655 d_image: None,
1656 }));
1657
1658 let cid = Cid::new_lamport(s_uuid, curtime, &ts_max);
1659 let cid_max = Arc::new(CowCell::new(cid));
1660
1661 let system_config = Arc::new(CowCell::new(SystemConfig::default()));
1663
1664 let dyngroup_cache = Arc::new(CowCell::new(DynGroupCache::default()));
1665
1666 let phase = Arc::new(CowCell::new(ServerPhase::Bootstrap));
1667
1668 let resolve_filter_cache = Arc::new(
1669 ARCacheBuilder::new()
1670 .set_size(RESOLVE_FILTER_CACHE_MAX, RESOLVE_FILTER_CACHE_LOCAL)
1671 .set_reader_quiesce(true)
1672 .build()
1673 .ok_or_else(|| {
1674 error!("Failed to build filter resolve cache");
1675 OperationError::DB0003FilterResolveCacheBuild
1676 })?,
1677 );
1678
1679 let key_providers = Arc::new(KeyProviders::default());
1680
1681 debug_assert!(pool_size > 0);
1684 let read_ticket_pool = std::cmp::max(pool_size - 1, 1);
1685
1686 Ok(QueryServer {
1687 phase,
1688 d_info,
1689 system_config,
1690 be,
1691 schema: Arc::new(schema),
1692 accesscontrols: Arc::new(AccessControls::default()),
1693 db_tickets: Arc::new(Semaphore::new(pool_size as usize)),
1694 read_tickets: Arc::new(Semaphore::new(read_ticket_pool as usize)),
1695 write_ticket: Arc::new(Semaphore::new(1)),
1696 resolve_filter_cache,
1697 dyngroup_cache,
1698 cid_max,
1699 key_providers,
1700 })
1701 }
1702
1703 pub fn try_quiesce(&self) {
1704 self.be.try_quiesce();
1705 self.accesscontrols.try_quiesce();
1706 self.resolve_filter_cache.try_quiesce();
1707 }
1708
1709 #[instrument(level = "debug", skip_all)]
1710 async fn read_acquire_ticket(&self) -> Option<(SemaphorePermit<'_>, SemaphorePermit<'_>)> {
1711 let read_ticket = if cfg!(test) {
1715 self.read_tickets
1716 .try_acquire()
1717 .inspect_err(|err| {
1718 error!(?err, "Unable to acquire read ticket!");
1719 })
1720 .ok()?
1721 } else {
1722 let fut = tokio::time::timeout(
1723 Duration::from_millis(DB_LOCK_ACQUIRE_TIMEOUT_MILLIS),
1724 self.read_tickets.acquire(),
1725 );
1726
1727 match fut.await {
1728 Ok(Ok(ticket)) => ticket,
1729 Ok(Err(_)) => {
1730 error!("Failed to acquire read ticket, may be poisoned.");
1731 return None;
1732 }
1733 Err(_) => {
1734 error!("Failed to acquire read ticket, server is overloaded.");
1735 return None;
1736 }
1737 }
1738 };
1739
1740 let db_ticket = if cfg!(test) {
1745 self.db_tickets
1746 .try_acquire()
1747 .inspect_err(|err| {
1748 error!(?err, "Unable to acquire database ticket!");
1749 })
1750 .ok()?
1751 } else {
1752 self.db_tickets
1753 .acquire()
1754 .await
1755 .inspect_err(|err| {
1756 error!(?err, "Unable to acquire database ticket!");
1757 })
1758 .ok()?
1759 };
1760
1761 Some((read_ticket, db_ticket))
1762 }
1763
1764 pub async fn read(&self) -> Result<QueryServerReadTransaction<'_>, OperationError> {
1765 let (read_ticket, db_ticket) = self
1766 .read_acquire_ticket()
1767 .await
1768 .ok_or(OperationError::DatabaseLockAcquisitionTimeout)?;
1769 let schema = self.schema.read();
1773
1774 let cid_max = self.cid_max.read();
1775 let trim_cid = cid_max.sub_secs(CHANGELOG_MAX_AGE)?;
1776
1777 let be_txn = self.be.read()?;
1778
1779 Ok(QueryServerReadTransaction {
1780 be_txn,
1781 schema,
1782 d_info: self.d_info.read(),
1783 system_config: self.system_config.read(),
1784 accesscontrols: self.accesscontrols.read(),
1785 key_providers: self.key_providers.read(),
1786 _db_ticket: db_ticket,
1787 _read_ticket: read_ticket,
1788 resolve_filter_cache: self.resolve_filter_cache.read(),
1789 trim_cid,
1790 })
1791 }
1792
1793 #[instrument(level = "debug", skip_all)]
1794 async fn write_acquire_ticket(&self) -> Option<(SemaphorePermit<'_>, SemaphorePermit<'_>)> {
1795 let write_ticket = if cfg!(test) {
1797 self.write_ticket
1798 .try_acquire()
1799 .inspect_err(|err| {
1800 error!(?err, "Unable to acquire write ticket!");
1801 })
1802 .ok()?
1803 } else {
1804 let fut = tokio::time::timeout(
1805 Duration::from_millis(DB_LOCK_ACQUIRE_TIMEOUT_MILLIS),
1806 self.write_ticket.acquire(),
1807 );
1808
1809 match fut.await {
1810 Ok(Ok(ticket)) => ticket,
1811 Ok(Err(_)) => {
1812 error!("Failed to acquire write ticket, may be poisoned.");
1813 return None;
1814 }
1815 Err(_) => {
1816 error!("Failed to acquire write ticket, server is overloaded.");
1817 return None;
1818 }
1819 }
1820 };
1821
1822 let db_ticket = if cfg!(test) {
1826 self.db_tickets
1827 .try_acquire()
1828 .inspect_err(|err| {
1829 error!(?err, "Unable to acquire write db_ticket!");
1830 })
1831 .ok()?
1832 } else {
1833 self.db_tickets
1834 .acquire()
1835 .await
1836 .inspect_err(|err| {
1837 error!(?err, "Unable to acquire write db_ticket!");
1838 })
1839 .ok()?
1840 };
1841
1842 Some((write_ticket, db_ticket))
1843 }
1844
1845 pub async fn write(
1846 &self,
1847 curtime: Duration,
1848 ) -> Result<QueryServerWriteTransaction<'_>, OperationError> {
1849 let (write_ticket, db_ticket) = self
1850 .write_acquire_ticket()
1851 .await
1852 .ok_or(OperationError::DatabaseLockAcquisitionTimeout)?;
1853
1854 let be_txn = self.be.write()?;
1859
1860 let schema_write = self.schema.write();
1861 let d_info = self.d_info.write();
1862 let system_config = self.system_config.write();
1863 let phase = self.phase.write();
1864
1865 let mut cid = self.cid_max.write();
1866 *cid = Cid::new_lamport(cid.s_uuid, curtime, &cid.ts);
1868
1869 let trim_cid = cid.sub_secs(CHANGELOG_MAX_AGE)?;
1870
1871 Ok(QueryServerWriteTransaction {
1872 committed: false,
1879 phase,
1880 d_info,
1881 system_config,
1882 curtime,
1883 cid,
1884 trim_cid,
1885 be_txn,
1886 schema: schema_write,
1887 accesscontrols: self.accesscontrols.write(),
1888 changed_flags: ChangeFlag::empty(),
1889 changed_uuid: HashSet::new(),
1890 _db_ticket: db_ticket,
1891 _write_ticket: write_ticket,
1892 resolve_filter_cache: self.resolve_filter_cache.read(),
1893 resolve_filter_cache_clear: false,
1894 resolve_filter_cache_write: self.resolve_filter_cache.write(),
1895 dyngroup_cache: self.dyngroup_cache.write(),
1896 key_providers: self.key_providers.write(),
1897 })
1898 }
1899
1900 #[cfg(any(test, debug_assertions))]
1901 pub async fn clear_cache(&self) -> Result<(), OperationError> {
1902 let ct = duration_from_epoch_now();
1903 let mut w_txn = self.write(ct).await?;
1904 w_txn.clear_cache()?;
1905 w_txn.commit()
1906 }
1907
1908 pub async fn verify(&self) -> Vec<Result<(), ConsistencyError>> {
1909 let current_time = duration_from_epoch_now();
1910 if self
1915 .write(current_time)
1916 .await
1917 .and_then(|mut txn| {
1918 txn.force_schema_reload();
1919 txn.commit()
1920 })
1921 .is_err()
1922 {
1923 return vec![Err(ConsistencyError::Unknown)];
1924 };
1925
1926 match self.read().await {
1927 Ok(mut r_txn) => r_txn.verify(),
1928 Err(_) => vec![Err(ConsistencyError::Unknown)],
1929 }
1930 }
1931}
1932
1933impl<'a> QueryServerWriteTransaction<'a> {
1934 pub(crate) fn get_server_uuid(&self) -> Uuid {
1935 self.cid.s_uuid
1937 }
1938
1939 pub(crate) fn reset_server_uuid(&mut self) -> Result<(), OperationError> {
1940 let s_uuid = self.be_txn.reset_db_s_uuid().map_err(|err| {
1941 error!(?err, "Failed to reset server replication uuid");
1942 err
1943 })?;
1944
1945 debug!(?s_uuid, "reset server replication uuid");
1946
1947 self.cid.s_uuid = s_uuid;
1948
1949 Ok(())
1950 }
1951
1952 pub(crate) fn get_curtime(&self) -> Duration {
1953 self.curtime
1954 }
1955
1956 pub(crate) fn get_cid(&self) -> &Cid {
1957 &self.cid
1958 }
1959
1960 pub(crate) fn get_key_providers_mut(&mut self) -> &mut KeyProvidersWriteTransaction<'a> {
1961 &mut self.key_providers
1962 }
1963
1964 pub(crate) fn get_dyngroup_cache(&mut self) -> &mut DynGroupCache {
1965 &mut self.dyngroup_cache
1966 }
1967
1968 pub fn domain_raise(&mut self, level: u32) -> Result<(), OperationError> {
1969 if level > DOMAIN_MAX_LEVEL {
1970 return Err(OperationError::MG0002RaiseDomainLevelExceedsMaximum);
1971 }
1972
1973 let modl = ModifyList::new_purge_and_set(Attribute::Version, Value::Uint32(level));
1974 let udi = PVUUID_DOMAIN_INFO.clone();
1975 let filt = filter_all!(f_eq(Attribute::Uuid, udi));
1976 self.internal_modify(&filt, &modl)
1977 }
1978
1979 pub fn domain_remigrate(&mut self, level: u32) -> Result<(), OperationError> {
1980 let mut_d_info = self.d_info.get_mut();
1981
1982 if level > mut_d_info.d_vers {
1983 return Ok(());
1985 } else if level < DOMAIN_MIN_REMIGRATION_LEVEL {
1986 return Err(OperationError::MG0001InvalidReMigrationLevel);
1987 };
1988
1989 info!(
1990 "Prepare to re-migrate from {} -> {}",
1991 level, mut_d_info.d_vers
1992 );
1993 mut_d_info.d_vers = level;
1994 self.changed_flags.insert(ChangeFlag::DOMAIN);
1995
1996 Ok(())
1997 }
1998
1999 #[instrument(level = "debug", skip_all)]
2000 pub(crate) fn reload_schema(&mut self) -> Result<(), OperationError> {
2001 let filt = filter!(f_eq(Attribute::Class, EntryClass::AttributeType.into()));
2004 let res = self.internal_search(filt).map_err(|e| {
2005 admin_error!("reload schema internal search failed {:?}", e);
2006 e
2007 })?;
2008 let attributetypes: Result<Vec<_>, _> =
2010 res.iter().map(|e| SchemaAttribute::try_from(e)).collect();
2011
2012 let attributetypes = attributetypes.map_err(|e| {
2013 admin_error!("reload schema attributetypes {:?}", e);
2014 e
2015 })?;
2016
2017 self.schema.update_attributes(attributetypes).map_err(|e| {
2018 admin_error!("reload schema update attributetypes {:?}", e);
2019 e
2020 })?;
2021
2022 let filt = filter!(f_eq(Attribute::Class, EntryClass::ClassType.into()));
2024 let res = self.internal_search(filt).map_err(|e| {
2025 admin_error!("reload schema internal search failed {:?}", e);
2026 e
2027 })?;
2028 let classtypes: Result<Vec<_>, _> = res.iter().map(|e| SchemaClass::try_from(e)).collect();
2030 let classtypes = classtypes.map_err(|e| {
2031 admin_error!("reload schema classtypes {:?}", e);
2032 e
2033 })?;
2034
2035 self.schema.update_classes(classtypes).map_err(|e| {
2036 admin_error!("reload schema update classtypes {:?}", e);
2037 e
2038 })?;
2039
2040 let valid_r = self.schema.validate();
2042
2043 if valid_r.is_empty() {
2045 trace!("Reloading idxmeta ...");
2047 self.be_txn
2048 .update_idxmeta(self.schema.reload_idxmeta())
2049 .map_err(|e| {
2050 admin_error!("reload schema update idxmeta {:?}", e);
2051 e
2052 })
2053 } else {
2054 admin_error!("Schema reload failed -> {:?}", valid_r);
2056 Err(OperationError::ConsistencyError(
2057 valid_r.into_iter().filter_map(|v| v.err()).collect(),
2058 ))
2059 }?;
2060
2061 self.resolve_filter_cache_clear = true;
2064
2065 DynGroup::reload(self)?;
2068
2069 Ok(())
2070 }
2071
2072 #[instrument(level = "debug", skip_all)]
2073 fn reload_accesscontrols(&mut self) -> Result<(), OperationError> {
2074 trace!("ACP reload started ...");
2082
2083 let filt = filter!(f_eq(Attribute::Class, EntryClass::SyncAccount.into()));
2086
2087 let res = self.internal_search(filt).map_err(|e| {
2088 admin_error!(
2089 err = ?e,
2090 "reload accesscontrols internal search failed",
2091 );
2092 e
2093 })?;
2094
2095 let sync_agreement_map: HashMap<Uuid, BTreeSet<Attribute>> = res
2096 .iter()
2097 .filter_map(|e| {
2098 e.get_ava_as_iutf8(Attribute::SyncYieldAuthority)
2099 .map(|set| {
2100 let set: BTreeSet<_> =
2101 set.iter().map(|s| Attribute::from(s.as_str())).collect();
2102 (e.get_uuid(), set)
2103 })
2104 })
2105 .collect();
2106
2107 self.accesscontrols
2108 .update_sync_agreements(sync_agreement_map);
2109
2110 let filt = filter!(f_and!([
2112 f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2113 f_eq(Attribute::Class, EntryClass::AccessControlSearch.into()),
2114 f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2115 ]));
2116
2117 let res = self.internal_search(filt).map_err(|e| {
2118 admin_error!(
2119 err = ?e,
2120 "reload accesscontrols internal search failed",
2121 );
2122 e
2123 })?;
2124 let search_acps: Result<Vec<_>, _> = res
2125 .iter()
2126 .map(|e| AccessControlSearch::try_from(self, e))
2127 .collect();
2128
2129 let search_acps = search_acps.map_err(|e| {
2130 admin_error!(err = ?e, "Unable to parse search accesscontrols");
2131 e
2132 })?;
2133
2134 self.accesscontrols
2135 .update_search(search_acps)
2136 .map_err(|e| {
2137 admin_error!(err = ?e, "Failed to update search accesscontrols");
2138 e
2139 })?;
2140 let filt = filter!(f_and!([
2142 f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2143 f_eq(Attribute::Class, EntryClass::AccessControlCreate.into()),
2144 f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2145 ]));
2146
2147 let res = self.internal_search(filt).map_err(|e| {
2148 admin_error!(
2149 err = ?e,
2150 "reload accesscontrols internal search failed"
2151 );
2152 e
2153 })?;
2154 let create_acps: Result<Vec<_>, _> = res
2155 .iter()
2156 .map(|e| AccessControlCreate::try_from(self, e))
2157 .collect();
2158
2159 let create_acps = create_acps.map_err(|e| {
2160 admin_error!(err = ?e, "Unable to parse create accesscontrols");
2161 e
2162 })?;
2163
2164 self.accesscontrols
2165 .update_create(create_acps)
2166 .map_err(|e| {
2167 admin_error!(err = ?e, "Failed to update create accesscontrols");
2168 e
2169 })?;
2170 let filt = filter!(f_and!([
2172 f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2173 f_eq(Attribute::Class, EntryClass::AccessControlModify.into()),
2174 f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2175 ]));
2176
2177 let res = self.internal_search(filt).map_err(|e| {
2178 admin_error!("reload accesscontrols internal search failed {:?}", e);
2179 e
2180 })?;
2181 let modify_acps: Result<Vec<_>, _> = res
2182 .iter()
2183 .map(|e| AccessControlModify::try_from(self, e))
2184 .collect();
2185
2186 let modify_acps = modify_acps.map_err(|e| {
2187 admin_error!("Unable to parse modify accesscontrols {:?}", e);
2188 e
2189 })?;
2190
2191 self.accesscontrols
2192 .update_modify(modify_acps)
2193 .map_err(|e| {
2194 admin_error!("Failed to update modify accesscontrols {:?}", e);
2195 e
2196 })?;
2197 let filt = filter!(f_and!([
2199 f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2200 f_eq(Attribute::Class, EntryClass::AccessControlDelete.into()),
2201 f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2202 ]));
2203
2204 let res = self.internal_search(filt).map_err(|e| {
2205 admin_error!("reload accesscontrols internal search failed {:?}", e);
2206 e
2207 })?;
2208 let delete_acps: Result<Vec<_>, _> = res
2209 .iter()
2210 .map(|e| AccessControlDelete::try_from(self, e))
2211 .collect();
2212
2213 let delete_acps = delete_acps.map_err(|e| {
2214 admin_error!("Unable to parse delete accesscontrols {:?}", e);
2215 e
2216 })?;
2217
2218 self.accesscontrols.update_delete(delete_acps).map_err(|e| {
2219 admin_error!("Failed to update delete accesscontrols {:?}", e);
2220 e
2221 })
2222 }
2223
2224 #[instrument(level = "debug", skip_all)]
2225 pub(crate) fn reload_key_material(&mut self) -> Result<(), OperationError> {
2226 let filt = filter!(f_eq(Attribute::Class, EntryClass::KeyProvider.into()));
2227
2228 let res = self.internal_search(filt).map_err(|e| {
2229 admin_error!(
2230 err = ?e,
2231 "reload key providers internal search failed",
2232 );
2233 e
2234 })?;
2235
2236 let providers = res
2239 .iter()
2240 .map(|e| KeyProvider::try_from(e).and_then(|kp| kp.test().map(|()| kp)))
2241 .collect::<Result<Vec<_>, _>>()?;
2242
2243 self.key_providers.update_providers(providers)?;
2244
2245 let filt = filter!(f_eq(Attribute::Class, EntryClass::KeyObject.into()));
2246
2247 let res = self.internal_search(filt).map_err(|e| {
2248 admin_error!(
2249 err = ?e,
2250 "reload key objects internal search failed",
2251 );
2252 e
2253 })?;
2254
2255 res.iter()
2256 .try_for_each(|entry| self.key_providers.load_key_object(entry.as_ref()))
2257 }
2258
2259 #[instrument(level = "debug", skip_all)]
2260 pub(crate) fn reload_system_config(&mut self) -> Result<(), OperationError> {
2261 let denied_names = self.get_sc_denied_names()?;
2262 let pw_badlist = self.get_sc_password_badlist()?;
2263
2264 let mut_system_config = self.system_config.get_mut();
2265 mut_system_config.denied_names = denied_names;
2266 mut_system_config.pw_badlist = pw_badlist;
2267 Ok(())
2268 }
2269
2270 #[instrument(level = "debug", skip_all)]
2272 pub(crate) fn reload_domain_info_version(&mut self) -> Result<(), OperationError> {
2273 let domain_info = self.internal_search_uuid(UUID_DOMAIN_INFO).map_err(|err| {
2274 error!(?err, "Error getting domain info");
2275 err
2276 })?;
2277
2278 let domain_info_version = domain_info
2279 .get_ava_single_uint32(Attribute::Version)
2280 .ok_or_else(|| {
2281 error!("domain info missing attribute version");
2282 OperationError::InvalidEntryState
2283 })?;
2284
2285 let domain_info_patch_level = domain_info
2286 .get_ava_single_uint32(Attribute::PatchLevel)
2287 .unwrap_or(0);
2288
2289 let current_devel_flag = option_env!("KANIDM_PRE_RELEASE").is_some();
2293 let domain_info_devel_taint = current_devel_flag
2294 || domain_info
2295 .get_ava_single_bool(Attribute::DomainDevelopmentTaint)
2296 .unwrap_or_default();
2297
2298 let domain_allow_easter_eggs = domain_info
2299 .get_ava_single_bool(Attribute::DomainAllowEasterEggs)
2300 .unwrap_or(option_env!("KANIDM_PRE_RELEASE").is_some());
2302
2303 let mut_d_info = self.d_info.get_mut();
2307 let previous_version = mut_d_info.d_vers;
2308 let previous_patch_level = mut_d_info.d_patch_level;
2309 mut_d_info.d_vers = domain_info_version;
2310 mut_d_info.d_patch_level = domain_info_patch_level;
2311 mut_d_info.d_devel_taint = domain_info_devel_taint;
2312 mut_d_info.d_allow_easter_eggs = domain_allow_easter_eggs;
2313
2314 if (previous_version == domain_info_version
2317 && previous_patch_level == domain_info_patch_level)
2318 || *self.phase < ServerPhase::DomainInfoReady
2319 {
2320 return Ok(());
2321 }
2322
2323 debug!(domain_previous_version = ?previous_version, domain_target_version = ?domain_info_version);
2324 debug!(domain_previous_patch_level = ?previous_patch_level, domain_target_patch_level = ?domain_info_patch_level);
2325
2326 if previous_version == DOMAIN_LEVEL_0 {
2330 debug!(
2331 "Server was just brought up, skipping migrations as we are already at target level"
2332 );
2333 return Ok(());
2334 }
2335
2336 if previous_version < DOMAIN_MIN_REMIGRATION_LEVEL {
2337 error!("UNABLE TO PROCEED. You are attempting a Skip update which is NOT SUPPORTED. You must upgrade one-version of Kanidm at a time.");
2338 error!("For more see: https://kanidm.github.io/kanidm/stable/support.html#upgrade-policy and https://kanidm.github.io/kanidm/stable/server_updates.html");
2339 error!(domain_previous_version = ?previous_version, domain_target_version = ?domain_info_version);
2340 error!(domain_previous_patch_level = ?previous_patch_level, domain_target_patch_level = ?domain_info_patch_level);
2341 return Err(OperationError::MG0008SkipUpgradeAttempted);
2342 }
2343
2344 if previous_version <= DOMAIN_LEVEL_8 && domain_info_version >= DOMAIN_LEVEL_9 {
2345 self.migrate_domain_8_to_9()?;
2347 }
2348
2349 if previous_patch_level < PATCH_LEVEL_2
2350 && domain_info_patch_level >= PATCH_LEVEL_2
2351 && domain_info_version == DOMAIN_LEVEL_9
2352 {
2353 self.migrate_domain_patch_level_2()?;
2354 }
2355
2356 if previous_version <= DOMAIN_LEVEL_9 && domain_info_version >= DOMAIN_LEVEL_10 {
2357 self.migrate_domain_9_to_10()?;
2359 }
2360
2361 if previous_version <= DOMAIN_LEVEL_10 && domain_info_version >= DOMAIN_LEVEL_11 {
2362 self.migrate_domain_10_to_11()?;
2364 }
2365
2366 if previous_version <= DOMAIN_LEVEL_11 && domain_info_version >= DOMAIN_LEVEL_12 {
2367 self.migrate_domain_11_to_12()?;
2369 }
2370
2371 debug_assert!(domain_info_version <= DOMAIN_MAX_LEVEL);
2375
2376 Ok(())
2377 }
2378
2379 #[instrument(level = "debug", skip_all)]
2381 pub(crate) fn reload_domain_info(&mut self) -> Result<(), OperationError> {
2382 let domain_entry = self.get_db_domain()?;
2383
2384 let domain_name = domain_entry
2385 .get_ava_single_iname(Attribute::DomainName)
2386 .map(str::to_string)
2387 .ok_or(OperationError::InvalidEntryState)?;
2388
2389 let display_name = domain_entry
2390 .get_ava_single_utf8(Attribute::DomainDisplayName)
2391 .map(str::to_string)
2392 .unwrap_or_else(|| format!("Kanidm {}", domain_name));
2393
2394 let domain_ldap_allow_unix_pw_bind = domain_entry
2395 .get_ava_single_bool(Attribute::LdapAllowUnixPwBind)
2396 .unwrap_or(true);
2397
2398 let domain_image = domain_entry.get_ava_single_image(Attribute::Image);
2399
2400 let domain_uuid = self.be_txn.get_db_d_uuid()?;
2401
2402 let mut_d_info = self.d_info.get_mut();
2403 mut_d_info.d_ldap_allow_unix_pw_bind = domain_ldap_allow_unix_pw_bind;
2404 if mut_d_info.d_uuid != domain_uuid {
2405 admin_warn!(
2406 "Using domain uuid from the database {} - was {} in memory",
2407 domain_name,
2408 mut_d_info.d_name,
2409 );
2410 mut_d_info.d_uuid = domain_uuid;
2411 }
2412 if mut_d_info.d_name != domain_name {
2413 admin_warn!(
2414 "Using domain name from the database {} - was {} in memory",
2415 domain_name,
2416 mut_d_info.d_name,
2417 );
2418 admin_warn!(
2419 "If you think this is an error, see https://kanidm.github.io/kanidm/master/domain_rename.html"
2420 );
2421 mut_d_info.d_name = domain_name;
2422 }
2423 mut_d_info.d_display = display_name;
2424 mut_d_info.d_image = domain_image;
2425 Ok(())
2426 }
2427
2428 pub fn set_domain_display_name(&mut self, new_domain_name: &str) -> Result<(), OperationError> {
2432 let modl = ModifyList::new_purge_and_set(
2433 Attribute::DomainDisplayName,
2434 Value::new_utf8(new_domain_name.to_string()),
2435 );
2436 let udi = PVUUID_DOMAIN_INFO.clone();
2437 let filt = filter_all!(f_eq(Attribute::Uuid, udi));
2438 self.internal_modify(&filt, &modl)
2439 }
2440
2441 pub fn danger_domain_rename(&mut self, new_domain_name: &str) -> Result<(), OperationError> {
2454 let modl =
2455 ModifyList::new_purge_and_set(Attribute::DomainName, Value::new_iname(new_domain_name));
2456 let udi = PVUUID_DOMAIN_INFO.clone();
2457 let filt = filter_all!(f_eq(Attribute::Uuid, udi));
2458 self.internal_modify(&filt, &modl)
2459 }
2460
2461 pub fn reindex(&mut self, immediate: bool) -> Result<(), OperationError> {
2462 self.be_txn.reindex(immediate)
2466 }
2467
2468 fn force_schema_reload(&mut self) {
2469 self.changed_flags.insert(ChangeFlag::SCHEMA);
2470 }
2471
2472 fn force_domain_reload(&mut self) {
2473 self.changed_flags.insert(ChangeFlag::DOMAIN);
2474 }
2475
2476 pub(crate) fn upgrade_reindex(&mut self, v: i64) -> Result<(), OperationError> {
2477 self.be_txn.upgrade_reindex(v)
2478 }
2479
2480 #[inline]
2481 pub(crate) fn get_changed_app(&self) -> bool {
2482 self.changed_flags.contains(ChangeFlag::APPLICATION)
2483 }
2484
2485 #[inline]
2486 pub(crate) fn get_changed_oauth2(&self) -> bool {
2487 self.changed_flags.contains(ChangeFlag::OAUTH2)
2488 }
2489
2490 #[inline]
2491 pub(crate) fn clear_changed_oauth2(&mut self) {
2492 self.changed_flags.remove(ChangeFlag::OAUTH2)
2493 }
2494
2495 pub(crate) fn set_phase_bootstrap(&mut self) {
2498 *self.phase = ServerPhase::Bootstrap;
2499 }
2500
2501 pub(crate) fn set_phase(&mut self, phase: ServerPhase) {
2503 if phase > *self.phase {
2505 *self.phase = phase
2506 }
2507 }
2508
2509 pub(crate) fn get_phase(&mut self) -> ServerPhase {
2510 *self.phase
2511 }
2512
2513 pub(crate) fn reload(&mut self) -> Result<(), OperationError> {
2514 if self.changed_flags.contains(ChangeFlag::DOMAIN) {
2517 self.reload_domain_info_version()?;
2518 }
2519
2520 if self.changed_flags.contains(ChangeFlag::SCHEMA) {
2525 self.reload_schema()?;
2526
2527 if *self.phase >= ServerPhase::Running {
2531 self.reindex(false)?;
2532 self.reload_schema()?;
2533 }
2534 }
2535
2536 if self
2539 .changed_flags
2540 .intersects(ChangeFlag::SCHEMA | ChangeFlag::KEY_MATERIAL)
2541 {
2542 self.reload_key_material()?;
2543 }
2544
2545 if self
2553 .changed_flags
2554 .intersects(ChangeFlag::SCHEMA | ChangeFlag::ACP | ChangeFlag::SYNC_AGREEMENT)
2555 {
2556 self.reload_accesscontrols()?;
2557 } else {
2558 }
2563
2564 if self.changed_flags.contains(ChangeFlag::SYSTEM_CONFIG) {
2565 self.reload_system_config()?;
2566 }
2567
2568 if self.changed_flags.contains(ChangeFlag::DOMAIN) {
2569 self.reload_domain_info()?;
2570 }
2571
2572 self.changed_flags.remove(
2574 ChangeFlag::DOMAIN
2575 | ChangeFlag::SCHEMA
2576 | ChangeFlag::SYSTEM_CONFIG
2577 | ChangeFlag::ACP
2578 | ChangeFlag::SYNC_AGREEMENT
2579 | ChangeFlag::KEY_MATERIAL,
2580 );
2581
2582 Ok(())
2583 }
2584
2585 #[cfg(any(test, debug_assertions))]
2586 #[instrument(level = "debug", skip_all)]
2587 pub fn clear_cache(&mut self) -> Result<(), OperationError> {
2588 self.be_txn.clear_cache()
2589 }
2590
2591 #[instrument(level = "info", name="qswt_commit" skip_all)]
2592 pub fn commit(mut self) -> Result<(), OperationError> {
2593 self.reload()?;
2594
2595 let QueryServerWriteTransaction {
2597 committed,
2598 phase,
2599 d_info,
2600 system_config,
2601 mut be_txn,
2602 schema,
2603 accesscontrols,
2604 cid,
2605 dyngroup_cache,
2606 key_providers,
2607 _db_ticket,
2609 _write_ticket,
2610 curtime: _,
2612 trim_cid: _,
2613 changed_flags,
2614 changed_uuid: _,
2615 resolve_filter_cache: _,
2616 resolve_filter_cache_clear,
2617 mut resolve_filter_cache_write,
2618 } = self;
2619 debug_assert!(!committed);
2620
2621 trace!(
2623 changed = ?changed_flags.iter_names().collect::<Vec<_>>(),
2624 );
2625
2626 be_txn.set_db_ts_max(cid.ts)?;
2629 cid.commit();
2630
2631 if resolve_filter_cache_clear {
2633 resolve_filter_cache_write.clear();
2634 }
2635 resolve_filter_cache_write.commit();
2636
2637 schema
2641 .commit()
2642 .map(|_| d_info.commit())
2643 .map(|_| system_config.commit())
2644 .map(|_| phase.commit())
2645 .map(|_| dyngroup_cache.commit())
2646 .and_then(|_| key_providers.commit())
2647 .and_then(|_| accesscontrols.commit())
2648 .and_then(|_| be_txn.commit())
2649 }
2650
2651 pub(crate) fn get_txn_cid(&self) -> &Cid {
2652 &self.cid
2653 }
2654}
2655
2656#[cfg(test)]
2657mod tests {
2658 use crate::prelude::*;
2659 use kanidm_proto::scim_v1::client::ScimFilter;
2660 use kanidm_proto::scim_v1::server::ScimReference;
2661 use kanidm_proto::scim_v1::JsonValue;
2662 use kanidm_proto::scim_v1::ScimEntryGetQuery;
2663
2664 #[qs_test]
2665 async fn test_name_to_uuid(server: &QueryServer) {
2666 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2667
2668 let t_uuid = Uuid::new_v4();
2669 assert!(server_txn
2670 .internal_create(vec![entry_init!(
2671 (Attribute::Class, EntryClass::Object.to_value()),
2672 (Attribute::Class, EntryClass::Account.to_value()),
2673 (Attribute::Class, EntryClass::Person.to_value()),
2674 (Attribute::Name, Value::new_iname("testperson1")),
2675 (Attribute::Uuid, Value::Uuid(t_uuid)),
2676 (Attribute::Description, Value::new_utf8s("testperson1")),
2677 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2678 ),])
2679 .is_ok());
2680
2681 let r1 = server_txn.name_to_uuid("testpers");
2683 assert!(r1.is_err());
2684 let r2 = server_txn.name_to_uuid("tEsTpErS");
2686 assert!(r2.is_err());
2687 let r3 = server_txn.name_to_uuid("testperson1");
2689 assert_eq!(r3, Ok(t_uuid));
2690 let r4 = server_txn.name_to_uuid("tEsTpErSoN1");
2692 assert_eq!(r4, Ok(t_uuid));
2693 let r5 = server_txn.name_to_uuid("name=testperson1");
2695 assert_eq!(r5, Ok(t_uuid));
2696 let r6 = server_txn.name_to_uuid("name=testperson1,o=example");
2698 assert_eq!(r6, Ok(t_uuid));
2699 }
2700
2701 #[qs_test]
2702 async fn test_external_id_to_uuid(server: &QueryServer) {
2703 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2704
2705 let t_uuid = Uuid::new_v4();
2706 assert!(server_txn
2707 .internal_create(vec![entry_init!(
2708 (Attribute::Class, EntryClass::Object.to_value()),
2709 (Attribute::Class, EntryClass::ExtensibleObject.to_value()),
2710 (Attribute::Uuid, Value::Uuid(t_uuid)),
2711 (
2712 Attribute::SyncExternalId,
2713 Value::new_iutf8("uid=testperson")
2714 )
2715 ),])
2716 .is_ok());
2717
2718 let r1 = server_txn.sync_external_id_to_uuid("tobias");
2720 assert_eq!(r1, Ok(None));
2721 let r2 = server_txn.sync_external_id_to_uuid("tObIAs");
2723 assert_eq!(r2, Ok(None));
2724 let r3 = server_txn.sync_external_id_to_uuid("uid=testperson");
2726 assert_eq!(r3, Ok(Some(t_uuid)));
2727 let r4 = server_txn.sync_external_id_to_uuid("uId=TeStPeRsOn");
2729 assert_eq!(r4, Ok(Some(t_uuid)));
2730 }
2731
2732 #[qs_test]
2733 async fn test_uuid_to_spn(server: &QueryServer) {
2734 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2735
2736 let e1 = entry_init!(
2737 (Attribute::Class, EntryClass::Object.to_value()),
2738 (Attribute::Class, EntryClass::Person.to_value()),
2739 (Attribute::Class, EntryClass::Account.to_value()),
2740 (Attribute::Name, Value::new_iname("testperson1")),
2741 (
2742 Attribute::Uuid,
2743 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2744 ),
2745 (Attribute::Description, Value::new_utf8s("testperson1")),
2746 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2747 );
2748 let ce = CreateEvent::new_internal(vec![e1]);
2749 let cr = server_txn.create(&ce);
2750 assert!(cr.is_ok());
2751
2752 let r1 = server_txn.uuid_to_spn(uuid!("bae3f507-e6c3-44ba-ad01-f8ff1083534a"));
2754 assert_eq!(r1, Ok(None));
2756 let r3 = server_txn.uuid_to_spn(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"));
2758 println!("{r3:?}");
2759 assert_eq!(
2760 r3.unwrap().unwrap(),
2761 Value::new_spn_str("testperson1", "example.com")
2762 );
2763 let r4 = server_txn.uuid_to_spn(uuid!("CC8E95B4-C24F-4D68-BA54-8BED76F63930"));
2765 assert_eq!(
2766 r4.unwrap().unwrap(),
2767 Value::new_spn_str("testperson1", "example.com")
2768 );
2769 }
2770
2771 #[qs_test]
2772 async fn test_uuid_to_rdn(server: &QueryServer) {
2773 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2774
2775 let e1 = entry_init!(
2776 (Attribute::Class, EntryClass::Object.to_value()),
2777 (Attribute::Class, EntryClass::Person.to_value()),
2778 (Attribute::Class, EntryClass::Account.to_value()),
2779 (Attribute::Name, Value::new_iname("testperson1")),
2780 (
2781 Attribute::Uuid,
2782 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2783 ),
2784 (Attribute::Description, Value::new_utf8s("testperson")),
2785 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2786 );
2787 let ce = CreateEvent::new_internal(vec![e1]);
2788 let cr = server_txn.create(&ce);
2789 assert!(cr.is_ok());
2790
2791 let r1 = server_txn.uuid_to_rdn(uuid!("bae3f507-e6c3-44ba-ad01-f8ff1083534a"));
2793 assert_eq!(r1.unwrap(), "uuid=bae3f507-e6c3-44ba-ad01-f8ff1083534a");
2795 let r3 = server_txn.uuid_to_rdn(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"));
2797 println!("{r3:?}");
2798 assert_eq!(r3.unwrap(), "spn=testperson1@example.com");
2799 let r4 = server_txn.uuid_to_rdn(uuid!("CC8E95B4-C24F-4D68-BA54-8BED76F63930"));
2801 assert_eq!(r4.unwrap(), "spn=testperson1@example.com");
2802 }
2803
2804 #[qs_test]
2805 async fn test_clone_value(server: &QueryServer) {
2806 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2807 let e1 = entry_init!(
2808 (Attribute::Class, EntryClass::Object.to_value()),
2809 (Attribute::Class, EntryClass::Account.to_value()),
2810 (Attribute::Class, EntryClass::Person.to_value()),
2811 (Attribute::Name, Value::new_iname("testperson1")),
2812 (
2813 Attribute::Uuid,
2814 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2815 ),
2816 (Attribute::Description, Value::new_utf8s("testperson1")),
2817 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2818 );
2819 let ce = CreateEvent::new_internal(vec![e1]);
2820 let cr = server_txn.create(&ce);
2821 assert!(cr.is_ok());
2822
2823 let r1 = server_txn.clone_value(&Attribute::from("tausau"), "naoeutnhaou");
2825
2826 assert!(r1.is_err());
2827
2828 let r2 = server_txn.clone_value(&Attribute::Custom("NaMe".into()), "NaMe");
2831
2832 assert!(r2.is_err());
2833
2834 let r3 = server_txn.clone_value(&Attribute::from("member"), "testperson1");
2836
2837 assert_eq!(
2838 r3,
2839 Ok(Value::Refer(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")))
2840 );
2841
2842 let r4 = server_txn.clone_value(
2844 &Attribute::from("member"),
2845 "cc8e95b4-c24f-4d68-ba54-8bed76f63930",
2846 );
2847
2848 debug!("{:?}", r4);
2849 assert_eq!(
2850 r4,
2851 Ok(Value::Refer(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")))
2852 );
2853 }
2854
2855 #[qs_test]
2856 async fn test_dynamic_schema_class(server: &QueryServer) {
2857 let e1 = entry_init!(
2858 (Attribute::Class, EntryClass::Object.to_value()),
2859 (Attribute::Class, EntryClass::TestClass.to_value()),
2860 (Attribute::Name, Value::new_iname("testobj1")),
2861 (
2862 Attribute::Uuid,
2863 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2864 )
2865 );
2866
2867 let e_cd = entry_init!(
2869 (Attribute::Class, EntryClass::Object.to_value()),
2870 (Attribute::Class, EntryClass::ClassType.to_value()),
2871 (Attribute::ClassName, EntryClass::TestClass.to_value()),
2872 (
2873 Attribute::Uuid,
2874 Value::Uuid(uuid!("cfcae205-31c3-484b-8ced-667d1709c5e3"))
2875 ),
2876 (Attribute::Description, Value::new_utf8s("Test Class")),
2877 (Attribute::May, Value::from(Attribute::Name))
2878 );
2879 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2880 let ce_class = CreateEvent::new_internal(vec![e_cd.clone()]);
2882 assert!(server_txn.create(&ce_class).is_ok());
2883 let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
2885 assert!(server_txn.create(&ce_fail).is_err());
2886
2887 server_txn.commit().expect("should not fail");
2889
2890 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2892 let ce_work = CreateEvent::new_internal(vec![e1.clone()]);
2895 assert!(server_txn.create(&ce_work).is_ok());
2896
2897 server_txn.commit().expect("should not fail");
2899
2900 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2902 let de_class = DeleteEvent::new_internal_invalid(filter!(f_eq(
2904 Attribute::ClassName,
2905 EntryClass::TestClass.into()
2906 )));
2907 assert!(server_txn.delete(&de_class).is_ok());
2908 server_txn.commit().expect("should not fail");
2910
2911 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2913 let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
2915 assert!(server_txn.create(&ce_fail).is_err());
2916 let testobj1 = server_txn
2918 .internal_search_uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2919 .expect("failed");
2920 assert!(testobj1.attribute_equality(Attribute::Class, &EntryClass::TestClass.into()));
2921
2922 server_txn.commit().expect("should not fail");
2924 }
2926
2927 #[qs_test]
2928 async fn test_dynamic_schema_attr(server: &QueryServer) {
2929 let e1 = entry_init!(
2930 (Attribute::Class, EntryClass::Object.to_value()),
2931 (Attribute::Class, EntryClass::ExtensibleObject.to_value()),
2932 (Attribute::Name, Value::new_iname("testobj1")),
2933 (
2934 Attribute::Uuid,
2935 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2936 ),
2937 (Attribute::TestAttr, Value::new_utf8s("test"))
2938 );
2939
2940 let e_ad = entry_init!(
2942 (Attribute::Class, EntryClass::Object.to_value()),
2943 (Attribute::Class, EntryClass::AttributeType.to_value()),
2944 (
2945 Attribute::Uuid,
2946 Value::Uuid(uuid!("cfcae205-31c3-484b-8ced-667d1709c5e3"))
2947 ),
2948 (Attribute::AttributeName, Value::from(Attribute::TestAttr)),
2949 (Attribute::Description, Value::new_utf8s("Test Attribute")),
2950 (Attribute::MultiValue, Value::new_bool(false)),
2951 (Attribute::Unique, Value::new_bool(false)),
2952 (
2953 Attribute::Syntax,
2954 Value::new_syntaxs("UTF8STRING").expect("syntax")
2955 )
2956 );
2957
2958 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2959 let ce_attr = CreateEvent::new_internal(vec![e_ad.clone()]);
2961 assert!(server_txn.create(&ce_attr).is_ok());
2962 let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
2964 assert!(server_txn.create(&ce_fail).is_err());
2965
2966 server_txn.commit().expect("should not fail");
2968
2969 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2971 let ce_work = CreateEvent::new_internal(vec![e1.clone()]);
2974 assert!(server_txn.create(&ce_work).is_ok());
2975
2976 server_txn.commit().expect("should not fail");
2978
2979 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2981 let de_attr = DeleteEvent::new_internal_invalid(filter!(f_eq(
2983 Attribute::AttributeName,
2984 PartialValue::from(Attribute::TestAttr)
2985 )));
2986 assert!(server_txn.delete(&de_attr).is_ok());
2987 server_txn.commit().expect("should not fail");
2989
2990 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2992 let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
2994 assert!(server_txn.create(&ce_fail).is_err());
2995 let filt = filter!(f_eq(Attribute::TestAttr, PartialValue::new_utf8s("test")));
2997 assert!(server_txn.internal_search(filt).is_err());
2998 let testobj1 = server_txn
3001 .internal_search_uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
3002 .expect("failed");
3003 assert!(testobj1.attribute_equality(Attribute::TestAttr, &PartialValue::new_utf8s("test")));
3004
3005 server_txn.commit().expect("should not fail");
3006 }
3008
3009 #[qs_test]
3010 async fn test_scim_entry_structure(server: &QueryServer) {
3011 let mut read_txn = server.read().await.unwrap();
3012
3013 let entry = read_txn
3015 .internal_search_uuid(UUID_IDM_PEOPLE_SELF_NAME_WRITE)
3016 .unwrap();
3017
3018 let reduced = entry.as_ref().clone().into_reduced();
3020 let scim_entry = reduced.to_scim_kanidm(&mut read_txn).unwrap();
3021
3022 assert_eq!(scim_entry.header.id, UUID_IDM_PEOPLE_SELF_NAME_WRITE);
3024 let name_scim = scim_entry.attrs.get(&Attribute::Name).unwrap();
3025 match name_scim {
3026 ScimValueKanidm::String(name) => {
3027 assert_eq!(name.clone(), "idm_people_self_name_write")
3028 }
3029 _ => {
3030 panic!("expected String, actual {:?}", name_scim);
3031 }
3032 }
3033
3034 let entry_managed_by_scim = scim_entry.attrs.get(&Attribute::EntryManagedBy).unwrap();
3036 match entry_managed_by_scim {
3037 ScimValueKanidm::EntryReferences(managed_by) => {
3038 assert_eq!(
3039 managed_by.first().unwrap().clone(),
3040 ScimReference {
3041 uuid: UUID_IDM_ADMINS,
3042 value: "idm_admins@example.com".to_string()
3043 }
3044 )
3045 }
3046 _ => {
3047 panic!(
3048 "expected EntryReference, actual {:?}",
3049 entry_managed_by_scim
3050 );
3051 }
3052 }
3053
3054 let members_scim = scim_entry.attrs.get(&Attribute::Member).unwrap();
3055 match members_scim {
3056 ScimValueKanidm::EntryReferences(members) => {
3057 assert_eq!(
3058 members.first().unwrap().clone(),
3059 ScimReference {
3060 uuid: UUID_IDM_ALL_PERSONS,
3061 value: "idm_all_persons@example.com".to_string()
3062 }
3063 )
3064 }
3065 _ => {
3066 panic!("expected EntryReferences, actual {:?}", members_scim);
3067 }
3068 }
3069 }
3070
3071 #[qs_test]
3072 async fn test_scim_effective_access_query(server: &QueryServer) {
3073 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3074
3075 let group_uuid = Uuid::new_v4();
3076 let e1 = entry_init!(
3077 (Attribute::Class, EntryClass::Object.to_value()),
3078 (Attribute::Class, EntryClass::Group.to_value()),
3079 (Attribute::Name, Value::new_iname("testgroup")),
3080 (Attribute::Uuid, Value::Uuid(group_uuid))
3081 );
3082
3083 assert!(server_txn.internal_create(vec![e1]).is_ok());
3084 assert!(server_txn.commit().is_ok());
3085
3086 let mut server_txn = server.read().await.unwrap();
3089
3090 let idm_admin_entry = server_txn.internal_search_uuid(UUID_IDM_ADMIN).unwrap();
3091 let idm_admin_ident = Identity::from_impersonate_entry_readwrite(idm_admin_entry);
3092
3093 let query = ScimEntryGetQuery {
3094 ext_access_check: true,
3095 ..Default::default()
3096 };
3097
3098 let scim_entry = server_txn
3099 .scim_entry_id_get_ext(group_uuid, EntryClass::Group, query, idm_admin_ident)
3100 .unwrap();
3101
3102 let ext_access_check = scim_entry.ext_access_check.unwrap();
3103
3104 trace!(?ext_access_check);
3105
3106 assert!(ext_access_check.delete);
3107 assert!(ext_access_check.search.check(&Attribute::DirectMemberOf));
3108 assert!(ext_access_check.search.check(&Attribute::MemberOf));
3109 assert!(ext_access_check.search.check(&Attribute::Name));
3110 assert!(ext_access_check.modify_present.check(&Attribute::Name));
3111 assert!(ext_access_check.modify_remove.check(&Attribute::Name));
3112 }
3113
3114 #[qs_test]
3115 async fn test_scim_basic_search_ext_query(server: &QueryServer) {
3116 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3117
3118 let group_uuid = Uuid::new_v4();
3119 let e1 = entry_init!(
3120 (Attribute::Class, EntryClass::Object.to_value()),
3121 (Attribute::Class, EntryClass::Group.to_value()),
3122 (Attribute::Name, Value::new_iname("testgroup")),
3123 (Attribute::Uuid, Value::Uuid(group_uuid))
3124 );
3125
3126 assert!(server_txn.internal_create(vec![e1]).is_ok());
3127 assert!(server_txn.commit().is_ok());
3128
3129 let mut server_txn = server.read().await.unwrap();
3131
3132 let idm_admin_entry = server_txn.internal_search_uuid(UUID_IDM_ADMIN).unwrap();
3133 let idm_admin_ident = Identity::from_impersonate_entry_readwrite(idm_admin_entry);
3134
3135 let filter = ScimFilter::And(
3136 Box::new(ScimFilter::Equal(
3137 Attribute::Class.into(),
3138 EntryClass::Group.into(),
3139 )),
3140 Box::new(ScimFilter::Equal(
3141 Attribute::Uuid.into(),
3142 JsonValue::String(group_uuid.to_string()),
3143 )),
3144 );
3145
3146 let base: Vec<ScimEntryKanidm> = server_txn
3147 .scim_search_ext(idm_admin_ident, filter, ScimEntryGetQuery::default())
3148 .unwrap();
3149
3150 assert_eq!(base.len(), 1);
3151 assert_eq!(base[0].header.id, group_uuid);
3152 }
3153}