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();
1547
1548 if !idx_errs.is_empty() {
1549 return idx_errs;
1550 }
1551
1552 let mut results = Vec::with_capacity(0);
1555
1556 let schema = self.get_schema();
1559
1560 let filt_all = filter!(f_pres(Attribute::Class));
1561 let all_entries = match self.internal_search(filt_all) {
1562 Ok(a) => a,
1563 Err(_e) => return vec![Err(ConsistencyError::QueryServerSearchFailure)],
1564 };
1565
1566 for e in all_entries {
1567 e.verify(schema, &mut results)
1568 }
1569
1570 self.get_be_txn().verify_ruv(&mut results);
1572
1573 Plugins::run_verify(self, &mut results);
1579 results
1582 }
1583
1584 #[instrument(level = "debug", skip_all)]
1585 pub fn scim_entry_id_get_ext(
1586 &mut self,
1587 uuid: Uuid,
1588 class: EntryClass,
1589 query: ScimEntryGetQuery,
1590 ident: Identity,
1591 ) -> Result<ScimEntryKanidm, OperationError> {
1592 let filter_intent = filter!(f_and!([
1593 f_eq(Attribute::Uuid, PartialValue::Uuid(uuid)),
1594 f_eq(Attribute::Class, class.into())
1595 ]));
1596
1597 let f_intent_valid = filter_intent
1598 .validate(self.get_schema())
1599 .map_err(OperationError::SchemaViolation)?;
1600
1601 let f_valid = f_intent_valid.clone().into_ignore_hidden();
1602
1603 let r_attrs = query
1604 .attributes
1605 .map(|attr_set| attr_set.into_iter().collect());
1606
1607 let se = SearchEvent {
1608 ident,
1609 filter: f_valid,
1610 filter_orig: f_intent_valid,
1611 attrs: r_attrs,
1612 effective_access_check: query.ext_access_check,
1613 };
1614
1615 let mut vs = self.search_ext(&se)?;
1616 match vs.pop() {
1617 Some(entry) if vs.is_empty() => entry.to_scim_kanidm(self),
1618 _ => {
1619 if vs.is_empty() {
1620 Err(OperationError::NoMatchingEntries)
1621 } else {
1622 Err(OperationError::UniqueConstraintViolation)
1624 }
1625 }
1626 }
1627 }
1628
1629 #[instrument(level = "debug", skip_all)]
1630 pub fn scim_search_ext(
1631 &mut self,
1632 ident: Identity,
1633 filter: ScimFilter,
1634 query: ScimEntryGetQuery,
1635 ) -> Result<Vec<ScimEntryKanidm>, OperationError> {
1636 let filter_intent = Filter::from_scim_ro(&ident, &filter, self)?;
1637
1638 let f_intent_valid = filter_intent
1639 .validate(self.get_schema())
1640 .map_err(OperationError::SchemaViolation)?;
1641
1642 let f_valid = f_intent_valid.clone().into_ignore_hidden();
1643
1644 let r_attrs = query
1645 .attributes
1646 .map(|attr_set| attr_set.into_iter().collect());
1647
1648 let se = SearchEvent {
1649 ident,
1650 filter: f_valid,
1651 filter_orig: f_intent_valid,
1652 attrs: r_attrs,
1653 effective_access_check: query.ext_access_check,
1654 };
1655
1656 let vs = self.search_ext(&se)?;
1657
1658 vs.into_iter()
1659 .map(|entry| entry.to_scim_kanidm(self))
1660 .collect()
1661 }
1662}
1663
1664impl<'a> QueryServerTransaction<'a> for QueryServerWriteTransaction<'a> {
1665 type AccessControlsTransactionType = AccessControlsWriteTransaction<'a>;
1666 type BackendTransactionType = BackendWriteTransaction<'a>;
1667 type SchemaTransactionType = SchemaWriteTransaction<'a>;
1668 type KeyProvidersTransactionType = KeyProvidersWriteTransaction<'a>;
1669
1670 fn get_be_txn(&mut self) -> &mut BackendWriteTransaction<'a> {
1671 &mut self.be_txn
1672 }
1673
1674 fn get_schema<'b>(&self) -> &'b SchemaWriteTransaction<'a> {
1675 unsafe {
1679 let s = (&self.schema) as *const _;
1680 &*s
1681 }
1682 }
1683
1684 fn get_accesscontrols(&self) -> &AccessControlsWriteTransaction<'a> {
1685 &self.accesscontrols
1686 }
1687
1688 fn get_key_providers(&self) -> &KeyProvidersWriteTransaction<'a> {
1689 &self.key_providers
1690 }
1691
1692 fn get_resolve_filter_cache(&mut self) -> Option<&mut ResolveFilterCacheReadTxn<'a>> {
1693 if self.resolve_filter_cache_clear || *self.phase < ServerPhase::SchemaReady {
1694 None
1695 } else {
1696 Some(&mut self.resolve_filter_cache)
1697 }
1698 }
1699
1700 fn get_resolve_filter_cache_and_be_txn(
1701 &mut self,
1702 ) -> (
1703 &mut BackendWriteTransaction<'a>,
1704 Option<&mut ResolveFilterCacheReadTxn<'a>>,
1705 ) {
1706 if self.resolve_filter_cache_clear || *self.phase < ServerPhase::SchemaReady {
1707 (&mut self.be_txn, None)
1708 } else {
1709 (&mut self.be_txn, Some(&mut self.resolve_filter_cache))
1710 }
1711 }
1712
1713 fn pw_badlist(&self) -> &HashSet<String> {
1714 &self.system_config.pw_badlist
1715 }
1716
1717 fn denied_names(&self) -> &HashSet<String> {
1718 &self.system_config.denied_names
1719 }
1720
1721 fn get_domain_version(&self) -> DomainVersion {
1722 self.d_info.d_vers
1723 }
1724
1725 fn get_domain_patch_level(&self) -> u32 {
1726 self.d_info.d_patch_level
1727 }
1728
1729 fn get_domain_development_taint(&self) -> bool {
1730 self.d_info.d_devel_taint
1731 }
1732
1733 fn get_domain_uuid(&self) -> Uuid {
1734 self.d_info.d_uuid
1735 }
1736
1737 fn get_domain_name(&self) -> &str {
1739 &self.d_info.d_name
1740 }
1741
1742 fn get_domain_display_name(&self) -> &str {
1743 &self.d_info.d_display
1744 }
1745
1746 fn get_domain_image_value(&self) -> Option<ImageValue> {
1747 self.d_info.d_image.clone()
1748 }
1749}
1750
1751impl QueryServer {
1752 pub fn new(
1753 be: Backend,
1754 schema: Schema,
1755 domain_name: String,
1756 curtime: Duration,
1757 ) -> Result<Self, OperationError> {
1758 let (s_uuid, d_uuid, ts_max) = {
1759 let mut wr = be.write()?;
1760 let s_uuid = wr.get_db_s_uuid()?;
1761 let d_uuid = wr.get_db_d_uuid()?;
1762 let ts_max = wr.get_db_ts_max(curtime)?;
1763 wr.commit()?;
1764 (s_uuid, d_uuid, ts_max)
1765 };
1766
1767 let pool_size = be.get_pool_size();
1768
1769 debug!("Server UUID -> {:?}", s_uuid);
1770 debug!("Domain UUID -> {:?}", d_uuid);
1771 debug!("Domain Name -> {:?}", domain_name);
1772
1773 let d_info = Arc::new(CowCell::new(DomainInfo {
1774 d_uuid,
1775 d_vers: DOMAIN_LEVEL_0,
1778 d_patch_level: 0,
1779 d_name: domain_name.clone(),
1780 d_display: domain_name,
1783 d_devel_taint: option_env!("KANIDM_PRE_RELEASE").is_some(),
1785 d_ldap_allow_unix_pw_bind: false,
1786 d_allow_easter_eggs: false,
1787 d_image: None,
1788 }));
1789
1790 let cid = Cid::new_lamport(s_uuid, curtime, &ts_max);
1791 let cid_max = Arc::new(CowCell::new(cid));
1792
1793 let system_config = Arc::new(CowCell::new(SystemConfig::default()));
1795
1796 let dyngroup_cache = Arc::new(CowCell::new(DynGroupCache::default()));
1797
1798 let phase = Arc::new(CowCell::new(ServerPhase::Bootstrap));
1799
1800 let resolve_filter_cache = Arc::new(
1801 ARCacheBuilder::new()
1802 .set_size(RESOLVE_FILTER_CACHE_MAX, RESOLVE_FILTER_CACHE_LOCAL)
1803 .set_reader_quiesce(true)
1804 .build()
1805 .ok_or_else(|| {
1806 error!("Failed to build filter resolve cache");
1807 OperationError::DB0003FilterResolveCacheBuild
1808 })?,
1809 );
1810
1811 let key_providers = Arc::new(KeyProviders::default());
1812
1813 debug_assert!(pool_size > 0);
1816 let read_ticket_pool = std::cmp::max(pool_size - 1, 1);
1817
1818 Ok(QueryServer {
1819 phase,
1820 d_info,
1821 system_config,
1822 be,
1823 schema: Arc::new(schema),
1824 accesscontrols: Arc::new(AccessControls::default()),
1825 db_tickets: Arc::new(Semaphore::new(pool_size as usize)),
1826 read_tickets: Arc::new(Semaphore::new(read_ticket_pool as usize)),
1827 write_ticket: Arc::new(Semaphore::new(1)),
1828 resolve_filter_cache,
1829 dyngroup_cache,
1830 cid_max,
1831 key_providers,
1832 })
1833 }
1834
1835 pub fn try_quiesce(&self) {
1836 self.be.try_quiesce();
1837 self.accesscontrols.try_quiesce();
1838 self.resolve_filter_cache.try_quiesce();
1839 }
1840
1841 #[instrument(level = "debug", skip_all)]
1842 async fn read_acquire_ticket(&self) -> Option<(SemaphorePermit<'_>, SemaphorePermit<'_>)> {
1843 let read_ticket = if cfg!(test) {
1847 self.read_tickets
1848 .try_acquire()
1849 .inspect_err(|err| {
1850 error!(?err, "Unable to acquire read ticket!");
1851 })
1852 .ok()?
1853 } else {
1854 let fut = tokio::time::timeout(
1855 Duration::from_millis(DB_LOCK_ACQUIRE_TIMEOUT_MILLIS),
1856 self.read_tickets.acquire(),
1857 );
1858
1859 match fut.await {
1860 Ok(Ok(ticket)) => ticket,
1861 Ok(Err(_)) => {
1862 error!("Failed to acquire read ticket, may be poisoned.");
1863 return None;
1864 }
1865 Err(_) => {
1866 error!("Failed to acquire read ticket, server is overloaded.");
1867 return None;
1868 }
1869 }
1870 };
1871
1872 let db_ticket = if cfg!(test) {
1877 self.db_tickets
1878 .try_acquire()
1879 .inspect_err(|err| {
1880 error!(?err, "Unable to acquire database ticket!");
1881 })
1882 .ok()?
1883 } else {
1884 self.db_tickets
1885 .acquire()
1886 .await
1887 .inspect_err(|err| {
1888 error!(?err, "Unable to acquire database ticket!");
1889 })
1890 .ok()?
1891 };
1892
1893 Some((read_ticket, db_ticket))
1894 }
1895
1896 pub async fn read(&self) -> Result<QueryServerReadTransaction<'_>, OperationError> {
1897 let (read_ticket, db_ticket) = self
1898 .read_acquire_ticket()
1899 .await
1900 .ok_or(OperationError::DatabaseLockAcquisitionTimeout)?;
1901 let schema = self.schema.read();
1905
1906 let cid_max = self.cid_max.read();
1907 let trim_cid = cid_max.sub_secs(CHANGELOG_MAX_AGE)?;
1908
1909 let be_txn = self.be.read()?;
1910
1911 Ok(QueryServerReadTransaction {
1912 be_txn,
1913 schema,
1914 d_info: self.d_info.read(),
1915 system_config: self.system_config.read(),
1916 accesscontrols: self.accesscontrols.read(),
1917 key_providers: self.key_providers.read(),
1918 _db_ticket: db_ticket,
1919 _read_ticket: read_ticket,
1920 resolve_filter_cache: self.resolve_filter_cache.read(),
1921 trim_cid,
1922 })
1923 }
1924
1925 #[instrument(level = "debug", skip_all)]
1926 async fn write_acquire_ticket(&self) -> Option<(SemaphorePermit<'_>, SemaphorePermit<'_>)> {
1927 let write_ticket = if cfg!(test) {
1929 self.write_ticket
1930 .try_acquire()
1931 .inspect_err(|err| {
1932 error!(?err, "Unable to acquire write ticket!");
1933 })
1934 .ok()?
1935 } else {
1936 let fut = tokio::time::timeout(
1937 Duration::from_millis(DB_LOCK_ACQUIRE_TIMEOUT_MILLIS),
1938 self.write_ticket.acquire(),
1939 );
1940
1941 match fut.await {
1942 Ok(Ok(ticket)) => ticket,
1943 Ok(Err(_)) => {
1944 error!("Failed to acquire write ticket, may be poisoned.");
1945 return None;
1946 }
1947 Err(_) => {
1948 error!("Failed to acquire write ticket, server is overloaded.");
1949 return None;
1950 }
1951 }
1952 };
1953
1954 let db_ticket = if cfg!(test) {
1958 self.db_tickets
1959 .try_acquire()
1960 .inspect_err(|err| {
1961 error!(?err, "Unable to acquire write db_ticket!");
1962 })
1963 .ok()?
1964 } else {
1965 self.db_tickets
1966 .acquire()
1967 .await
1968 .inspect_err(|err| {
1969 error!(?err, "Unable to acquire write db_ticket!");
1970 })
1971 .ok()?
1972 };
1973
1974 Some((write_ticket, db_ticket))
1975 }
1976
1977 pub async fn write(
1978 &self,
1979 curtime: Duration,
1980 ) -> Result<QueryServerWriteTransaction<'_>, OperationError> {
1981 let (write_ticket, db_ticket) = self
1982 .write_acquire_ticket()
1983 .await
1984 .ok_or(OperationError::DatabaseLockAcquisitionTimeout)?;
1985
1986 let be_txn = self.be.write()?;
1991
1992 let schema_write = self.schema.write();
1993 let d_info = self.d_info.write();
1994 let system_config = self.system_config.write();
1995 let phase = self.phase.write();
1996
1997 let mut cid = self.cid_max.write();
1998 *cid = Cid::new_lamport(cid.s_uuid, curtime, &cid.ts);
2000
2001 let trim_cid = cid.sub_secs(CHANGELOG_MAX_AGE)?;
2002
2003 Ok(QueryServerWriteTransaction {
2004 committed: false,
2011 phase,
2012 d_info,
2013 system_config,
2014 curtime,
2015 cid,
2016 trim_cid,
2017 be_txn,
2018 schema: schema_write,
2019 accesscontrols: self.accesscontrols.write(),
2020 changed_flags: ChangeFlag::empty(),
2021 changed_uuid: HashSet::new(),
2022 _db_ticket: db_ticket,
2023 _write_ticket: write_ticket,
2024 resolve_filter_cache: self.resolve_filter_cache.read(),
2025 resolve_filter_cache_clear: false,
2026 resolve_filter_cache_write: self.resolve_filter_cache.write(),
2027 dyngroup_cache: self.dyngroup_cache.write(),
2028 key_providers: self.key_providers.write(),
2029 })
2030 }
2031
2032 #[cfg(any(test, debug_assertions))]
2033 pub async fn clear_cache(&self) -> Result<(), OperationError> {
2034 let ct = duration_from_epoch_now();
2035 let mut w_txn = self.write(ct).await?;
2036 w_txn.clear_cache()?;
2037 w_txn.commit()
2038 }
2039
2040 pub async fn verify(&self) -> Vec<Result<(), ConsistencyError>> {
2041 match self.read().await {
2042 Ok(mut r_txn) => r_txn.verify(),
2043 Err(_) => vec![Err(ConsistencyError::Unknown)],
2044 }
2045 }
2046}
2047
2048impl<'a> QueryServerWriteTransaction<'a> {
2049 pub(crate) fn get_server_uuid(&self) -> Uuid {
2050 self.cid.s_uuid
2052 }
2053
2054 pub(crate) fn reset_server_uuid(&mut self) -> Result<(), OperationError> {
2055 let s_uuid = self.be_txn.reset_db_s_uuid().map_err(|err| {
2056 error!(?err, "Failed to reset server replication uuid");
2057 err
2058 })?;
2059
2060 debug!(?s_uuid, "reset server replication uuid");
2061
2062 self.cid.s_uuid = s_uuid;
2063
2064 Ok(())
2065 }
2066
2067 pub(crate) fn get_curtime(&self) -> Duration {
2068 self.curtime
2069 }
2070
2071 pub(crate) fn get_cid(&self) -> &Cid {
2072 &self.cid
2073 }
2074
2075 pub(crate) fn get_key_providers_mut(&mut self) -> &mut KeyProvidersWriteTransaction<'a> {
2076 &mut self.key_providers
2077 }
2078
2079 pub(crate) fn get_dyngroup_cache(&mut self) -> &mut DynGroupCache {
2080 &mut self.dyngroup_cache
2081 }
2082
2083 pub fn domain_raise(&mut self, level: u32) -> Result<(), OperationError> {
2084 if level > DOMAIN_MAX_LEVEL {
2085 return Err(OperationError::MG0002RaiseDomainLevelExceedsMaximum);
2086 }
2087
2088 let modl = ModifyList::new_purge_and_set(Attribute::Version, Value::Uint32(level));
2089 let udi = PVUUID_DOMAIN_INFO.clone();
2090 let filt = filter_all!(f_eq(Attribute::Uuid, udi));
2091 self.internal_modify(&filt, &modl)
2092 }
2093
2094 pub fn domain_remigrate(&mut self, level: u32) -> Result<(), OperationError> {
2095 let mut_d_info = self.d_info.get_mut();
2096
2097 if level > mut_d_info.d_vers {
2098 return Ok(());
2100 } else if level < DOMAIN_MIN_REMIGRATION_LEVEL {
2101 return Err(OperationError::MG0001InvalidReMigrationLevel);
2102 };
2103
2104 info!(
2105 "Prepare to re-migrate from {} -> {}",
2106 level, mut_d_info.d_vers
2107 );
2108 mut_d_info.d_vers = level;
2109 self.changed_flags.insert(ChangeFlag::DOMAIN);
2110
2111 Ok(())
2112 }
2113
2114 #[instrument(level = "debug", skip_all)]
2115 pub(crate) fn reload_schema(&mut self) -> Result<(), OperationError> {
2116 let filt = filter!(f_eq(Attribute::Class, EntryClass::AttributeType.into()));
2119 let res = self.internal_search(filt).map_err(|e| {
2120 admin_error!("reload schema internal search failed {:?}", e);
2121 e
2122 })?;
2123 let attributetypes: Result<Vec<_>, _> =
2125 res.iter().map(|e| SchemaAttribute::try_from(e)).collect();
2126
2127 let attributetypes = attributetypes.map_err(|e| {
2128 admin_error!("reload schema attributetypes {:?}", e);
2129 e
2130 })?;
2131
2132 self.schema.update_attributes(attributetypes).map_err(|e| {
2133 admin_error!("reload schema update attributetypes {:?}", e);
2134 e
2135 })?;
2136
2137 let filt = filter!(f_eq(Attribute::Class, EntryClass::ClassType.into()));
2139 let res = self.internal_search(filt).map_err(|e| {
2140 admin_error!("reload schema internal search failed {:?}", e);
2141 e
2142 })?;
2143 let classtypes: Result<Vec<_>, _> = res.iter().map(|e| SchemaClass::try_from(e)).collect();
2145 let classtypes = classtypes.map_err(|e| {
2146 admin_error!("reload schema classtypes {:?}", e);
2147 e
2148 })?;
2149
2150 self.schema.update_classes(classtypes).map_err(|e| {
2151 admin_error!("reload schema update classtypes {:?}", e);
2152 e
2153 })?;
2154
2155 let valid_r = self.schema.validate();
2157
2158 if valid_r.is_empty() {
2160 trace!("Reloading idxmeta ...");
2162 self.be_txn
2163 .update_idxmeta(self.schema.reload_idxmeta())
2164 .map_err(|e| {
2165 admin_error!("reload schema update idxmeta {:?}", e);
2166 e
2167 })
2168 } else {
2169 admin_error!("Schema reload failed -> {:?}", valid_r);
2171 Err(OperationError::ConsistencyError(
2172 valid_r.into_iter().filter_map(|v| v.err()).collect(),
2173 ))
2174 }?;
2175
2176 self.resolve_filter_cache_clear = true;
2179
2180 DynGroup::reload(self)?;
2183
2184 Ok(())
2185 }
2186
2187 #[instrument(level = "debug", skip_all)]
2188 fn reload_accesscontrols(&mut self) -> Result<(), OperationError> {
2189 trace!("ACP reload started ...");
2197
2198 let filt = filter!(f_eq(Attribute::Class, EntryClass::SyncAccount.into()));
2201
2202 let res = self.internal_search(filt).map_err(|e| {
2203 admin_error!(
2204 err = ?e,
2205 "reload accesscontrols internal search failed",
2206 );
2207 e
2208 })?;
2209
2210 let sync_agreement_map: HashMap<Uuid, BTreeSet<Attribute>> = res
2211 .iter()
2212 .filter_map(|e| {
2213 e.get_ava_as_iutf8(Attribute::SyncYieldAuthority)
2214 .map(|set| {
2215 let set: BTreeSet<_> =
2216 set.iter().map(|s| Attribute::from(s.as_str())).collect();
2217 (e.get_uuid(), set)
2218 })
2219 })
2220 .collect();
2221
2222 self.accesscontrols
2223 .update_sync_agreements(sync_agreement_map);
2224
2225 let filt = filter!(f_and!([
2227 f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2228 f_eq(Attribute::Class, EntryClass::AccessControlSearch.into()),
2229 f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2230 ]));
2231
2232 let res = self.internal_search(filt).map_err(|e| {
2233 admin_error!(
2234 err = ?e,
2235 "reload accesscontrols internal search failed",
2236 );
2237 e
2238 })?;
2239 let search_acps: Result<Vec<_>, _> = res
2240 .iter()
2241 .map(|e| AccessControlSearch::try_from(self, e))
2242 .collect();
2243
2244 let search_acps = search_acps.map_err(|e| {
2245 admin_error!(err = ?e, "Unable to parse search accesscontrols");
2246 e
2247 })?;
2248
2249 self.accesscontrols
2250 .update_search(search_acps)
2251 .map_err(|e| {
2252 admin_error!(err = ?e, "Failed to update search accesscontrols");
2253 e
2254 })?;
2255 let filt = filter!(f_and!([
2257 f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2258 f_eq(Attribute::Class, EntryClass::AccessControlCreate.into()),
2259 f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2260 ]));
2261
2262 let res = self.internal_search(filt).map_err(|e| {
2263 admin_error!(
2264 err = ?e,
2265 "reload accesscontrols internal search failed"
2266 );
2267 e
2268 })?;
2269 let create_acps: Result<Vec<_>, _> = res
2270 .iter()
2271 .map(|e| AccessControlCreate::try_from(self, e))
2272 .collect();
2273
2274 let create_acps = create_acps.map_err(|e| {
2275 admin_error!(err = ?e, "Unable to parse create accesscontrols");
2276 e
2277 })?;
2278
2279 self.accesscontrols
2280 .update_create(create_acps)
2281 .map_err(|e| {
2282 admin_error!(err = ?e, "Failed to update create accesscontrols");
2283 e
2284 })?;
2285 let filt = filter!(f_and!([
2287 f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2288 f_eq(Attribute::Class, EntryClass::AccessControlModify.into()),
2289 f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2290 ]));
2291
2292 let res = self.internal_search(filt).map_err(|e| {
2293 admin_error!("reload accesscontrols internal search failed {:?}", e);
2294 e
2295 })?;
2296 let modify_acps: Result<Vec<_>, _> = res
2297 .iter()
2298 .map(|e| AccessControlModify::try_from(self, e))
2299 .collect();
2300
2301 let modify_acps = modify_acps.map_err(|e| {
2302 admin_error!("Unable to parse modify accesscontrols {:?}", e);
2303 e
2304 })?;
2305
2306 self.accesscontrols
2307 .update_modify(modify_acps)
2308 .map_err(|e| {
2309 admin_error!("Failed to update modify accesscontrols {:?}", e);
2310 e
2311 })?;
2312 let filt = filter!(f_and!([
2314 f_eq(Attribute::Class, EntryClass::AccessControlProfile.into()),
2315 f_eq(Attribute::Class, EntryClass::AccessControlDelete.into()),
2316 f_andnot(f_eq(Attribute::AcpEnable, PV_FALSE.clone())),
2317 ]));
2318
2319 let res = self.internal_search(filt).map_err(|e| {
2320 admin_error!("reload accesscontrols internal search failed {:?}", e);
2321 e
2322 })?;
2323 let delete_acps: Result<Vec<_>, _> = res
2324 .iter()
2325 .map(|e| AccessControlDelete::try_from(self, e))
2326 .collect();
2327
2328 let delete_acps = delete_acps.map_err(|e| {
2329 admin_error!("Unable to parse delete accesscontrols {:?}", e);
2330 e
2331 })?;
2332
2333 self.accesscontrols.update_delete(delete_acps).map_err(|e| {
2334 admin_error!("Failed to update delete accesscontrols {:?}", e);
2335 e
2336 })
2337 }
2338
2339 #[instrument(level = "debug", skip_all)]
2340 pub(crate) fn reload_key_material(&mut self) -> Result<(), OperationError> {
2341 let filt = filter!(f_eq(Attribute::Class, EntryClass::KeyProvider.into()));
2342
2343 let res = self.internal_search(filt).map_err(|e| {
2344 admin_error!(
2345 err = ?e,
2346 "reload key providers internal search failed",
2347 );
2348 e
2349 })?;
2350
2351 let providers = res
2354 .iter()
2355 .map(|e| KeyProvider::try_from(e).and_then(|kp| kp.test().map(|()| kp)))
2356 .collect::<Result<Vec<_>, _>>()?;
2357
2358 self.key_providers.update_providers(providers)?;
2359
2360 let filt = filter!(f_eq(Attribute::Class, EntryClass::KeyObject.into()));
2361
2362 let res = self.internal_search(filt).map_err(|e| {
2363 admin_error!(
2364 err = ?e,
2365 "reload key objects internal search failed",
2366 );
2367 e
2368 })?;
2369
2370 res.iter()
2371 .try_for_each(|entry| self.key_providers.load_key_object(entry.as_ref()))
2372 }
2373
2374 #[instrument(level = "debug", skip_all)]
2375 pub(crate) fn reload_system_config(&mut self) -> Result<(), OperationError> {
2376 let denied_names = self.get_sc_denied_names()?;
2377 let pw_badlist = self.get_sc_password_badlist()?;
2378
2379 let mut_system_config = self.system_config.get_mut();
2380 mut_system_config.denied_names = denied_names;
2381 mut_system_config.pw_badlist = pw_badlist;
2382 Ok(())
2383 }
2384
2385 #[instrument(level = "debug", skip_all)]
2387 pub(crate) fn reload_domain_info_version(&mut self) -> Result<(), OperationError> {
2388 let domain_info = self.internal_search_uuid(UUID_DOMAIN_INFO).map_err(|err| {
2389 error!(?err, "Error getting domain info");
2390 err
2391 })?;
2392
2393 let domain_info_version = domain_info
2394 .get_ava_single_uint32(Attribute::Version)
2395 .ok_or_else(|| {
2396 error!("domain info missing attribute version");
2397 OperationError::InvalidEntryState
2398 })?;
2399
2400 let domain_info_patch_level = domain_info
2401 .get_ava_single_uint32(Attribute::PatchLevel)
2402 .unwrap_or(0);
2403
2404 let current_devel_flag = option_env!("KANIDM_PRE_RELEASE").is_some();
2408 let domain_info_devel_taint = current_devel_flag
2409 || domain_info
2410 .get_ava_single_bool(Attribute::DomainDevelopmentTaint)
2411 .unwrap_or_default();
2412
2413 let domain_allow_easter_eggs = domain_info
2414 .get_ava_single_bool(Attribute::DomainAllowEasterEggs)
2415 .unwrap_or(option_env!("KANIDM_PRE_RELEASE").is_some());
2417
2418 let mut_d_info = self.d_info.get_mut();
2422 let previous_version = mut_d_info.d_vers;
2423 let previous_patch_level = mut_d_info.d_patch_level;
2424 mut_d_info.d_vers = domain_info_version;
2425 mut_d_info.d_patch_level = domain_info_patch_level;
2426 mut_d_info.d_devel_taint = domain_info_devel_taint;
2427 mut_d_info.d_allow_easter_eggs = domain_allow_easter_eggs;
2428
2429 if (previous_version == domain_info_version
2432 && previous_patch_level == domain_info_patch_level)
2433 || *self.phase < ServerPhase::DomainInfoReady
2434 {
2435 return Ok(());
2436 }
2437
2438 debug!(domain_previous_version = ?previous_version, domain_target_version = ?domain_info_version);
2439 debug!(domain_previous_patch_level = ?previous_patch_level, domain_target_patch_level = ?domain_info_patch_level);
2440
2441 if previous_version == DOMAIN_LEVEL_0 {
2445 debug!(
2446 "Server was just brought up, skipping migrations as we are already at target level"
2447 );
2448 return Ok(());
2449 }
2450
2451 if previous_version < DOMAIN_MIN_REMIGRATION_LEVEL {
2452 error!("UNABLE TO PROCEED. You are attempting a Skip update which is NOT SUPPORTED. You must upgrade one-version of Kanidm at a time.");
2453 error!("For more see: https://kanidm.github.io/kanidm/stable/support.html#upgrade-policy and https://kanidm.github.io/kanidm/stable/server_updates.html");
2454 error!(domain_previous_version = ?previous_version, domain_target_version = ?domain_info_version);
2455 error!(domain_previous_patch_level = ?previous_patch_level, domain_target_patch_level = ?domain_info_patch_level);
2456 return Err(OperationError::MG0008SkipUpgradeAttempted);
2457 }
2458
2459 if previous_version <= DOMAIN_LEVEL_8 && domain_info_version >= DOMAIN_LEVEL_9 {
2460 self.migrate_domain_8_to_9()?;
2462 }
2463
2464 if previous_patch_level < PATCH_LEVEL_2
2465 && domain_info_patch_level >= PATCH_LEVEL_2
2466 && domain_info_version == DOMAIN_LEVEL_9
2467 {
2468 self.migrate_domain_patch_level_2()?;
2469 }
2470
2471 if previous_version <= DOMAIN_LEVEL_9 && domain_info_version >= DOMAIN_LEVEL_10 {
2472 self.migrate_domain_9_to_10()?;
2474 }
2475
2476 if previous_version <= DOMAIN_LEVEL_10 && domain_info_version >= DOMAIN_LEVEL_11 {
2477 self.migrate_domain_10_to_11()?;
2479 }
2480
2481 debug_assert!(domain_info_version <= DOMAIN_MAX_LEVEL);
2485
2486 Ok(())
2487 }
2488
2489 #[instrument(level = "debug", skip_all)]
2491 pub(crate) fn reload_domain_info(&mut self) -> Result<(), OperationError> {
2492 let domain_entry = self.get_db_domain()?;
2493
2494 let domain_name = domain_entry
2495 .get_ava_single_iname(Attribute::DomainName)
2496 .map(str::to_string)
2497 .ok_or(OperationError::InvalidEntryState)?;
2498
2499 let display_name = domain_entry
2500 .get_ava_single_utf8(Attribute::DomainDisplayName)
2501 .map(str::to_string)
2502 .unwrap_or_else(|| format!("Kanidm {}", domain_name));
2503
2504 let domain_ldap_allow_unix_pw_bind = domain_entry
2505 .get_ava_single_bool(Attribute::LdapAllowUnixPwBind)
2506 .unwrap_or(true);
2507
2508 let domain_image = domain_entry.get_ava_single_image(Attribute::Image);
2509
2510 let domain_uuid = self.be_txn.get_db_d_uuid()?;
2511
2512 let mut_d_info = self.d_info.get_mut();
2513 mut_d_info.d_ldap_allow_unix_pw_bind = domain_ldap_allow_unix_pw_bind;
2514 if mut_d_info.d_uuid != domain_uuid {
2515 admin_warn!(
2516 "Using domain uuid from the database {} - was {} in memory",
2517 domain_name,
2518 mut_d_info.d_name,
2519 );
2520 mut_d_info.d_uuid = domain_uuid;
2521 }
2522 if mut_d_info.d_name != domain_name {
2523 admin_warn!(
2524 "Using domain name from the database {} - was {} in memory",
2525 domain_name,
2526 mut_d_info.d_name,
2527 );
2528 admin_warn!(
2529 "If you think this is an error, see https://kanidm.github.io/kanidm/master/domain_rename.html"
2530 );
2531 mut_d_info.d_name = domain_name;
2532 }
2533 mut_d_info.d_display = display_name;
2534 mut_d_info.d_image = domain_image;
2535 Ok(())
2536 }
2537
2538 pub fn set_domain_display_name(&mut self, new_domain_name: &str) -> Result<(), OperationError> {
2542 let modl = ModifyList::new_purge_and_set(
2543 Attribute::DomainDisplayName,
2544 Value::new_utf8(new_domain_name.to_string()),
2545 );
2546 let udi = PVUUID_DOMAIN_INFO.clone();
2547 let filt = filter_all!(f_eq(Attribute::Uuid, udi));
2548 self.internal_modify(&filt, &modl)
2549 }
2550
2551 pub fn danger_domain_rename(&mut self, new_domain_name: &str) -> Result<(), OperationError> {
2564 let modl =
2565 ModifyList::new_purge_and_set(Attribute::DomainName, Value::new_iname(new_domain_name));
2566 let udi = PVUUID_DOMAIN_INFO.clone();
2567 let filt = filter_all!(f_eq(Attribute::Uuid, udi));
2568 self.internal_modify(&filt, &modl)
2569 }
2570
2571 pub fn reindex(&mut self, immediate: bool) -> Result<(), OperationError> {
2572 self.be_txn.reindex(immediate)
2576 }
2577
2578 fn force_schema_reload(&mut self) {
2579 self.changed_flags.insert(ChangeFlag::SCHEMA);
2580 }
2581
2582 fn force_domain_reload(&mut self) {
2583 self.changed_flags.insert(ChangeFlag::DOMAIN);
2584 }
2585
2586 pub(crate) fn upgrade_reindex(&mut self, v: i64) -> Result<(), OperationError> {
2587 self.be_txn.upgrade_reindex(v)
2588 }
2589
2590 #[inline]
2591 pub(crate) fn get_changed_app(&self) -> bool {
2592 self.changed_flags.contains(ChangeFlag::APPLICATION)
2593 }
2594
2595 #[inline]
2596 pub(crate) fn get_changed_oauth2(&self) -> bool {
2597 self.changed_flags.contains(ChangeFlag::OAUTH2)
2598 }
2599
2600 #[inline]
2601 pub(crate) fn clear_changed_oauth2(&mut self) {
2602 self.changed_flags.remove(ChangeFlag::OAUTH2)
2603 }
2604
2605 pub(crate) fn set_phase_bootstrap(&mut self) {
2608 *self.phase = ServerPhase::Bootstrap;
2609 }
2610
2611 pub(crate) fn set_phase(&mut self, phase: ServerPhase) {
2613 if phase > *self.phase {
2615 *self.phase = phase
2616 }
2617 }
2618
2619 pub(crate) fn get_phase(&mut self) -> ServerPhase {
2620 *self.phase
2621 }
2622
2623 pub(crate) fn reload(&mut self) -> Result<(), OperationError> {
2624 if self.changed_flags.contains(ChangeFlag::DOMAIN) {
2627 self.reload_domain_info_version()?;
2628 }
2629
2630 if self.changed_flags.contains(ChangeFlag::SCHEMA) {
2635 self.reload_schema()?;
2636
2637 if *self.phase >= ServerPhase::Running {
2641 self.reindex(false)?;
2642 self.reload_schema()?;
2643 }
2644 }
2645
2646 if self
2649 .changed_flags
2650 .intersects(ChangeFlag::SCHEMA | ChangeFlag::KEY_MATERIAL)
2651 {
2652 self.reload_key_material()?;
2653 }
2654
2655 if self
2663 .changed_flags
2664 .intersects(ChangeFlag::SCHEMA | ChangeFlag::ACP | ChangeFlag::SYNC_AGREEMENT)
2665 {
2666 self.reload_accesscontrols()?;
2667 } else {
2668 }
2673
2674 if self.changed_flags.contains(ChangeFlag::SYSTEM_CONFIG) {
2675 self.reload_system_config()?;
2676 }
2677
2678 if self.changed_flags.contains(ChangeFlag::DOMAIN) {
2679 self.reload_domain_info()?;
2680 }
2681
2682 self.changed_flags.remove(
2684 ChangeFlag::DOMAIN
2685 | ChangeFlag::SCHEMA
2686 | ChangeFlag::SYSTEM_CONFIG
2687 | ChangeFlag::ACP
2688 | ChangeFlag::SYNC_AGREEMENT
2689 | ChangeFlag::KEY_MATERIAL,
2690 );
2691
2692 Ok(())
2693 }
2694
2695 #[cfg(any(test, debug_assertions))]
2696 #[instrument(level = "debug", skip_all)]
2697 pub fn clear_cache(&mut self) -> Result<(), OperationError> {
2698 self.be_txn.clear_cache()
2699 }
2700
2701 #[instrument(level = "info", name="qswt_commit" skip_all)]
2702 pub fn commit(mut self) -> Result<(), OperationError> {
2703 self.reload()?;
2704
2705 let QueryServerWriteTransaction {
2707 committed,
2708 phase,
2709 d_info,
2710 system_config,
2711 mut be_txn,
2712 schema,
2713 accesscontrols,
2714 cid,
2715 dyngroup_cache,
2716 key_providers,
2717 _db_ticket,
2719 _write_ticket,
2720 curtime: _,
2722 trim_cid: _,
2723 changed_flags,
2724 changed_uuid: _,
2725 resolve_filter_cache: _,
2726 resolve_filter_cache_clear,
2727 mut resolve_filter_cache_write,
2728 } = self;
2729 debug_assert!(!committed);
2730
2731 trace!(
2733 changed = ?changed_flags.iter_names().collect::<Vec<_>>(),
2734 );
2735
2736 be_txn.set_db_ts_max(cid.ts)?;
2739 cid.commit();
2740
2741 if resolve_filter_cache_clear {
2743 resolve_filter_cache_write.clear();
2744 }
2745 resolve_filter_cache_write.commit();
2746
2747 schema
2751 .commit()
2752 .map(|_| d_info.commit())
2753 .map(|_| system_config.commit())
2754 .map(|_| phase.commit())
2755 .map(|_| dyngroup_cache.commit())
2756 .and_then(|_| key_providers.commit())
2757 .and_then(|_| accesscontrols.commit())
2758 .and_then(|_| be_txn.commit())
2759 }
2760
2761 pub(crate) fn get_txn_cid(&self) -> &Cid {
2762 &self.cid
2763 }
2764}
2765
2766#[cfg(test)]
2767mod tests {
2768 use crate::prelude::*;
2769 use kanidm_proto::scim_v1::client::ScimFilter;
2770 use kanidm_proto::scim_v1::server::ScimReference;
2771 use kanidm_proto::scim_v1::JsonValue;
2772 use kanidm_proto::scim_v1::ScimEntryGetQuery;
2773
2774 #[qs_test]
2775 async fn test_name_to_uuid(server: &QueryServer) {
2776 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2777
2778 let t_uuid = Uuid::new_v4();
2779 assert!(server_txn
2780 .internal_create(vec![entry_init!(
2781 (Attribute::Class, EntryClass::Object.to_value()),
2782 (Attribute::Class, EntryClass::Account.to_value()),
2783 (Attribute::Class, EntryClass::Person.to_value()),
2784 (Attribute::Name, Value::new_iname("testperson1")),
2785 (Attribute::Uuid, Value::Uuid(t_uuid)),
2786 (Attribute::Description, Value::new_utf8s("testperson1")),
2787 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2788 ),])
2789 .is_ok());
2790
2791 let r1 = server_txn.name_to_uuid("testpers");
2793 assert!(r1.is_err());
2794 let r2 = server_txn.name_to_uuid("tEsTpErS");
2796 assert!(r2.is_err());
2797 let r3 = server_txn.name_to_uuid("testperson1");
2799 assert_eq!(r3, Ok(t_uuid));
2800 let r4 = server_txn.name_to_uuid("tEsTpErSoN1");
2802 assert_eq!(r4, Ok(t_uuid));
2803 let r5 = server_txn.name_to_uuid("name=testperson1");
2805 assert_eq!(r5, Ok(t_uuid));
2806 let r6 = server_txn.name_to_uuid("name=testperson1,o=example");
2808 assert_eq!(r6, Ok(t_uuid));
2809 }
2810
2811 #[qs_test]
2812 async fn test_external_id_to_uuid(server: &QueryServer) {
2813 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2814
2815 let t_uuid = Uuid::new_v4();
2816 assert!(server_txn
2817 .internal_create(vec![entry_init!(
2818 (Attribute::Class, EntryClass::Object.to_value()),
2819 (Attribute::Class, EntryClass::ExtensibleObject.to_value()),
2820 (Attribute::Uuid, Value::Uuid(t_uuid)),
2821 (
2822 Attribute::SyncExternalId,
2823 Value::new_iutf8("uid=testperson")
2824 )
2825 ),])
2826 .is_ok());
2827
2828 let r1 = server_txn.sync_external_id_to_uuid("tobias");
2830 assert_eq!(r1, Ok(None));
2831 let r2 = server_txn.sync_external_id_to_uuid("tObIAs");
2833 assert_eq!(r2, Ok(None));
2834 let r3 = server_txn.sync_external_id_to_uuid("uid=testperson");
2836 assert_eq!(r3, Ok(Some(t_uuid)));
2837 let r4 = server_txn.sync_external_id_to_uuid("uId=TeStPeRsOn");
2839 assert_eq!(r4, Ok(Some(t_uuid)));
2840 }
2841
2842 #[qs_test]
2843 async fn test_uuid_to_spn(server: &QueryServer) {
2844 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2845
2846 let e1 = entry_init!(
2847 (Attribute::Class, EntryClass::Object.to_value()),
2848 (Attribute::Class, EntryClass::Person.to_value()),
2849 (Attribute::Class, EntryClass::Account.to_value()),
2850 (Attribute::Name, Value::new_iname("testperson1")),
2851 (
2852 Attribute::Uuid,
2853 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2854 ),
2855 (Attribute::Description, Value::new_utf8s("testperson1")),
2856 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2857 );
2858 let ce = CreateEvent::new_internal(vec![e1]);
2859 let cr = server_txn.create(&ce);
2860 assert!(cr.is_ok());
2861
2862 let r1 = server_txn.uuid_to_spn(uuid!("bae3f507-e6c3-44ba-ad01-f8ff1083534a"));
2864 assert_eq!(r1, Ok(None));
2866 let r3 = server_txn.uuid_to_spn(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"));
2868 println!("{r3:?}");
2869 assert_eq!(
2870 r3.unwrap().unwrap(),
2871 Value::new_spn_str("testperson1", "example.com")
2872 );
2873 let r4 = server_txn.uuid_to_spn(uuid!("CC8E95B4-C24F-4D68-BA54-8BED76F63930"));
2875 assert_eq!(
2876 r4.unwrap().unwrap(),
2877 Value::new_spn_str("testperson1", "example.com")
2878 );
2879 }
2880
2881 #[qs_test]
2882 async fn test_uuid_to_rdn(server: &QueryServer) {
2883 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2884
2885 let e1 = entry_init!(
2886 (Attribute::Class, EntryClass::Object.to_value()),
2887 (Attribute::Class, EntryClass::Person.to_value()),
2888 (Attribute::Class, EntryClass::Account.to_value()),
2889 (Attribute::Name, Value::new_iname("testperson1")),
2890 (
2891 Attribute::Uuid,
2892 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2893 ),
2894 (Attribute::Description, Value::new_utf8s("testperson")),
2895 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2896 );
2897 let ce = CreateEvent::new_internal(vec![e1]);
2898 let cr = server_txn.create(&ce);
2899 assert!(cr.is_ok());
2900
2901 let r1 = server_txn.uuid_to_rdn(uuid!("bae3f507-e6c3-44ba-ad01-f8ff1083534a"));
2903 assert_eq!(r1.unwrap(), "uuid=bae3f507-e6c3-44ba-ad01-f8ff1083534a");
2905 let r3 = server_txn.uuid_to_rdn(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"));
2907 println!("{r3:?}");
2908 assert_eq!(r3.unwrap(), "spn=testperson1@example.com");
2909 let r4 = server_txn.uuid_to_rdn(uuid!("CC8E95B4-C24F-4D68-BA54-8BED76F63930"));
2911 assert_eq!(r4.unwrap(), "spn=testperson1@example.com");
2912 }
2913
2914 #[qs_test]
2915 async fn test_clone_value(server: &QueryServer) {
2916 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2917 let e1 = entry_init!(
2918 (Attribute::Class, EntryClass::Object.to_value()),
2919 (Attribute::Class, EntryClass::Account.to_value()),
2920 (Attribute::Class, EntryClass::Person.to_value()),
2921 (Attribute::Name, Value::new_iname("testperson1")),
2922 (
2923 Attribute::Uuid,
2924 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2925 ),
2926 (Attribute::Description, Value::new_utf8s("testperson1")),
2927 (Attribute::DisplayName, Value::new_utf8s("testperson1"))
2928 );
2929 let ce = CreateEvent::new_internal(vec![e1]);
2930 let cr = server_txn.create(&ce);
2931 assert!(cr.is_ok());
2932
2933 let r1 = server_txn.clone_value(&Attribute::from("tausau"), "naoeutnhaou");
2935
2936 assert!(r1.is_err());
2937
2938 let r2 = server_txn.clone_value(&Attribute::Custom("NaMe".into()), "NaMe");
2941
2942 assert!(r2.is_err());
2943
2944 let r3 = server_txn.clone_value(&Attribute::from("member"), "testperson1");
2946
2947 assert_eq!(
2948 r3,
2949 Ok(Value::Refer(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")))
2950 );
2951
2952 let r4 = server_txn.clone_value(
2954 &Attribute::from("member"),
2955 "cc8e95b4-c24f-4d68-ba54-8bed76f63930",
2956 );
2957
2958 debug!("{:?}", r4);
2959 assert_eq!(
2960 r4,
2961 Ok(Value::Refer(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")))
2962 );
2963 }
2964
2965 #[qs_test]
2966 async fn test_dynamic_schema_class(server: &QueryServer) {
2967 let e1 = entry_init!(
2968 (Attribute::Class, EntryClass::Object.to_value()),
2969 (Attribute::Class, EntryClass::TestClass.to_value()),
2970 (Attribute::Name, Value::new_iname("testobj1")),
2971 (
2972 Attribute::Uuid,
2973 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
2974 )
2975 );
2976
2977 let e_cd = entry_init!(
2979 (Attribute::Class, EntryClass::Object.to_value()),
2980 (Attribute::Class, EntryClass::ClassType.to_value()),
2981 (Attribute::ClassName, EntryClass::TestClass.to_value()),
2982 (
2983 Attribute::Uuid,
2984 Value::Uuid(uuid!("cfcae205-31c3-484b-8ced-667d1709c5e3"))
2985 ),
2986 (Attribute::Description, Value::new_utf8s("Test Class")),
2987 (Attribute::May, Value::from(Attribute::Name))
2988 );
2989 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
2990 let ce_class = CreateEvent::new_internal(vec![e_cd.clone()]);
2992 assert!(server_txn.create(&ce_class).is_ok());
2993 let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
2995 assert!(server_txn.create(&ce_fail).is_err());
2996
2997 server_txn.commit().expect("should not fail");
2999
3000 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3002 let ce_work = CreateEvent::new_internal(vec![e1.clone()]);
3005 assert!(server_txn.create(&ce_work).is_ok());
3006
3007 server_txn.commit().expect("should not fail");
3009
3010 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3012 let de_class = DeleteEvent::new_internal_invalid(filter!(f_eq(
3014 Attribute::ClassName,
3015 EntryClass::TestClass.into()
3016 )));
3017 assert!(server_txn.delete(&de_class).is_ok());
3018 server_txn.commit().expect("should not fail");
3020
3021 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3023 let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
3025 assert!(server_txn.create(&ce_fail).is_err());
3026 let testobj1 = server_txn
3028 .internal_search_uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
3029 .expect("failed");
3030 assert!(testobj1.attribute_equality(Attribute::Class, &EntryClass::TestClass.into()));
3031
3032 server_txn.commit().expect("should not fail");
3034 }
3036
3037 #[qs_test]
3038 async fn test_dynamic_schema_attr(server: &QueryServer) {
3039 let e1 = entry_init!(
3040 (Attribute::Class, EntryClass::Object.to_value()),
3041 (Attribute::Class, EntryClass::ExtensibleObject.to_value()),
3042 (Attribute::Name, Value::new_iname("testobj1")),
3043 (
3044 Attribute::Uuid,
3045 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
3046 ),
3047 (Attribute::TestAttr, Value::new_utf8s("test"))
3048 );
3049
3050 let e_ad = entry_init!(
3052 (Attribute::Class, EntryClass::Object.to_value()),
3053 (Attribute::Class, EntryClass::AttributeType.to_value()),
3054 (
3055 Attribute::Uuid,
3056 Value::Uuid(uuid!("cfcae205-31c3-484b-8ced-667d1709c5e3"))
3057 ),
3058 (Attribute::AttributeName, Value::from(Attribute::TestAttr)),
3059 (Attribute::Description, Value::new_utf8s("Test Attribute")),
3060 (Attribute::MultiValue, Value::new_bool(false)),
3061 (Attribute::Unique, Value::new_bool(false)),
3062 (
3063 Attribute::Syntax,
3064 Value::new_syntaxs("UTF8STRING").expect("syntax")
3065 )
3066 );
3067
3068 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3069 let ce_attr = CreateEvent::new_internal(vec![e_ad.clone()]);
3071 assert!(server_txn.create(&ce_attr).is_ok());
3072 let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
3074 assert!(server_txn.create(&ce_fail).is_err());
3075
3076 server_txn.commit().expect("should not fail");
3078
3079 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3081 let ce_work = CreateEvent::new_internal(vec![e1.clone()]);
3084 assert!(server_txn.create(&ce_work).is_ok());
3085
3086 server_txn.commit().expect("should not fail");
3088
3089 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3091 let de_attr = DeleteEvent::new_internal_invalid(filter!(f_eq(
3093 Attribute::AttributeName,
3094 PartialValue::from(Attribute::TestAttr)
3095 )));
3096 assert!(server_txn.delete(&de_attr).is_ok());
3097 server_txn.commit().expect("should not fail");
3099
3100 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3102 let ce_fail = CreateEvent::new_internal(vec![e1.clone()]);
3104 assert!(server_txn.create(&ce_fail).is_err());
3105 let filt = filter!(f_eq(Attribute::TestAttr, PartialValue::new_utf8s("test")));
3107 assert!(server_txn.internal_search(filt).is_err());
3108 let testobj1 = server_txn
3111 .internal_search_uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
3112 .expect("failed");
3113 assert!(testobj1.attribute_equality(Attribute::TestAttr, &PartialValue::new_utf8s("test")));
3114
3115 server_txn.commit().expect("should not fail");
3116 }
3118
3119 #[qs_test]
3120 async fn test_scim_entry_structure(server: &QueryServer) {
3121 let mut read_txn = server.read().await.unwrap();
3122
3123 let entry = read_txn
3125 .internal_search_uuid(UUID_IDM_PEOPLE_SELF_NAME_WRITE)
3126 .unwrap();
3127
3128 let reduced = entry.as_ref().clone().into_reduced();
3130 let scim_entry = reduced.to_scim_kanidm(&mut read_txn).unwrap();
3131
3132 assert_eq!(scim_entry.header.id, UUID_IDM_PEOPLE_SELF_NAME_WRITE);
3134 let name_scim = scim_entry.attrs.get(&Attribute::Name).unwrap();
3135 match name_scim {
3136 ScimValueKanidm::String(name) => {
3137 assert_eq!(name.clone(), "idm_people_self_name_write")
3138 }
3139 _ => {
3140 panic!("expected String, actual {:?}", name_scim);
3141 }
3142 }
3143
3144 let entry_managed_by_scim = scim_entry.attrs.get(&Attribute::EntryManagedBy).unwrap();
3146 match entry_managed_by_scim {
3147 ScimValueKanidm::EntryReferences(managed_by) => {
3148 assert_eq!(
3149 managed_by.first().unwrap().clone(),
3150 ScimReference {
3151 uuid: UUID_IDM_ADMINS,
3152 value: "idm_admins@example.com".to_string()
3153 }
3154 )
3155 }
3156 _ => {
3157 panic!(
3158 "expected EntryReference, actual {:?}",
3159 entry_managed_by_scim
3160 );
3161 }
3162 }
3163
3164 let members_scim = scim_entry.attrs.get(&Attribute::Member).unwrap();
3165 match members_scim {
3166 ScimValueKanidm::EntryReferences(members) => {
3167 assert_eq!(
3168 members.first().unwrap().clone(),
3169 ScimReference {
3170 uuid: UUID_IDM_ALL_PERSONS,
3171 value: "idm_all_persons@example.com".to_string()
3172 }
3173 )
3174 }
3175 _ => {
3176 panic!("expected EntryReferences, actual {:?}", members_scim);
3177 }
3178 }
3179 }
3180
3181 #[qs_test]
3182 async fn test_scim_effective_access_query(server: &QueryServer) {
3183 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3184
3185 let group_uuid = Uuid::new_v4();
3186 let e1 = entry_init!(
3187 (Attribute::Class, EntryClass::Object.to_value()),
3188 (Attribute::Class, EntryClass::Group.to_value()),
3189 (Attribute::Name, Value::new_iname("testgroup")),
3190 (Attribute::Uuid, Value::Uuid(group_uuid))
3191 );
3192
3193 assert!(server_txn.internal_create(vec![e1]).is_ok());
3194 assert!(server_txn.commit().is_ok());
3195
3196 let mut server_txn = server.read().await.unwrap();
3199
3200 let idm_admin_entry = server_txn.internal_search_uuid(UUID_IDM_ADMIN).unwrap();
3201 let idm_admin_ident = Identity::from_impersonate_entry_readwrite(idm_admin_entry);
3202
3203 let query = ScimEntryGetQuery {
3204 ext_access_check: true,
3205 ..Default::default()
3206 };
3207
3208 let scim_entry = server_txn
3209 .scim_entry_id_get_ext(group_uuid, EntryClass::Group, query, idm_admin_ident)
3210 .unwrap();
3211
3212 let ext_access_check = scim_entry.ext_access_check.unwrap();
3213
3214 trace!(?ext_access_check);
3215
3216 assert!(ext_access_check.delete);
3217 assert!(ext_access_check.search.check(&Attribute::DirectMemberOf));
3218 assert!(ext_access_check.search.check(&Attribute::MemberOf));
3219 assert!(ext_access_check.search.check(&Attribute::Name));
3220 assert!(ext_access_check.modify_present.check(&Attribute::Name));
3221 assert!(ext_access_check.modify_remove.check(&Attribute::Name));
3222 }
3223
3224 #[qs_test]
3225 async fn test_scim_basic_search_ext_query(server: &QueryServer) {
3226 let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
3227
3228 let group_uuid = Uuid::new_v4();
3229 let e1 = entry_init!(
3230 (Attribute::Class, EntryClass::Object.to_value()),
3231 (Attribute::Class, EntryClass::Group.to_value()),
3232 (Attribute::Name, Value::new_iname("testgroup")),
3233 (Attribute::Uuid, Value::Uuid(group_uuid))
3234 );
3235
3236 assert!(server_txn.internal_create(vec![e1]).is_ok());
3237 assert!(server_txn.commit().is_ok());
3238
3239 let mut server_txn = server.read().await.unwrap();
3241
3242 let idm_admin_entry = server_txn.internal_search_uuid(UUID_IDM_ADMIN).unwrap();
3243 let idm_admin_ident = Identity::from_impersonate_entry_readwrite(idm_admin_entry);
3244
3245 let filter = ScimFilter::And(
3246 Box::new(ScimFilter::Equal(
3247 Attribute::Class.into(),
3248 EntryClass::Group.into(),
3249 )),
3250 Box::new(ScimFilter::Equal(
3251 Attribute::Uuid.into(),
3252 JsonValue::String(group_uuid.to_string()),
3253 )),
3254 );
3255
3256 let base: Vec<ScimEntryKanidm> = server_txn
3257 .scim_search_ext(idm_admin_ident, filter, ScimEntryGetQuery::default())
3258 .unwrap();
3259
3260 assert_eq!(base.len(), 1);
3261 assert_eq!(base[0].header.id, group_uuid);
3262 }
3263}