1use crate::be::dbentry::{DbEntry, DbEntryVers};
28use crate::be::dbvalue::DbValueSetV2;
29use crate::be::{IdxKey, IdxSlope};
30use crate::credential::apppwd::ApplicationPassword;
31use crate::credential::Credential;
32use crate::filter::{Filter, FilterInvalid, FilterResolved, FilterValidResolved};
33use crate::idm::ldap::ldap_vattr_map;
34use crate::modify::{Modify, ModifyInvalid, ModifyList, ModifyValid};
35use crate::prelude::*;
36use crate::repl::cid::Cid;
37use crate::repl::entry::EntryChangeState;
38use crate::repl::proto::{ReplEntryV1, ReplIncrementalEntryV1};
39use crate::schema::{SchemaAttribute, SchemaClass, SchemaTransaction};
40use crate::server::access::AccessEffectivePermission;
41use crate::value::{
42 ApiToken, CredentialType, IndexType, IntentTokenState, Oauth2Session, PartialValue, Session,
43 SyntaxType, Value,
44};
45use crate::valueset::{self, ScimResolveStatus, ValueSet};
46use compact_jwt::JwsEs256Signer;
47use hashbrown::{HashMap, HashSet};
48use kanidm_proto::internal::ImageValue;
49use kanidm_proto::internal::{
50 ConsistencyError, Filter as ProtoFilter, OperationError, SchemaError, UiHint,
51};
52use kanidm_proto::scim_v1::server::ScimEffectiveAccess;
53use kanidm_proto::v1::Entry as ProtoEntry;
54use ldap3_proto::simple::{LdapPartialAttribute, LdapSearchResultEntry};
55use openssl::ec::EcKey;
56use openssl::pkey::{Private, Public};
57use std::cmp::Ordering;
58pub use std::collections::BTreeSet as Set;
59use std::collections::{BTreeMap as Map, BTreeMap, BTreeSet};
60use std::sync::Arc;
61use time::OffsetDateTime;
62use tracing::trace;
63use uuid::Uuid;
64use webauthn_rs::prelude::{
65 AttestationCaList, AttestedPasskey as AttestedPasskeyV4, Passkey as PasskeyV4,
66};
67
68pub type EntryInitNew = Entry<EntryInit, EntryNew>;
69pub type EntryInvalidNew = Entry<EntryInvalid, EntryNew>;
70pub type EntryRefreshNew = Entry<EntryRefresh, EntryNew>;
71pub type EntrySealedNew = Entry<EntrySealed, EntryNew>;
72pub type EntryValidCommitted = Entry<EntryValid, EntryCommitted>;
73pub type EntrySealedCommitted = Entry<EntrySealed, EntryCommitted>;
74pub type EntryInvalidCommitted = Entry<EntryInvalid, EntryCommitted>;
75pub type EntryReducedCommitted = Entry<EntryReduced, EntryCommitted>;
76pub type EntryTuple = (Arc<EntrySealedCommitted>, EntryInvalidCommitted);
77
78pub type EntryIncrementalNew = Entry<EntryIncremental, EntryNew>;
79pub type EntryIncrementalCommitted = Entry<EntryIncremental, EntryCommitted>;
80
81#[derive(Clone, Debug)]
94pub struct EntryNew; #[derive(Clone, Debug)]
98pub struct EntryCommitted {
99 id: u64,
100}
101
102#[derive(Clone, Debug)]
103pub struct EntryInit;
104
105#[derive(Clone, Debug)]
112pub struct EntryInvalid {
113 cid: Cid,
114 ecstate: EntryChangeState,
115}
116
117#[derive(Clone, Debug)]
119pub struct EntryRefresh {
120 ecstate: EntryChangeState,
121}
122
123#[derive(Clone, Debug)]
125pub struct EntryIncremental {
126 uuid: Uuid,
128 ecstate: EntryChangeState,
129}
130
131#[derive(Clone, Debug)]
137pub struct EntryValid {
138 uuid: Uuid,
140 ecstate: EntryChangeState,
141}
142
143#[derive(Clone, Debug)]
150pub struct EntrySealed {
151 uuid: Uuid,
152 ecstate: EntryChangeState,
153}
154
155#[derive(Clone, Debug)]
161pub struct EntryReduced {
162 uuid: Uuid,
163 effective_access: Option<Box<AccessEffectivePermission>>,
164}
165
166pub type Eattrs = Map<Attribute, ValueSet>;
169
170pub trait GetUuid {
171 fn get_uuid(&self) -> Uuid;
172}
173
174pub trait Committed {}
175
176impl Committed for EntrySealed {}
177impl Committed for EntryReduced {}
178
179pub(crate) fn compare_attrs(left: &Eattrs, right: &Eattrs) -> bool {
180 let allkeys: Set<&Attribute> = left
183 .keys()
184 .chain(right.keys())
185 .filter(|k| *k != &Attribute::LastModifiedCid && *k != &Attribute::CreatedAtCid)
186 .collect();
187
188 allkeys.into_iter().all(|k| {
189 let left_vs = left.get(k);
191 let right_vs = right.get(k);
192 let r = match (left_vs, right_vs) {
193 (Some(l), Some(r)) => l.eq(r),
194 _ => false,
195 };
196 if !r {
197 trace!(?k, ?left_vs, ?right_vs, "compare_attrs_allkeys");
198 }
199 r
200 })
201}
202
203pub struct Entry<VALID, STATE> {
230 valid: VALID,
231 state: STATE,
232 attrs: Eattrs,
234}
235
236impl<VALID, STATE> std::fmt::Debug for Entry<VALID, STATE>
237where
238 STATE: std::fmt::Debug,
239 VALID: std::fmt::Debug,
240{
241 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
242 f.debug_struct("Entry<EntrySealed, _>")
243 .field("state", &self.state)
244 .field("valid", &self.valid)
245 .field("attrs", &self.attrs)
246 .finish()
247 }
248}
249
250impl<STATE> std::fmt::Display for Entry<EntrySealed, STATE>
251where
252 STATE: Clone,
253{
254 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
255 write!(f, "{}", self.get_uuid())
256 }
257}
258
259impl<STATE> std::fmt::Display for Entry<EntryInit, STATE>
260where
261 STATE: Clone,
262{
263 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
264 write!(f, "Entry in initial state")
265 }
266}
267
268impl<STATE> Entry<EntryInit, STATE>
269where
270 STATE: Clone,
271{
272 pub fn get_uuid(&self) -> Option<Uuid> {
274 self.attrs
275 .get(&Attribute::Uuid)
276 .and_then(|vs| vs.to_uuid_single())
277 }
278}
279
280impl Default for Entry<EntryInit, EntryNew> {
281 fn default() -> Self {
282 Self::new()
283 }
284}
285
286impl FromIterator<(Attribute, ValueSet)> for EntryInitNew {
287 fn from_iter<I: IntoIterator<Item = (Attribute, ValueSet)>>(iter: I) -> Self {
288 let attrs = Eattrs::from_iter(iter);
289
290 Entry {
291 valid: EntryInit,
292 state: EntryNew,
293 attrs,
294 }
295 }
296}
297
298impl Entry<EntryInit, EntryNew> {
299 pub fn new() -> Self {
300 Entry {
301 valid: EntryInit,
303 state: EntryNew,
304 attrs: Map::new(),
305 }
306 }
307
308 pub fn from_proto_entry(
311 e: &ProtoEntry,
312 qs: &mut QueryServerWriteTransaction,
313 ) -> Result<Self, OperationError> {
314 trace!("from_proto_entry");
315 let map2: Result<Eattrs, OperationError> = e
322 .attrs
323 .iter()
324 .filter(|(_, v)| !v.is_empty())
325 .map(|(k, v)| {
326 trace!(?k, ?v, "attribute");
327 let attr_nk = Attribute::from(k.as_str());
328 let nv = valueset::from_result_value_iter(
329 v.iter().map(|vr| qs.clone_value(&attr_nk, vr)),
330 );
331 trace!(?nv, "new valueset transform");
332 match nv {
333 Ok(nvi) => Ok((attr_nk, nvi)),
334 Err(e) => Err(e),
335 }
336 })
337 .collect();
338
339 let x = map2?;
340
341 Ok(Entry {
342 state: EntryNew,
343 valid: EntryInit,
344 attrs: x,
345 })
346 }
347
348 #[instrument(level = "debug", skip_all)]
351 pub fn from_proto_entry_str(
352 es: &str,
353 qs: &mut QueryServerWriteTransaction,
354 ) -> Result<Self, OperationError> {
355 if cfg!(test) {
356 if es.len() > 256 {
357 let (dsp_es, _) = es.split_at(255);
358 trace!("Parsing -> {}...", dsp_es);
359 } else {
360 trace!("Parsing -> {}", es);
361 }
362 }
363 let pe: ProtoEntry = serde_json::from_str(es).map_err(|e| {
365 admin_error!(?e, "SerdeJson Failure");
368 OperationError::SerdeJsonError
369 })?;
370 Self::from_proto_entry(&pe, qs)
372 }
373
374 pub fn assign_cid(
377 mut self,
378 cid: Cid,
379 schema: &dyn SchemaTransaction,
380 ) -> Entry<EntryInvalid, EntryNew> {
381 let ecstate = EntryChangeState::new(&cid, &self.attrs, schema);
387
388 let cv = vs_cid![cid.clone()];
391 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
392 let cv = vs_cid![cid.clone()];
393 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
394
395 Entry {
396 valid: EntryInvalid { cid, ecstate },
397 state: EntryNew,
398 attrs: self.attrs,
399 }
400 }
401
402 pub fn compare(&self, rhs: &Entry<EntrySealed, EntryCommitted>) -> bool {
404 compare_attrs(&self.attrs, &rhs.attrs)
405 }
406
407 #[cfg(test)]
411 pub fn into_invalid_new(mut self) -> Entry<EntryInvalid, EntryNew> {
412 let cid = Cid::new_zero();
413 self.set_last_changed(cid.clone());
414
415 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
416
417 Entry {
418 valid: EntryInvalid { cid, ecstate },
419 state: EntryNew,
420 attrs: self.attrs,
421 }
422 }
423
424 #[cfg(test)]
428 pub fn into_valid_new(mut self) -> Entry<EntryValid, EntryNew> {
429 let cid = Cid::new_zero();
430 self.set_last_changed(cid.clone());
431 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
432
433 Entry {
434 valid: EntryValid {
435 ecstate,
436 uuid: self.get_uuid().expect("Invalid uuid"),
437 },
438 state: EntryNew,
439 attrs: self.attrs,
440 }
441 }
442
443 #[cfg(test)]
447 pub fn into_sealed_committed(mut self) -> Entry<EntrySealed, EntryCommitted> {
448 let cid = Cid::new_zero();
449 self.set_last_changed(cid.clone());
450 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
451 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
452 Entry {
453 valid: EntrySealed { uuid, ecstate },
454 state: EntryCommitted { id: 0 },
455 attrs: self.attrs,
456 }
457 }
458
459 #[cfg(test)]
463 pub fn into_sealed_new(mut self) -> Entry<EntrySealed, EntryNew> {
464 let cid = Cid::new_zero();
465 self.set_last_changed(cid.clone());
466 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
467
468 Entry {
469 valid: EntrySealed {
470 uuid: self.get_uuid().expect("Invalid uuid"),
471 ecstate,
472 },
473 state: EntryNew,
474 attrs: self.attrs,
475 }
476 }
477
478 pub fn add_ava(&mut self, attr: Attribute, value: Value) {
484 self.add_ava_int(attr, value);
485 }
486
487 pub fn remove_ava(&mut self, attr: &Attribute) {
488 self.attrs.remove(attr);
489 }
490
491 pub fn set_ava_set(&mut self, attr: &Attribute, vs: ValueSet) {
493 self.attrs.insert(attr.clone(), vs);
494 }
495
496 pub fn set_ava<T>(&mut self, attr: Attribute, iter: T)
498 where
499 T: IntoIterator<Item = Value>,
500 {
501 self.set_ava_iter_int(attr, iter);
502 }
503
504 pub fn get_ava_mut<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<&mut ValueSet> {
505 self.attrs.get_mut(attr.as_ref())
506 }
507}
508
509impl From<SchemaAttribute> for EntryInitNew {
510 fn from(value: SchemaAttribute) -> Self {
511 EntryInitNew::from(&value)
512 }
513}
514
515impl From<&SchemaAttribute> for EntryInitNew {
516 fn from(s: &SchemaAttribute) -> Self {
517 let mut attrs = Eattrs::new();
519 attrs.insert(Attribute::AttributeName, vs_iutf8![s.name.as_str()]);
520 attrs.insert(Attribute::Description, vs_utf8![s.description.to_owned()]);
521 attrs.insert(Attribute::Uuid, vs_uuid![s.uuid]);
522 attrs.insert(Attribute::MultiValue, vs_bool![s.multivalue]);
523 attrs.insert(Attribute::Phantom, vs_bool![s.phantom]);
524 attrs.insert(Attribute::SyncAllowed, vs_bool![s.sync_allowed]);
525 attrs.insert(Attribute::Replicated, vs_bool![s.replicated.into()]);
526 attrs.insert(Attribute::Unique, vs_bool![s.unique]);
527 attrs.insert(Attribute::Indexed, vs_bool![s.indexed]);
528 attrs.insert(Attribute::Syntax, vs_syntax![s.syntax]);
529 attrs.insert(
530 Attribute::Class,
531 vs_iutf8![
532 EntryClass::Object.into(),
533 EntryClass::System.into(),
534 EntryClass::AttributeType.into()
535 ],
536 );
537
538 Entry {
541 valid: EntryInit,
542 state: EntryNew,
543 attrs,
544 }
545 }
546}
547
548impl From<SchemaClass> for EntryInitNew {
549 fn from(value: SchemaClass) -> Self {
550 EntryInitNew::from(&value)
551 }
552}
553
554impl From<&SchemaClass> for EntryInitNew {
555 fn from(s: &SchemaClass) -> Self {
556 let mut attrs = Eattrs::new();
557 attrs.insert(Attribute::ClassName, vs_iutf8![s.name.as_str()]);
558 attrs.insert(Attribute::Description, vs_utf8![s.description.to_owned()]);
559 attrs.insert(Attribute::SyncAllowed, vs_bool![s.sync_allowed]);
560 attrs.insert(Attribute::Uuid, vs_uuid![s.uuid]);
561 attrs.insert(
562 Attribute::Class,
563 vs_iutf8![
564 EntryClass::Object.into(),
565 EntryClass::System.into(),
566 EntryClass::ClassType.into()
567 ],
568 );
569
570 let vs_systemmay = ValueSetIutf8::from_iter(s.systemmay.iter().map(|sm| sm.as_str()));
571 if let Some(vs) = vs_systemmay {
572 attrs.insert(Attribute::SystemMay, vs);
573 }
574
575 let vs_systemmust = ValueSetIutf8::from_iter(s.systemmust.iter().map(|sm| sm.as_str()));
576 if let Some(vs) = vs_systemmust {
577 attrs.insert(Attribute::SystemMust, vs);
578 }
579
580 let vs_systemexcludes =
581 ValueSetIutf8::from_iter(s.systemexcludes.iter().map(|sm| sm.as_str()));
582 if let Some(vs) = vs_systemexcludes {
583 attrs.insert(Attribute::SystemExcludes, vs);
584 }
585
586 let vs_systemsupplements =
587 ValueSetIutf8::from_iter(s.systemsupplements.iter().map(|sm| sm.as_str()));
588 if let Some(vs) = vs_systemsupplements {
589 attrs.insert(Attribute::SystemSupplements, vs);
590 }
591
592 Entry {
593 valid: EntryInit,
594 state: EntryNew,
595 attrs,
596 }
597 }
598}
599
600impl Entry<EntryRefresh, EntryNew> {
601 pub fn from_repl_entry_v1(repl_entry: ReplEntryV1) -> Result<Self, OperationError> {
602 let (ecstate, mut attrs) = repl_entry.rehydrate()?;
604
605 let last_mod_cid = ecstate.get_max_cid();
608 let cv = vs_cid![last_mod_cid.clone()];
609 let _ = attrs.insert(Attribute::LastModifiedCid, cv);
610
611 let create_at_cid = ecstate.at();
612 let cv = vs_cid![create_at_cid.clone()];
613 let _ = attrs.insert(Attribute::CreatedAtCid, cv);
614
615 Ok(Entry {
616 valid: EntryRefresh { ecstate },
617 state: EntryNew,
618 attrs,
619 })
620 }
621}
622
623impl<STATE> Entry<EntryRefresh, STATE> {
624 pub fn validate(
625 self,
626 schema: &dyn SchemaTransaction,
627 ) -> Result<Entry<EntryValid, STATE>, SchemaError> {
628 let uuid: Uuid = self
629 .attrs
630 .get(&Attribute::Uuid)
631 .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
632 .and_then(|vs| {
633 vs.to_uuid_single()
634 .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
635 })?;
636
637 let ne = Entry {
639 valid: EntryValid {
640 uuid,
641 ecstate: self.valid.ecstate,
642 },
643 state: self.state,
644 attrs: self.attrs,
645 };
646
647 ne.validate(schema).map(|()| ne)
648 }
649}
650
651impl<STATE> Entry<EntryIncremental, STATE> {
652 pub fn get_uuid(&self) -> Uuid {
653 self.valid.uuid
654 }
655}
656
657impl Entry<EntryIncremental, EntryNew> {
658 fn stub_ecstate(&self) -> EntryChangeState {
659 self.valid.ecstate.stub()
660 }
661
662 pub fn rehydrate(repl_inc_entry: ReplIncrementalEntryV1) -> Result<Self, OperationError> {
663 let (uuid, ecstate, attrs) = repl_inc_entry.rehydrate()?;
664
665 Ok(Entry {
666 valid: EntryIncremental { uuid, ecstate },
667 state: EntryNew,
668 attrs,
669 })
670 }
671
672 pub(crate) fn is_add_conflict(&self, db_entry: &EntrySealedCommitted) -> bool {
673 use crate::repl::entry::State;
674 debug_assert_eq!(self.valid.uuid, db_entry.valid.uuid);
675 let self_cs = &self.valid.ecstate;
677 let db_cs = db_entry.get_changestate();
678
679 match (self_cs.current(), db_cs.current()) {
681 (State::Live { at: at_left, .. }, State::Live { at: at_right, .. }) => {
682 at_left != at_right
683 }
684 _ => false,
686 }
687 }
688
689 pub(crate) fn resolve_add_conflict(
690 &self,
691 cid: &Cid,
692 db_ent: &EntrySealedCommitted,
693 ) -> (Option<EntrySealedNew>, EntryIncrementalCommitted) {
694 use crate::repl::entry::State;
695 debug_assert_eq!(self.valid.uuid, db_ent.valid.uuid);
696 let self_cs = &self.valid.ecstate;
697 let db_cs = db_ent.get_changestate();
698
699 match (self_cs.current(), db_cs.current()) {
700 (
701 State::Live {
702 at: at_left,
703 changes: _changes_left,
704 },
705 State::Live {
706 at: at_right,
707 changes: _changes_right,
708 },
709 ) => {
710 debug_assert!(at_left != at_right);
711 if at_left > at_right {
722 trace!("RI > DE, return DE");
723 (
724 None,
725 Entry {
726 valid: EntryIncremental {
727 uuid: db_ent.valid.uuid,
728 ecstate: db_cs.clone(),
729 },
730 state: EntryCommitted {
731 id: db_ent.state.id,
732 },
733 attrs: db_ent.attrs.clone(),
734 },
735 )
736 }
737 else {
747 trace!("RI < DE, return RI");
748 let conflict = if at_right.s_uuid == cid.s_uuid {
750 trace!("Origin process conflict entry");
751 let mut cnf_ent = Entry {
754 valid: EntryInvalid {
755 cid: cid.clone(),
756 ecstate: db_cs.clone(),
757 },
758 state: EntryNew,
759 attrs: db_ent.attrs.clone(),
760 };
761
762 cnf_ent.add_ava(Attribute::SourceUuid, Value::Uuid(db_ent.valid.uuid));
764
765 let new_uuid = Uuid::new_v4();
767 cnf_ent.purge_ava(Attribute::Uuid);
768 cnf_ent.add_ava(Attribute::Uuid, Value::Uuid(new_uuid));
769 cnf_ent.add_ava(Attribute::Class, EntryClass::Recycled.into());
770 cnf_ent.add_ava(Attribute::Class, EntryClass::Conflict.into());
771
772 let cv = vs_cid![cid.clone()];
776 let _ = cnf_ent.attrs.insert(Attribute::LastModifiedCid, cv);
777 let cv = vs_cid![cid.clone()];
779 let _ = cnf_ent.attrs.insert(Attribute::CreatedAtCid, cv);
780
781 let Entry {
785 valid: EntryInvalid { cid: _, ecstate },
786 state,
787 attrs,
788 } = cnf_ent;
789
790 let cnf_ent = Entry {
791 valid: EntrySealed {
792 uuid: new_uuid,
793 ecstate,
794 },
795 state,
796 attrs,
797 };
798
799 Some(cnf_ent)
800 } else {
801 None
802 };
803
804 let mut attrs = self.attrs.clone();
808 let ecstate = self_cs.clone();
809
810 let last_mod_cid = ecstate.get_max_cid();
811 let cv = vs_cid![last_mod_cid.clone()];
812 let _ = attrs.insert(Attribute::LastModifiedCid, cv);
813
814 let create_at_cid = ecstate.at();
815 let cv = vs_cid![create_at_cid.clone()];
816 let _ = attrs.insert(Attribute::CreatedAtCid, cv);
817
818 (
819 conflict,
820 Entry {
821 valid: EntryIncremental {
822 uuid: self.valid.uuid,
823 ecstate,
824 },
825 state: EntryCommitted {
826 id: db_ent.state.id,
827 },
828 attrs,
829 },
830 )
831 }
832 }
833 _ => unreachable!(),
835 }
836 }
837
838 pub(crate) fn merge_state(
839 &self,
840 db_ent: &EntrySealedCommitted,
841 schema: &dyn SchemaTransaction,
842 trim_cid: &Cid,
843 ) -> EntryIncrementalCommitted {
844 use crate::repl::entry::State;
845
846 debug_assert_eq!(self.valid.uuid, db_ent.valid.uuid);
848
849 let self_cs = &self.valid.ecstate;
852 let db_cs = db_ent.get_changestate();
853
854 match (self_cs.current(), db_cs.current()) {
855 (
856 State::Live {
857 at: at_left,
858 changes: changes_left,
859 },
860 State::Live {
861 at: at_right,
862 changes: changes_right,
863 },
864 ) => {
865 debug_assert_eq!(at_left, at_right);
866 let mut attr_set: Vec<_> =
871 changes_left.keys().chain(changes_right.keys()).collect();
872 attr_set.shrink_to_fit();
873 attr_set.sort_unstable();
874 attr_set.dedup();
875
876 let mut changes = BTreeMap::default();
878 let mut eattrs = Eattrs::default();
879
880 for attr_name in attr_set.into_iter() {
882 match (changes_left.get(attr_name), changes_right.get(attr_name)) {
883 (Some(cid_left), Some(cid_right)) => {
884 let take_left = cid_left > cid_right;
891
892 match (self.attrs.get(attr_name), db_ent.attrs.get(attr_name)) {
893 (Some(vs_left), Some(vs_right)) if take_left => {
894 changes.insert(attr_name.clone(), cid_left.clone());
895 #[allow(clippy::todo)]
896 if let Some(merged_attr_state) =
897 vs_left.repl_merge_valueset(vs_right, trim_cid)
898 {
899 eattrs.insert(attr_name.clone(), merged_attr_state);
902 } else {
903 eattrs.insert(attr_name.clone(), vs_left.clone());
904 }
905 }
906 (Some(vs_left), Some(vs_right)) => {
907 changes.insert(attr_name.clone(), cid_right.clone());
908 #[allow(clippy::todo)]
909 if let Some(merged_attr_state) =
910 vs_right.repl_merge_valueset(vs_left, trim_cid)
911 {
912 eattrs.insert(attr_name.clone(), merged_attr_state);
915 } else {
916 eattrs.insert(attr_name.clone(), vs_right.clone());
917 }
918 }
919 (Some(vs_left), None) if take_left => {
920 changes.insert(attr_name.clone(), cid_left.clone());
921 eattrs.insert(attr_name.clone(), vs_left.clone());
922 }
923 (Some(_vs_left), None) => {
924 changes.insert(attr_name.clone(), cid_right.clone());
925 }
927 (None, Some(_vs_right)) if take_left => {
928 changes.insert(attr_name.clone(), cid_left.clone());
929 }
931 (None, Some(vs_right)) => {
932 changes.insert(attr_name.clone(), cid_right.clone());
933 eattrs.insert(attr_name.clone(), vs_right.clone());
934 }
935 (None, None) if take_left => {
936 changes.insert(attr_name.clone(), cid_left.clone());
937 }
939 (None, None) => {
940 changes.insert(attr_name.clone(), cid_right.clone());
941 }
943 }
944 }
946 (Some(cid_left), None) => {
947 changes.insert(attr_name.clone(), cid_left.clone());
949 if let Some(valueset) = self.attrs.get(attr_name) {
950 eattrs.insert(attr_name.clone(), valueset.clone());
951 }
952 }
953 (None, Some(cid_right)) => {
954 changes.insert(attr_name.clone(), cid_right.clone());
956 if let Some(valueset) = db_ent.attrs.get(attr_name) {
957 eattrs.insert(attr_name.clone(), valueset.clone());
958 }
959 }
960 (None, None) => {
961 debug_assert!(false);
963 }
964 }
965 }
966
967 let mut ecstate = EntryChangeState::build(State::Live {
968 at: at_left.clone(),
969 changes,
970 });
971
972 ecstate.retain(|k, _| schema.is_replicated(k));
976
977 let cv = vs_cid![ecstate.get_max_cid().clone()];
978 let _ = eattrs.insert(Attribute::LastModifiedCid, cv);
979
980 let cv = vs_cid![ecstate.at().clone()];
981 let _ = eattrs.insert(Attribute::CreatedAtCid, cv);
982
983 Entry {
984 valid: EntryIncremental {
985 uuid: self.valid.uuid,
986 ecstate,
987 },
988 state: EntryCommitted {
989 id: db_ent.state.id,
990 },
991 attrs: eattrs,
992 }
993 }
994 (State::Tombstone { at: left_at }, State::Live { .. }) => {
995 let mut attrs_new: Eattrs = Map::new();
999 let class_ava = vs_iutf8![EntryClass::Object.into(), EntryClass::Tombstone.into()];
1000 let last_mod_ava = vs_cid![left_at.clone()];
1001 let created_ava = vs_cid![left_at.clone()];
1002
1003 attrs_new.insert(Attribute::Uuid, vs_uuid![self.valid.uuid]);
1004 attrs_new.insert(Attribute::Class, class_ava);
1005 attrs_new.insert(Attribute::LastModifiedCid, last_mod_ava);
1006 attrs_new.insert(Attribute::CreatedAtCid, created_ava);
1007
1008 Entry {
1009 valid: EntryIncremental {
1010 uuid: self.valid.uuid,
1011 ecstate: self.valid.ecstate.clone(),
1012 },
1013 state: EntryCommitted {
1014 id: db_ent.state.id,
1015 },
1016 attrs: attrs_new,
1017 }
1018 }
1019 (State::Live { .. }, State::Tombstone { .. }) => {
1020 Entry {
1028 valid: EntryIncremental {
1029 uuid: db_ent.valid.uuid,
1030 ecstate: db_ent.valid.ecstate.clone(),
1031 },
1032 state: EntryCommitted {
1033 id: db_ent.state.id,
1034 },
1035 attrs: db_ent.attrs.clone(),
1036 }
1037 }
1038 (State::Tombstone { at: left_at }, State::Tombstone { at: right_at }) => {
1039 let (at, ecstate) = if left_at < right_at {
1044 (left_at, self.valid.ecstate.clone())
1045 } else {
1046 (right_at, db_ent.valid.ecstate.clone())
1047 };
1048
1049 let mut attrs_new: Eattrs = Map::new();
1050 let class_ava = vs_iutf8![EntryClass::Object.into(), EntryClass::Tombstone.into()];
1051 let last_mod_ava = vs_cid![at.clone()];
1052 let created_ava = vs_cid![at.clone()];
1053
1054 attrs_new.insert(Attribute::Uuid, vs_uuid![db_ent.valid.uuid]);
1055 attrs_new.insert(Attribute::Class, class_ava);
1056 attrs_new.insert(Attribute::LastModifiedCid, last_mod_ava);
1057 attrs_new.insert(Attribute::CreatedAtCid, created_ava);
1058
1059 Entry {
1060 valid: EntryIncremental {
1061 uuid: db_ent.valid.uuid,
1062 ecstate,
1063 },
1064 state: EntryCommitted {
1065 id: db_ent.state.id,
1066 },
1067 attrs: attrs_new,
1068 }
1069 }
1070 }
1071 }
1072}
1073
1074impl Entry<EntryIncremental, EntryCommitted> {
1075 pub(crate) fn validate_repl(self, schema: &dyn SchemaTransaction) -> EntryValidCommitted {
1076 let mut ne = Entry {
1081 valid: EntryValid {
1082 uuid: self.valid.uuid,
1083 ecstate: self.valid.ecstate,
1084 },
1085 state: self.state,
1086 attrs: self.attrs,
1087 };
1088
1089 if let Err(e) = ne.validate(schema) {
1090 warn!(uuid = ?self.valid.uuid, err = ?e, "Entry failed schema check, moving to a conflict state");
1091 ne.add_ava_int(Attribute::Class, EntryClass::Recycled.into());
1092 ne.add_ava_int(Attribute::Class, EntryClass::Conflict.into());
1093 ne.add_ava_int(Attribute::SourceUuid, Value::Uuid(self.valid.uuid));
1094 }
1095 ne
1096 }
1097}
1098
1099impl<STATE> Entry<EntryInvalid, STATE> {
1100 pub(crate) fn get_uuid(&self) -> Option<Uuid> {
1101 self.attrs
1102 .get(&Attribute::Uuid)
1103 .and_then(|vs| vs.to_uuid_single())
1104 }
1105
1106 pub fn validate(
1109 self,
1110 schema: &dyn SchemaTransaction,
1111 ) -> Result<Entry<EntryValid, STATE>, SchemaError> {
1112 let uuid: Uuid = self
1113 .attrs
1114 .get(&Attribute::Uuid)
1115 .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
1116 .and_then(|vs| {
1117 vs.to_uuid_single()
1118 .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
1119 })?;
1120
1121 let ne = Entry {
1123 valid: EntryValid {
1124 uuid,
1125 ecstate: self.valid.ecstate,
1126 },
1127 state: self.state,
1128 attrs: self.attrs,
1129 };
1130
1131 ne.validate(schema).map(|()| ne)
1132 }
1133
1134 pub(crate) fn get_ava_refer_mut<A: AsRef<Attribute>>(
1138 &mut self,
1139 attr: A,
1140 ) -> Option<&mut BTreeSet<Uuid>> {
1141 self.attrs
1142 .get_mut(attr.as_ref())
1143 .and_then(|vs| vs.as_refer_set_mut())
1144 }
1145}
1146
1147impl<VALID, STATE> Clone for Entry<VALID, STATE>
1148where
1149 VALID: Clone,
1150 STATE: Clone,
1151{
1152 fn clone(&self) -> Entry<VALID, STATE> {
1154 Entry {
1155 valid: self.valid.clone(),
1156 state: self.state.clone(),
1157 attrs: self.attrs.clone(),
1158 }
1159 }
1160}
1161
1162impl Entry<EntryInvalid, EntryCommitted> {
1163 #[cfg(test)]
1167 pub fn into_valid_new(self) -> Entry<EntryValid, EntryNew> {
1168 let uuid = self.get_uuid().expect("Invalid uuid");
1169 Entry {
1170 valid: EntryValid {
1171 uuid,
1172 ecstate: self.valid.ecstate,
1173 },
1174 state: EntryNew,
1175 attrs: self.attrs,
1176 }
1177 }
1178
1179 pub fn to_recycled(mut self) -> Self {
1181 self.add_ava(Attribute::Class, EntryClass::Recycled.into());
1183
1184 Entry {
1188 valid: self.valid,
1189 state: self.state,
1190 attrs: self.attrs,
1191 }
1192 }
1193
1194 pub fn to_conflict<T>(&mut self, iter: T)
1196 where
1197 T: IntoIterator<Item = Uuid>,
1198 {
1199 self.add_ava(Attribute::Class, EntryClass::Recycled.into());
1200 self.add_ava(Attribute::Class, EntryClass::Conflict.into());
1201 for source_uuid in iter {
1203 self.add_ava(Attribute::SourceUuid, Value::Uuid(source_uuid));
1204 }
1205 }
1206
1207 pub fn to_revived(mut self) -> Self {
1209 self.remove_ava(Attribute::Class, &EntryClass::Recycled.into());
1211 self.remove_ava(Attribute::Class, &EntryClass::Conflict.into());
1212 self.purge_ava(Attribute::SourceUuid);
1213 self.purge_ava(Attribute::RecycledDirectMemberOf);
1214
1215 Entry {
1219 valid: self.valid,
1220 state: self.state,
1221 attrs: self.attrs,
1222 }
1223 }
1224}
1225impl Entry<EntryInvalid, EntryNew> {
1228 #[cfg(test)]
1231 pub fn into_init_new(self) -> Entry<EntryInit, EntryNew> {
1232 Entry {
1233 valid: EntryInit,
1234 state: EntryNew,
1235 attrs: self.attrs,
1236 }
1237 }
1238
1239 #[cfg(test)]
1243 pub fn into_valid_new(self) -> Entry<EntryValid, EntryNew> {
1244 let uuid = self.get_uuid().expect("Invalid uuid");
1245 Entry {
1246 valid: EntryValid {
1247 uuid,
1248 ecstate: self.valid.ecstate,
1249 },
1250 state: EntryNew,
1251 attrs: self.attrs,
1252 }
1253 }
1254
1255 #[cfg(test)]
1259 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1260 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
1261 Entry {
1262 valid: EntrySealed {
1263 uuid,
1264 ecstate: self.valid.ecstate,
1265 },
1266 state: EntryCommitted { id: 0 },
1267 attrs: self.attrs,
1268 }
1269 }
1270
1271 #[cfg(test)]
1275 pub fn into_valid_committed(self) -> Entry<EntryValid, EntryCommitted> {
1276 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
1277 Entry {
1278 valid: EntryValid {
1279 uuid,
1280 ecstate: self.valid.ecstate,
1281 },
1282 state: EntryCommitted { id: 0 },
1283 attrs: self.attrs,
1284 }
1285 }
1286}
1287
1288impl Entry<EntryInvalid, EntryCommitted> {
1289 #[cfg(test)]
1293 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1294 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
1295 Entry {
1296 valid: EntrySealed {
1297 uuid,
1298 ecstate: self.valid.ecstate,
1299 },
1300 state: self.state,
1301 attrs: self.attrs,
1302 }
1303 }
1304}
1305
1306impl Entry<EntrySealed, EntryNew> {
1307 #[cfg(test)]
1311 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1312 Entry {
1313 valid: self.valid,
1314 state: EntryCommitted { id: 0 },
1315 attrs: self.attrs,
1316 }
1317 }
1318
1319 pub fn into_sealed_committed_id(self, id: u64) -> Entry<EntrySealed, EntryCommitted> {
1322 Entry {
1323 valid: self.valid,
1324 state: EntryCommitted { id },
1325 attrs: self.attrs,
1326 }
1327 }
1328
1329 pub fn compare(&self, rhs: &Entry<EntrySealed, EntryNew>) -> bool {
1330 compare_attrs(&self.attrs, &rhs.attrs)
1331 }
1332}
1333
1334type IdxDiff<'a> =
1335 Vec<Result<(&'a Attribute, IndexType, String), (&'a Attribute, IndexType, String)>>;
1336
1337impl<VALID> Entry<VALID, EntryCommitted> {
1338 pub fn get_id(&self) -> u64 {
1340 self.state.id
1341 }
1342}
1343
1344impl<STATE> Entry<EntrySealed, STATE> {
1345 pub fn into_init(self) -> Entry<EntryInit, STATE> {
1346 Entry {
1347 valid: EntryInit,
1348 state: self.state,
1349 attrs: self.attrs,
1350 }
1351 }
1352}
1353
1354impl Entry<EntrySealed, EntryCommitted> {
1355 #[cfg(test)]
1356 pub(crate) fn get_last_changed(&self) -> Cid {
1357 self.valid.ecstate.get_max_cid().clone()
1358 }
1359
1360 #[cfg(test)]
1362 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1363 self
1365 }
1366
1367 pub(crate) fn stub_sealed_committed_id(
1368 id: u64,
1369 ctx_ent: &EntryIncrementalNew,
1370 ) -> EntrySealedCommitted {
1371 let uuid = ctx_ent.get_uuid();
1372 let ecstate = ctx_ent.stub_ecstate();
1373
1374 Entry {
1375 valid: EntrySealed { uuid, ecstate },
1376 state: EntryCommitted { id },
1377 attrs: Default::default(),
1378 }
1379 }
1380
1381 pub fn insert_claim(&mut self, value: &str) {
1384 self.add_ava_int(Attribute::Claim, Value::new_iutf8(value));
1385 }
1386
1387 pub fn compare(&self, rhs: &Entry<EntrySealed, EntryCommitted>) -> bool {
1388 compare_attrs(&self.attrs, &rhs.attrs)
1389 }
1390
1391 pub fn to_dbentry(&self) -> DbEntry {
1393 DbEntry {
1396 ent: DbEntryVers::V3 {
1397 changestate: self.valid.ecstate.to_db_changestate(),
1398 attrs: self
1399 .attrs
1400 .iter()
1401 .map(|(k, vs)| {
1402 let dbvs: DbValueSetV2 = vs.to_db_valueset_v2();
1403 (k.clone(), dbvs)
1404 })
1405 .collect(),
1406 },
1407 }
1408 }
1409
1410 #[inline]
1411 fn get_name2uuid_cands(&self) -> Set<String> {
1414 let cands = [Attribute::Spn, Attribute::Name, Attribute::GidNumber];
1420 cands
1421 .iter()
1422 .filter_map(|cand| {
1423 self.attrs
1424 .get(cand)
1425 .map(|vs| vs.to_proto_string_clone_iter())
1426 })
1427 .flatten()
1428 .collect()
1429 }
1430
1431 #[inline]
1432 fn get_externalid2uuid(&self) -> Option<String> {
1435 self.attrs
1436 .get(&Attribute::SyncExternalId)
1437 .and_then(|vs| vs.to_proto_string_single())
1438 }
1439
1440 #[inline]
1441 pub(crate) fn get_uuid2spn(&self) -> Value {
1444 self.attrs
1445 .get(&Attribute::Spn)
1446 .and_then(|vs| vs.to_value_single())
1447 .or_else(|| {
1448 self.attrs
1449 .get(&Attribute::Name)
1450 .and_then(|vs| vs.to_value_single())
1451 })
1452 .unwrap_or_else(|| Value::Uuid(self.get_uuid()))
1453 }
1454
1455 #[inline]
1456 pub(crate) fn get_uuid2rdn(&self) -> String {
1460 self.attrs
1461 .get(&Attribute::Spn)
1462 .and_then(|vs| vs.to_proto_string_single().map(|v| format!("spn={v}")))
1463 .or_else(|| {
1464 self.attrs
1465 .get(&Attribute::Name)
1466 .and_then(|vs| vs.to_proto_string_single().map(|v| format!("name={v}")))
1467 })
1468 .unwrap_or_else(|| format!("uuid={}", self.get_uuid().as_hyphenated()))
1469 }
1470
1471 pub(crate) fn idx_name2uuid_diff(
1474 pre: Option<&Self>,
1475 post: Option<&Self>,
1476 ) -> (
1477 Option<Set<String>>,
1479 Option<Set<String>>,
1481 ) {
1482 match (pre, post) {
1484 (None, None) => {
1485 (None, None)
1487 }
1488 (None, Some(b)) => {
1489 (Some(b.get_name2uuid_cands()), None)
1492 }
1493 (Some(a), None) => {
1494 (None, Some(a.get_name2uuid_cands()))
1496 }
1497 (Some(a), Some(b)) => {
1498 let pre_set = a.get_name2uuid_cands();
1499 let post_set = b.get_name2uuid_cands();
1500
1501 let add_set: Set<_> = post_set.difference(&pre_set).cloned().collect();
1503 let rem_set: Set<_> = pre_set.difference(&post_set).cloned().collect();
1505 (Some(add_set), Some(rem_set))
1506 }
1507 }
1508 }
1509
1510 pub(crate) fn idx_externalid2uuid_diff(
1512 pre: Option<&Self>,
1513 post: Option<&Self>,
1514 ) -> (Option<String>, Option<String>) {
1515 match (pre, post) {
1516 (None, None) => {
1517 (None, None)
1519 }
1520 (None, Some(b)) => {
1521 (b.get_externalid2uuid(), None)
1523 }
1524 (Some(a), None) => {
1525 (None, a.get_externalid2uuid())
1527 }
1528 (Some(a), Some(b)) => {
1529 let ia = a.get_externalid2uuid();
1530 let ib = b.get_externalid2uuid();
1531 if ia != ib {
1532 (ib, ia)
1535 } else {
1536 (None, None)
1538 }
1539 }
1540 }
1541 }
1542
1543 pub(crate) fn idx_uuid2spn_diff(
1546 pre: Option<&Self>,
1547 post: Option<&Self>,
1548 ) -> Option<Result<Value, ()>> {
1549 match (pre, post) {
1550 (None, None) => {
1551 None
1553 }
1554 (None, Some(b)) => {
1555 Some(Ok(b.get_uuid2spn()))
1557 }
1558 (Some(_a), None) => {
1559 Some(Err(()))
1561 }
1562 (Some(a), Some(b)) => {
1563 let ia = a.get_uuid2spn();
1564 let ib = b.get_uuid2spn();
1565 if ia != ib {
1566 Some(Ok(ib))
1568 } else {
1569 None
1571 }
1572 }
1573 }
1574 }
1575
1576 pub(crate) fn idx_uuid2rdn_diff(
1579 pre: Option<&Self>,
1580 post: Option<&Self>,
1581 ) -> Option<Result<String, ()>> {
1582 match (pre, post) {
1583 (None, None) => {
1584 None
1586 }
1587 (None, Some(b)) => {
1588 Some(Ok(b.get_uuid2rdn()))
1590 }
1591 (Some(_a), None) => {
1592 Some(Err(()))
1594 }
1595 (Some(a), Some(b)) => {
1596 let ia = a.get_uuid2rdn();
1597 let ib = b.get_uuid2rdn();
1598 if ia != ib {
1599 Some(Ok(ib))
1601 } else {
1602 None
1604 }
1605 }
1606 }
1607 }
1608
1609 pub(crate) fn idx_diff<'a>(
1612 idxmeta: &'a HashMap<IdxKey, IdxSlope>,
1613 pre: Option<&Self>,
1614 post: Option<&Self>,
1615 ) -> IdxDiff<'a> {
1616 match (pre, post) {
1621 (None, None) => {
1622 Vec::with_capacity(0)
1624 }
1625 (Some(pre_e), None) => {
1626 idxmeta
1628 .keys()
1629 .flat_map(|ikey| {
1630 match pre_e.get_ava_set(&ikey.attr) {
1631 None => Vec::with_capacity(0),
1632 Some(vs) => {
1633 let changes: Vec<Result<_, _>> = match ikey.itype {
1634 IndexType::Equality => {
1635 vs.generate_idx_eq_keys()
1637 .into_iter()
1638 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1639 .collect()
1640 }
1641 IndexType::Presence => {
1642 vec![Err((&ikey.attr, ikey.itype, "_".to_string()))]
1643 }
1644 IndexType::SubString => vs
1645 .generate_idx_sub_keys()
1646 .into_iter()
1647 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1648 .collect(),
1649 };
1650 changes
1651 }
1652 }
1653 })
1654 .collect()
1655 }
1656 (None, Some(post_e)) => {
1657 idxmeta
1659 .keys()
1660 .flat_map(|ikey| {
1661 match post_e.get_ava_set(&ikey.attr) {
1662 None => Vec::with_capacity(0),
1663 Some(vs) => {
1664 let changes: Vec<Result<_, _>> = match ikey.itype {
1665 IndexType::Equality => vs
1666 .generate_idx_eq_keys()
1667 .into_iter()
1668 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1669 .collect(),
1670 IndexType::Presence => {
1671 vec![Ok((&ikey.attr, ikey.itype, "_".to_string()))]
1672 }
1673 IndexType::SubString => vs
1674 .generate_idx_sub_keys()
1675 .into_iter()
1676 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1677 .collect(),
1678 };
1679 changes
1682 }
1683 }
1684 })
1685 .collect()
1686 }
1687 (Some(pre_e), Some(post_e)) => {
1688 assert_eq!(pre_e.state.id, post_e.state.id);
1689 idxmeta
1690 .keys()
1691 .flat_map(|ikey| {
1692 match (
1693 pre_e.get_ava_set(&ikey.attr),
1694 post_e.get_ava_set(&ikey.attr),
1695 ) {
1696 (None, None) => {
1697 Vec::with_capacity(0)
1699 }
1700 (Some(pre_vs), None) => {
1701 let changes: Vec<Result<_, _>> = match ikey.itype {
1703 IndexType::Equality => {
1704 pre_vs
1707 .generate_idx_eq_keys()
1708 .into_iter()
1709 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1710 .collect()
1711 }
1712 IndexType::Presence => {
1713 vec![Err((&ikey.attr, ikey.itype, "_".to_string()))]
1714 }
1715 IndexType::SubString => pre_vs
1716 .generate_idx_sub_keys()
1717 .into_iter()
1718 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1719 .collect(),
1720 };
1721 changes
1722 }
1723 (None, Some(post_vs)) => {
1724 let changes: Vec<Result<_, _>> = match ikey.itype {
1726 IndexType::Equality => {
1727 post_vs
1730 .generate_idx_eq_keys()
1731 .into_iter()
1732 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1733 .collect()
1734 }
1735 IndexType::Presence => {
1736 vec![Ok((&ikey.attr, ikey.itype, "_".to_string()))]
1737 }
1738 IndexType::SubString => post_vs
1739 .generate_idx_sub_keys()
1740 .into_iter()
1741 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1742 .collect(),
1743 };
1744 changes
1745 }
1746 (Some(pre_vs), Some(post_vs)) => {
1747 let (mut pre_idx_keys, mut post_idx_keys) = match ikey.itype {
1749 IndexType::Equality => (
1750 pre_vs.generate_idx_eq_keys(),
1751 post_vs.generate_idx_eq_keys(),
1752 ),
1753 IndexType::Presence => {
1754 (Vec::with_capacity(0), Vec::with_capacity(0))
1756 }
1757 IndexType::SubString => (
1758 pre_vs.generate_idx_sub_keys(),
1759 post_vs.generate_idx_sub_keys(),
1760 ),
1761 };
1762
1763 let sz = if pre_idx_keys.len() > post_idx_keys.len() {
1764 pre_idx_keys.len()
1765 } else {
1766 post_idx_keys.len()
1767 };
1768
1769 let mut added_vs = Vec::with_capacity(sz);
1770 let mut removed_vs = Vec::with_capacity(sz);
1771
1772 if sz > 0 {
1773 pre_idx_keys.sort_unstable();
1774 post_idx_keys.sort_unstable();
1775
1776 let mut pre_iter = pre_idx_keys.iter();
1777 let mut post_iter = post_idx_keys.iter();
1778
1779 let mut pre = pre_iter.next();
1780 let mut post = post_iter.next();
1781
1782 loop {
1783 match (pre, post) {
1784 (Some(a), Some(b)) => {
1785 match a.cmp(b) {
1786 Ordering::Less => {
1787 removed_vs.push(a.clone());
1788 pre = pre_iter.next();
1789 }
1790 Ordering::Equal => {
1791 pre = pre_iter.next();
1793 post = post_iter.next();
1794 }
1795 Ordering::Greater => {
1796 added_vs.push(b.clone());
1797 post = post_iter.next();
1798 }
1799 }
1800 }
1801 (Some(a), None) => {
1802 removed_vs.push(a.clone());
1803 pre = pre_iter.next();
1804 }
1805 (None, Some(b)) => {
1806 added_vs.push(b.clone());
1807 post = post_iter.next();
1808 }
1809 (None, None) => {
1810 break;
1811 }
1812 }
1813 }
1814 } let mut diff =
1817 Vec::with_capacity(removed_vs.len() + added_vs.len());
1818
1819 match ikey.itype {
1820 IndexType::SubString | IndexType::Equality => {
1821 removed_vs
1822 .into_iter()
1823 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1824 .for_each(|v| diff.push(v));
1825 added_vs
1826 .into_iter()
1827 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1828 .for_each(|v| diff.push(v));
1829 }
1830 IndexType::Presence => {
1831 }
1833 };
1834 diff
1836 }
1837 }
1838 })
1839 .collect()
1840 }
1842 }
1843 }
1844
1845 pub fn from_dbentry(db_e: DbEntry, id: u64) -> Option<Self> {
1846 let (attrs, ecstate) = match db_e.ent {
1849 DbEntryVers::V3 { changestate, attrs } => {
1850 let ecstate = EntryChangeState::from_db_changestate(changestate);
1851
1852 let r_attrs = attrs
1853 .into_iter()
1854 .filter(|(_k, vs)| !vs.is_empty())
1856 .map(|(k, dbvs)| {
1857 valueset::from_db_valueset_v2(dbvs)
1858 .map(|vs: ValueSet| (k, vs))
1859 .map_err(|e| {
1860 error!(?e, "from_dbentry failed");
1861 })
1862 })
1863 .collect::<Result<Eattrs, ()>>()
1864 .ok()?;
1865
1866 (r_attrs, ecstate)
1867 }
1868 };
1869
1870 let uuid = attrs
1871 .get(&Attribute::Uuid)
1872 .and_then(|vs| vs.to_uuid_single())?;
1873
1874 Some(Entry {
1875 valid: EntrySealed { uuid, ecstate },
1876 state: EntryCommitted { id },
1877 attrs,
1878 })
1879 }
1880
1881 #[cfg(test)]
1889 pub(crate) fn into_reduced(self) -> Entry<EntryReduced, EntryCommitted> {
1890 Entry {
1891 valid: EntryReduced {
1892 uuid: self.valid.uuid,
1893 effective_access: None,
1894 },
1895 state: self.state,
1896 attrs: self.attrs,
1897 }
1898 }
1899
1900 pub fn reduce_attributes(
1903 &self,
1904 allowed_attrs: &BTreeSet<Attribute>,
1905 effective_access: Option<Box<AccessEffectivePermission>>,
1906 ) -> Entry<EntryReduced, EntryCommitted> {
1907 let f_attrs: Map<_, _> = self
1909 .attrs
1910 .iter()
1911 .filter_map(|(k, v)| {
1912 if allowed_attrs.contains(k) {
1913 Some((k.clone(), v.clone()))
1914 } else {
1915 None
1916 }
1917 })
1918 .collect();
1919
1920 let valid = EntryReduced {
1921 uuid: self.valid.uuid,
1922 effective_access,
1923 };
1924 let state = self.state.clone();
1925
1926 Entry {
1927 valid,
1928 state,
1929 attrs: f_attrs,
1930 }
1931 }
1932
1933 pub fn to_tombstone(&self, cid: Cid) -> Entry<EntryInvalid, EntryCommitted> {
1935 let mut ecstate = self.valid.ecstate.clone();
1936 let mut attrs_new: Eattrs = Map::new();
1938
1939 let class_ava = vs_iutf8![EntryClass::Object.into(), EntryClass::Tombstone.into()];
1940 let last_mod_ava = vs_cid![cid.clone()];
1941 let created_ava = vs_cid![cid.clone()];
1942
1943 attrs_new.insert(Attribute::Uuid, vs_uuid![self.get_uuid()]);
1944 attrs_new.insert(Attribute::Class, class_ava);
1945 attrs_new.insert(Attribute::LastModifiedCid, last_mod_ava);
1946 attrs_new.insert(Attribute::CreatedAtCid, created_ava);
1947
1948 ecstate.tombstone(&cid);
1950
1951 Entry {
1952 valid: EntryInvalid { cid, ecstate },
1953 state: self.state.clone(),
1954 attrs: attrs_new,
1955 }
1956 }
1957
1958 pub fn into_valid(self, ecstate: EntryChangeState) -> Entry<EntryValid, EntryCommitted> {
1960 Entry {
1961 valid: EntryValid {
1962 uuid: self.valid.uuid,
1963 ecstate,
1964 },
1965 state: self.state,
1966 attrs: self.attrs,
1967 }
1968 }
1969
1970 pub fn verify(
1971 &self,
1972 schema: &dyn SchemaTransaction,
1973 results: &mut Vec<Result<(), ConsistencyError>>,
1974 ) {
1975 self.valid
1976 .ecstate
1977 .verify(schema, &self.attrs, self.state.id, results);
1978 }
1979}
1980
1981impl<STATE> Entry<EntryValid, STATE> {
1982 fn validate(&self, schema: &dyn SchemaTransaction) -> Result<(), SchemaError> {
1983 let schema_classes = schema.get_classes();
1984 let schema_attributes = schema.get_attributes();
1985
1986 trace!(?self.attrs, "Entry::validate -> target");
1988
1989 if !self.attribute_pres(Attribute::Class) {
1991 return Err(SchemaError::NoClassFound);
1993 }
1994
1995 if self.attribute_equality(Attribute::Class, &EntryClass::Conflict.into()) {
1996 trace!("Skipping schema validation on conflict entry");
1998 return Ok(());
1999 };
2000
2001 let recycled = self.attribute_equality(Attribute::Class, &EntryClass::Recycled.into());
2003
2004 let extensible =
2007 self.attribute_equality(Attribute::Class, &EntryClass::ExtensibleObject.into());
2008
2009 let entry_classes = self.get_ava_set(Attribute::Class).ok_or_else(|| {
2010 admin_debug!("Attribute '{}' missing from entry", Attribute::Class);
2011 SchemaError::NoClassFound
2012 })?;
2013 let mut invalid_classes = Vec::with_capacity(0);
2014
2015 let mut classes: Vec<&SchemaClass> = Vec::with_capacity(entry_classes.len());
2016
2017 let entry_classes = if let Some(ec) = entry_classes.as_iutf8_set() {
2020 ec.iter()
2021 .for_each(|s| match schema_classes.get(s.as_str()) {
2022 Some(x) => classes.push(x),
2023 None => {
2024 admin_debug!("invalid class: {:?}", s);
2025 invalid_classes.push(s.to_string())
2026 }
2027 });
2028 ec
2029 } else {
2030 admin_debug!("corrupt class attribute");
2031 return Err(SchemaError::NoClassFound);
2032 };
2033
2034 if !invalid_classes.is_empty() {
2035 return Err(SchemaError::InvalidClass(invalid_classes));
2036 };
2037
2038 let supplements_classes: Vec<_> = classes
2042 .iter()
2043 .flat_map(|cls| cls.systemsupplements.iter().chain(cls.supplements.iter()))
2044 .collect();
2045
2046 let valid_supplements = if supplements_classes.is_empty() {
2048 true
2050 } else {
2051 supplements_classes
2052 .iter()
2053 .any(|class| entry_classes.contains(class.as_str()))
2054 };
2055
2056 if !valid_supplements {
2057 warn!(
2058 "Validation error, the following possible supplement classes are missing - {:?}",
2059 supplements_classes
2060 );
2061 let supplements_classes = supplements_classes.iter().map(|s| s.to_string()).collect();
2062 return Err(SchemaError::SupplementsNotSatisfied(supplements_classes));
2063 }
2064
2065 let excludes_classes: Vec<_> = classes
2066 .iter()
2067 .flat_map(|cls| cls.systemexcludes.iter().chain(cls.excludes.iter()))
2068 .collect();
2069
2070 let mut invalid_excludes = Vec::with_capacity(0);
2071
2072 excludes_classes.iter().for_each(|class| {
2073 if entry_classes.contains(class.as_str()) {
2074 invalid_excludes.push(class.to_string())
2075 }
2076 });
2077
2078 if !invalid_excludes.is_empty() {
2079 admin_warn!(
2080 "Validation error, the following excluded classes are present - {:?}",
2081 invalid_excludes
2082 );
2083 return Err(SchemaError::ExcludesNotSatisfied(invalid_excludes));
2084 }
2085
2086 let must: Result<Vec<&SchemaAttribute>, _> = classes
2099 .iter()
2100 .flat_map(|cls| cls.systemmust.iter().chain(cls.must.iter()))
2102 .map(|s| {
2103 schema_attributes.get(s).ok_or(SchemaError::Corrupted)
2106 })
2107 .collect();
2108
2109 let must = must?;
2110
2111 let mut missing_must = Vec::with_capacity(0);
2114 for attr in must.iter() {
2115 let avas = self.get_ava_set(&attr.name);
2116 if avas.is_none() {
2117 missing_must.push(attr.name.clone());
2118 }
2119 }
2120
2121 if !missing_must.is_empty() {
2122 admin_warn!(
2123 "Validation error, the following required ({}) (must) attributes are missing - {:?}",
2124 self.get_display_id(), missing_must
2125 );
2126 if !recycled {
2132 return Err(SchemaError::MissingMustAttribute(missing_must));
2133 }
2134 }
2135
2136 if extensible {
2137 self.attrs.iter().try_for_each(|(attr_name, avas)| {
2138 match schema_attributes.get(attr_name) {
2139 Some(a_schema) => {
2140 if a_schema.phantom {
2143 admin_warn!(
2144 "Rejecting attempt to add phantom attribute to extensible object: {}",
2145 attr_name
2146 );
2147 Err(SchemaError::PhantomAttribute(attr_name.to_string()))
2148 } else {
2149 a_schema.validate_ava(attr_name, avas)
2150 }
2152 }
2153 None => {
2154 admin_error!(
2155 "Invalid Attribute {}, undefined in schema_attributes",
2156 attr_name.to_string()
2157 );
2158 Err(SchemaError::InvalidAttribute(
2159 attr_name.to_string()
2160 ))
2161 }
2162 }
2163 })?;
2164 } else {
2165 let may: Result<Map<&Attribute, &SchemaAttribute>, _> = classes
2173 .iter()
2174 .flat_map(|cls| {
2176 trace!(?cls);
2177 cls.systemmust
2178 .iter()
2179 .chain(cls.must.iter())
2180 .chain(cls.systemmay.iter())
2181 .chain(cls.may.iter())
2182 })
2183 .map(|s| {
2184 Ok((s, schema_attributes.get(s).ok_or(SchemaError::Corrupted)?))
2187 })
2188 .collect();
2189
2190 let may = may?;
2191
2192 self.attrs.iter().try_for_each(|(attr_name, avas)| {
2201 match may.get(attr_name) {
2202 Some(a_schema) => {
2203 a_schema.validate_ava(attr_name, avas)
2206 }
2208 None => {
2209 admin_error!(
2210 "{} {} - not found in the list of valid attributes for this set of classes {:?} - valid attributes are {:?}",
2211
2212 attr_name.as_str(),
2213 self.get_display_id(),
2214 entry_classes.iter().collect::<Vec<_>>(),
2215 may.keys().collect::<Vec<_>>()
2216 );
2217 Err(SchemaError::AttributeNotValidForClass(
2218 attr_name.to_string()
2219 ))
2220 }
2221 }
2222 })?;
2223 }
2224
2225 Ok(())
2227 }
2228
2229 pub fn seal(mut self, schema: &dyn SchemaTransaction) -> Entry<EntrySealed, STATE> {
2230 let EntryValid { uuid, mut ecstate } = self.valid;
2231
2232 ecstate.retain(|k, _| schema.is_replicated(k));
2236
2237 let last_mod_cid = ecstate.get_max_cid();
2239 let cv = vs_cid![last_mod_cid.clone()];
2240 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
2241
2242 let create_at_cid = ecstate.at();
2246 let cv = vs_cid![create_at_cid.clone()];
2247 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
2248
2249 Entry {
2250 valid: EntrySealed { uuid, ecstate },
2251 state: self.state,
2252 attrs: self.attrs,
2253 }
2254 }
2255
2256 pub fn get_uuid(&self) -> Uuid {
2257 self.valid.uuid
2258 }
2259}
2260
2261impl<STATE> GetUuid for Entry<EntrySealed, STATE>
2262where
2263 STATE: Clone,
2264{
2265 fn get_uuid(&self) -> Uuid {
2266 self.valid.uuid
2267 }
2268}
2269
2270impl<STATE> Entry<EntrySealed, STATE>
2271where
2272 STATE: Clone,
2273{
2274 pub fn invalidate(mut self, cid: Cid, trim_cid: &Cid) -> Entry<EntryInvalid, STATE> {
2275 for vs in self.attrs.values_mut() {
2277 vs.trim(trim_cid);
2278 }
2279
2280 let last_mod_cid = self.valid.ecstate.get_max_cid();
2288 let cv = vs_cid![last_mod_cid.clone()];
2289 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
2290
2291 let create_at_cid = self.valid.ecstate.at();
2292 let cv = vs_cid![create_at_cid.clone()];
2293 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
2294
2295 Entry {
2296 valid: EntryInvalid {
2297 cid,
2298 ecstate: self.valid.ecstate,
2299 },
2300 state: self.state,
2301 attrs: self.attrs,
2302 }
2303 }
2304
2305 pub fn get_uuid(&self) -> Uuid {
2306 self.valid.uuid
2307 }
2308
2309 pub fn get_changestate(&self) -> &EntryChangeState {
2310 &self.valid.ecstate
2311 }
2312
2313 pub(crate) fn entry_changed_excluding_attribute<A: AsRef<Attribute>>(
2317 &self,
2318 attr: A,
2319 cid: &Cid,
2320 ) -> bool {
2321 let attr_ref = attr.as_ref();
2322
2323 use crate::repl::entry::State;
2324
2325 match self.get_changestate().current() {
2326 State::Live { at: _, changes } => {
2327 changes.iter().any(|(change_attr, change_id)| {
2328 change_id >= cid &&
2329 *change_attr != *attr_ref &&
2330 *change_attr != Attribute::LastModifiedCid
2332 })
2333 }
2334 State::Tombstone { at } => at == cid,
2335 }
2336 }
2337
2338 #[cfg(test)]
2342 pub(crate) fn into_invalid(mut self) -> Entry<EntryInvalid, STATE> {
2343 let cid = Cid::new_zero();
2344 self.set_last_changed(cid.clone());
2345
2346 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
2347
2348 Entry {
2349 valid: EntryInvalid { cid, ecstate },
2350 state: self.state,
2351 attrs: self.attrs,
2352 }
2353 }
2354}
2355
2356impl GetUuid for Entry<EntryReduced, EntryCommitted> {
2357 fn get_uuid(&self) -> Uuid {
2358 self.valid.uuid
2359 }
2360}
2361
2362impl Entry<EntryReduced, EntryCommitted> {
2363 pub fn get_uuid(&self) -> Uuid {
2364 self.valid.uuid
2365 }
2366
2367 pub fn to_pe(&self, qs: &mut QueryServerReadTransaction) -> Result<ProtoEntry, OperationError> {
2369 let attrs: Result<_, _> = self
2371 .attrs
2372 .iter()
2373 .map(|(k, vs)| qs.resolve_valueset(vs).map(|pvs| (k.to_string(), pvs)))
2374 .collect();
2375 Ok(ProtoEntry { attrs: attrs? })
2376 }
2377
2378 pub fn to_scim_kanidm<'a, TXN>(
2379 &self,
2380 read_txn: &mut TXN,
2381 ) -> Result<ScimEntryKanidm, OperationError>
2382 where
2383 TXN: QueryServerTransaction<'a>,
2384 {
2385 let result: Result<BTreeMap<Attribute, ScimValueKanidm>, OperationError> = self
2386 .attrs
2387 .iter()
2388 .filter(|(k, _vs)| **k != Attribute::Uuid)
2390 .filter_map(|(k, vs)| {
2391 let opt_resolve_status = vs.to_scim_value();
2392 let res_opt_scim_value = match opt_resolve_status {
2393 None => Ok(None),
2394 Some(ScimResolveStatus::Resolved(scim_value_kani)) => Ok(Some(scim_value_kani)),
2395 Some(ScimResolveStatus::NeedsResolution(scim_value_interim)) => {
2396 read_txn.resolve_scim_interim(scim_value_interim)
2397 }
2398 };
2399 res_opt_scim_value
2400 .transpose()
2401 .map(|scim_res| scim_res.map(|scim_value| (k.clone(), scim_value)))
2402 })
2403 .collect();
2404
2405 let attrs = result?;
2406
2407 let ext_access_check = self.valid.effective_access.as_ref().map(|eff_acc| {
2408 let ident = eff_acc.ident;
2409 let delete = eff_acc.delete;
2410 let search = (&eff_acc.search).into();
2411 let modify_present = (&eff_acc.modify_pres).into();
2412 let modify_remove = (&eff_acc.modify_rem).into();
2413
2414 ScimEffectiveAccess {
2415 ident,
2416 delete,
2417 search,
2418 modify_present,
2419 modify_remove,
2420 }
2421 });
2422
2423 let id = self.get_uuid();
2424
2425 let schemas = Vec::with_capacity(0);
2428
2429 Ok(ScimEntryKanidm {
2430 header: ScimEntryHeader {
2431 schemas,
2432 id,
2433 external_id: None,
2435 meta: None,
2438 },
2439 ext_access_check,
2440 attrs,
2441 })
2442 }
2443
2444 pub fn to_ldap(
2446 &self,
2447 qs: &mut QueryServerReadTransaction,
2448 basedn: &str,
2449 all_attrs: bool,
2451 l_attrs: &[String],
2454 ) -> Result<LdapSearchResultEntry, OperationError> {
2455 let rdn = qs.uuid_to_rdn(self.get_uuid())?;
2456
2457 let dn = format!("{rdn},{basedn}");
2458
2459 let attr_map: Result<Map<&str, Vec<Vec<u8>>>, _> = self
2464 .attrs
2465 .iter()
2466 .map(|(k, vs)| {
2467 qs.resolve_valueset_ldap(vs, basedn)
2468 .map(|pvs| (k.as_str(), pvs))
2469 })
2470 .collect();
2471 let attr_map = attr_map?;
2472
2473 let attr_names: Vec<(&str, &str)> = if all_attrs {
2476 self.attrs
2478 .keys()
2479 .map(|k| (k.as_str(), k.as_str()))
2480 .chain(
2481 l_attrs
2482 .iter()
2483 .map(|k| (k.as_str(), ldap_vattr_map(k.as_str()).unwrap_or(k.as_str()))),
2484 )
2485 .collect()
2486 } else {
2487 l_attrs
2489 .iter()
2490 .map(|k| (k.as_str(), ldap_vattr_map(k.as_str()).unwrap_or(k.as_str())))
2491 .collect()
2492 };
2493
2494 let attributes: Vec<_> = attr_names
2496 .into_iter()
2497 .filter_map(|(ldap_a, kani_a)| {
2498 match ldap_a {
2500 LDAP_ATTR_DN => Some(LdapPartialAttribute {
2501 atype: LDAP_ATTR_DN.to_string(),
2502 vals: vec![dn.as_bytes().to_vec()],
2503 }),
2504 LDAP_ATTR_ENTRYDN => Some(LdapPartialAttribute {
2505 atype: LDAP_ATTR_ENTRYDN.to_string(),
2506 vals: vec![dn.as_bytes().to_vec()],
2507 }),
2508 LDAP_ATTR_MAIL_PRIMARY | LDAP_ATTR_EMAIL_PRIMARY => {
2509 attr_map.get(kani_a).map(|pvs| LdapPartialAttribute {
2510 atype: ldap_a.to_string(),
2511 vals: pvs
2512 .first()
2513 .map(|first| vec![first.clone()])
2514 .unwrap_or_default(),
2515 })
2516 }
2517 LDAP_ATTR_MAIL_ALTERNATIVE | LDAP_ATTR_EMAIL_ALTERNATIVE => {
2518 attr_map.get(kani_a).map(|pvs| LdapPartialAttribute {
2519 atype: ldap_a.to_string(),
2520 vals: pvs
2521 .split_first()
2522 .map(|(_, rest)| rest.to_vec())
2523 .unwrap_or_default(),
2524 })
2525 }
2526 _ => attr_map.get(kani_a).map(|pvs| LdapPartialAttribute {
2527 atype: ldap_a.to_string(),
2528 vals: pvs.clone(),
2529 }),
2530 }
2531 })
2532 .collect();
2533
2534 Ok(LdapSearchResultEntry { dn, attributes })
2535 }
2536}
2537
2538impl<VALID, STATE> Entry<VALID, STATE> {
2540 fn add_ava_int(&mut self, attr: Attribute, value: Value) -> bool {
2545 if let Some(vs) = self.attrs.get_mut(&attr) {
2546 let r = vs.insert_checked(value);
2547 debug_assert!(r.is_ok());
2548 r.unwrap_or(false)
2550 } else {
2551 #[allow(clippy::expect_used)]
2552 let vs = valueset::from_value_iter(std::iter::once(value))
2553 .expect("Unable to fail - non-zero iter, and single value type!");
2554 self.attrs.insert(attr, vs);
2555 true
2557 }
2558 }
2560
2561 fn set_ava_iter_int<T>(&mut self, attr: Attribute, iter: T)
2563 where
2564 T: IntoIterator<Item = Value>,
2565 {
2566 let Ok(vs) = valueset::from_value_iter(iter.into_iter()) else {
2567 trace!("set_ava_iter_int - empty from_value_iter, skipping");
2568 return;
2569 };
2570
2571 if let Some(existing_vs) = self.attrs.get_mut(&attr) {
2572 let _ = existing_vs.merge(&vs);
2574 } else {
2575 self.attrs.insert(attr, vs);
2577 }
2578 }
2579
2580 #[cfg(test)]
2582 fn set_last_changed(&mut self, cid: Cid) {
2583 let cv = vs_cid![cid.clone()];
2584 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
2585 let cv = vs_cid![cid];
2586 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
2587 }
2588
2589 pub(crate) fn get_display_id(&self) -> String {
2590 self.attrs
2591 .get(&Attribute::Spn)
2592 .and_then(|vs| vs.to_value_single())
2593 .or_else(|| {
2594 self.attrs
2595 .get(&Attribute::Name)
2596 .and_then(|vs| vs.to_value_single())
2597 })
2598 .or_else(|| {
2599 self.attrs
2600 .get(&Attribute::Uuid)
2601 .and_then(|vs| vs.to_value_single())
2602 })
2603 .map(|value| value.to_proto_string_clone())
2604 .unwrap_or_else(|| "no entry id available".to_string())
2605 }
2606
2607 pub fn get_ava_names(&self) -> impl Iterator<Item = &str> {
2609 self.attrs.keys().map(|a| a.as_str())
2611 }
2612
2613 pub fn get_ava(&self) -> &Eattrs {
2615 &self.attrs
2616 }
2617
2618 pub fn get_ava_iter(&self) -> impl Iterator<Item = (&Attribute, &ValueSet)> {
2619 self.attrs.iter()
2620 }
2621
2622 pub fn get_ava_set<A: AsRef<Attribute>>(&self, attr: A) -> Option<&ValueSet> {
2624 self.attrs.get(attr.as_ref())
2625 }
2626
2627 pub fn get_ava_refer<A: AsRef<Attribute>>(&self, attr: A) -> Option<&BTreeSet<Uuid>> {
2628 self.get_ava_set(attr).and_then(|vs| vs.as_refer_set())
2629 }
2630
2631 pub fn get_ava_as_iutf8_iter<A: AsRef<Attribute>>(
2632 &self,
2633 attr: A,
2634 ) -> Option<impl Iterator<Item = &str>> {
2635 self.get_ava_set(attr).and_then(|vs| vs.as_iutf8_iter())
2636 }
2637
2638 pub fn get_ava_as_iutf8<A: AsRef<Attribute>>(&self, attr: A) -> Option<&BTreeSet<String>> {
2639 self.get_ava_set(attr).and_then(|vs| vs.as_iutf8_set())
2640 }
2641
2642 pub fn get_ava_as_image<A: AsRef<Attribute>>(&self, attr: A) -> Option<&HashSet<ImageValue>> {
2643 self.get_ava_set(attr).and_then(|vs| vs.as_imageset())
2644 }
2645
2646 pub fn get_ava_single_image<A: AsRef<Attribute>>(&self, attr: A) -> Option<ImageValue> {
2647 let images = self.get_ava_set(attr).and_then(|vs| vs.as_imageset())?;
2648 images.iter().next().cloned()
2649 }
2650
2651 pub fn get_ava_single_credential_type<A: AsRef<Attribute>>(
2652 &self,
2653 attr: A,
2654 ) -> Option<CredentialType> {
2655 self.get_ava_set(attr)
2656 .and_then(|vs| vs.to_credentialtype_single())
2657 }
2658
2659 pub fn get_ava_as_oauthscopes<A: AsRef<Attribute>>(
2660 &self,
2661 attr: A,
2662 ) -> Option<impl Iterator<Item = &str>> {
2663 self.get_ava_set(attr)
2664 .and_then(|vs| vs.as_oauthscope_iter())
2665 }
2666
2667 pub fn get_ava_as_oauthscopemaps<A: AsRef<Attribute>>(
2668 &self,
2669 attr: A,
2670 ) -> Option<&std::collections::BTreeMap<Uuid, std::collections::BTreeSet<String>>> {
2671 self.get_ava_set(attr).and_then(|vs| vs.as_oauthscopemap())
2672 }
2673
2674 pub fn get_ava_as_intenttokens<A: AsRef<Attribute>>(
2675 &self,
2676 attr: A,
2677 ) -> Option<&std::collections::BTreeMap<String, IntentTokenState>> {
2678 self.get_ava_set(attr)
2679 .and_then(|vs| vs.as_intenttoken_map())
2680 }
2681
2682 pub fn get_ava_as_session_map<A: AsRef<Attribute>>(
2683 &self,
2684 attr: A,
2685 ) -> Option<&std::collections::BTreeMap<Uuid, Session>> {
2686 self.get_ava_set(attr).and_then(|vs| vs.as_session_map())
2687 }
2688
2689 pub fn get_ava_as_apitoken_map<A: AsRef<Attribute>>(
2690 &self,
2691 attr: A,
2692 ) -> Option<&std::collections::BTreeMap<Uuid, ApiToken>> {
2693 self.get_ava_set(attr).and_then(|vs| vs.as_apitoken_map())
2694 }
2695
2696 pub fn get_ava_as_oauth2session_map<A: AsRef<Attribute>>(
2697 &self,
2698 attr: A,
2699 ) -> Option<&std::collections::BTreeMap<Uuid, Oauth2Session>> {
2700 self.get_ava_set(attr)
2701 .and_then(|vs| vs.as_oauth2session_map())
2702 }
2703
2704 pub fn get_ava_iter_iname<A: AsRef<Attribute>>(
2706 &self,
2707 attr: A,
2708 ) -> Option<impl Iterator<Item = &str>> {
2709 self.get_ava_set(attr).and_then(|vs| vs.as_iname_iter())
2710 }
2711
2712 pub fn get_ava_iter_iutf8<A: AsRef<Attribute>>(
2714 &self,
2715 attr: A,
2716 ) -> Option<impl Iterator<Item = &str>> {
2717 self.get_ava_set(attr).and_then(|vs| vs.as_iutf8_iter())
2718 }
2719
2720 pub fn get_ava_as_refuuid<A: AsRef<Attribute>>(
2722 &self,
2723 attr: A,
2724 ) -> Option<Box<dyn Iterator<Item = Uuid> + '_>> {
2725 self.get_ava_set(attr).and_then(|vs| vs.as_ref_uuid_iter())
2727 }
2728
2729 pub fn get_ava_iter_sshpubkeys<A: AsRef<Attribute>>(
2731 &self,
2732 attr: A,
2733 ) -> Option<impl Iterator<Item = String> + '_> {
2734 self.get_ava_set(attr)
2735 .and_then(|vs| vs.as_sshpubkey_string_iter())
2736 }
2737
2738 pub fn get_ava_single<A: AsRef<Attribute>>(&self, attr: A) -> Option<Value> {
2744 self.get_ava_set(attr).and_then(|vs| vs.to_value_single())
2745 }
2746
2747 pub fn get_ava_single_proto_string<A: AsRef<Attribute>>(&self, attr: A) -> Option<String> {
2748 self.get_ava_set(attr)
2749 .and_then(|vs| vs.to_proto_string_single())
2750 }
2751
2752 pub fn get_ava_single_bool<A: AsRef<Attribute>>(&self, attr: A) -> Option<bool> {
2754 self.get_ava_set(attr).and_then(|vs| vs.to_bool_single())
2755 }
2756
2757 pub fn get_ava_single_uint32<A: AsRef<Attribute>>(&self, attr: A) -> Option<u32> {
2759 self.get_ava_set(attr).and_then(|vs| vs.to_uint32_single())
2760 }
2761
2762 pub fn get_ava_single_syntax<A: AsRef<Attribute>>(&self, attr: A) -> Option<SyntaxType> {
2764 self.get_ava_set(attr)
2765 .and_then(|vs| vs.to_syntaxtype_single())
2766 }
2767
2768 pub fn get_ava_single_credential<A: AsRef<Attribute>>(&self, attr: A) -> Option<&Credential> {
2770 self.get_ava_set(attr)
2771 .and_then(|vs| vs.to_credential_single())
2772 }
2773
2774 pub fn get_ava_passkeys<A: AsRef<Attribute>>(
2776 &self,
2777 attr: A,
2778 ) -> Option<&BTreeMap<Uuid, (String, PasskeyV4)>> {
2779 self.get_ava_set(attr).and_then(|vs| vs.as_passkey_map())
2780 }
2781
2782 pub fn get_ava_attestedpasskeys<A: AsRef<Attribute>>(
2784 &self,
2785 attr: A,
2786 ) -> Option<&BTreeMap<Uuid, (String, AttestedPasskeyV4)>> {
2787 self.get_ava_set(attr)
2788 .and_then(|vs| vs.as_attestedpasskey_map())
2789 }
2790
2791 pub fn get_ava_uihint<A: AsRef<Attribute>>(&self, attr: A) -> Option<&BTreeSet<UiHint>> {
2793 self.get_ava_set(attr).and_then(|vs| vs.as_uihint_set())
2794 }
2795
2796 pub fn get_ava_single_secret<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2798 self.get_ava_set(attr).and_then(|vs| vs.to_secret_single())
2799 }
2800
2801 pub fn get_ava_single_datetime<A: AsRef<Attribute>>(&self, attr: A) -> Option<OffsetDateTime> {
2803 self.get_ava_set(attr)
2804 .and_then(|vs| vs.to_datetime_single())
2805 }
2806
2807 pub(crate) fn get_ava_single_utf8<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2809 self.get_ava_set(attr).and_then(|vs| vs.to_utf8_single())
2810 }
2811
2812 pub(crate) fn get_ava_single_iutf8<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2814 self.get_ava_set(attr).and_then(|vs| vs.to_iutf8_single())
2815 }
2816
2817 pub(crate) fn get_ava_single_iname<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2819 self.get_ava_set(attr).and_then(|vs| vs.to_iname_single())
2820 }
2821
2822 pub fn get_ava_single_url<A: AsRef<Attribute>>(&self, attr: A) -> Option<&Url> {
2824 self.get_ava_set(attr).and_then(|vs| vs.to_url_single())
2825 }
2826
2827 pub fn get_ava_single_uuid<A: AsRef<Attribute>>(&self, attr: A) -> Option<Uuid> {
2828 self.get_ava_set(attr).and_then(|vs| vs.to_uuid_single())
2829 }
2830
2831 pub fn get_ava_single_refer<A: AsRef<Attribute>>(&self, attr: A) -> Option<Uuid> {
2832 self.get_ava_set(attr).and_then(|vs| vs.to_refer_single())
2833 }
2834
2835 pub fn get_ava_mail_primary<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2836 self.get_ava_set(attr)
2837 .and_then(|vs| vs.to_email_address_primary_str())
2838 }
2839
2840 pub fn get_ava_iter_mail<A: AsRef<Attribute>>(
2841 &self,
2842 attr: A,
2843 ) -> Option<impl Iterator<Item = &str>> {
2844 self.get_ava_set(attr).and_then(|vs| vs.as_email_str_iter())
2845 }
2846
2847 pub fn get_ava_single_protofilter<A: AsRef<Attribute>>(&self, attr: A) -> Option<&ProtoFilter> {
2849 self.get_ava_set(attr)
2850 .and_then(|vs| vs.to_json_filter_single())
2851 }
2852
2853 pub fn get_ava_single_private_binary<A: AsRef<Attribute>>(&self, attr: A) -> Option<&[u8]> {
2854 self.get_ava_set(attr)
2855 .and_then(|vs| vs.to_private_binary_single())
2856 }
2857
2858 pub fn get_ava_single_jws_key_es256<A: AsRef<Attribute>>(
2859 &self,
2860 attr: A,
2861 ) -> Option<&JwsEs256Signer> {
2862 self.get_ava_set(attr)
2863 .and_then(|vs| vs.to_jws_key_es256_single())
2864 }
2865
2866 pub fn get_ava_single_eckey_private<A: AsRef<Attribute>>(
2867 &self,
2868 attr: A,
2869 ) -> Option<&EcKey<Private>> {
2870 self.get_ava_set(attr)
2871 .and_then(|vs| vs.to_eckey_private_single())
2872 }
2873
2874 pub fn get_ava_single_eckey_public<A: AsRef<Attribute>>(
2875 &self,
2876 attr: A,
2877 ) -> Option<&EcKey<Public>> {
2878 self.get_ava_set(attr)
2879 .and_then(|vs| vs.to_eckey_public_single())
2880 }
2881
2882 pub fn get_ava_webauthn_attestation_ca_list<A: AsRef<Attribute>>(
2883 &self,
2884 attr: A,
2885 ) -> Option<&AttestationCaList> {
2886 self.get_ava_set(attr)
2887 .and_then(|vs| vs.as_webauthn_attestation_ca_list())
2888 }
2889
2890 pub fn get_ava_application_password<A: AsRef<Attribute>>(
2891 &self,
2892 attr: A,
2893 ) -> Option<&BTreeMap<Uuid, Vec<ApplicationPassword>>> {
2894 self.get_ava_set(attr)
2895 .and_then(|vs| vs.as_application_password_map())
2896 }
2897
2898 pub(crate) fn generate_spn(&self, domain_name: &str) -> Option<Value> {
2900 self.get_ava_single_iname(Attribute::Name)
2901 .map(|name| Value::new_spn_str(name, domain_name))
2902 }
2903
2904 pub fn attribute_pres<A: AsRef<Attribute>>(&self, attr: A) -> bool {
2906 self.attrs.contains_key(attr.as_ref())
2907 }
2908
2909 pub fn attribute_equality<A: AsRef<Attribute>>(&self, attr: A, value: &PartialValue) -> bool {
2912 match self.attrs.get(attr.as_ref()) {
2917 Some(v_list) => v_list.contains(value),
2918 None => false,
2919 }
2920 }
2921
2922 pub fn attribute_substring<A: AsRef<Attribute>>(
2925 &self,
2926 attr: A,
2927 subvalue: &PartialValue,
2928 ) -> bool {
2929 self.get_ava_set(attr)
2930 .map(|vset| vset.substring(subvalue))
2931 .unwrap_or(false)
2932 }
2933
2934 pub fn attribute_startswith<A: AsRef<Attribute>>(
2937 &self,
2938 attr: A,
2939 subvalue: &PartialValue,
2940 ) -> bool {
2941 self.get_ava_set(attr)
2942 .map(|vset| vset.startswith(subvalue))
2943 .unwrap_or(false)
2944 }
2945
2946 pub fn attribute_endswith<A: AsRef<Attribute>>(
2949 &self,
2950 attr: A,
2951 subvalue: &PartialValue,
2952 ) -> bool {
2953 self.get_ava_set(attr)
2954 .map(|vset| vset.endswith(subvalue))
2955 .unwrap_or(false)
2956 }
2957
2958 pub fn attribute_lessthan<A: AsRef<Attribute>>(
2961 &self,
2962 attr: A,
2963 subvalue: &PartialValue,
2964 ) -> bool {
2965 self.get_ava_set(attr)
2966 .map(|vset| vset.lessthan(subvalue))
2967 .unwrap_or(false)
2968 }
2969
2970 #[inline(always)]
2975 #[instrument(level = "trace", name = "entry::entry_match_no_index", skip(self))]
2976 pub fn entry_match_no_index(&self, filter: &Filter<FilterValidResolved>) -> bool {
2978 self.entry_match_no_index_inner(filter.to_inner())
2979 }
2980
2981 fn entry_match_no_index_inner(&self, filter: &FilterResolved) -> bool {
2985 match filter {
2988 FilterResolved::Eq(attr, value, _) => self.attribute_equality(attr, value),
2989 FilterResolved::Cnt(attr, subvalue, _) => self.attribute_substring(attr, subvalue),
2990 FilterResolved::Stw(attr, subvalue, _) => self.attribute_startswith(attr, subvalue),
2991 FilterResolved::Enw(attr, subvalue, _) => self.attribute_endswith(attr, subvalue),
2992 FilterResolved::Pres(attr, _) => self.attribute_pres(attr),
2993 FilterResolved::LessThan(attr, subvalue, _) => self.attribute_lessthan(attr, subvalue),
2994 FilterResolved::Or(l, _) => l.iter().any(|f| self.entry_match_no_index_inner(f)),
2996 FilterResolved::And(l, _) => l.iter().all(|f| self.entry_match_no_index_inner(f)),
2998 FilterResolved::Inclusion(_, _) => {
2999 false
3003 }
3004 FilterResolved::AndNot(f, _) => !self.entry_match_no_index_inner(f),
3005 FilterResolved::Invalid(_) => false,
3006 }
3007 }
3008
3009 pub fn filter_from_attrs(&self, attrs: &[Attribute]) -> Option<Filter<FilterInvalid>> {
3012 let mut pairs: Vec<(Attribute, PartialValue)> = Vec::with_capacity(0);
3024
3025 for attr in attrs {
3026 match self.attrs.get(attr) {
3027 Some(values) => values
3028 .to_partialvalue_iter()
3029 .for_each(|pv| pairs.push((attr.clone(), pv))),
3030 None => return None,
3031 }
3032 }
3033
3034 let res: Vec<FC> = pairs
3035 .into_iter()
3036 .map(|(attr, pv)| FC::Eq(attr, pv))
3037 .collect();
3038 Some(filter_all!(f_and(res)))
3039 }
3040
3041 pub fn gen_modlist_assert(
3044 &self,
3045 schema: &dyn SchemaTransaction,
3046 ) -> Result<ModifyList<ModifyInvalid>, SchemaError> {
3047 let mut mods = ModifyList::new();
3051
3052 for (k, vs) in self.attrs.iter() {
3053 if *k == Attribute::Uuid {
3069 continue;
3070 }
3071 match schema.is_multivalue(k) {
3073 Ok(r) => {
3074 if !r ||
3077 *k == Attribute::AcpReceiverGroup ||
3080 *k == Attribute::AcpCreateAttr ||
3081 *k == Attribute::AcpCreateClass ||
3082 *k == Attribute::AcpModifyPresentAttr ||
3083 *k == Attribute::AcpModifyRemovedAttr ||
3084 *k == Attribute::AcpModifyClass ||
3085 *k == Attribute::SystemMust ||
3086 *k == Attribute::SystemMay
3087 {
3088 mods.push_mod(Modify::Purged(k.clone()));
3089 }
3090 }
3091 Err(e) => return Err(e),
3093 }
3094 for v in vs.to_value_iter() {
3095 mods.push_mod(Modify::Present(k.clone(), v.clone()));
3096 }
3097 }
3098
3099 Ok(mods)
3100 }
3101
3102 pub fn mask_recycled_ts(&self) -> Option<&Self> {
3105 match self.attrs.get(&Attribute::Class) {
3107 Some(cls) => {
3108 if cls.contains(&EntryClass::Tombstone.to_partialvalue())
3109 || cls.contains(&EntryClass::Recycled.to_partialvalue())
3110 {
3111 None
3112 } else {
3113 Some(self)
3114 }
3115 }
3116 None => Some(self),
3117 }
3118 }
3119
3120 pub fn mask_recycled(&self) -> Option<&Self> {
3123 match self.attrs.get(&Attribute::Class) {
3125 Some(cls) => {
3126 if cls.contains(&EntryClass::Recycled.to_partialvalue()) {
3127 None
3128 } else {
3129 Some(self)
3130 }
3131 }
3132 None => Some(self),
3133 }
3134 }
3135
3136 pub fn mask_tombstone(&self) -> Option<&Self> {
3139 match self.attrs.get(&Attribute::Class) {
3141 Some(cls) => {
3142 if cls.contains(&EntryClass::Tombstone.to_partialvalue()) {
3143 None
3144 } else {
3145 Some(self)
3146 }
3147 }
3148 None => Some(self),
3149 }
3150 }
3151}
3152
3153impl<STATE> Entry<EntryInvalid, STATE>
3154where
3155 STATE: Clone,
3156{
3157 pub fn add_ava(&mut self, attr: Attribute, value: Value) {
3162 self.valid.ecstate.change_ava(&self.valid.cid, &attr);
3163 self.add_ava_int(attr, value);
3164 }
3165
3166 pub fn add_ava_if_not_exist<A: AsRef<Attribute>>(&mut self, attr: A, value: Value) {
3167 let attr_ref = attr.as_ref();
3168 if self.add_ava_int(attr_ref.clone(), value) {
3170 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3172 }
3173 }
3174
3175 fn assert_ava<A: AsRef<Attribute>>(
3176 &mut self,
3177 attr: A,
3178 value: &PartialValue,
3179 ) -> Result<(), OperationError> {
3180 self.valid
3181 .ecstate
3182 .change_ava(&self.valid.cid, attr.as_ref());
3183
3184 if self.attribute_equality(attr, value) {
3185 Ok(())
3186 } else {
3187 Err(OperationError::ModifyAssertionFailed)
3188 }
3189 }
3190
3191 pub(crate) fn remove_ava<A: AsRef<Attribute>>(&mut self, attr: A, value: &PartialValue) {
3194 let attr_ref = attr.as_ref();
3195 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3196
3197 let rm = if let Some(vs) = self.attrs.get_mut(attr_ref) {
3198 vs.remove(value, &self.valid.cid);
3199 vs.is_empty()
3200 } else {
3201 false
3202 };
3203 if rm {
3204 self.attrs.remove(attr_ref);
3205 };
3206 }
3207
3208 pub(crate) fn remove_avas<A: AsRef<Attribute>>(
3209 &mut self,
3210 attr: A,
3211 values: &BTreeSet<PartialValue>,
3212 ) {
3213 let attr_ref = attr.as_ref();
3214 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3215
3216 let rm = if let Some(vs) = self.attrs.get_mut(attr_ref) {
3217 values.iter().for_each(|k| {
3218 vs.remove(k, &self.valid.cid);
3219 });
3220 vs.is_empty()
3221 } else {
3222 false
3223 };
3224 if rm {
3225 self.attrs.remove(attr_ref);
3226 };
3227 }
3228
3229 pub(crate) fn purge_ava<A: AsRef<Attribute>>(&mut self, attr: A) {
3232 let attr_ref = attr.as_ref();
3233 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3234 let can_remove = self
3237 .attrs
3238 .get_mut(attr_ref)
3239 .map(|vs| vs.purge(&self.valid.cid))
3240 .unwrap_or_default();
3242 if can_remove {
3243 self.attrs.remove(attr_ref);
3244 }
3245 }
3246
3247 pub fn pop_ava<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<ValueSet> {
3249 let attr_ref = attr.as_ref();
3250 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3251
3252 let mut vs = self.attrs.remove(attr_ref)?;
3253 if vs.purge(&self.valid.cid) {
3254 Some(vs)
3256 } else {
3257 let r_vs = vs.clone();
3259 self.attrs.insert(attr_ref.clone(), vs);
3260 Some(r_vs)
3261 }
3262 }
3263
3264 #[cfg(test)]
3269 pub(crate) fn force_trim_ava<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<ValueSet> {
3270 self.valid
3271 .ecstate
3272 .change_ava(&self.valid.cid, attr.as_ref());
3273 self.attrs.remove(attr.as_ref())
3274 }
3275
3276 pub fn set_ava<T>(&mut self, attr: &Attribute, iter: T)
3279 where
3280 T: Clone + IntoIterator<Item = Value>,
3281 {
3282 self.purge_ava(attr);
3283 self.set_ava_iter_int(attr.clone(), iter)
3284 }
3285
3286 pub fn set_ava_set(&mut self, attr: &Attribute, vs: ValueSet) {
3289 self.purge_ava(attr);
3290 if let Some(existing_vs) = self.attrs.get_mut(attr) {
3291 let _ = existing_vs.merge(&vs);
3292 } else {
3293 self.attrs.insert(attr.clone(), vs);
3294 }
3295 }
3296
3297 pub fn merge_ava_set(&mut self, attr: &Attribute, vs: ValueSet) -> Result<(), OperationError> {
3300 self.valid.ecstate.change_ava(&self.valid.cid, attr);
3301 if let Some(existing_vs) = self.attrs.get_mut(attr) {
3302 existing_vs.merge(&vs)
3303 } else {
3304 self.attrs.insert(attr.clone(), vs);
3305 Ok(())
3306 }
3307 }
3308
3309 pub fn apply_modlist(
3311 &mut self,
3312 modlist: &ModifyList<ModifyValid>,
3313 ) -> Result<(), OperationError> {
3314 for modify in modlist {
3315 match modify {
3316 Modify::Present(attr, value) => {
3317 self.add_ava(attr.clone(), value.clone());
3318 }
3319 Modify::Removed(attr, value) => {
3320 self.remove_ava(attr, value);
3321 }
3322 Modify::Purged(attr) => {
3323 self.purge_ava(attr);
3324 }
3325 Modify::Assert(attr, value) => {
3326 self.assert_ava(attr, value).inspect_err(|_e| {
3327 error!("Modification assertion was not met. {} {:?}", attr, value);
3328 })?;
3329 }
3330 Modify::Set(attr, valueset) => self.set_ava_set(attr, valueset.clone()),
3331 }
3332 }
3333 Ok(())
3334 }
3335}
3336
3337impl<VALID, STATE> PartialEq for Entry<VALID, STATE> {
3338 fn eq(&self, rhs: &Entry<VALID, STATE>) -> bool {
3339 compare_attrs(&self.attrs, &rhs.attrs)
3350 }
3351}
3352
3353#[cfg(test)]
3354mod tests {
3355 use crate::prelude::*;
3356 use std::collections::BTreeSet as Set;
3357
3358 use hashbrown::HashMap;
3359
3360 use crate::be::{IdxKey, IdxSlope};
3361 use crate::entry::{Entry, EntryInit, EntryInvalid, EntryNew};
3362 use crate::modify::{Modify, ModifyList};
3363 use crate::value::{IndexType, PartialValue, Value};
3364
3365 #[test]
3366 fn test_entry_basic() {
3367 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3368
3369 e.add_ava(Attribute::UserId, Value::from("william"));
3370 }
3371
3372 #[test]
3373 fn test_entry_dup_value() {
3374 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3381 e.add_ava(Attribute::UserId, Value::from("william"));
3382 e.add_ava(Attribute::UserId, Value::from("william"));
3383
3384 let values = e.get_ava_set(Attribute::UserId).expect("Failed to get ava");
3385 assert_eq!(values.len(), 1)
3387 }
3388
3389 #[test]
3390 fn test_entry_pres() {
3391 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3392 e.add_ava(Attribute::UserId, Value::from("william"));
3393
3394 assert!(e.attribute_pres(Attribute::UserId));
3395 assert!(!e.attribute_pres(Attribute::Name));
3396 }
3397
3398 #[test]
3399 fn test_entry_equality() {
3400 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3401
3402 e.add_ava(Attribute::UserId, Value::from("william"));
3403
3404 assert!(e.attribute_equality(Attribute::UserId, &PartialValue::new_utf8s("william")));
3405 assert!(!e.attribute_equality(Attribute::UserId, &PartialValue::new_utf8s("test")));
3406 assert!(!e.attribute_equality(Attribute::NonExist, &PartialValue::new_utf8s("william")));
3407 assert!(!e.attribute_equality(Attribute::UserId, &PartialValue::new_iutf8("william")));
3409 }
3410
3411 #[test]
3412 fn test_entry_substring() {
3413 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3414
3415 e.add_ava(Attribute::UserId, Value::from("william"));
3416
3417 assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("william")));
3418 assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("will")));
3419 assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("liam")));
3420 assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("lli")));
3421 assert!(!e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("llim")));
3422 assert!(!e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("bob")));
3423 assert!(!e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("wl")));
3424
3425 assert!(e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("will")));
3426 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("liam")));
3427 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("lli")));
3428 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("llim")));
3429 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("bob")));
3430 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("wl")));
3431
3432 assert!(e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("liam")));
3433 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("will")));
3434 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("lli")));
3435 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("llim")));
3436 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("bob")));
3437 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("wl")));
3438 }
3439
3440 #[test]
3441 fn test_entry_lessthan() {
3442 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3443
3444 let pv2 = PartialValue::new_uint32(2);
3445 let pv8 = PartialValue::new_uint32(8);
3446 let pv10 = PartialValue::new_uint32(10);
3447 let pv15 = PartialValue::new_uint32(15);
3448
3449 e1.add_ava(Attribute::TestAttr, Value::new_uint32(10));
3450
3451 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv2));
3452 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv8));
3453 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv10));
3454 assert!(e1.attribute_lessthan(Attribute::TestAttr, &pv15));
3455
3456 e1.add_ava(Attribute::TestAttr, Value::new_uint32(8));
3457
3458 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv2));
3459 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv8));
3460 assert!(e1.attribute_lessthan(Attribute::TestAttr, &pv10));
3461 assert!(e1.attribute_lessthan(Attribute::TestAttr, &pv15));
3462 }
3463
3464 #[test]
3465 fn test_entry_apply_modlist() {
3466 let mut e: Entry<EntryInvalid, EntryNew> = Entry::new().into_invalid_new();
3468
3469 e.add_ava(Attribute::UserId, Value::from("william"));
3470
3471 let present_single_mods = ModifyList::new_valid_list(vec![Modify::Present(
3472 Attribute::Attr,
3473 Value::new_iutf8("value"),
3474 )]);
3475
3476 assert!(e.apply_modlist(&present_single_mods).is_ok());
3477
3478 assert!(e.attribute_equality(Attribute::UserId, &PartialValue::new_utf8s("william")));
3480 assert!(e.attribute_equality(Attribute::Attr, &PartialValue::new_iutf8("value")));
3481
3482 let present_multivalue_mods = ModifyList::new_valid_list(vec![
3484 Modify::Present(Attribute::Class, Value::new_iutf8("test")),
3485 Modify::Present(Attribute::Class, Value::new_iutf8("multi_test")),
3486 ]);
3487
3488 assert!(e.apply_modlist(&present_multivalue_mods).is_ok());
3489
3490 assert!(e.attribute_equality(Attribute::Class, &PartialValue::new_iutf8("test")));
3491 assert!(e.attribute_equality(Attribute::Class, &PartialValue::new_iutf8("multi_test")));
3492
3493 let purge_single_mods = ModifyList::new_valid_list(vec![Modify::Purged(Attribute::Attr)]);
3495
3496 assert!(e.apply_modlist(&purge_single_mods).is_ok());
3497
3498 assert!(!e.attribute_pres(Attribute::Attr));
3499
3500 let purge_multi_mods = ModifyList::new_valid_list(vec![Modify::Purged(Attribute::Class)]);
3501
3502 assert!(e.apply_modlist(&purge_multi_mods).is_ok());
3503
3504 assert!(!e.attribute_pres(Attribute::Class));
3505
3506 let purge_empty_mods = purge_single_mods;
3507
3508 assert!(e.apply_modlist(&purge_empty_mods).is_ok());
3509
3510 let remove_mods = ModifyList::new_valid_list(vec![Modify::Removed(
3512 Attribute::Attr,
3513 PartialValue::new_iutf8("value"),
3514 )]);
3515
3516 assert!(e.apply_modlist(&present_single_mods).is_ok());
3517 assert!(e.attribute_equality(Attribute::Attr, &PartialValue::new_iutf8("value")));
3518 assert!(e.apply_modlist(&remove_mods).is_ok());
3519 assert!(!e.attrs.contains_key(&Attribute::Attr));
3520
3521 let remove_empty_mods = remove_mods;
3522
3523 assert!(e.apply_modlist(&remove_empty_mods).is_ok());
3524
3525 assert!(!e.attrs.contains_key(&Attribute::Attr));
3526 }
3527
3528 #[test]
3529 fn test_entry_idx_diff() {
3530 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3531 e1.add_ava(Attribute::UserId, Value::from("william"));
3532 let mut e1_mod = e1.clone();
3533 e1_mod.add_ava(Attribute::Extra, Value::from("test"));
3534
3535 let e1 = e1.into_sealed_committed();
3536 let e1_mod = e1_mod.into_sealed_committed();
3537
3538 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3539 e2.add_ava(Attribute::UserId, Value::from("claire"));
3540 let e2 = e2.into_sealed_committed();
3541
3542 let mut idxmeta = HashMap::with_capacity(8);
3543 idxmeta.insert(
3544 IdxKey {
3545 attr: Attribute::UserId,
3546 itype: IndexType::Equality,
3547 },
3548 IdxSlope::MAX,
3549 );
3550 idxmeta.insert(
3551 IdxKey {
3552 attr: Attribute::UserId,
3553 itype: IndexType::Presence,
3554 },
3555 IdxSlope::MAX,
3556 );
3557 idxmeta.insert(
3558 IdxKey {
3559 attr: Attribute::Extra,
3560 itype: IndexType::Equality,
3561 },
3562 IdxSlope::MAX,
3563 );
3564
3565 let r1 = Entry::idx_diff(&idxmeta, None, None);
3567 eprintln!("{r1:?}");
3568 assert_eq!(r1, Vec::with_capacity(0));
3569
3570 let mut del_r = Entry::idx_diff(&idxmeta, Some(&e1), None);
3572 del_r.sort_unstable();
3573 eprintln!("del_r {del_r:?}");
3574 assert!(
3575 del_r[0]
3576 == Err((
3577 &Attribute::UserId,
3578 IndexType::Equality,
3579 "william".to_string()
3580 ))
3581 );
3582 assert!(del_r[1] == Err((&Attribute::UserId, IndexType::Presence, "_".to_string())));
3583
3584 let mut add_r = Entry::idx_diff(&idxmeta, None, Some(&e1));
3586 add_r.sort_unstable();
3587 eprintln!("{add_r:?}");
3588 assert!(
3589 add_r[0]
3590 == Ok((
3591 &Attribute::UserId,
3592 IndexType::Equality,
3593 "william".to_string()
3594 ))
3595 );
3596 assert!(add_r[1] == Ok((&Attribute::UserId, IndexType::Presence, "_".to_string())));
3597
3598 let no_r = Entry::idx_diff(&idxmeta, Some(&e1), Some(&e1));
3602 assert!(no_r.is_empty());
3603
3604 let add_a_r = Entry::idx_diff(&idxmeta, Some(&e1), Some(&e1_mod));
3606 assert!(add_a_r[0] == Ok((&Attribute::Extra, IndexType::Equality, "test".to_string())));
3607
3608 let del_a_r = Entry::idx_diff(&idxmeta, Some(&e1_mod), Some(&e1));
3610 assert!(del_a_r[0] == Err((&Attribute::Extra, IndexType::Equality, "test".to_string())));
3611
3612 let mut chg_r = Entry::idx_diff(&idxmeta, Some(&e1), Some(&e2));
3614 chg_r.sort_unstable();
3615 eprintln!("{chg_r:?}");
3616 assert!(
3617 chg_r[1]
3618 == Err((
3619 &Attribute::UserId,
3620 IndexType::Equality,
3621 "william".to_string()
3622 ))
3623 );
3624
3625 assert!(
3626 chg_r[0]
3627 == Ok((
3628 &Attribute::UserId,
3629 IndexType::Equality,
3630 "claire".to_string()
3631 ))
3632 );
3633 }
3634
3635 #[test]
3636 fn test_entry_mask_recycled_ts() {
3637 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3638 e1.add_ava(Attribute::Class, EntryClass::Person.to_value());
3639 let e1 = e1.into_sealed_committed();
3640 assert!(e1.mask_recycled_ts().is_some());
3641
3642 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3643 e2.add_ava(Attribute::Class, EntryClass::Person.to_value());
3644 e2.add_ava(Attribute::Class, EntryClass::Recycled.into());
3645 let e2 = e2.into_sealed_committed();
3646 assert!(e2.mask_recycled_ts().is_none());
3647
3648 let mut e3: Entry<EntryInit, EntryNew> = Entry::new();
3649 e3.add_ava(Attribute::Class, EntryClass::Tombstone.into());
3650 let e3 = e3.into_sealed_committed();
3651 assert!(e3.mask_recycled_ts().is_none());
3652 }
3653
3654 #[test]
3655 fn test_entry_idx_name2uuid_diff() {
3656 let r = Entry::idx_name2uuid_diff(None, None);
3658 assert_eq!(r, (None, None));
3659
3660 {
3662 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3663 e.add_ava(Attribute::Class, EntryClass::Person.to_value());
3664 let e = e.into_sealed_committed();
3665
3666 assert!(Entry::idx_name2uuid_diff(None, Some(&e)) == (Some(Set::new()), None));
3667 }
3668
3669 {
3670 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3671 e.add_ava(Attribute::Class, EntryClass::Person.to_value());
3672 e.add_ava(Attribute::GidNumber, Value::new_uint32(1300));
3673 e.add_ava(Attribute::Name, Value::new_iname("testperson"));
3674 e.add_ava(
3675 Attribute::Spn,
3676 Value::new_spn_str("testperson", "example.com"),
3677 );
3678 e.add_ava(
3679 Attribute::Uuid,
3680 Value::Uuid(uuid!("9fec0398-c46c-4df4-9df5-b0016f7d563f")),
3681 );
3682 let e = e.into_sealed_committed();
3683
3684 assert!(
3686 Entry::idx_name2uuid_diff(None, Some(&e))
3687 == (
3688 Some(btreeset![
3689 "1300".to_string(),
3690 "testperson".to_string(),
3691 "testperson@example.com".to_string()
3692 ]),
3693 None
3694 )
3695 );
3696 assert!(
3699 Entry::idx_name2uuid_diff(Some(&e), None)
3700 == (
3701 None,
3702 Some(btreeset![
3703 "1300".to_string(),
3704 "testperson".to_string(),
3705 "testperson@example.com".to_string()
3706 ])
3707 )
3708 );
3709
3710 assert!(
3712 Entry::idx_name2uuid_diff(Some(&e), Some(&e))
3713 == (Some(Set::new()), Some(Set::new()))
3714 );
3715 }
3716 {
3719 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3720 e1.add_ava(Attribute::Class, EntryClass::Person.to_value());
3721 e1.add_ava(
3722 Attribute::Spn,
3723 Value::new_spn_str("testperson", "example.com"),
3724 );
3725 let e1 = e1.into_sealed_committed();
3726
3727 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3728 e2.add_ava(Attribute::Class, EntryClass::Person.to_value());
3729 e2.add_ava(Attribute::Name, Value::new_iname("testperson"));
3730 e2.add_ava(
3731 Attribute::Spn,
3732 Value::new_spn_str("testperson", "example.com"),
3733 );
3734 let e2 = e2.into_sealed_committed();
3735
3736 assert!(
3738 Entry::idx_name2uuid_diff(Some(&e1), Some(&e2))
3739 == (Some(btreeset!["testperson".to_string()]), Some(Set::new()))
3740 );
3741
3742 assert!(
3744 Entry::idx_name2uuid_diff(Some(&e2), Some(&e1))
3745 == (Some(Set::new()), Some(btreeset!["testperson".to_string()]))
3746 );
3747 }
3748
3749 {
3751 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3752 e1.add_ava(Attribute::Class, EntryClass::Person.to_value());
3753 e1.add_ava(
3754 Attribute::Spn,
3755 Value::new_spn_str("testperson", "example.com"),
3756 );
3757 let e1 = e1.into_sealed_committed();
3758
3759 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3760 e2.add_ava(Attribute::Class, EntryClass::Person.to_value());
3761 e2.add_ava(
3762 Attribute::Spn,
3763 Value::new_spn_str("renameperson", "example.com"),
3764 );
3765 let e2 = e2.into_sealed_committed();
3766
3767 assert!(
3768 Entry::idx_name2uuid_diff(Some(&e1), Some(&e2))
3769 == (
3770 Some(btreeset!["renameperson@example.com".to_string()]),
3771 Some(btreeset!["testperson@example.com".to_string()])
3772 )
3773 );
3774 }
3775 }
3776
3777 #[test]
3778 fn test_entry_idx_uuid2spn_diff() {
3779 assert!(Entry::idx_uuid2spn_diff(None, None).is_none());
3780
3781 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3782 e1.add_ava(
3783 Attribute::Spn,
3784 Value::new_spn_str("testperson", "example.com"),
3785 );
3786 let e1 = e1.into_sealed_committed();
3787
3788 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3789 e2.add_ava(
3790 Attribute::Spn,
3791 Value::new_spn_str("renameperson", "example.com"),
3792 );
3793 let e2 = e2.into_sealed_committed();
3794
3795 assert!(
3796 Entry::idx_uuid2spn_diff(None, Some(&e1))
3797 == Some(Ok(Value::new_spn_str("testperson", "example.com")))
3798 );
3799 assert!(Entry::idx_uuid2spn_diff(Some(&e1), None) == Some(Err(())));
3800 assert!(Entry::idx_uuid2spn_diff(Some(&e1), Some(&e1)).is_none());
3801 assert!(
3802 Entry::idx_uuid2spn_diff(Some(&e1), Some(&e2))
3803 == Some(Ok(Value::new_spn_str("renameperson", "example.com")))
3804 );
3805 }
3806
3807 #[test]
3808 fn test_entry_idx_uuid2rdn_diff() {
3809 assert!(Entry::idx_uuid2rdn_diff(None, None).is_none());
3810
3811 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3812 e1.add_ava(
3813 Attribute::Spn,
3814 Value::new_spn_str("testperson", "example.com"),
3815 );
3816 let e1 = e1.into_sealed_committed();
3817
3818 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3819 e2.add_ava(
3820 Attribute::Spn,
3821 Value::new_spn_str("renameperson", "example.com"),
3822 );
3823 let e2 = e2.into_sealed_committed();
3824
3825 assert!(
3826 Entry::idx_uuid2rdn_diff(None, Some(&e1))
3827 == Some(Ok("spn=testperson@example.com".to_string()))
3828 );
3829 assert!(Entry::idx_uuid2rdn_diff(Some(&e1), None) == Some(Err(())));
3830 assert!(Entry::idx_uuid2rdn_diff(Some(&e1), Some(&e1)).is_none());
3831 assert!(
3832 Entry::idx_uuid2rdn_diff(Some(&e1), Some(&e2))
3833 == Some(Ok("spn=renameperson@example.com".to_string()))
3834 );
3835 }
3836}