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