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, ValueSetSpn};
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
1213 self.purge_ava(Attribute::CascadeDeleted);
1214 self.purge_ava(Attribute::SourceUuid);
1215 self.purge_ava(Attribute::RecycledDirectMemberOf);
1216
1217 Entry {
1221 valid: self.valid,
1222 state: self.state,
1223 attrs: self.attrs,
1224 }
1225 }
1226}
1227impl Entry<EntryInvalid, EntryNew> {
1230 #[cfg(test)]
1233 pub fn into_init_new(self) -> Entry<EntryInit, EntryNew> {
1234 Entry {
1235 valid: EntryInit,
1236 state: EntryNew,
1237 attrs: self.attrs,
1238 }
1239 }
1240
1241 #[cfg(test)]
1245 pub fn into_valid_new(self) -> Entry<EntryValid, EntryNew> {
1246 let uuid = self.get_uuid().expect("Invalid uuid");
1247 Entry {
1248 valid: EntryValid {
1249 uuid,
1250 ecstate: self.valid.ecstate,
1251 },
1252 state: EntryNew,
1253 attrs: self.attrs,
1254 }
1255 }
1256
1257 #[cfg(test)]
1261 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1262 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
1263 Entry {
1264 valid: EntrySealed {
1265 uuid,
1266 ecstate: self.valid.ecstate,
1267 },
1268 state: EntryCommitted { id: 0 },
1269 attrs: self.attrs,
1270 }
1271 }
1272
1273 #[cfg(test)]
1277 pub fn into_valid_committed(self) -> Entry<EntryValid, EntryCommitted> {
1278 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
1279 Entry {
1280 valid: EntryValid {
1281 uuid,
1282 ecstate: self.valid.ecstate,
1283 },
1284 state: EntryCommitted { id: 0 },
1285 attrs: self.attrs,
1286 }
1287 }
1288}
1289
1290impl Entry<EntryInvalid, EntryCommitted> {
1291 #[cfg(test)]
1295 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1296 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
1297 Entry {
1298 valid: EntrySealed {
1299 uuid,
1300 ecstate: self.valid.ecstate,
1301 },
1302 state: self.state,
1303 attrs: self.attrs,
1304 }
1305 }
1306}
1307
1308impl Entry<EntrySealed, EntryNew> {
1309 #[cfg(test)]
1313 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1314 Entry {
1315 valid: self.valid,
1316 state: EntryCommitted { id: 0 },
1317 attrs: self.attrs,
1318 }
1319 }
1320
1321 pub fn into_sealed_committed_id(self, id: u64) -> Entry<EntrySealed, EntryCommitted> {
1324 Entry {
1325 valid: self.valid,
1326 state: EntryCommitted { id },
1327 attrs: self.attrs,
1328 }
1329 }
1330
1331 pub fn compare(&self, rhs: &Entry<EntrySealed, EntryNew>) -> bool {
1332 compare_attrs(&self.attrs, &rhs.attrs)
1333 }
1334}
1335
1336type IdxDiff<'a> =
1337 Vec<Result<(&'a Attribute, IndexType, String), (&'a Attribute, IndexType, String)>>;
1338
1339impl<VALID> Entry<VALID, EntryCommitted> {
1340 pub fn get_id(&self) -> u64 {
1342 self.state.id
1343 }
1344}
1345
1346impl<STATE> Entry<EntrySealed, STATE> {
1347 pub fn into_init(self) -> Entry<EntryInit, STATE> {
1348 Entry {
1349 valid: EntryInit,
1350 state: self.state,
1351 attrs: self.attrs,
1352 }
1353 }
1354}
1355
1356impl Entry<EntrySealed, EntryCommitted> {
1357 #[cfg(test)]
1358 pub(crate) fn get_last_changed(&self) -> Cid {
1359 self.valid.ecstate.get_max_cid().clone()
1360 }
1361
1362 #[cfg(test)]
1364 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1365 self
1367 }
1368
1369 pub(crate) fn stub_sealed_committed_id(
1370 id: u64,
1371 ctx_ent: &EntryIncrementalNew,
1372 ) -> EntrySealedCommitted {
1373 let uuid = ctx_ent.get_uuid();
1374 let ecstate = ctx_ent.stub_ecstate();
1375
1376 Entry {
1377 valid: EntrySealed { uuid, ecstate },
1378 state: EntryCommitted { id },
1379 attrs: Default::default(),
1380 }
1381 }
1382
1383 pub fn insert_claim(&mut self, value: &str) {
1386 self.add_ava_int(Attribute::Claim, Value::new_iutf8(value));
1387 }
1388
1389 pub fn compare(&self, rhs: &Entry<EntrySealed, EntryCommitted>) -> bool {
1390 compare_attrs(&self.attrs, &rhs.attrs)
1391 }
1392
1393 pub fn to_dbentry(&self) -> DbEntry {
1395 DbEntry {
1398 ent: DbEntryVers::V3 {
1399 changestate: self.valid.ecstate.to_db_changestate(),
1400 attrs: self
1401 .attrs
1402 .iter()
1403 .map(|(k, vs)| {
1404 let dbvs: DbValueSetV2 = vs.to_db_valueset_v2();
1405 (k.clone(), dbvs)
1406 })
1407 .collect(),
1408 },
1409 }
1410 }
1411
1412 #[inline]
1413 fn get_name2uuid_cands(&self) -> Set<String> {
1416 let cands = [Attribute::Spn, Attribute::Name, Attribute::GidNumber];
1422 cands
1423 .iter()
1424 .filter_map(|cand| {
1425 self.attrs
1426 .get(cand)
1427 .map(|vs| vs.to_proto_string_clone_iter())
1428 })
1429 .flatten()
1430 .collect()
1431 }
1432
1433 #[inline]
1434 fn get_externalid2uuid(&self) -> Option<String> {
1437 self.attrs
1438 .get(&Attribute::SyncExternalId)
1439 .and_then(|vs| vs.to_proto_string_single())
1440 }
1441
1442 #[inline]
1443 pub(crate) fn get_uuid2spn(&self) -> Value {
1446 self.attrs
1447 .get(&Attribute::Spn)
1448 .and_then(|vs| vs.to_value_single())
1449 .or_else(|| {
1450 self.attrs
1451 .get(&Attribute::Name)
1452 .and_then(|vs| vs.to_value_single())
1453 })
1454 .unwrap_or_else(|| Value::Uuid(self.get_uuid()))
1455 }
1456
1457 #[inline]
1458 pub(crate) fn get_uuid2rdn(&self) -> String {
1462 self.attrs
1463 .get(&Attribute::Spn)
1464 .and_then(|vs| vs.to_proto_string_single().map(|v| format!("spn={v}")))
1465 .or_else(|| {
1466 self.attrs
1467 .get(&Attribute::Name)
1468 .and_then(|vs| vs.to_proto_string_single().map(|v| format!("name={v}")))
1469 })
1470 .unwrap_or_else(|| format!("uuid={}", self.get_uuid().as_hyphenated()))
1471 }
1472
1473 pub(crate) fn idx_name2uuid_diff(
1476 pre: Option<&Self>,
1477 post: Option<&Self>,
1478 ) -> (
1479 Option<Set<String>>,
1481 Option<Set<String>>,
1483 ) {
1484 match (pre, post) {
1486 (None, None) => {
1487 (None, None)
1489 }
1490 (None, Some(b)) => {
1491 (Some(b.get_name2uuid_cands()), None)
1494 }
1495 (Some(a), None) => {
1496 (None, Some(a.get_name2uuid_cands()))
1498 }
1499 (Some(a), Some(b)) => {
1500 let pre_set = a.get_name2uuid_cands();
1501 let post_set = b.get_name2uuid_cands();
1502
1503 let add_set: Set<_> = post_set.difference(&pre_set).cloned().collect();
1505 let rem_set: Set<_> = pre_set.difference(&post_set).cloned().collect();
1507 (Some(add_set), Some(rem_set))
1508 }
1509 }
1510 }
1511
1512 pub(crate) fn idx_externalid2uuid_diff(
1514 pre: Option<&Self>,
1515 post: Option<&Self>,
1516 ) -> (Option<String>, Option<String>) {
1517 match (pre, post) {
1518 (None, None) => {
1519 (None, None)
1521 }
1522 (None, Some(b)) => {
1523 (b.get_externalid2uuid(), None)
1525 }
1526 (Some(a), None) => {
1527 (None, a.get_externalid2uuid())
1529 }
1530 (Some(a), Some(b)) => {
1531 let ia = a.get_externalid2uuid();
1532 let ib = b.get_externalid2uuid();
1533 if ia != ib {
1534 (ib, ia)
1537 } else {
1538 (None, None)
1540 }
1541 }
1542 }
1543 }
1544
1545 pub(crate) fn idx_uuid2spn_diff(
1548 pre: Option<&Self>,
1549 post: Option<&Self>,
1550 ) -> Option<Result<Value, ()>> {
1551 match (pre, post) {
1552 (None, None) => {
1553 None
1555 }
1556 (None, Some(b)) => {
1557 Some(Ok(b.get_uuid2spn()))
1559 }
1560 (Some(_a), None) => {
1561 Some(Err(()))
1563 }
1564 (Some(a), Some(b)) => {
1565 let ia = a.get_uuid2spn();
1566 let ib = b.get_uuid2spn();
1567 if ia != ib {
1568 Some(Ok(ib))
1570 } else {
1571 None
1573 }
1574 }
1575 }
1576 }
1577
1578 pub(crate) fn idx_uuid2rdn_diff(
1581 pre: Option<&Self>,
1582 post: Option<&Self>,
1583 ) -> Option<Result<String, ()>> {
1584 match (pre, post) {
1585 (None, None) => {
1586 None
1588 }
1589 (None, Some(b)) => {
1590 Some(Ok(b.get_uuid2rdn()))
1592 }
1593 (Some(_a), None) => {
1594 Some(Err(()))
1596 }
1597 (Some(a), Some(b)) => {
1598 let ia = a.get_uuid2rdn();
1599 let ib = b.get_uuid2rdn();
1600 if ia != ib {
1601 Some(Ok(ib))
1603 } else {
1604 None
1606 }
1607 }
1608 }
1609 }
1610
1611 pub(crate) fn idx_diff<'a>(
1614 idxmeta: &'a HashMap<IdxKey, IdxSlope>,
1615 pre: Option<&Self>,
1616 post: Option<&Self>,
1617 ) -> IdxDiff<'a> {
1618 match (pre, post) {
1623 (None, None) => {
1624 Vec::with_capacity(0)
1626 }
1627 (Some(pre_e), None) => {
1628 idxmeta
1630 .keys()
1631 .flat_map(|ikey| {
1632 match pre_e.get_ava_set(&ikey.attr) {
1633 None => Vec::with_capacity(0),
1634 Some(vs) => {
1635 let changes: Vec<Result<_, _>> = match ikey.itype {
1636 IndexType::Equality => {
1637 vs.generate_idx_eq_keys()
1639 .into_iter()
1640 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1641 .collect()
1642 }
1643 IndexType::Presence => {
1644 vec![Err((&ikey.attr, ikey.itype, "_".to_string()))]
1645 }
1646 IndexType::SubString => vs
1647 .generate_idx_sub_keys()
1648 .into_iter()
1649 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1650 .collect(),
1651 IndexType::Ordering => vs
1652 .generate_idx_ord_keys()
1653 .into_iter()
1654 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1655 .collect(),
1656 };
1657 changes
1658 }
1659 }
1660 })
1661 .collect()
1662 }
1663 (None, Some(post_e)) => {
1664 idxmeta
1666 .keys()
1667 .flat_map(|ikey| {
1668 match post_e.get_ava_set(&ikey.attr) {
1669 None => Vec::with_capacity(0),
1670 Some(vs) => {
1671 let changes: Vec<Result<_, _>> = match ikey.itype {
1672 IndexType::Equality => vs
1673 .generate_idx_eq_keys()
1674 .into_iter()
1675 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1676 .collect(),
1677 IndexType::Presence => {
1678 vec![Ok((&ikey.attr, ikey.itype, "_".to_string()))]
1679 }
1680 IndexType::SubString => vs
1681 .generate_idx_sub_keys()
1682 .into_iter()
1683 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1684 .collect(),
1685 IndexType::Ordering => vs
1686 .generate_idx_ord_keys()
1687 .into_iter()
1688 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1689 .collect(),
1690 };
1691 changes
1694 }
1695 }
1696 })
1697 .collect()
1698 }
1699 (Some(pre_e), Some(post_e)) => {
1700 assert_eq!(pre_e.state.id, post_e.state.id);
1701 idxmeta
1702 .keys()
1703 .flat_map(|ikey| {
1704 match (
1705 pre_e.get_ava_set(&ikey.attr),
1706 post_e.get_ava_set(&ikey.attr),
1707 ) {
1708 (None, None) => {
1709 Vec::with_capacity(0)
1711 }
1712 (Some(pre_vs), None) => {
1713 let changes: Vec<Result<_, _>> = match ikey.itype {
1715 IndexType::Equality => {
1716 pre_vs
1719 .generate_idx_eq_keys()
1720 .into_iter()
1721 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1722 .collect()
1723 }
1724 IndexType::Presence => {
1725 vec![Err((&ikey.attr, ikey.itype, "_".to_string()))]
1726 }
1727 IndexType::SubString => pre_vs
1728 .generate_idx_sub_keys()
1729 .into_iter()
1730 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1731 .collect(),
1732 IndexType::Ordering => pre_vs
1733 .generate_idx_ord_keys()
1734 .into_iter()
1735 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1736 .collect(),
1737 };
1738 changes
1739 }
1740 (None, Some(post_vs)) => {
1741 let changes: Vec<Result<_, _>> = match ikey.itype {
1743 IndexType::Equality => {
1744 post_vs
1747 .generate_idx_eq_keys()
1748 .into_iter()
1749 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1750 .collect()
1751 }
1752 IndexType::Presence => {
1753 vec![Ok((&ikey.attr, ikey.itype, "_".to_string()))]
1754 }
1755 IndexType::SubString => post_vs
1756 .generate_idx_sub_keys()
1757 .into_iter()
1758 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1759 .collect(),
1760 IndexType::Ordering => post_vs
1761 .generate_idx_ord_keys()
1762 .into_iter()
1763 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1764 .collect(),
1765 };
1766 changes
1767 }
1768 (Some(pre_vs), Some(post_vs)) => {
1769 let (mut pre_idx_keys, mut post_idx_keys) = match ikey.itype {
1771 IndexType::Equality => (
1772 pre_vs.generate_idx_eq_keys(),
1773 post_vs.generate_idx_eq_keys(),
1774 ),
1775 IndexType::Presence => {
1776 (Vec::with_capacity(0), Vec::with_capacity(0))
1778 }
1779 IndexType::SubString => (
1780 pre_vs.generate_idx_sub_keys(),
1781 post_vs.generate_idx_sub_keys(),
1782 ),
1783 IndexType::Ordering => (
1784 pre_vs.generate_idx_ord_keys(),
1785 post_vs.generate_idx_ord_keys(),
1786 ),
1787 };
1788
1789 let sz = if pre_idx_keys.len() > post_idx_keys.len() {
1790 pre_idx_keys.len()
1791 } else {
1792 post_idx_keys.len()
1793 };
1794
1795 let mut added_vs = Vec::with_capacity(sz);
1796 let mut removed_vs = Vec::with_capacity(sz);
1797
1798 if sz > 0 {
1799 pre_idx_keys.sort_unstable();
1800 post_idx_keys.sort_unstable();
1801
1802 let mut pre_iter = pre_idx_keys.iter();
1803 let mut post_iter = post_idx_keys.iter();
1804
1805 let mut pre = pre_iter.next();
1806 let mut post = post_iter.next();
1807
1808 loop {
1809 match (pre, post) {
1810 (Some(a), Some(b)) => {
1811 match a.cmp(b) {
1812 Ordering::Less => {
1813 removed_vs.push(a.clone());
1814 pre = pre_iter.next();
1815 }
1816 Ordering::Equal => {
1817 pre = pre_iter.next();
1819 post = post_iter.next();
1820 }
1821 Ordering::Greater => {
1822 added_vs.push(b.clone());
1823 post = post_iter.next();
1824 }
1825 }
1826 }
1827 (Some(a), None) => {
1828 removed_vs.push(a.clone());
1829 pre = pre_iter.next();
1830 }
1831 (None, Some(b)) => {
1832 added_vs.push(b.clone());
1833 post = post_iter.next();
1834 }
1835 (None, None) => {
1836 break;
1837 }
1838 }
1839 }
1840 } let mut diff =
1843 Vec::with_capacity(removed_vs.len() + added_vs.len());
1844
1845 match ikey.itype {
1846 IndexType::SubString
1847 | IndexType::Ordering
1848 | IndexType::Equality => {
1849 removed_vs
1850 .into_iter()
1851 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1852 .for_each(|v| diff.push(v));
1853 added_vs
1854 .into_iter()
1855 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1856 .for_each(|v| diff.push(v));
1857 }
1858 IndexType::Presence => {
1859 }
1861 };
1862 diff
1864 }
1865 }
1866 })
1867 .collect()
1868 }
1870 }
1871 }
1872
1873 pub fn from_dbentry(db_e: DbEntry, id: u64) -> Option<Self> {
1874 let (attrs, ecstate) = match db_e.ent {
1877 DbEntryVers::V3 { changestate, attrs } => {
1878 let ecstate = EntryChangeState::from_db_changestate(changestate);
1879
1880 let r_attrs = attrs
1881 .into_iter()
1882 .filter(|(_k, vs)| !vs.is_empty())
1884 .map(|(k, dbvs)| {
1885 valueset::from_db_valueset_v2(dbvs)
1886 .map(|vs: ValueSet| (k, vs))
1887 .map_err(|e| {
1888 error!(?e, "from_dbentry failed");
1889 })
1890 })
1891 .collect::<Result<Eattrs, ()>>()
1892 .ok()?;
1893
1894 (r_attrs, ecstate)
1895 }
1896 };
1897
1898 let uuid = attrs
1899 .get(&Attribute::Uuid)
1900 .and_then(|vs| vs.to_uuid_single())?;
1901
1902 Some(Entry {
1903 valid: EntrySealed { uuid, ecstate },
1904 state: EntryCommitted { id },
1905 attrs,
1906 })
1907 }
1908
1909 #[cfg(test)]
1917 pub(crate) fn into_reduced(self) -> Entry<EntryReduced, EntryCommitted> {
1918 Entry {
1919 valid: EntryReduced {
1920 uuid: self.valid.uuid,
1921 effective_access: None,
1922 },
1923 state: self.state,
1924 attrs: self.attrs,
1925 }
1926 }
1927
1928 pub fn reduce_attributes(
1931 &self,
1932 allowed_attrs: &BTreeSet<Attribute>,
1933 effective_access: Option<Box<AccessEffectivePermission>>,
1934 ) -> Entry<EntryReduced, EntryCommitted> {
1935 let f_attrs: Map<_, _> = self
1937 .attrs
1938 .iter()
1939 .filter_map(|(k, v)| {
1940 if allowed_attrs.contains(k) {
1941 Some((k.clone(), v.clone()))
1942 } else {
1943 None
1944 }
1945 })
1946 .collect();
1947
1948 let valid = EntryReduced {
1949 uuid: self.valid.uuid,
1950 effective_access,
1951 };
1952 let state = self.state.clone();
1953
1954 Entry {
1955 valid,
1956 state,
1957 attrs: f_attrs,
1958 }
1959 }
1960
1961 pub fn to_tombstone(&self, cid: Cid) -> Entry<EntryInvalid, EntryCommitted> {
1963 let mut ecstate = self.valid.ecstate.clone();
1964 let mut attrs_new: Eattrs = Map::new();
1966
1967 let class_ava = vs_iutf8![EntryClass::Object.into(), EntryClass::Tombstone.into()];
1968 let last_mod_ava = vs_cid![cid.clone()];
1969 let created_ava = vs_cid![cid.clone()];
1970
1971 attrs_new.insert(Attribute::Uuid, vs_uuid![self.get_uuid()]);
1972 attrs_new.insert(Attribute::Class, class_ava);
1973 attrs_new.insert(Attribute::LastModifiedCid, last_mod_ava);
1974 attrs_new.insert(Attribute::CreatedAtCid, created_ava);
1975
1976 ecstate.tombstone(&cid);
1978
1979 Entry {
1980 valid: EntryInvalid { cid, ecstate },
1981 state: self.state.clone(),
1982 attrs: attrs_new,
1983 }
1984 }
1985
1986 pub fn into_valid(self, ecstate: EntryChangeState) -> Entry<EntryValid, EntryCommitted> {
1988 Entry {
1989 valid: EntryValid {
1990 uuid: self.valid.uuid,
1991 ecstate,
1992 },
1993 state: self.state,
1994 attrs: self.attrs,
1995 }
1996 }
1997
1998 pub fn verify(
1999 &self,
2000 schema: &dyn SchemaTransaction,
2001 results: &mut Vec<Result<(), ConsistencyError>>,
2002 ) {
2003 self.valid
2004 .ecstate
2005 .verify(schema, &self.attrs, self.state.id, results);
2006 }
2007}
2008
2009impl<STATE> Entry<EntryValid, STATE> {
2010 fn validate(&self, schema: &dyn SchemaTransaction) -> Result<(), SchemaError> {
2011 let schema_classes = schema.get_classes();
2012 let schema_attributes = schema.get_attributes();
2013
2014 trace!(?self.attrs, "Entry::validate -> target");
2016
2017 if !self.attribute_pres(Attribute::Class) {
2019 return Err(SchemaError::NoClassFound);
2021 }
2022
2023 if self.attribute_equality(Attribute::Class, &EntryClass::Conflict.into()) {
2024 trace!("Skipping schema validation on conflict entry");
2026 return Ok(());
2027 };
2028
2029 let recycled = self.attribute_equality(Attribute::Class, &EntryClass::Recycled.into());
2031
2032 let extensible =
2035 self.attribute_equality(Attribute::Class, &EntryClass::ExtensibleObject.into());
2036
2037 let entry_classes = self.get_ava_set(Attribute::Class).ok_or_else(|| {
2038 admin_debug!("Attribute '{}' missing from entry", Attribute::Class);
2039 SchemaError::NoClassFound
2040 })?;
2041 let mut invalid_classes = Vec::with_capacity(0);
2042
2043 let mut classes: Vec<&SchemaClass> = Vec::with_capacity(entry_classes.len());
2044
2045 let entry_classes = if let Some(ec) = entry_classes.as_iutf8_set() {
2048 ec.iter()
2049 .for_each(|s| match schema_classes.get(s.as_str()) {
2050 Some(x) => classes.push(x),
2051 None => {
2052 admin_debug!("invalid class: {:?}", s);
2053 invalid_classes.push(s.to_string())
2054 }
2055 });
2056 ec
2057 } else {
2058 admin_debug!("corrupt class attribute");
2059 return Err(SchemaError::NoClassFound);
2060 };
2061
2062 if !invalid_classes.is_empty() {
2063 return Err(SchemaError::InvalidClass(invalid_classes));
2064 };
2065
2066 let supplements_classes: Vec<_> = classes
2070 .iter()
2071 .flat_map(|cls| cls.systemsupplements.iter().chain(cls.supplements.iter()))
2072 .collect();
2073
2074 let valid_supplements = if supplements_classes.is_empty() {
2076 true
2078 } else {
2079 supplements_classes
2080 .iter()
2081 .any(|class| entry_classes.contains(class.as_str()))
2082 };
2083
2084 if !valid_supplements {
2085 warn!(
2086 "Validation error, the following possible supplement classes are missing - {:?}",
2087 supplements_classes
2088 );
2089 let supplements_classes = supplements_classes.iter().map(|s| s.to_string()).collect();
2090 return Err(SchemaError::SupplementsNotSatisfied(supplements_classes));
2091 }
2092
2093 let excludes_classes: Vec<_> = classes
2094 .iter()
2095 .flat_map(|cls| cls.systemexcludes.iter().chain(cls.excludes.iter()))
2096 .collect();
2097
2098 let mut invalid_excludes = Vec::with_capacity(0);
2099
2100 excludes_classes.iter().for_each(|class| {
2101 if entry_classes.contains(class.as_str()) {
2102 invalid_excludes.push(class.to_string())
2103 }
2104 });
2105
2106 if !invalid_excludes.is_empty() {
2107 admin_warn!(
2108 "Validation error, the following excluded classes are present - {:?}",
2109 invalid_excludes
2110 );
2111 return Err(SchemaError::ExcludesNotSatisfied(invalid_excludes));
2112 }
2113
2114 let must: Result<Vec<&SchemaAttribute>, _> = classes
2127 .iter()
2128 .flat_map(|cls| cls.systemmust.iter().chain(cls.must.iter()))
2130 .map(|s| {
2131 schema_attributes.get(s).ok_or(SchemaError::Corrupted)
2134 })
2135 .collect();
2136
2137 let must = must?;
2138
2139 let mut missing_must = Vec::with_capacity(0);
2142 for attr in must.iter() {
2143 let avas = self.get_ava_set(&attr.name);
2144 if avas.is_none() {
2145 missing_must.push(attr.name.clone());
2146 }
2147 }
2148
2149 if !missing_must.is_empty() {
2150 admin_warn!(
2151 "Validation error, the following required ({}) (must) attributes are missing - {:?}",
2152 self.get_display_id(), missing_must
2153 );
2154 if !recycled {
2160 return Err(SchemaError::MissingMustAttribute(missing_must));
2161 }
2162 }
2163
2164 if extensible {
2165 self.attrs.iter().try_for_each(|(attr_name, avas)| {
2166 match schema_attributes.get(attr_name) {
2167 Some(a_schema) => {
2168 if a_schema.phantom {
2171 admin_warn!(
2172 "Rejecting attempt to add phantom attribute to extensible object: {}",
2173 attr_name
2174 );
2175 Err(SchemaError::PhantomAttribute(attr_name.to_string()))
2176 } else {
2177 a_schema.validate_ava(attr_name, avas)
2178 }
2180 }
2181 None => {
2182 admin_error!(
2183 "Invalid Attribute {}, undefined in schema_attributes",
2184 attr_name.to_string()
2185 );
2186 Err(SchemaError::InvalidAttribute(
2187 attr_name.to_string()
2188 ))
2189 }
2190 }
2191 })?;
2192 } else {
2193 let may: Result<Map<&Attribute, &SchemaAttribute>, _> = classes
2201 .iter()
2202 .flat_map(|cls| {
2204 trace!(?cls);
2205 cls.systemmust
2206 .iter()
2207 .chain(cls.must.iter())
2208 .chain(cls.systemmay.iter())
2209 .chain(cls.may.iter())
2210 })
2211 .map(|s| {
2212 Ok((s, schema_attributes.get(s).ok_or(SchemaError::Corrupted)?))
2215 })
2216 .collect();
2217
2218 let may = may?;
2219
2220 self.attrs.iter().try_for_each(|(attr_name, avas)| {
2229 match may.get(attr_name) {
2230 Some(a_schema) => {
2231 a_schema.validate_ava(attr_name, avas)
2234 }
2236 None => {
2237 admin_error!(
2238 "{} {} - not found in the list of valid attributes for this set of classes {:?} - valid attributes are {:?}",
2239
2240 attr_name.as_str(),
2241 self.get_display_id(),
2242 entry_classes.iter().collect::<Vec<_>>(),
2243 may.keys().collect::<Vec<_>>()
2244 );
2245 Err(SchemaError::AttributeNotValidForClass(
2246 attr_name.to_string()
2247 ))
2248 }
2249 }
2250 })?;
2251 }
2252
2253 Ok(())
2255 }
2256
2257 pub fn seal(mut self, schema: &dyn SchemaTransaction) -> Entry<EntrySealed, STATE> {
2258 let EntryValid { uuid, mut ecstate } = self.valid;
2259
2260 ecstate.retain(|k, _| schema.is_replicated(k));
2264
2265 let last_mod_cid = ecstate.get_max_cid();
2267 let cv = vs_cid![last_mod_cid.clone()];
2268 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
2269
2270 let create_at_cid = ecstate.at();
2274 let cv = vs_cid![create_at_cid.clone()];
2275 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
2276
2277 Entry {
2278 valid: EntrySealed { uuid, ecstate },
2279 state: self.state,
2280 attrs: self.attrs,
2281 }
2282 }
2283
2284 pub fn get_uuid(&self) -> Uuid {
2285 self.valid.uuid
2286 }
2287}
2288
2289impl<STATE> GetUuid for Entry<EntrySealed, STATE>
2290where
2291 STATE: Clone,
2292{
2293 fn get_uuid(&self) -> Uuid {
2294 self.valid.uuid
2295 }
2296}
2297
2298impl<STATE> Entry<EntrySealed, STATE>
2299where
2300 STATE: Clone,
2301{
2302 pub fn invalidate(mut self, cid: Cid, trim_cid: &Cid) -> Entry<EntryInvalid, STATE> {
2303 for vs in self.attrs.values_mut() {
2305 vs.trim(trim_cid);
2306 }
2307
2308 let last_mod_cid = self.valid.ecstate.get_max_cid();
2316 let cv = vs_cid![last_mod_cid.clone()];
2317 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
2318
2319 let create_at_cid = self.valid.ecstate.at();
2320 let cv = vs_cid![create_at_cid.clone()];
2321 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
2322
2323 Entry {
2324 valid: EntryInvalid {
2325 cid,
2326 ecstate: self.valid.ecstate,
2327 },
2328 state: self.state,
2329 attrs: self.attrs,
2330 }
2331 }
2332
2333 pub fn get_uuid(&self) -> Uuid {
2334 self.valid.uuid
2335 }
2336
2337 pub fn get_changestate(&self) -> &EntryChangeState {
2338 &self.valid.ecstate
2339 }
2340
2341 pub(crate) fn entry_changed_excluding_attribute<A: AsRef<Attribute>>(
2345 &self,
2346 attr: A,
2347 cid: &Cid,
2348 ) -> bool {
2349 let attr_ref = attr.as_ref();
2350
2351 use crate::repl::entry::State;
2352
2353 match self.get_changestate().current() {
2354 State::Live { at: _, changes } => {
2355 changes.iter().any(|(change_attr, change_id)| {
2356 change_id >= cid &&
2357 *change_attr != *attr_ref &&
2358 *change_attr != Attribute::LastModifiedCid
2360 })
2361 }
2362 State::Tombstone { at } => at == cid,
2363 }
2364 }
2365
2366 #[cfg(test)]
2370 pub(crate) fn into_invalid(mut self) -> Entry<EntryInvalid, STATE> {
2371 let cid = Cid::new_zero();
2372 self.set_last_changed(cid.clone());
2373
2374 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
2375
2376 Entry {
2377 valid: EntryInvalid { cid, ecstate },
2378 state: self.state,
2379 attrs: self.attrs,
2380 }
2381 }
2382}
2383
2384impl GetUuid for Entry<EntryReduced, EntryCommitted> {
2385 fn get_uuid(&self) -> Uuid {
2386 self.valid.uuid
2387 }
2388}
2389
2390impl Entry<EntryReduced, EntryCommitted> {
2391 pub fn get_uuid(&self) -> Uuid {
2392 self.valid.uuid
2393 }
2394
2395 pub fn to_pe(&self, qs: &mut QueryServerReadTransaction) -> Result<ProtoEntry, OperationError> {
2397 let attrs: Result<_, _> = self
2399 .attrs
2400 .iter()
2401 .map(|(k, vs)| qs.resolve_valueset(vs).map(|pvs| (k.to_string(), pvs)))
2402 .collect();
2403 Ok(ProtoEntry { attrs: attrs? })
2404 }
2405
2406 pub fn to_scim_kanidm<'a, TXN>(
2407 &self,
2408 read_txn: &mut TXN,
2409 ) -> Result<ScimEntryKanidm, OperationError>
2410 where
2411 TXN: QueryServerTransaction<'a>,
2412 {
2413 let result: Result<BTreeMap<Attribute, ScimValueKanidm>, OperationError> = self
2414 .attrs
2415 .iter()
2416 .filter(|(k, _vs)| **k != Attribute::Uuid)
2418 .filter_map(|(k, vs)| {
2419 let opt_resolve_status = vs.to_scim_value();
2420 let res_opt_scim_value = match opt_resolve_status {
2421 None => Ok(None),
2422 Some(ScimResolveStatus::Resolved(scim_value_kani)) => Ok(Some(scim_value_kani)),
2423 Some(ScimResolveStatus::NeedsResolution(scim_value_interim)) => {
2424 read_txn.resolve_scim_interim(scim_value_interim)
2425 }
2426 };
2427 res_opt_scim_value
2428 .transpose()
2429 .map(|scim_res| scim_res.map(|scim_value| (k.clone(), scim_value)))
2430 })
2431 .collect();
2432
2433 let attrs = result?;
2434
2435 let ext_access_check = self.valid.effective_access.as_ref().map(|eff_acc| {
2436 let ident = eff_acc.ident;
2437 let delete = eff_acc.delete;
2438 let search = (&eff_acc.search).into();
2439 let modify_present = (&eff_acc.modify_pres).into();
2440 let modify_remove = (&eff_acc.modify_rem).into();
2441
2442 ScimEffectiveAccess {
2443 ident,
2444 delete,
2445 search,
2446 modify_present,
2447 modify_remove,
2448 }
2449 });
2450
2451 let id = self.get_uuid();
2452
2453 let schemas = Vec::with_capacity(0);
2456
2457 Ok(ScimEntryKanidm {
2458 header: ScimEntryHeader {
2459 schemas,
2460 id,
2461 external_id: None,
2463 meta: None,
2466 },
2467 ext_access_check,
2468 attrs,
2469 })
2470 }
2471
2472 pub fn to_ldap(
2474 &self,
2475 qs: &mut QueryServerReadTransaction,
2476 basedn: &str,
2477 all_attrs: bool,
2479 l_attrs: &[String],
2482 ) -> Result<LdapSearchResultEntry, OperationError> {
2483 let rdn = qs.uuid_to_rdn(self.get_uuid())?;
2484
2485 let dn = format!("{rdn},{basedn}");
2486
2487 let attr_map: Result<Map<&str, Vec<Vec<u8>>>, _> = self
2492 .attrs
2493 .iter()
2494 .map(|(k, vs)| {
2495 qs.resolve_valueset_ldap(vs, basedn)
2496 .map(|pvs| (k.as_str(), pvs))
2497 })
2498 .collect();
2499 let attr_map = attr_map?;
2500
2501 let attr_names: Vec<(&str, &str)> = if all_attrs {
2504 self.attrs
2506 .keys()
2507 .map(|k| (k.as_str(), k.as_str()))
2508 .chain(
2509 l_attrs
2510 .iter()
2511 .map(|k| (k.as_str(), ldap_vattr_map(k.as_str()).unwrap_or(k.as_str()))),
2512 )
2513 .collect()
2514 } else {
2515 l_attrs
2517 .iter()
2518 .map(|k| (k.as_str(), ldap_vattr_map(k.as_str()).unwrap_or(k.as_str())))
2519 .collect()
2520 };
2521
2522 let attributes: Vec<_> = attr_names
2524 .into_iter()
2525 .filter_map(|(ldap_a, kani_a)| {
2526 match ldap_a {
2528 LDAP_ATTR_DN => Some(LdapPartialAttribute {
2529 atype: LDAP_ATTR_DN.to_string(),
2530 vals: vec![dn.as_bytes().to_vec()],
2531 }),
2532 LDAP_ATTR_ENTRYDN => Some(LdapPartialAttribute {
2533 atype: LDAP_ATTR_ENTRYDN.to_string(),
2534 vals: vec![dn.as_bytes().to_vec()],
2535 }),
2536 LDAP_ATTR_MAIL_PRIMARY | LDAP_ATTR_EMAIL_PRIMARY => {
2537 attr_map.get(kani_a).map(|pvs| LdapPartialAttribute {
2538 atype: ldap_a.to_string(),
2539 vals: pvs
2540 .first()
2541 .map(|first| vec![first.clone()])
2542 .unwrap_or_default(),
2543 })
2544 }
2545 LDAP_ATTR_MAIL_ALTERNATIVE | LDAP_ATTR_EMAIL_ALTERNATIVE => {
2546 attr_map.get(kani_a).map(|pvs| LdapPartialAttribute {
2547 atype: ldap_a.to_string(),
2548 vals: pvs
2549 .split_first()
2550 .map(|(_, rest)| rest.to_vec())
2551 .unwrap_or_default(),
2552 })
2553 }
2554 _ => attr_map.get(kani_a).map(|pvs| LdapPartialAttribute {
2555 atype: ldap_a.to_string(),
2556 vals: pvs.clone(),
2557 }),
2558 }
2559 })
2560 .collect();
2561
2562 Ok(LdapSearchResultEntry { dn, attributes })
2563 }
2564}
2565
2566impl<VALID, STATE> Entry<VALID, STATE> {
2568 fn add_ava_int(&mut self, attr: Attribute, value: Value) -> bool {
2573 if let Some(vs) = self.attrs.get_mut(&attr) {
2574 let r = vs.insert_checked(value);
2575 debug_assert!(r.is_ok());
2576 r.unwrap_or(false)
2578 } else {
2579 #[allow(clippy::expect_used)]
2580 let vs = valueset::from_value_iter(std::iter::once(value))
2581 .expect("Unable to fail - non-zero iter, and single value type!");
2582 self.attrs.insert(attr, vs);
2583 true
2585 }
2586 }
2588
2589 fn set_ava_iter_int<T>(&mut self, attr: Attribute, iter: T)
2591 where
2592 T: IntoIterator<Item = Value>,
2593 {
2594 let Ok(vs) = valueset::from_value_iter(iter.into_iter()) else {
2595 trace!("set_ava_iter_int - empty from_value_iter, skipping");
2596 return;
2597 };
2598
2599 if let Some(existing_vs) = self.attrs.get_mut(&attr) {
2600 let _ = existing_vs.merge(&vs);
2602 } else {
2603 self.attrs.insert(attr, vs);
2605 }
2606 }
2607
2608 #[cfg(test)]
2610 fn set_last_changed(&mut self, cid: Cid) {
2611 let cv = vs_cid![cid.clone()];
2612 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
2613 let cv = vs_cid![cid];
2614 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
2615 }
2616
2617 pub(crate) fn get_display_id(&self) -> String {
2618 self.attrs
2619 .get(&Attribute::Spn)
2620 .and_then(|vs| vs.to_value_single())
2621 .or_else(|| {
2622 self.attrs
2623 .get(&Attribute::Name)
2624 .and_then(|vs| vs.to_value_single())
2625 })
2626 .or_else(|| {
2627 self.attrs
2628 .get(&Attribute::Uuid)
2629 .and_then(|vs| vs.to_value_single())
2630 })
2631 .map(|value| value.to_proto_string_clone())
2632 .unwrap_or_else(|| "no entry id available".to_string())
2633 }
2634
2635 pub fn get_ava_names(&self) -> impl Iterator<Item = &str> {
2637 self.attrs.keys().map(|a| a.as_str())
2639 }
2640
2641 pub fn get_ava(&self) -> &Eattrs {
2643 &self.attrs
2644 }
2645
2646 pub fn get_ava_iter(&self) -> impl Iterator<Item = (&Attribute, &ValueSet)> {
2647 self.attrs.iter()
2648 }
2649
2650 pub fn get_ava_set<A: AsRef<Attribute>>(&self, attr: A) -> Option<&ValueSet> {
2652 self.attrs.get(attr.as_ref())
2653 }
2654
2655 pub fn get_ava_refer<A: AsRef<Attribute>>(&self, attr: A) -> Option<&BTreeSet<Uuid>> {
2656 self.get_ava_set(attr).and_then(|vs| vs.as_refer_set())
2657 }
2658
2659 pub fn get_ava_as_iutf8_iter<A: AsRef<Attribute>>(
2660 &self,
2661 attr: A,
2662 ) -> Option<impl Iterator<Item = &str>> {
2663 self.get_ava_set(attr).and_then(|vs| vs.as_iutf8_iter())
2664 }
2665
2666 pub fn get_ava_as_iutf8<A: AsRef<Attribute>>(&self, attr: A) -> Option<&BTreeSet<String>> {
2667 self.get_ava_set(attr).and_then(|vs| vs.as_iutf8_set())
2668 }
2669
2670 pub fn get_ava_as_image<A: AsRef<Attribute>>(&self, attr: A) -> Option<&HashSet<ImageValue>> {
2671 self.get_ava_set(attr).and_then(|vs| vs.as_imageset())
2672 }
2673
2674 pub fn get_ava_single_image<A: AsRef<Attribute>>(&self, attr: A) -> Option<ImageValue> {
2675 let images = self.get_ava_set(attr).and_then(|vs| vs.as_imageset())?;
2676 images.iter().next().cloned()
2677 }
2678
2679 pub fn get_ava_single_credential_type<A: AsRef<Attribute>>(
2680 &self,
2681 attr: A,
2682 ) -> Option<CredentialType> {
2683 self.get_ava_set(attr)
2684 .and_then(|vs| vs.to_credentialtype_single())
2685 }
2686
2687 pub fn get_ava_as_oauthscopes<A: AsRef<Attribute>>(
2688 &self,
2689 attr: A,
2690 ) -> Option<impl Iterator<Item = &str>> {
2691 self.get_ava_set(attr)
2692 .and_then(|vs| vs.as_oauthscope_iter())
2693 }
2694
2695 pub fn get_ava_as_oauthscopemaps<A: AsRef<Attribute>>(
2696 &self,
2697 attr: A,
2698 ) -> Option<&std::collections::BTreeMap<Uuid, std::collections::BTreeSet<String>>> {
2699 self.get_ava_set(attr).and_then(|vs| vs.as_oauthscopemap())
2700 }
2701
2702 pub fn get_ava_as_intenttokens<A: AsRef<Attribute>>(
2703 &self,
2704 attr: A,
2705 ) -> Option<&std::collections::BTreeMap<String, IntentTokenState>> {
2706 self.get_ava_set(attr)
2707 .and_then(|vs| vs.as_intenttoken_map())
2708 }
2709
2710 pub fn get_ava_as_session_map<A: AsRef<Attribute>>(
2711 &self,
2712 attr: A,
2713 ) -> Option<&std::collections::BTreeMap<Uuid, Session>> {
2714 self.get_ava_set(attr).and_then(|vs| vs.as_session_map())
2715 }
2716
2717 pub fn get_ava_as_apitoken_map<A: AsRef<Attribute>>(
2718 &self,
2719 attr: A,
2720 ) -> Option<&std::collections::BTreeMap<Uuid, ApiToken>> {
2721 self.get_ava_set(attr).and_then(|vs| vs.as_apitoken_map())
2722 }
2723
2724 pub fn get_ava_as_oauth2session_map<A: AsRef<Attribute>>(
2725 &self,
2726 attr: A,
2727 ) -> Option<&std::collections::BTreeMap<Uuid, Oauth2Session>> {
2728 self.get_ava_set(attr)
2729 .and_then(|vs| vs.as_oauth2session_map())
2730 }
2731
2732 pub fn get_ava_iter_iname<A: AsRef<Attribute>>(
2734 &self,
2735 attr: A,
2736 ) -> Option<impl Iterator<Item = &str>> {
2737 self.get_ava_set(attr).and_then(|vs| vs.as_iname_iter())
2738 }
2739
2740 pub fn get_ava_iter_iutf8<A: AsRef<Attribute>>(
2742 &self,
2743 attr: A,
2744 ) -> Option<impl Iterator<Item = &str>> {
2745 self.get_ava_set(attr).and_then(|vs| vs.as_iutf8_iter())
2746 }
2747
2748 pub fn get_ava_as_refuuid<A: AsRef<Attribute>>(
2750 &self,
2751 attr: A,
2752 ) -> Option<Box<dyn Iterator<Item = Uuid> + '_>> {
2753 self.get_ava_set(attr).and_then(|vs| vs.as_ref_uuid_iter())
2755 }
2756
2757 pub fn get_ava_iter_sshpubkeys<A: AsRef<Attribute>>(
2759 &self,
2760 attr: A,
2761 ) -> Option<impl Iterator<Item = String> + '_> {
2762 self.get_ava_set(attr)
2763 .and_then(|vs| vs.as_sshpubkey_string_iter())
2764 }
2765
2766 pub fn get_ava_single<A: AsRef<Attribute>>(&self, attr: A) -> Option<Value> {
2772 self.get_ava_set(attr).and_then(|vs| vs.to_value_single())
2773 }
2774
2775 pub fn get_ava_single_proto_string<A: AsRef<Attribute>>(&self, attr: A) -> Option<String> {
2776 self.get_ava_set(attr)
2777 .and_then(|vs| vs.to_proto_string_single())
2778 }
2779
2780 pub fn get_ava_single_bool<A: AsRef<Attribute>>(&self, attr: A) -> Option<bool> {
2782 self.get_ava_set(attr).and_then(|vs| vs.to_bool_single())
2783 }
2784
2785 pub fn get_ava_single_uint32<A: AsRef<Attribute>>(&self, attr: A) -> Option<u32> {
2787 self.get_ava_set(attr).and_then(|vs| vs.to_uint32_single())
2788 }
2789
2790 pub fn get_ava_single_syntax<A: AsRef<Attribute>>(&self, attr: A) -> Option<SyntaxType> {
2792 self.get_ava_set(attr)
2793 .and_then(|vs| vs.to_syntaxtype_single())
2794 }
2795
2796 pub fn get_ava_single_credential<A: AsRef<Attribute>>(&self, attr: A) -> Option<&Credential> {
2798 self.get_ava_set(attr)
2799 .and_then(|vs| vs.to_credential_single())
2800 }
2801
2802 pub fn get_ava_passkeys<A: AsRef<Attribute>>(
2804 &self,
2805 attr: A,
2806 ) -> Option<&BTreeMap<Uuid, (String, PasskeyV4)>> {
2807 self.get_ava_set(attr).and_then(|vs| vs.as_passkey_map())
2808 }
2809
2810 pub fn get_ava_attestedpasskeys<A: AsRef<Attribute>>(
2812 &self,
2813 attr: A,
2814 ) -> Option<&BTreeMap<Uuid, (String, AttestedPasskeyV4)>> {
2815 self.get_ava_set(attr)
2816 .and_then(|vs| vs.as_attestedpasskey_map())
2817 }
2818
2819 pub fn get_ava_uihint<A: AsRef<Attribute>>(&self, attr: A) -> Option<&BTreeSet<UiHint>> {
2821 self.get_ava_set(attr).and_then(|vs| vs.as_uihint_set())
2822 }
2823
2824 pub fn get_ava_single_secret<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2826 self.get_ava_set(attr).and_then(|vs| vs.to_secret_single())
2827 }
2828
2829 pub fn get_ava_single_datetime<A: AsRef<Attribute>>(&self, attr: A) -> Option<OffsetDateTime> {
2831 self.get_ava_set(attr)
2832 .and_then(|vs| vs.to_datetime_single())
2833 }
2834
2835 pub(crate) fn get_ava_single_utf8<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2837 self.get_ava_set(attr).and_then(|vs| vs.to_utf8_single())
2838 }
2839
2840 pub(crate) fn get_ava_single_iutf8<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2842 self.get_ava_set(attr).and_then(|vs| vs.to_iutf8_single())
2843 }
2844
2845 pub(crate) fn get_ava_single_iname<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2847 self.get_ava_set(attr).and_then(|vs| vs.to_iname_single())
2848 }
2849
2850 pub fn get_ava_single_url<A: AsRef<Attribute>>(&self, attr: A) -> Option<&Url> {
2852 self.get_ava_set(attr).and_then(|vs| vs.to_url_single())
2853 }
2854
2855 pub fn get_ava_single_uuid<A: AsRef<Attribute>>(&self, attr: A) -> Option<Uuid> {
2856 self.get_ava_set(attr).and_then(|vs| vs.to_uuid_single())
2857 }
2858
2859 pub fn get_ava_single_refer<A: AsRef<Attribute>>(&self, attr: A) -> Option<Uuid> {
2860 self.get_ava_set(attr).and_then(|vs| vs.to_refer_single())
2861 }
2862
2863 pub fn get_ava_mail_primary<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2864 self.get_ava_set(attr)
2865 .and_then(|vs| vs.to_email_address_primary_str())
2866 }
2867
2868 pub fn get_ava_iter_mail<A: AsRef<Attribute>>(
2869 &self,
2870 attr: A,
2871 ) -> Option<impl Iterator<Item = &str>> {
2872 self.get_ava_set(attr).and_then(|vs| vs.as_email_str_iter())
2873 }
2874
2875 pub fn get_ava_single_protofilter<A: AsRef<Attribute>>(&self, attr: A) -> Option<&ProtoFilter> {
2877 self.get_ava_set(attr)
2878 .and_then(|vs| vs.to_json_filter_single())
2879 }
2880
2881 pub fn get_ava_single_private_binary<A: AsRef<Attribute>>(&self, attr: A) -> Option<&[u8]> {
2882 self.get_ava_set(attr)
2883 .and_then(|vs| vs.to_private_binary_single())
2884 }
2885
2886 pub fn get_ava_single_jws_key_es256<A: AsRef<Attribute>>(
2887 &self,
2888 attr: A,
2889 ) -> Option<&JwsEs256Signer> {
2890 self.get_ava_set(attr)
2891 .and_then(|vs| vs.to_jws_key_es256_single())
2892 }
2893
2894 pub fn get_ava_single_eckey_private<A: AsRef<Attribute>>(
2895 &self,
2896 attr: A,
2897 ) -> Option<&EcKey<Private>> {
2898 self.get_ava_set(attr)
2899 .and_then(|vs| vs.to_eckey_private_single())
2900 }
2901
2902 pub fn get_ava_single_eckey_public<A: AsRef<Attribute>>(
2903 &self,
2904 attr: A,
2905 ) -> Option<&EcKey<Public>> {
2906 self.get_ava_set(attr)
2907 .and_then(|vs| vs.to_eckey_public_single())
2908 }
2909
2910 pub fn get_ava_webauthn_attestation_ca_list<A: AsRef<Attribute>>(
2911 &self,
2912 attr: A,
2913 ) -> Option<&AttestationCaList> {
2914 self.get_ava_set(attr)
2915 .and_then(|vs| vs.as_webauthn_attestation_ca_list())
2916 }
2917
2918 pub fn get_ava_application_password<A: AsRef<Attribute>>(
2919 &self,
2920 attr: A,
2921 ) -> Option<&BTreeMap<Uuid, Vec<ApplicationPassword>>> {
2922 self.get_ava_set(attr)
2923 .and_then(|vs| vs.as_application_password_map())
2924 }
2925
2926 pub(crate) fn generate_spn(&self, domain_name: &str) -> Option<ValueSet> {
2928 if let Some(name) = self.get_ava_single_iname(Attribute::Name) {
2929 return Some(ValueSetSpn::new((name.into(), domain_name.into())));
2931 }
2932
2933 let spn_set = self.get_ava_set(Attribute::Spn)?;
2935
2936 if spn_set.syntax() == SyntaxType::SecurityPrincipalName {
2937 Some(spn_set.clone())
2939 } else if let Some(name) = spn_set.to_iname_single() {
2940 Some(ValueSetSpn::new((name.into(), domain_name.into())))
2942 } else {
2943 None
2945 }
2946 }
2947
2948 pub fn attribute_pres<A: AsRef<Attribute>>(&self, attr: A) -> bool {
2950 self.attrs.contains_key(attr.as_ref())
2951 }
2952
2953 pub fn attribute_equality<A: AsRef<Attribute>>(&self, attr: A, value: &PartialValue) -> bool {
2956 match self.attrs.get(attr.as_ref()) {
2961 Some(v_list) => v_list.contains(value),
2962 None => false,
2963 }
2964 }
2965
2966 pub fn attribute_substring<A: AsRef<Attribute>>(
2969 &self,
2970 attr: A,
2971 subvalue: &PartialValue,
2972 ) -> bool {
2973 self.get_ava_set(attr)
2974 .map(|vset| vset.substring(subvalue))
2975 .unwrap_or(false)
2976 }
2977
2978 pub fn attribute_startswith<A: AsRef<Attribute>>(
2981 &self,
2982 attr: A,
2983 subvalue: &PartialValue,
2984 ) -> bool {
2985 self.get_ava_set(attr)
2986 .map(|vset| vset.startswith(subvalue))
2987 .unwrap_or(false)
2988 }
2989
2990 pub fn attribute_endswith<A: AsRef<Attribute>>(
2993 &self,
2994 attr: A,
2995 subvalue: &PartialValue,
2996 ) -> bool {
2997 self.get_ava_set(attr)
2998 .map(|vset| vset.endswith(subvalue))
2999 .unwrap_or(false)
3000 }
3001
3002 pub fn attribute_lessthan<A: AsRef<Attribute>>(
3005 &self,
3006 attr: A,
3007 subvalue: &PartialValue,
3008 ) -> bool {
3009 self.get_ava_set(attr)
3010 .map(|vset| vset.lessthan(subvalue))
3011 .unwrap_or(false)
3012 }
3013
3014 #[inline(always)]
3019 #[instrument(level = "trace", name = "entry::entry_match_no_index", skip(self))]
3020 pub fn entry_match_no_index(&self, filter: &Filter<FilterValidResolved>) -> bool {
3022 self.entry_match_no_index_inner(filter.to_inner())
3023 }
3024
3025 fn entry_match_no_index_inner(&self, filter: &FilterResolved) -> bool {
3029 match filter {
3032 FilterResolved::Eq(attr, value, _) => self.attribute_equality(attr, value),
3033 FilterResolved::Cnt(attr, subvalue, _) => self.attribute_substring(attr, subvalue),
3034 FilterResolved::Stw(attr, subvalue, _) => self.attribute_startswith(attr, subvalue),
3035 FilterResolved::Enw(attr, subvalue, _) => self.attribute_endswith(attr, subvalue),
3036 FilterResolved::Pres(attr, _) => self.attribute_pres(attr),
3037 FilterResolved::LessThan(attr, subvalue, _) => self.attribute_lessthan(attr, subvalue),
3038 FilterResolved::Or(l, _) => l.iter().any(|f| self.entry_match_no_index_inner(f)),
3040 FilterResolved::And(l, _) => l.iter().all(|f| self.entry_match_no_index_inner(f)),
3042 FilterResolved::Inclusion(_, _) => {
3043 false
3047 }
3048 FilterResolved::AndNot(f, _) => !self.entry_match_no_index_inner(f),
3049 FilterResolved::Invalid(_) => false,
3050 }
3051 }
3052
3053 pub fn filter_from_attrs(&self, attrs: &[Attribute]) -> Option<Filter<FilterInvalid>> {
3056 let mut pairs: Vec<(Attribute, PartialValue)> = Vec::with_capacity(0);
3068
3069 for attr in attrs {
3070 match self.attrs.get(attr) {
3071 Some(values) => values
3072 .to_partialvalue_iter()
3073 .for_each(|pv| pairs.push((attr.clone(), pv))),
3074 None => return None,
3075 }
3076 }
3077
3078 let res: Vec<FC> = pairs
3079 .into_iter()
3080 .map(|(attr, pv)| FC::Eq(attr, pv))
3081 .collect();
3082 Some(filter_all!(f_and(res)))
3083 }
3084
3085 pub fn gen_modlist_assert(
3088 &self,
3089 schema: &dyn SchemaTransaction,
3090 ) -> Result<ModifyList<ModifyInvalid>, SchemaError> {
3091 let mut mods = ModifyList::new();
3095
3096 for (k, vs) in self.attrs.iter() {
3097 if *k == Attribute::Uuid {
3113 continue;
3114 }
3115 match schema.is_multivalue(k) {
3117 Ok(r) => {
3118 if !r ||
3121 *k == Attribute::AcpReceiverGroup ||
3124 *k == Attribute::AcpCreateAttr ||
3125 *k == Attribute::AcpCreateClass ||
3126 *k == Attribute::AcpModifyPresentAttr ||
3127 *k == Attribute::AcpModifyRemovedAttr ||
3128 *k == Attribute::AcpModifyClass ||
3129 *k == Attribute::SystemMust ||
3130 *k == Attribute::SystemMay
3131 {
3132 mods.push_mod(Modify::Purged(k.clone()));
3133 }
3134 }
3135 Err(e) => return Err(e),
3137 }
3138 for v in vs.to_value_iter() {
3139 mods.push_mod(Modify::Present(k.clone(), v.clone()));
3140 }
3141 }
3142
3143 Ok(mods)
3144 }
3145
3146 pub fn mask_recycled_ts(&self) -> Option<&Self> {
3149 match self.attrs.get(&Attribute::Class) {
3151 Some(cls) => {
3152 if cls.contains(&EntryClass::Tombstone.to_partialvalue())
3153 || cls.contains(&EntryClass::Recycled.to_partialvalue())
3154 {
3155 None
3156 } else {
3157 Some(self)
3158 }
3159 }
3160 None => Some(self),
3161 }
3162 }
3163
3164 pub fn mask_recycled(&self) -> Option<&Self> {
3167 match self.attrs.get(&Attribute::Class) {
3169 Some(cls) => {
3170 if cls.contains(&EntryClass::Recycled.to_partialvalue()) {
3171 None
3172 } else {
3173 Some(self)
3174 }
3175 }
3176 None => Some(self),
3177 }
3178 }
3179
3180 pub fn mask_tombstone(&self) -> Option<&Self> {
3183 match self.attrs.get(&Attribute::Class) {
3185 Some(cls) => {
3186 if cls.contains(&EntryClass::Tombstone.to_partialvalue()) {
3187 None
3188 } else {
3189 Some(self)
3190 }
3191 }
3192 None => Some(self),
3193 }
3194 }
3195}
3196
3197impl<STATE> Entry<EntryInvalid, STATE>
3198where
3199 STATE: Clone,
3200{
3201 pub fn add_ava(&mut self, attr: Attribute, value: Value) {
3206 self.valid.ecstate.change_ava(&self.valid.cid, &attr);
3207 self.add_ava_int(attr, value);
3208 }
3209
3210 pub fn add_ava_if_not_exist<A: AsRef<Attribute>>(&mut self, attr: A, value: Value) {
3211 let attr_ref = attr.as_ref();
3212 if self.add_ava_int(attr_ref.clone(), value) {
3214 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3216 }
3217 }
3218
3219 fn assert_ava<A: AsRef<Attribute>>(
3220 &mut self,
3221 attr: A,
3222 value: &PartialValue,
3223 ) -> Result<(), OperationError> {
3224 self.valid
3225 .ecstate
3226 .change_ava(&self.valid.cid, attr.as_ref());
3227
3228 if self.attribute_equality(attr, value) {
3229 Ok(())
3230 } else {
3231 Err(OperationError::ModifyAssertionFailed)
3232 }
3233 }
3234
3235 pub(crate) fn remove_ava<A: AsRef<Attribute>>(&mut self, attr: A, value: &PartialValue) {
3238 let attr_ref = attr.as_ref();
3239 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3240
3241 let rm = if let Some(vs) = self.attrs.get_mut(attr_ref) {
3242 vs.remove(value, &self.valid.cid);
3243 vs.is_empty()
3244 } else {
3245 false
3246 };
3247 if rm {
3248 self.attrs.remove(attr_ref);
3249 };
3250 }
3251
3252 pub(crate) fn remove_avas<A: AsRef<Attribute>>(
3253 &mut self,
3254 attr: A,
3255 values: &BTreeSet<PartialValue>,
3256 ) {
3257 let attr_ref = attr.as_ref();
3258 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3259
3260 let rm = if let Some(vs) = self.attrs.get_mut(attr_ref) {
3261 values.iter().for_each(|k| {
3262 vs.remove(k, &self.valid.cid);
3263 });
3264 vs.is_empty()
3265 } else {
3266 false
3267 };
3268 if rm {
3269 self.attrs.remove(attr_ref);
3270 };
3271 }
3272
3273 pub(crate) fn purge_ava<A: AsRef<Attribute>>(&mut self, attr: A) {
3276 let attr_ref = attr.as_ref();
3277 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3278 let can_remove = self
3281 .attrs
3282 .get_mut(attr_ref)
3283 .map(|vs| vs.purge(&self.valid.cid))
3284 .unwrap_or_default();
3286 if can_remove {
3287 self.attrs.remove(attr_ref);
3288 }
3289 }
3290
3291 pub fn pop_ava<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<ValueSet> {
3293 let attr_ref = attr.as_ref();
3294 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3295
3296 let mut vs = self.attrs.remove(attr_ref)?;
3297 if vs.purge(&self.valid.cid) {
3298 Some(vs)
3300 } else {
3301 let r_vs = vs.clone();
3303 self.attrs.insert(attr_ref.clone(), vs);
3304 Some(r_vs)
3305 }
3306 }
3307
3308 #[cfg(test)]
3313 pub(crate) fn force_trim_ava<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<ValueSet> {
3314 self.valid
3315 .ecstate
3316 .change_ava(&self.valid.cid, attr.as_ref());
3317 self.attrs.remove(attr.as_ref())
3318 }
3319
3320 pub fn set_ava<T>(&mut self, attr: &Attribute, iter: T)
3323 where
3324 T: Clone + IntoIterator<Item = Value>,
3325 {
3326 self.purge_ava(attr);
3327 self.set_ava_iter_int(attr.clone(), iter)
3328 }
3329
3330 pub fn set_ava_set(&mut self, attr: &Attribute, vs: ValueSet) {
3333 self.purge_ava(attr);
3334 if let Some(existing_vs) = self.attrs.get_mut(attr) {
3335 let _ = existing_vs.merge(&vs);
3336 } else {
3337 self.attrs.insert(attr.clone(), vs);
3338 }
3339 }
3340
3341 pub fn merge_ava_set(&mut self, attr: &Attribute, vs: ValueSet) -> Result<(), OperationError> {
3344 self.valid.ecstate.change_ava(&self.valid.cid, attr);
3345 if let Some(existing_vs) = self.attrs.get_mut(attr) {
3346 existing_vs.merge(&vs)
3347 } else {
3348 self.attrs.insert(attr.clone(), vs);
3349 Ok(())
3350 }
3351 }
3352
3353 pub fn apply_modlist(
3355 &mut self,
3356 modlist: &ModifyList<ModifyValid>,
3357 ) -> Result<(), OperationError> {
3358 for modify in modlist {
3359 match modify {
3360 Modify::Present(attr, value) => {
3361 self.add_ava(attr.clone(), value.clone());
3362 }
3363 Modify::Removed(attr, value) => {
3364 self.remove_ava(attr, value);
3365 }
3366 Modify::Purged(attr) => {
3367 self.purge_ava(attr);
3368 }
3369 Modify::Assert(attr, value) => {
3370 self.assert_ava(attr, value).inspect_err(|_e| {
3371 error!("Modification assertion was not met. {} {:?}", attr, value);
3372 })?;
3373 }
3374 Modify::Set(attr, valueset) => self.set_ava_set(attr, valueset.clone()),
3375 }
3376 }
3377 Ok(())
3378 }
3379}
3380
3381impl<VALID, STATE> PartialEq for Entry<VALID, STATE> {
3382 fn eq(&self, rhs: &Entry<VALID, STATE>) -> bool {
3383 compare_attrs(&self.attrs, &rhs.attrs)
3394 }
3395}
3396
3397#[cfg(test)]
3398mod tests {
3399 use crate::prelude::*;
3400 use std::collections::BTreeSet as Set;
3401
3402 use hashbrown::HashMap;
3403
3404 use crate::be::{IdxKey, IdxSlope};
3405 use crate::entry::{Entry, EntryInit, EntryInvalid, EntryNew};
3406 use crate::modify::{Modify, ModifyList};
3407 use crate::value::{IndexType, PartialValue, Value};
3408
3409 #[test]
3410 fn test_entry_basic() {
3411 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3412
3413 e.add_ava(Attribute::UserId, Value::from("william"));
3414 }
3415
3416 #[test]
3417 fn test_entry_dup_value() {
3418 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3425 e.add_ava(Attribute::UserId, Value::from("william"));
3426 e.add_ava(Attribute::UserId, Value::from("william"));
3427
3428 let values = e.get_ava_set(Attribute::UserId).expect("Failed to get ava");
3429 assert_eq!(values.len(), 1)
3431 }
3432
3433 #[test]
3434 fn test_entry_pres() {
3435 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3436 e.add_ava(Attribute::UserId, Value::from("william"));
3437
3438 assert!(e.attribute_pres(Attribute::UserId));
3439 assert!(!e.attribute_pres(Attribute::Name));
3440 }
3441
3442 #[test]
3443 fn test_entry_equality() {
3444 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3445
3446 e.add_ava(Attribute::UserId, Value::from("william"));
3447
3448 assert!(e.attribute_equality(Attribute::UserId, &PartialValue::new_utf8s("william")));
3449 assert!(!e.attribute_equality(Attribute::UserId, &PartialValue::new_utf8s("test")));
3450 assert!(!e.attribute_equality(Attribute::NonExist, &PartialValue::new_utf8s("william")));
3451 assert!(!e.attribute_equality(Attribute::UserId, &PartialValue::new_iutf8("william")));
3453 }
3454
3455 #[test]
3456 fn test_entry_substring() {
3457 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3458
3459 e.add_ava(Attribute::UserId, Value::from("william"));
3460
3461 assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("william")));
3462 assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("will")));
3463 assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("liam")));
3464 assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("lli")));
3465 assert!(!e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("llim")));
3466 assert!(!e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("bob")));
3467 assert!(!e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("wl")));
3468
3469 assert!(e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("will")));
3470 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("liam")));
3471 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("lli")));
3472 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("llim")));
3473 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("bob")));
3474 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("wl")));
3475
3476 assert!(e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("liam")));
3477 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("will")));
3478 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("lli")));
3479 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("llim")));
3480 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("bob")));
3481 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("wl")));
3482 }
3483
3484 #[test]
3485 fn test_entry_lessthan() {
3486 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3487
3488 let pv2 = PartialValue::new_uint32(2);
3489 let pv8 = PartialValue::new_uint32(8);
3490 let pv10 = PartialValue::new_uint32(10);
3491 let pv15 = PartialValue::new_uint32(15);
3492
3493 e1.add_ava(Attribute::TestAttr, Value::new_uint32(10));
3494
3495 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv2));
3496 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv8));
3497 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv10));
3498 assert!(e1.attribute_lessthan(Attribute::TestAttr, &pv15));
3499
3500 e1.add_ava(Attribute::TestAttr, Value::new_uint32(8));
3501
3502 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv2));
3503 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv8));
3504 assert!(e1.attribute_lessthan(Attribute::TestAttr, &pv10));
3505 assert!(e1.attribute_lessthan(Attribute::TestAttr, &pv15));
3506 }
3507
3508 #[test]
3509 fn test_entry_apply_modlist() {
3510 let mut e: Entry<EntryInvalid, EntryNew> = Entry::new().into_invalid_new();
3512
3513 e.add_ava(Attribute::UserId, Value::from("william"));
3514
3515 let present_single_mods = ModifyList::new_valid_list(vec![Modify::Present(
3516 Attribute::Attr,
3517 Value::new_iutf8("value"),
3518 )]);
3519
3520 assert!(e.apply_modlist(&present_single_mods).is_ok());
3521
3522 assert!(e.attribute_equality(Attribute::UserId, &PartialValue::new_utf8s("william")));
3524 assert!(e.attribute_equality(Attribute::Attr, &PartialValue::new_iutf8("value")));
3525
3526 let present_multivalue_mods = ModifyList::new_valid_list(vec![
3528 Modify::Present(Attribute::Class, Value::new_iutf8("test")),
3529 Modify::Present(Attribute::Class, Value::new_iutf8("multi_test")),
3530 ]);
3531
3532 assert!(e.apply_modlist(&present_multivalue_mods).is_ok());
3533
3534 assert!(e.attribute_equality(Attribute::Class, &PartialValue::new_iutf8("test")));
3535 assert!(e.attribute_equality(Attribute::Class, &PartialValue::new_iutf8("multi_test")));
3536
3537 let purge_single_mods = ModifyList::new_valid_list(vec![Modify::Purged(Attribute::Attr)]);
3539
3540 assert!(e.apply_modlist(&purge_single_mods).is_ok());
3541
3542 assert!(!e.attribute_pres(Attribute::Attr));
3543
3544 let purge_multi_mods = ModifyList::new_valid_list(vec![Modify::Purged(Attribute::Class)]);
3545
3546 assert!(e.apply_modlist(&purge_multi_mods).is_ok());
3547
3548 assert!(!e.attribute_pres(Attribute::Class));
3549
3550 let purge_empty_mods = purge_single_mods;
3551
3552 assert!(e.apply_modlist(&purge_empty_mods).is_ok());
3553
3554 let remove_mods = ModifyList::new_valid_list(vec![Modify::Removed(
3556 Attribute::Attr,
3557 PartialValue::new_iutf8("value"),
3558 )]);
3559
3560 assert!(e.apply_modlist(&present_single_mods).is_ok());
3561 assert!(e.attribute_equality(Attribute::Attr, &PartialValue::new_iutf8("value")));
3562 assert!(e.apply_modlist(&remove_mods).is_ok());
3563 assert!(!e.attrs.contains_key(&Attribute::Attr));
3564
3565 let remove_empty_mods = remove_mods;
3566
3567 assert!(e.apply_modlist(&remove_empty_mods).is_ok());
3568
3569 assert!(!e.attrs.contains_key(&Attribute::Attr));
3570 }
3571
3572 #[test]
3573 fn test_entry_idx_diff() {
3574 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3575 e1.add_ava(Attribute::UserId, Value::from("william"));
3576 let mut e1_mod = e1.clone();
3577 e1_mod.add_ava(Attribute::Extra, Value::from("test"));
3578
3579 let e1 = e1.into_sealed_committed();
3580 let e1_mod = e1_mod.into_sealed_committed();
3581
3582 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3583 e2.add_ava(Attribute::UserId, Value::from("claire"));
3584 let e2 = e2.into_sealed_committed();
3585
3586 let mut idxmeta = HashMap::with_capacity(8);
3587 idxmeta.insert(
3588 IdxKey {
3589 attr: Attribute::UserId,
3590 itype: IndexType::Equality,
3591 },
3592 IdxSlope::MAX,
3593 );
3594 idxmeta.insert(
3595 IdxKey {
3596 attr: Attribute::UserId,
3597 itype: IndexType::Presence,
3598 },
3599 IdxSlope::MAX,
3600 );
3601 idxmeta.insert(
3602 IdxKey {
3603 attr: Attribute::Extra,
3604 itype: IndexType::Equality,
3605 },
3606 IdxSlope::MAX,
3607 );
3608
3609 let r1 = Entry::idx_diff(&idxmeta, None, None);
3611 eprintln!("{r1:?}");
3612 assert_eq!(r1, Vec::with_capacity(0));
3613
3614 let mut del_r = Entry::idx_diff(&idxmeta, Some(&e1), None);
3616 del_r.sort_unstable();
3617 eprintln!("del_r {del_r:?}");
3618 assert!(
3619 del_r[0]
3620 == Err((
3621 &Attribute::UserId,
3622 IndexType::Equality,
3623 "william".to_string()
3624 ))
3625 );
3626 assert!(del_r[1] == Err((&Attribute::UserId, IndexType::Presence, "_".to_string())));
3627
3628 let mut add_r = Entry::idx_diff(&idxmeta, None, Some(&e1));
3630 add_r.sort_unstable();
3631 eprintln!("{add_r:?}");
3632 assert!(
3633 add_r[0]
3634 == Ok((
3635 &Attribute::UserId,
3636 IndexType::Equality,
3637 "william".to_string()
3638 ))
3639 );
3640 assert!(add_r[1] == Ok((&Attribute::UserId, IndexType::Presence, "_".to_string())));
3641
3642 let no_r = Entry::idx_diff(&idxmeta, Some(&e1), Some(&e1));
3646 assert!(no_r.is_empty());
3647
3648 let add_a_r = Entry::idx_diff(&idxmeta, Some(&e1), Some(&e1_mod));
3650 assert!(add_a_r[0] == Ok((&Attribute::Extra, IndexType::Equality, "test".to_string())));
3651
3652 let del_a_r = Entry::idx_diff(&idxmeta, Some(&e1_mod), Some(&e1));
3654 assert!(del_a_r[0] == Err((&Attribute::Extra, IndexType::Equality, "test".to_string())));
3655
3656 let mut chg_r = Entry::idx_diff(&idxmeta, Some(&e1), Some(&e2));
3658 chg_r.sort_unstable();
3659 eprintln!("{chg_r:?}");
3660 assert!(
3661 chg_r[1]
3662 == Err((
3663 &Attribute::UserId,
3664 IndexType::Equality,
3665 "william".to_string()
3666 ))
3667 );
3668
3669 assert!(
3670 chg_r[0]
3671 == Ok((
3672 &Attribute::UserId,
3673 IndexType::Equality,
3674 "claire".to_string()
3675 ))
3676 );
3677 }
3678
3679 #[test]
3680 fn test_entry_mask_recycled_ts() {
3681 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3682 e1.add_ava(Attribute::Class, EntryClass::Person.to_value());
3683 let e1 = e1.into_sealed_committed();
3684 assert!(e1.mask_recycled_ts().is_some());
3685
3686 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3687 e2.add_ava(Attribute::Class, EntryClass::Person.to_value());
3688 e2.add_ava(Attribute::Class, EntryClass::Recycled.into());
3689 let e2 = e2.into_sealed_committed();
3690 assert!(e2.mask_recycled_ts().is_none());
3691
3692 let mut e3: Entry<EntryInit, EntryNew> = Entry::new();
3693 e3.add_ava(Attribute::Class, EntryClass::Tombstone.into());
3694 let e3 = e3.into_sealed_committed();
3695 assert!(e3.mask_recycled_ts().is_none());
3696 }
3697
3698 #[test]
3699 fn test_entry_idx_name2uuid_diff() {
3700 let r = Entry::idx_name2uuid_diff(None, None);
3702 assert_eq!(r, (None, None));
3703
3704 {
3706 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3707 e.add_ava(Attribute::Class, EntryClass::Person.to_value());
3708 let e = e.into_sealed_committed();
3709
3710 assert!(Entry::idx_name2uuid_diff(None, Some(&e)) == (Some(Set::new()), None));
3711 }
3712
3713 {
3714 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3715 e.add_ava(Attribute::Class, EntryClass::Person.to_value());
3716 e.add_ava(Attribute::GidNumber, Value::new_uint32(1300));
3717 e.add_ava(Attribute::Name, Value::new_iname("testperson"));
3718 e.add_ava(
3719 Attribute::Spn,
3720 Value::new_spn_str("testperson", "example.com"),
3721 );
3722 e.add_ava(
3723 Attribute::Uuid,
3724 Value::Uuid(uuid!("9fec0398-c46c-4df4-9df5-b0016f7d563f")),
3725 );
3726 let e = e.into_sealed_committed();
3727
3728 assert!(
3730 Entry::idx_name2uuid_diff(None, Some(&e))
3731 == (
3732 Some(btreeset![
3733 "1300".to_string(),
3734 "testperson".to_string(),
3735 "testperson@example.com".to_string()
3736 ]),
3737 None
3738 )
3739 );
3740 assert!(
3743 Entry::idx_name2uuid_diff(Some(&e), None)
3744 == (
3745 None,
3746 Some(btreeset![
3747 "1300".to_string(),
3748 "testperson".to_string(),
3749 "testperson@example.com".to_string()
3750 ])
3751 )
3752 );
3753
3754 assert!(
3756 Entry::idx_name2uuid_diff(Some(&e), Some(&e))
3757 == (Some(Set::new()), Some(Set::new()))
3758 );
3759 }
3760 {
3763 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3764 e1.add_ava(Attribute::Class, EntryClass::Person.to_value());
3765 e1.add_ava(
3766 Attribute::Spn,
3767 Value::new_spn_str("testperson", "example.com"),
3768 );
3769 let e1 = e1.into_sealed_committed();
3770
3771 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3772 e2.add_ava(Attribute::Class, EntryClass::Person.to_value());
3773 e2.add_ava(Attribute::Name, Value::new_iname("testperson"));
3774 e2.add_ava(
3775 Attribute::Spn,
3776 Value::new_spn_str("testperson", "example.com"),
3777 );
3778 let e2 = e2.into_sealed_committed();
3779
3780 assert!(
3782 Entry::idx_name2uuid_diff(Some(&e1), Some(&e2))
3783 == (Some(btreeset!["testperson".to_string()]), Some(Set::new()))
3784 );
3785
3786 assert!(
3788 Entry::idx_name2uuid_diff(Some(&e2), Some(&e1))
3789 == (Some(Set::new()), Some(btreeset!["testperson".to_string()]))
3790 );
3791 }
3792
3793 {
3795 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3796 e1.add_ava(Attribute::Class, EntryClass::Person.to_value());
3797 e1.add_ava(
3798 Attribute::Spn,
3799 Value::new_spn_str("testperson", "example.com"),
3800 );
3801 let e1 = e1.into_sealed_committed();
3802
3803 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3804 e2.add_ava(Attribute::Class, EntryClass::Person.to_value());
3805 e2.add_ava(
3806 Attribute::Spn,
3807 Value::new_spn_str("renameperson", "example.com"),
3808 );
3809 let e2 = e2.into_sealed_committed();
3810
3811 assert!(
3812 Entry::idx_name2uuid_diff(Some(&e1), Some(&e2))
3813 == (
3814 Some(btreeset!["renameperson@example.com".to_string()]),
3815 Some(btreeset!["testperson@example.com".to_string()])
3816 )
3817 );
3818 }
3819 }
3820
3821 #[test]
3822 fn test_entry_idx_uuid2spn_diff() {
3823 assert!(Entry::idx_uuid2spn_diff(None, None).is_none());
3824
3825 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3826 e1.add_ava(
3827 Attribute::Spn,
3828 Value::new_spn_str("testperson", "example.com"),
3829 );
3830 let e1 = e1.into_sealed_committed();
3831
3832 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3833 e2.add_ava(
3834 Attribute::Spn,
3835 Value::new_spn_str("renameperson", "example.com"),
3836 );
3837 let e2 = e2.into_sealed_committed();
3838
3839 assert!(
3840 Entry::idx_uuid2spn_diff(None, Some(&e1))
3841 == Some(Ok(Value::new_spn_str("testperson", "example.com")))
3842 );
3843 assert!(Entry::idx_uuid2spn_diff(Some(&e1), None) == Some(Err(())));
3844 assert!(Entry::idx_uuid2spn_diff(Some(&e1), Some(&e1)).is_none());
3845 assert!(
3846 Entry::idx_uuid2spn_diff(Some(&e1), Some(&e2))
3847 == Some(Ok(Value::new_spn_str("renameperson", "example.com")))
3848 );
3849 }
3850
3851 #[test]
3852 fn test_entry_idx_uuid2rdn_diff() {
3853 assert!(Entry::idx_uuid2rdn_diff(None, None).is_none());
3854
3855 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3856 e1.add_ava(
3857 Attribute::Spn,
3858 Value::new_spn_str("testperson", "example.com"),
3859 );
3860 let e1 = e1.into_sealed_committed();
3861
3862 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3863 e2.add_ava(
3864 Attribute::Spn,
3865 Value::new_spn_str("renameperson", "example.com"),
3866 );
3867 let e2 = e2.into_sealed_committed();
3868
3869 assert!(
3870 Entry::idx_uuid2rdn_diff(None, Some(&e1))
3871 == Some(Ok("spn=testperson@example.com".to_string()))
3872 );
3873 assert!(Entry::idx_uuid2rdn_diff(Some(&e1), None) == Some(Err(())));
3874 assert!(Entry::idx_uuid2rdn_diff(Some(&e1), Some(&e1)).is_none());
3875 assert!(
3876 Entry::idx_uuid2rdn_diff(Some(&e1), Some(&e2))
3877 == Some(Ok("spn=renameperson@example.com".to_string()))
3878 );
3879 }
3880}