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 crypto_glue::s256::Sha256Output;
48use hashbrown::{HashMap, HashSet};
49use kanidm_proto::internal::ImageValue;
50use kanidm_proto::internal::{
51 ConsistencyError, Filter as ProtoFilter, OperationError, SchemaError, UiHint,
52};
53use kanidm_proto::scim_v1::server::ScimEffectiveAccess;
54use kanidm_proto::v1::Entry as ProtoEntry;
55use ldap3_proto::simple::{LdapPartialAttribute, LdapSearchResultEntry};
56use std::cmp::Ordering;
57pub use std::collections::BTreeSet as Set;
58use std::collections::{BTreeMap as Map, BTreeMap, BTreeSet};
59use std::sync::Arc;
60use time::OffsetDateTime;
61use tracing::trace;
62use uuid::Uuid;
63use webauthn_rs::prelude::{
64 AttestationCaList, AttestedPasskey as AttestedPasskeyV4, Passkey as PasskeyV4,
65};
66
67pub type EntryInitNew = Entry<EntryInit, EntryNew>;
68pub type EntryInvalidNew = Entry<EntryInvalid, EntryNew>;
69pub type EntryRefreshNew = Entry<EntryRefresh, EntryNew>;
70pub type EntrySealedNew = Entry<EntrySealed, EntryNew>;
71pub type EntryValidCommitted = Entry<EntryValid, EntryCommitted>;
72pub type EntrySealedCommitted = Entry<EntrySealed, EntryCommitted>;
73pub type EntryInvalidCommitted = Entry<EntryInvalid, EntryCommitted>;
74pub type EntryReducedCommitted = Entry<EntryReduced, EntryCommitted>;
75pub type EntryTuple = (Arc<EntrySealedCommitted>, EntryInvalidCommitted);
76
77pub type EntryIncrementalNew = Entry<EntryIncremental, EntryNew>;
78pub type EntryIncrementalCommitted = Entry<EntryIncremental, EntryCommitted>;
79
80#[derive(Clone, Debug)]
93pub struct EntryNew; #[derive(Clone, Debug)]
97pub struct EntryCommitted {
98 id: u64,
99}
100
101#[derive(Clone, Debug)]
102pub struct EntryInit;
103
104#[derive(Clone, Debug)]
111pub struct EntryInvalid {
112 cid: Cid,
113 ecstate: EntryChangeState,
114}
115
116#[derive(Clone, Debug)]
118pub struct EntryRefresh {
119 ecstate: EntryChangeState,
120}
121
122#[derive(Clone, Debug)]
124pub struct EntryIncremental {
125 uuid: Uuid,
127 ecstate: EntryChangeState,
128}
129
130#[derive(Clone, Debug)]
136pub struct EntryValid {
137 uuid: Uuid,
139 ecstate: EntryChangeState,
140}
141
142#[derive(Clone, Debug)]
149pub struct EntrySealed {
150 uuid: Uuid,
151 ecstate: EntryChangeState,
152}
153
154#[derive(Clone, Debug)]
160pub struct EntryReduced {
161 uuid: Uuid,
162 effective_access: Option<Box<AccessEffectivePermission>>,
163}
164
165pub type Eattrs = Map<Attribute, ValueSet>;
168
169pub trait GetUuid {
170 fn get_uuid(&self) -> Uuid;
171}
172
173pub trait Committed {}
174
175impl Committed for EntrySealed {}
176impl Committed for EntryReduced {}
177
178pub(crate) fn compare_attrs(left: &Eattrs, right: &Eattrs) -> bool {
179 let allkeys: Set<&Attribute> = left
182 .keys()
183 .chain(right.keys())
184 .filter(|k| *k != &Attribute::LastModifiedCid && *k != &Attribute::CreatedAtCid)
185 .collect();
186
187 allkeys.into_iter().all(|k| {
188 let left_vs = left.get(k);
190 let right_vs = right.get(k);
191 let r = match (left_vs, right_vs) {
192 (Some(l), Some(r)) => l.eq(r),
193 _ => false,
194 };
195 if !r {
196 trace!(?k, ?left_vs, ?right_vs, "compare_attrs_allkeys");
197 }
198 r
199 })
200}
201
202pub struct Entry<VALID, STATE> {
229 valid: VALID,
230 state: STATE,
231 attrs: Eattrs,
233}
234
235impl<VALID, STATE> std::fmt::Debug for Entry<VALID, STATE>
236where
237 STATE: std::fmt::Debug,
238 VALID: std::fmt::Debug,
239{
240 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
241 f.debug_struct("Entry<EntrySealed, _>")
242 .field("state", &self.state)
243 .field("valid", &self.valid)
244 .field("attrs", &self.attrs)
245 .finish()
246 }
247}
248
249impl<STATE> std::fmt::Display for Entry<EntrySealed, STATE>
250where
251 STATE: Clone,
252{
253 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
254 write!(f, "{}", self.get_uuid())
255 }
256}
257
258impl<STATE> std::fmt::Display for Entry<EntryInit, STATE>
259where
260 STATE: Clone,
261{
262 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
263 write!(f, "Entry in initial state")
264 }
265}
266
267impl<STATE> Entry<EntryInit, STATE>
268where
269 STATE: Clone,
270{
271 pub fn get_uuid(&self) -> Option<Uuid> {
273 self.attrs
274 .get(&Attribute::Uuid)
275 .and_then(|vs| vs.to_uuid_single())
276 }
277}
278
279impl Default for Entry<EntryInit, EntryNew> {
280 fn default() -> Self {
281 Self::new()
282 }
283}
284
285impl FromIterator<(Attribute, ValueSet)> for EntryInitNew {
286 fn from_iter<I: IntoIterator<Item = (Attribute, ValueSet)>>(iter: I) -> Self {
287 let attrs = Eattrs::from_iter(iter);
288
289 Entry {
290 valid: EntryInit,
291 state: EntryNew,
292 attrs,
293 }
294 }
295}
296
297impl Entry<EntryInit, EntryNew> {
298 pub fn new() -> Self {
299 Entry {
300 valid: EntryInit,
302 state: EntryNew,
303 attrs: Map::new(),
304 }
305 }
306
307 pub fn from_proto_entry(
310 e: &ProtoEntry,
311 qs: &mut QueryServerWriteTransaction,
312 ) -> Result<Self, OperationError> {
313 trace!("from_proto_entry");
314 let map2: Result<Eattrs, OperationError> = e
321 .attrs
322 .iter()
323 .filter(|(_, v)| !v.is_empty())
324 .map(|(k, v)| {
325 trace!(?k, ?v, "attribute");
326 let attr_nk = Attribute::from(k.as_str());
327 let nv = valueset::from_result_value_iter(
328 v.iter().map(|vr| qs.clone_value(&attr_nk, vr)),
329 );
330 trace!(?nv, "new valueset transform");
331 match nv {
332 Ok(nvi) => Ok((attr_nk, nvi)),
333 Err(e) => Err(e),
334 }
335 })
336 .collect();
337
338 let x = map2?;
339
340 Ok(Entry {
341 state: EntryNew,
342 valid: EntryInit,
343 attrs: x,
344 })
345 }
346
347 #[instrument(level = "debug", skip_all)]
350 pub fn from_proto_entry_str(
351 es: &str,
352 qs: &mut QueryServerWriteTransaction,
353 ) -> Result<Self, OperationError> {
354 if cfg!(test) {
355 if es.len() > 256 {
356 let (dsp_es, _) = es.split_at(255);
357 trace!("Parsing -> {}...", dsp_es);
358 } else {
359 trace!("Parsing -> {}", es);
360 }
361 }
362 let pe: ProtoEntry = serde_json::from_str(es).map_err(|e| {
364 admin_error!(?e, "SerdeJson Failure");
367 OperationError::SerdeJsonError
368 })?;
369 Self::from_proto_entry(&pe, qs)
371 }
372
373 pub fn assign_cid(
376 mut self,
377 cid: Cid,
378 schema: &dyn SchemaTransaction,
379 ) -> Entry<EntryInvalid, EntryNew> {
380 let ecstate = EntryChangeState::new(&cid, &self.attrs, schema);
386
387 let cv = vs_cid![cid.clone()];
390 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
391 let cv = vs_cid![cid.clone()];
392 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
393
394 Entry {
395 valid: EntryInvalid { cid, ecstate },
396 state: EntryNew,
397 attrs: self.attrs,
398 }
399 }
400
401 pub fn compare(&self, rhs: &Entry<EntrySealed, EntryCommitted>) -> bool {
403 compare_attrs(&self.attrs, &rhs.attrs)
404 }
405
406 #[cfg(test)]
410 pub fn into_invalid_new(mut self) -> Entry<EntryInvalid, EntryNew> {
411 let cid = Cid::new_zero();
412 self.set_last_changed(cid.clone());
413
414 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
415
416 Entry {
417 valid: EntryInvalid { cid, ecstate },
418 state: EntryNew,
419 attrs: self.attrs,
420 }
421 }
422
423 #[cfg(test)]
427 pub fn into_valid_new(mut self) -> Entry<EntryValid, EntryNew> {
428 let cid = Cid::new_zero();
429 self.set_last_changed(cid.clone());
430 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
431
432 Entry {
433 valid: EntryValid {
434 ecstate,
435 uuid: self.get_uuid().expect("Invalid uuid"),
436 },
437 state: EntryNew,
438 attrs: self.attrs,
439 }
440 }
441
442 #[cfg(test)]
446 pub fn into_sealed_committed(mut self) -> Entry<EntrySealed, EntryCommitted> {
447 let cid = Cid::new_zero();
448 self.set_last_changed(cid.clone());
449 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
450 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
451 Entry {
452 valid: EntrySealed { uuid, ecstate },
453 state: EntryCommitted { id: 0 },
454 attrs: self.attrs,
455 }
456 }
457
458 #[cfg(test)]
462 pub fn into_sealed_new(mut self) -> Entry<EntrySealed, EntryNew> {
463 let cid = Cid::new_zero();
464 self.set_last_changed(cid.clone());
465 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
466
467 Entry {
468 valid: EntrySealed {
469 uuid: self.get_uuid().expect("Invalid uuid"),
470 ecstate,
471 },
472 state: EntryNew,
473 attrs: self.attrs,
474 }
475 }
476
477 pub fn add_ava(&mut self, attr: Attribute, value: Value) {
483 self.add_ava_int(attr, value);
484 }
485
486 pub fn pop_ava<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<ValueSet> {
487 self.attrs.remove(attr.as_ref())
488 }
489
490 pub fn remove_ava<A: AsRef<Attribute>>(&mut self, attr: A) {
491 self.pop_ava(attr);
492 }
493
494 pub fn set_ava_set(&mut self, attr: &Attribute, vs: ValueSet) {
496 self.attrs.insert(attr.clone(), vs);
497 }
498
499 pub fn set_ava<T>(&mut self, attr: Attribute, iter: T)
501 where
502 T: IntoIterator<Item = Value>,
503 {
504 self.set_ava_iter_int(attr, iter);
505 }
506
507 pub fn get_ava_mut<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<&mut ValueSet> {
508 self.attrs.get_mut(attr.as_ref())
509 }
510}
511
512impl From<SchemaAttribute> for EntryInitNew {
513 fn from(value: SchemaAttribute) -> Self {
514 EntryInitNew::from(&value)
515 }
516}
517
518impl From<&SchemaAttribute> for EntryInitNew {
519 fn from(s: &SchemaAttribute) -> Self {
520 let mut attrs = Eattrs::new();
522 attrs.insert(Attribute::AttributeName, vs_iutf8![s.name.as_str()]);
523 attrs.insert(Attribute::Description, vs_utf8![s.description.to_owned()]);
524 attrs.insert(Attribute::Uuid, vs_uuid![s.uuid]);
525 attrs.insert(Attribute::MultiValue, vs_bool![s.multivalue]);
526 attrs.insert(Attribute::Phantom, vs_bool![s.phantom]);
527 attrs.insert(Attribute::SyncAllowed, vs_bool![s.sync_allowed]);
528 attrs.insert(Attribute::Replicated, vs_bool![s.replicated.into()]);
529 attrs.insert(Attribute::Unique, vs_bool![s.unique]);
530 attrs.insert(Attribute::Indexed, vs_bool![s.indexed]);
531 attrs.insert(Attribute::Syntax, vs_syntax![s.syntax]);
532 attrs.insert(
533 Attribute::Class,
534 vs_iutf8![
535 EntryClass::Object.into(),
536 EntryClass::System.into(),
537 EntryClass::AttributeType.into()
538 ],
539 );
540
541 Entry {
544 valid: EntryInit,
545 state: EntryNew,
546 attrs,
547 }
548 }
549}
550
551impl From<SchemaClass> for EntryInitNew {
552 fn from(value: SchemaClass) -> Self {
553 EntryInitNew::from(&value)
554 }
555}
556
557impl From<&SchemaClass> for EntryInitNew {
558 fn from(s: &SchemaClass) -> Self {
559 let mut attrs = Eattrs::new();
560 attrs.insert(Attribute::ClassName, vs_iutf8![s.name.as_str()]);
561 attrs.insert(Attribute::Description, vs_utf8![s.description.to_owned()]);
562 attrs.insert(Attribute::SyncAllowed, vs_bool![s.sync_allowed]);
563 attrs.insert(Attribute::Uuid, vs_uuid![s.uuid]);
564 attrs.insert(
565 Attribute::Class,
566 vs_iutf8![
567 EntryClass::Object.into(),
568 EntryClass::System.into(),
569 EntryClass::ClassType.into()
570 ],
571 );
572
573 let vs_systemmay = ValueSetIutf8::from_iter(s.systemmay.iter().map(|sm| sm.as_str()));
574 if let Some(vs) = vs_systemmay {
575 attrs.insert(Attribute::SystemMay, vs);
576 }
577
578 let vs_systemmust = ValueSetIutf8::from_iter(s.systemmust.iter().map(|sm| sm.as_str()));
579 if let Some(vs) = vs_systemmust {
580 attrs.insert(Attribute::SystemMust, vs);
581 }
582
583 let vs_systemexcludes =
584 ValueSetIutf8::from_iter(s.systemexcludes.iter().map(|sm| sm.as_str()));
585 if let Some(vs) = vs_systemexcludes {
586 attrs.insert(Attribute::SystemExcludes, vs);
587 }
588
589 let vs_systemsupplements =
590 ValueSetIutf8::from_iter(s.systemsupplements.iter().map(|sm| sm.as_str()));
591 if let Some(vs) = vs_systemsupplements {
592 attrs.insert(Attribute::SystemSupplements, vs);
593 }
594
595 Entry {
596 valid: EntryInit,
597 state: EntryNew,
598 attrs,
599 }
600 }
601}
602
603impl Entry<EntryRefresh, EntryNew> {
604 pub fn from_repl_entry_v1(repl_entry: ReplEntryV1) -> Result<Self, OperationError> {
605 let (ecstate, mut attrs) = repl_entry.rehydrate()?;
607
608 let last_mod_cid = ecstate.get_max_cid();
611 let cv = vs_cid![last_mod_cid.clone()];
612 let _ = attrs.insert(Attribute::LastModifiedCid, cv);
613
614 let create_at_cid = ecstate.at();
615 let cv = vs_cid![create_at_cid.clone()];
616 let _ = attrs.insert(Attribute::CreatedAtCid, cv);
617
618 Ok(Entry {
619 valid: EntryRefresh { ecstate },
620 state: EntryNew,
621 attrs,
622 })
623 }
624}
625
626impl<STATE> Entry<EntryRefresh, STATE> {
627 pub fn validate(
628 self,
629 schema: &dyn SchemaTransaction,
630 ) -> Result<Entry<EntryValid, STATE>, SchemaError> {
631 let uuid: Uuid = self
632 .attrs
633 .get(&Attribute::Uuid)
634 .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
635 .and_then(|vs| {
636 vs.to_uuid_single()
637 .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
638 })?;
639
640 let ne = Entry {
642 valid: EntryValid {
643 uuid,
644 ecstate: self.valid.ecstate,
645 },
646 state: self.state,
647 attrs: self.attrs,
648 };
649
650 ne.validate(schema).map(|()| ne)
651 }
652}
653
654impl<STATE> Entry<EntryIncremental, STATE> {
655 pub fn get_uuid(&self) -> Uuid {
656 self.valid.uuid
657 }
658}
659
660impl Entry<EntryIncremental, EntryNew> {
661 fn stub_ecstate(&self) -> EntryChangeState {
662 self.valid.ecstate.stub()
663 }
664
665 pub fn rehydrate(repl_inc_entry: ReplIncrementalEntryV1) -> Result<Self, OperationError> {
666 let (uuid, ecstate, attrs) = repl_inc_entry.rehydrate()?;
667
668 Ok(Entry {
669 valid: EntryIncremental { uuid, ecstate },
670 state: EntryNew,
671 attrs,
672 })
673 }
674
675 pub(crate) fn is_add_conflict(&self, db_entry: &EntrySealedCommitted) -> bool {
676 use crate::repl::entry::State;
677 debug_assert_eq!(self.valid.uuid, db_entry.valid.uuid);
678 let self_cs = &self.valid.ecstate;
680 let db_cs = db_entry.get_changestate();
681
682 match (self_cs.current(), db_cs.current()) {
684 (State::Live { at: at_left, .. }, State::Live { at: at_right, .. }) => {
685 at_left != at_right
686 }
687 _ => false,
689 }
690 }
691
692 pub(crate) fn resolve_add_conflict(
693 &self,
694 cid: &Cid,
695 db_ent: &EntrySealedCommitted,
696 ) -> (Option<EntrySealedNew>, EntryIncrementalCommitted) {
697 use crate::repl::entry::State;
698 debug_assert_eq!(self.valid.uuid, db_ent.valid.uuid);
699 let self_cs = &self.valid.ecstate;
700 let db_cs = db_ent.get_changestate();
701
702 match (self_cs.current(), db_cs.current()) {
703 (
704 State::Live {
705 at: at_left,
706 changes: _changes_left,
707 },
708 State::Live {
709 at: at_right,
710 changes: _changes_right,
711 },
712 ) => {
713 debug_assert!(at_left != at_right);
714 if at_left > at_right {
725 trace!("RI > DE, return DE");
726 (
727 None,
728 Entry {
729 valid: EntryIncremental {
730 uuid: db_ent.valid.uuid,
731 ecstate: db_cs.clone(),
732 },
733 state: EntryCommitted {
734 id: db_ent.state.id,
735 },
736 attrs: db_ent.attrs.clone(),
737 },
738 )
739 }
740 else {
750 trace!("RI < DE, return RI");
751 let conflict = if at_right.s_uuid == cid.s_uuid {
753 trace!("Origin process conflict entry");
754 let mut cnf_ent = Entry {
757 valid: EntryInvalid {
758 cid: cid.clone(),
759 ecstate: db_cs.clone(),
760 },
761 state: EntryNew,
762 attrs: db_ent.attrs.clone(),
763 };
764
765 cnf_ent.add_ava(Attribute::SourceUuid, Value::Uuid(db_ent.valid.uuid));
767
768 let new_uuid = Uuid::new_v4();
770 cnf_ent.purge_ava(Attribute::Uuid);
771 cnf_ent.add_ava(Attribute::Uuid, Value::Uuid(new_uuid));
772 cnf_ent.add_ava(Attribute::Class, EntryClass::Recycled.into());
773 cnf_ent.add_ava(Attribute::Class, EntryClass::Conflict.into());
774
775 let cv = vs_cid![cid.clone()];
779 let _ = cnf_ent.attrs.insert(Attribute::LastModifiedCid, cv);
780 let cv = vs_cid![cid.clone()];
782 let _ = cnf_ent.attrs.insert(Attribute::CreatedAtCid, cv);
783
784 let Entry {
788 valid: EntryInvalid { cid: _, ecstate },
789 state,
790 attrs,
791 } = cnf_ent;
792
793 let cnf_ent = Entry {
794 valid: EntrySealed {
795 uuid: new_uuid,
796 ecstate,
797 },
798 state,
799 attrs,
800 };
801
802 Some(cnf_ent)
803 } else {
804 None
805 };
806
807 let mut attrs = self.attrs.clone();
811 let ecstate = self_cs.clone();
812
813 let last_mod_cid = ecstate.get_max_cid();
814 let cv = vs_cid![last_mod_cid.clone()];
815 let _ = attrs.insert(Attribute::LastModifiedCid, cv);
816
817 let create_at_cid = ecstate.at();
818 let cv = vs_cid![create_at_cid.clone()];
819 let _ = attrs.insert(Attribute::CreatedAtCid, cv);
820
821 (
822 conflict,
823 Entry {
824 valid: EntryIncremental {
825 uuid: self.valid.uuid,
826 ecstate,
827 },
828 state: EntryCommitted {
829 id: db_ent.state.id,
830 },
831 attrs,
832 },
833 )
834 }
835 }
836 _ => unreachable!(),
840 }
841 }
842
843 pub(crate) fn merge_state(
844 &self,
845 db_ent: &EntrySealedCommitted,
846 schema: &dyn SchemaTransaction,
847 trim_cid: &Cid,
848 ) -> EntryIncrementalCommitted {
849 use crate::repl::entry::State;
850
851 debug_assert_eq!(self.valid.uuid, db_ent.valid.uuid);
853
854 let self_cs = &self.valid.ecstate;
857 let db_cs = db_ent.get_changestate();
858
859 match (self_cs.current(), db_cs.current()) {
860 (
861 State::Live {
862 at: at_left,
863 changes: changes_left,
864 },
865 State::Live {
866 at: at_right,
867 changes: changes_right,
868 },
869 ) => {
870 debug_assert_eq!(at_left, at_right);
871 let mut attr_set: Vec<_> =
876 changes_left.keys().chain(changes_right.keys()).collect();
877 attr_set.shrink_to_fit();
878 attr_set.sort_unstable();
879 attr_set.dedup();
880
881 let mut changes = BTreeMap::default();
883 let mut eattrs = Eattrs::default();
884
885 for attr_name in attr_set.into_iter() {
887 match (changes_left.get(attr_name), changes_right.get(attr_name)) {
888 (Some(cid_left), Some(cid_right)) => {
889 let take_left = cid_left > cid_right;
896
897 match (self.attrs.get(attr_name), db_ent.attrs.get(attr_name)) {
898 (Some(vs_left), Some(vs_right)) if take_left => {
899 changes.insert(attr_name.clone(), cid_left.clone());
900 #[allow(clippy::todo)]
901 if let Some(merged_attr_state) =
902 vs_left.repl_merge_valueset(vs_right, trim_cid)
903 {
904 eattrs.insert(attr_name.clone(), merged_attr_state);
907 } else {
908 eattrs.insert(attr_name.clone(), vs_left.clone());
909 }
910 }
911 (Some(vs_left), Some(vs_right)) => {
912 changes.insert(attr_name.clone(), cid_right.clone());
913 #[allow(clippy::todo)]
914 if let Some(merged_attr_state) =
915 vs_right.repl_merge_valueset(vs_left, trim_cid)
916 {
917 eattrs.insert(attr_name.clone(), merged_attr_state);
920 } else {
921 eattrs.insert(attr_name.clone(), vs_right.clone());
922 }
923 }
924 (Some(vs_left), None) if take_left => {
925 changes.insert(attr_name.clone(), cid_left.clone());
926 eattrs.insert(attr_name.clone(), vs_left.clone());
927 }
928 (Some(_vs_left), None) => {
929 changes.insert(attr_name.clone(), cid_right.clone());
930 }
932 (None, Some(_vs_right)) if take_left => {
933 changes.insert(attr_name.clone(), cid_left.clone());
934 }
936 (None, Some(vs_right)) => {
937 changes.insert(attr_name.clone(), cid_right.clone());
938 eattrs.insert(attr_name.clone(), vs_right.clone());
939 }
940 (None, None) if take_left => {
941 changes.insert(attr_name.clone(), cid_left.clone());
942 }
944 (None, None) => {
945 changes.insert(attr_name.clone(), cid_right.clone());
946 }
948 }
949 }
951 (Some(cid_left), None) => {
952 changes.insert(attr_name.clone(), cid_left.clone());
954 if let Some(valueset) = self.attrs.get(attr_name) {
955 eattrs.insert(attr_name.clone(), valueset.clone());
956 }
957 }
958 (None, Some(cid_right)) => {
959 changes.insert(attr_name.clone(), cid_right.clone());
961 if let Some(valueset) = db_ent.attrs.get(attr_name) {
962 eattrs.insert(attr_name.clone(), valueset.clone());
963 }
964 }
965 (None, None) => {
966 debug_assert!(false);
968 }
969 }
970 }
971
972 let mut ecstate = EntryChangeState::build(State::Live {
973 at: at_left.clone(),
974 changes,
975 });
976
977 ecstate.retain(|k, _| schema.is_replicated(k));
981
982 let cv = vs_cid![ecstate.get_max_cid().clone()];
983 let _ = eattrs.insert(Attribute::LastModifiedCid, cv);
984
985 let cv = vs_cid![ecstate.at().clone()];
986 let _ = eattrs.insert(Attribute::CreatedAtCid, cv);
987
988 Entry {
989 valid: EntryIncremental {
990 uuid: self.valid.uuid,
991 ecstate,
992 },
993 state: EntryCommitted {
994 id: db_ent.state.id,
995 },
996 attrs: eattrs,
997 }
998 }
999 (State::Tombstone { at: left_at }, State::Live { .. }) => {
1000 let mut attrs_new: Eattrs = Map::new();
1004 let class_ava = vs_iutf8![EntryClass::Object.into(), EntryClass::Tombstone.into()];
1005 let last_mod_ava = vs_cid![left_at.clone()];
1006 let created_ava = vs_cid![left_at.clone()];
1007
1008 attrs_new.insert(Attribute::Uuid, vs_uuid![self.valid.uuid]);
1009 attrs_new.insert(Attribute::Class, class_ava);
1010 attrs_new.insert(Attribute::LastModifiedCid, last_mod_ava);
1011 attrs_new.insert(Attribute::CreatedAtCid, created_ava);
1012
1013 Entry {
1014 valid: EntryIncremental {
1015 uuid: self.valid.uuid,
1016 ecstate: self.valid.ecstate.clone(),
1017 },
1018 state: EntryCommitted {
1019 id: db_ent.state.id,
1020 },
1021 attrs: attrs_new,
1022 }
1023 }
1024 (State::Live { .. }, State::Tombstone { .. }) => {
1025 Entry {
1033 valid: EntryIncremental {
1034 uuid: db_ent.valid.uuid,
1035 ecstate: db_ent.valid.ecstate.clone(),
1036 },
1037 state: EntryCommitted {
1038 id: db_ent.state.id,
1039 },
1040 attrs: db_ent.attrs.clone(),
1041 }
1042 }
1043 (State::Tombstone { at: left_at }, State::Tombstone { at: right_at }) => {
1044 let (at, ecstate) = if left_at < right_at {
1049 (left_at, self.valid.ecstate.clone())
1050 } else {
1051 (right_at, db_ent.valid.ecstate.clone())
1052 };
1053
1054 let mut attrs_new: Eattrs = Map::new();
1055 let class_ava = vs_iutf8![EntryClass::Object.into(), EntryClass::Tombstone.into()];
1056 let last_mod_ava = vs_cid![at.clone()];
1057 let created_ava = vs_cid![at.clone()];
1058
1059 attrs_new.insert(Attribute::Uuid, vs_uuid![db_ent.valid.uuid]);
1060 attrs_new.insert(Attribute::Class, class_ava);
1061 attrs_new.insert(Attribute::LastModifiedCid, last_mod_ava);
1062 attrs_new.insert(Attribute::CreatedAtCid, created_ava);
1063
1064 Entry {
1065 valid: EntryIncremental {
1066 uuid: db_ent.valid.uuid,
1067 ecstate,
1068 },
1069 state: EntryCommitted {
1070 id: db_ent.state.id,
1071 },
1072 attrs: attrs_new,
1073 }
1074 }
1075 }
1076 }
1077}
1078
1079impl Entry<EntryIncremental, EntryCommitted> {
1080 pub(crate) fn validate_repl(self, schema: &dyn SchemaTransaction) -> EntryValidCommitted {
1081 let mut ne = Entry {
1086 valid: EntryValid {
1087 uuid: self.valid.uuid,
1088 ecstate: self.valid.ecstate,
1089 },
1090 state: self.state,
1091 attrs: self.attrs,
1092 };
1093
1094 if let Err(e) = ne.validate(schema) {
1095 warn!(uuid = ?self.valid.uuid, err = ?e, "Entry failed schema check, moving to a conflict state");
1096 ne.add_ava_int(Attribute::Class, EntryClass::Recycled.into());
1097 ne.add_ava_int(Attribute::Class, EntryClass::Conflict.into());
1098 ne.add_ava_int(Attribute::SourceUuid, Value::Uuid(self.valid.uuid));
1099 }
1100 ne
1101 }
1102}
1103
1104impl<STATE> Entry<EntryInvalid, STATE> {
1105 pub(crate) fn get_uuid(&self) -> Option<Uuid> {
1106 self.attrs
1107 .get(&Attribute::Uuid)
1108 .and_then(|vs| vs.to_uuid_single())
1109 }
1110
1111 pub fn validate(
1114 self,
1115 schema: &dyn SchemaTransaction,
1116 ) -> Result<Entry<EntryValid, STATE>, SchemaError> {
1117 let uuid: Uuid = self
1118 .attrs
1119 .get(&Attribute::Uuid)
1120 .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
1121 .and_then(|vs| {
1122 vs.to_uuid_single()
1123 .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
1124 })?;
1125
1126 let ne = Entry {
1128 valid: EntryValid {
1129 uuid,
1130 ecstate: self.valid.ecstate,
1131 },
1132 state: self.state,
1133 attrs: self.attrs,
1134 };
1135
1136 ne.validate(schema).map(|()| ne)
1137 }
1138
1139 pub(crate) fn get_ava_refer_mut<A: AsRef<Attribute>>(
1143 &mut self,
1144 attr: A,
1145 ) -> Option<&mut BTreeSet<Uuid>> {
1146 self.get_ava_mut(attr).and_then(|vs| vs.as_refer_set_mut())
1147 }
1148
1149 pub(crate) fn get_ava_mut<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<&mut ValueSet> {
1150 let attr_ref = attr.as_ref();
1151 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
1152 self.attrs.get_mut(attr.as_ref())
1153 }
1154}
1155
1156impl<VALID, STATE> Clone for Entry<VALID, STATE>
1157where
1158 VALID: Clone,
1159 STATE: Clone,
1160{
1161 fn clone(&self) -> Entry<VALID, STATE> {
1163 Entry {
1164 valid: self.valid.clone(),
1165 state: self.state.clone(),
1166 attrs: self.attrs.clone(),
1167 }
1168 }
1169}
1170
1171impl Entry<EntryInvalid, EntryCommitted> {
1172 #[cfg(test)]
1176 pub fn into_valid_new(self) -> Entry<EntryValid, EntryNew> {
1177 let uuid = self.get_uuid().expect("Invalid uuid");
1178 Entry {
1179 valid: EntryValid {
1180 uuid,
1181 ecstate: self.valid.ecstate,
1182 },
1183 state: EntryNew,
1184 attrs: self.attrs,
1185 }
1186 }
1187
1188 pub fn to_recycled(mut self) -> Self {
1190 self.add_ava(Attribute::Class, EntryClass::Recycled.into());
1192
1193 Entry {
1197 valid: self.valid,
1198 state: self.state,
1199 attrs: self.attrs,
1200 }
1201 }
1202
1203 pub fn to_conflict<T>(&mut self, iter: T)
1205 where
1206 T: IntoIterator<Item = Uuid>,
1207 {
1208 self.add_ava(Attribute::Class, EntryClass::Recycled.into());
1209 self.add_ava(Attribute::Class, EntryClass::Conflict.into());
1210 for source_uuid in iter {
1212 self.add_ava(Attribute::SourceUuid, Value::Uuid(source_uuid));
1213 }
1214 }
1215
1216 pub fn to_revived(mut self) -> Self {
1218 self.remove_ava(Attribute::Class, &EntryClass::Recycled.into());
1220 self.remove_ava(Attribute::Class, &EntryClass::Conflict.into());
1221
1222 self.purge_ava(Attribute::CascadeDeleted);
1223 self.purge_ava(Attribute::SourceUuid);
1224 self.purge_ava(Attribute::RecycledDirectMemberOf);
1225
1226 Entry {
1230 valid: self.valid,
1231 state: self.state,
1232 attrs: self.attrs,
1233 }
1234 }
1235}
1236impl Entry<EntryInvalid, EntryNew> {
1239 #[cfg(test)]
1242 pub fn into_init_new(self) -> Entry<EntryInit, EntryNew> {
1243 Entry {
1244 valid: EntryInit,
1245 state: EntryNew,
1246 attrs: self.attrs,
1247 }
1248 }
1249
1250 #[cfg(test)]
1254 pub fn into_valid_new(self) -> Entry<EntryValid, EntryNew> {
1255 let uuid = self.get_uuid().expect("Invalid uuid");
1256 Entry {
1257 valid: EntryValid {
1258 uuid,
1259 ecstate: self.valid.ecstate,
1260 },
1261 state: EntryNew,
1262 attrs: self.attrs,
1263 }
1264 }
1265
1266 #[cfg(test)]
1270 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1271 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
1272 Entry {
1273 valid: EntrySealed {
1274 uuid,
1275 ecstate: self.valid.ecstate,
1276 },
1277 state: EntryCommitted { id: 0 },
1278 attrs: self.attrs,
1279 }
1280 }
1281
1282 #[cfg(test)]
1286 pub fn into_valid_committed(self) -> Entry<EntryValid, EntryCommitted> {
1287 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
1288 Entry {
1289 valid: EntryValid {
1290 uuid,
1291 ecstate: self.valid.ecstate,
1292 },
1293 state: EntryCommitted { id: 0 },
1294 attrs: self.attrs,
1295 }
1296 }
1297}
1298
1299impl Entry<EntryInvalid, EntryCommitted> {
1300 #[cfg(test)]
1304 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1305 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
1306 Entry {
1307 valid: EntrySealed {
1308 uuid,
1309 ecstate: self.valid.ecstate,
1310 },
1311 state: self.state,
1312 attrs: self.attrs,
1313 }
1314 }
1315}
1316
1317impl Entry<EntrySealed, EntryNew> {
1318 #[cfg(test)]
1322 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1323 Entry {
1324 valid: self.valid,
1325 state: EntryCommitted { id: 0 },
1326 attrs: self.attrs,
1327 }
1328 }
1329
1330 pub fn into_sealed_committed_id(self, id: u64) -> Entry<EntrySealed, EntryCommitted> {
1333 Entry {
1334 valid: self.valid,
1335 state: EntryCommitted { id },
1336 attrs: self.attrs,
1337 }
1338 }
1339
1340 pub fn compare(&self, rhs: &Entry<EntrySealed, EntryNew>) -> bool {
1341 compare_attrs(&self.attrs, &rhs.attrs)
1342 }
1343}
1344
1345type IdxDiff<'a> =
1346 Vec<Result<(&'a Attribute, IndexType, String), (&'a Attribute, IndexType, String)>>;
1347
1348impl<VALID> Entry<VALID, EntryCommitted> {
1349 pub fn get_id(&self) -> u64 {
1351 self.state.id
1352 }
1353}
1354
1355impl<STATE> Entry<EntrySealed, STATE> {
1356 pub fn into_init(self) -> Entry<EntryInit, STATE> {
1357 Entry {
1358 valid: EntryInit,
1359 state: self.state,
1360 attrs: self.attrs,
1361 }
1362 }
1363}
1364
1365impl Entry<EntrySealed, EntryCommitted> {
1366 #[cfg(test)]
1367 pub(crate) fn get_last_changed(&self) -> Cid {
1368 self.valid.ecstate.get_max_cid().clone()
1369 }
1370
1371 #[cfg(test)]
1373 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1374 self
1376 }
1377
1378 pub(crate) fn stub_sealed_committed_id(
1379 id: u64,
1380 ctx_ent: &EntryIncrementalNew,
1381 ) -> EntrySealedCommitted {
1382 let uuid = ctx_ent.get_uuid();
1383 let ecstate = ctx_ent.stub_ecstate();
1384
1385 Entry {
1386 valid: EntrySealed { uuid, ecstate },
1387 state: EntryCommitted { id },
1388 attrs: Default::default(),
1389 }
1390 }
1391
1392 pub fn insert_claim(&mut self, value: &str) {
1395 self.add_ava_int(Attribute::Claim, Value::new_iutf8(value));
1396 }
1397
1398 pub fn compare(&self, rhs: &Entry<EntrySealed, EntryCommitted>) -> bool {
1399 compare_attrs(&self.attrs, &rhs.attrs)
1400 }
1401
1402 pub fn to_dbentry(&self) -> DbEntry {
1404 DbEntry {
1407 ent: DbEntryVers::V3 {
1408 changestate: self.valid.ecstate.to_db_changestate(),
1409 attrs: self
1410 .attrs
1411 .iter()
1412 .map(|(k, vs)| {
1413 let dbvs: DbValueSetV2 = vs.to_db_valueset_v2();
1414 (k.clone(), dbvs)
1415 })
1416 .collect(),
1417 },
1418 }
1419 }
1420
1421 #[inline]
1422 fn get_name2uuid_cands(&self) -> Set<String> {
1425 let cands = [Attribute::Spn, Attribute::Name, Attribute::GidNumber];
1431 cands
1432 .iter()
1433 .filter_map(|cand| {
1434 self.attrs
1435 .get(cand)
1436 .map(|vs| vs.to_proto_string_clone_iter())
1437 })
1438 .flatten()
1439 .collect()
1440 }
1441
1442 #[inline]
1443 fn get_externalid2uuid(&self) -> Option<String> {
1446 self.attrs
1447 .get(&Attribute::SyncExternalId)
1448 .and_then(|vs| vs.to_proto_string_single())
1449 }
1450
1451 #[inline]
1452 pub(crate) fn get_uuid2spn(&self) -> Value {
1455 self.attrs
1456 .get(&Attribute::Spn)
1457 .and_then(|vs| vs.to_value_single())
1458 .or_else(|| {
1459 self.attrs
1460 .get(&Attribute::Name)
1461 .and_then(|vs| vs.to_value_single())
1462 })
1463 .unwrap_or_else(|| Value::Uuid(self.get_uuid()))
1464 }
1465
1466 #[inline]
1467 pub(crate) fn get_uuid2rdn(&self) -> String {
1471 self.attrs
1472 .get(&Attribute::Spn)
1473 .and_then(|vs| vs.to_proto_string_single().map(|v| format!("spn={v}")))
1474 .or_else(|| {
1475 self.attrs
1476 .get(&Attribute::Name)
1477 .and_then(|vs| vs.to_proto_string_single().map(|v| format!("name={v}")))
1478 })
1479 .unwrap_or_else(|| format!("uuid={}", self.get_uuid().as_hyphenated()))
1480 }
1481
1482 pub(crate) fn idx_name2uuid_diff(
1485 pre: Option<&Self>,
1486 post: Option<&Self>,
1487 ) -> (
1488 Option<Set<String>>,
1490 Option<Set<String>>,
1492 ) {
1493 match (pre, post) {
1495 (None, None) => {
1496 (None, None)
1498 }
1499 (None, Some(b)) => {
1500 (Some(b.get_name2uuid_cands()), None)
1503 }
1504 (Some(a), None) => {
1505 (None, Some(a.get_name2uuid_cands()))
1507 }
1508 (Some(a), Some(b)) => {
1509 let pre_set = a.get_name2uuid_cands();
1510 let post_set = b.get_name2uuid_cands();
1511
1512 let add_set: Set<_> = post_set.difference(&pre_set).cloned().collect();
1514 let rem_set: Set<_> = pre_set.difference(&post_set).cloned().collect();
1516 (Some(add_set), Some(rem_set))
1517 }
1518 }
1519 }
1520
1521 pub(crate) fn idx_externalid2uuid_diff(
1523 pre: Option<&Self>,
1524 post: Option<&Self>,
1525 ) -> (Option<String>, Option<String>) {
1526 match (pre, post) {
1527 (None, None) => {
1528 (None, None)
1530 }
1531 (None, Some(b)) => {
1532 (b.get_externalid2uuid(), None)
1534 }
1535 (Some(a), None) => {
1536 (None, a.get_externalid2uuid())
1538 }
1539 (Some(a), Some(b)) => {
1540 let ia = a.get_externalid2uuid();
1541 let ib = b.get_externalid2uuid();
1542 if ia != ib {
1543 (ib, ia)
1546 } else {
1547 (None, None)
1549 }
1550 }
1551 }
1552 }
1553
1554 pub(crate) fn idx_uuid2spn_diff(
1557 pre: Option<&Self>,
1558 post: Option<&Self>,
1559 ) -> Option<Result<Value, ()>> {
1560 match (pre, post) {
1561 (None, None) => {
1562 None
1564 }
1565 (None, Some(b)) => {
1566 Some(Ok(b.get_uuid2spn()))
1568 }
1569 (Some(_a), None) => {
1570 Some(Err(()))
1572 }
1573 (Some(a), Some(b)) => {
1574 let ia = a.get_uuid2spn();
1575 let ib = b.get_uuid2spn();
1576 if ia != ib {
1577 Some(Ok(ib))
1579 } else {
1580 None
1582 }
1583 }
1584 }
1585 }
1586
1587 pub(crate) fn idx_uuid2rdn_diff(
1590 pre: Option<&Self>,
1591 post: Option<&Self>,
1592 ) -> Option<Result<String, ()>> {
1593 match (pre, post) {
1594 (None, None) => {
1595 None
1597 }
1598 (None, Some(b)) => {
1599 Some(Ok(b.get_uuid2rdn()))
1601 }
1602 (Some(_a), None) => {
1603 Some(Err(()))
1605 }
1606 (Some(a), Some(b)) => {
1607 let ia = a.get_uuid2rdn();
1608 let ib = b.get_uuid2rdn();
1609 if ia != ib {
1610 Some(Ok(ib))
1612 } else {
1613 None
1615 }
1616 }
1617 }
1618 }
1619
1620 pub(crate) fn idx_diff<'a>(
1623 idxmeta: &'a HashMap<IdxKey, IdxSlope>,
1624 pre: Option<&Self>,
1625 post: Option<&Self>,
1626 ) -> IdxDiff<'a> {
1627 match (pre, post) {
1632 (None, None) => {
1633 Vec::with_capacity(0)
1635 }
1636 (Some(pre_e), None) => {
1637 idxmeta
1639 .keys()
1640 .flat_map(|ikey| {
1641 match pre_e.get_ava_set(&ikey.attr) {
1642 None => Vec::with_capacity(0),
1643 Some(vs) => {
1644 let changes: Vec<Result<_, _>> = match ikey.itype {
1645 IndexType::Equality => {
1646 vs.generate_idx_eq_keys()
1648 .into_iter()
1649 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1650 .collect()
1651 }
1652 IndexType::Presence => {
1653 vec![Err((&ikey.attr, ikey.itype, "_".to_string()))]
1654 }
1655 IndexType::SubString => vs
1656 .generate_idx_sub_keys()
1657 .into_iter()
1658 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1659 .collect(),
1660 IndexType::Ordering => vs
1661 .generate_idx_ord_keys()
1662 .into_iter()
1663 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1664 .collect(),
1665 };
1666 changes
1667 }
1668 }
1669 })
1670 .collect()
1671 }
1672 (None, Some(post_e)) => {
1673 idxmeta
1675 .keys()
1676 .flat_map(|ikey| {
1677 match post_e.get_ava_set(&ikey.attr) {
1678 None => Vec::with_capacity(0),
1679 Some(vs) => {
1680 let changes: Vec<Result<_, _>> = match ikey.itype {
1681 IndexType::Equality => vs
1682 .generate_idx_eq_keys()
1683 .into_iter()
1684 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1685 .collect(),
1686 IndexType::Presence => {
1687 vec![Ok((&ikey.attr, ikey.itype, "_".to_string()))]
1688 }
1689 IndexType::SubString => vs
1690 .generate_idx_sub_keys()
1691 .into_iter()
1692 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1693 .collect(),
1694 IndexType::Ordering => vs
1695 .generate_idx_ord_keys()
1696 .into_iter()
1697 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1698 .collect(),
1699 };
1700 changes
1703 }
1704 }
1705 })
1706 .collect()
1707 }
1708 (Some(pre_e), Some(post_e)) => {
1709 assert_eq!(pre_e.state.id, post_e.state.id);
1710 idxmeta
1711 .keys()
1712 .flat_map(|ikey| {
1713 match (
1714 pre_e.get_ava_set(&ikey.attr),
1715 post_e.get_ava_set(&ikey.attr),
1716 ) {
1717 (None, None) => {
1718 Vec::with_capacity(0)
1720 }
1721 (Some(pre_vs), None) => {
1722 let changes: Vec<Result<_, _>> = match ikey.itype {
1724 IndexType::Equality => {
1725 pre_vs
1728 .generate_idx_eq_keys()
1729 .into_iter()
1730 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1731 .collect()
1732 }
1733 IndexType::Presence => {
1734 vec![Err((&ikey.attr, ikey.itype, "_".to_string()))]
1735 }
1736 IndexType::SubString => pre_vs
1737 .generate_idx_sub_keys()
1738 .into_iter()
1739 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1740 .collect(),
1741 IndexType::Ordering => pre_vs
1742 .generate_idx_ord_keys()
1743 .into_iter()
1744 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1745 .collect(),
1746 };
1747 changes
1748 }
1749 (None, Some(post_vs)) => {
1750 let changes: Vec<Result<_, _>> = match ikey.itype {
1752 IndexType::Equality => {
1753 post_vs
1756 .generate_idx_eq_keys()
1757 .into_iter()
1758 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1759 .collect()
1760 }
1761 IndexType::Presence => {
1762 vec![Ok((&ikey.attr, ikey.itype, "_".to_string()))]
1763 }
1764 IndexType::SubString => post_vs
1765 .generate_idx_sub_keys()
1766 .into_iter()
1767 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1768 .collect(),
1769 IndexType::Ordering => post_vs
1770 .generate_idx_ord_keys()
1771 .into_iter()
1772 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1773 .collect(),
1774 };
1775 changes
1776 }
1777 (Some(pre_vs), Some(post_vs)) => {
1778 let (mut pre_idx_keys, mut post_idx_keys) = match ikey.itype {
1780 IndexType::Equality => (
1781 pre_vs.generate_idx_eq_keys(),
1782 post_vs.generate_idx_eq_keys(),
1783 ),
1784 IndexType::Presence => {
1785 (Vec::with_capacity(0), Vec::with_capacity(0))
1787 }
1788 IndexType::SubString => (
1789 pre_vs.generate_idx_sub_keys(),
1790 post_vs.generate_idx_sub_keys(),
1791 ),
1792 IndexType::Ordering => (
1793 pre_vs.generate_idx_ord_keys(),
1794 post_vs.generate_idx_ord_keys(),
1795 ),
1796 };
1797
1798 let sz = if pre_idx_keys.len() > post_idx_keys.len() {
1799 pre_idx_keys.len()
1800 } else {
1801 post_idx_keys.len()
1802 };
1803
1804 let mut added_vs = Vec::with_capacity(sz);
1805 let mut removed_vs = Vec::with_capacity(sz);
1806
1807 if sz > 0 {
1808 pre_idx_keys.sort_unstable();
1809 post_idx_keys.sort_unstable();
1810
1811 let mut pre_iter = pre_idx_keys.iter();
1812 let mut post_iter = post_idx_keys.iter();
1813
1814 let mut pre = pre_iter.next();
1815 let mut post = post_iter.next();
1816
1817 loop {
1818 match (pre, post) {
1819 (Some(a), Some(b)) => {
1820 match a.cmp(b) {
1821 Ordering::Less => {
1822 removed_vs.push(a.clone());
1823 pre = pre_iter.next();
1824 }
1825 Ordering::Equal => {
1826 pre = pre_iter.next();
1828 post = post_iter.next();
1829 }
1830 Ordering::Greater => {
1831 added_vs.push(b.clone());
1832 post = post_iter.next();
1833 }
1834 }
1835 }
1836 (Some(a), None) => {
1837 removed_vs.push(a.clone());
1838 pre = pre_iter.next();
1839 }
1840 (None, Some(b)) => {
1841 added_vs.push(b.clone());
1842 post = post_iter.next();
1843 }
1844 (None, None) => {
1845 break;
1846 }
1847 }
1848 }
1849 } let mut diff =
1852 Vec::with_capacity(removed_vs.len() + added_vs.len());
1853
1854 match ikey.itype {
1855 IndexType::SubString
1856 | IndexType::Ordering
1857 | IndexType::Equality => {
1858 removed_vs
1859 .into_iter()
1860 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1861 .for_each(|v| diff.push(v));
1862 added_vs
1863 .into_iter()
1864 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1865 .for_each(|v| diff.push(v));
1866 }
1867 IndexType::Presence => {
1868 }
1870 };
1871 diff
1873 }
1874 }
1875 })
1876 .collect()
1877 }
1879 }
1880 }
1881
1882 pub fn from_dbentry(db_e: DbEntry, id: u64) -> Option<Self> {
1883 let (attrs, ecstate) = match db_e.ent {
1886 DbEntryVers::V3 { changestate, attrs } => {
1887 let ecstate = EntryChangeState::from_db_changestate(changestate);
1888
1889 let r_attrs = attrs
1890 .into_iter()
1891 .filter(|(_k, vs)| !vs.is_empty())
1893 .map(|(k, dbvs)| {
1894 valueset::from_db_valueset_v2(dbvs)
1895 .map(|vs: ValueSet| (k, vs))
1896 .map_err(|e| {
1897 error!(?e, "from_dbentry failed");
1898 })
1899 })
1900 .collect::<Result<Eattrs, ()>>()
1901 .ok()?;
1902
1903 (r_attrs, ecstate)
1904 }
1905 };
1906
1907 let uuid = attrs
1908 .get(&Attribute::Uuid)
1909 .and_then(|vs| vs.to_uuid_single())?;
1910
1911 Some(Entry {
1912 valid: EntrySealed { uuid, ecstate },
1913 state: EntryCommitted { id },
1914 attrs,
1915 })
1916 }
1917
1918 #[cfg(test)]
1926 pub(crate) fn into_reduced(self) -> Entry<EntryReduced, EntryCommitted> {
1927 Entry {
1928 valid: EntryReduced {
1929 uuid: self.valid.uuid,
1930 effective_access: None,
1931 },
1932 state: self.state,
1933 attrs: self.attrs,
1934 }
1935 }
1936
1937 pub fn reduce_attributes(
1940 &self,
1941 allowed_attrs: &BTreeSet<Attribute>,
1942 effective_access: Option<Box<AccessEffectivePermission>>,
1943 ) -> Entry<EntryReduced, EntryCommitted> {
1944 let f_attrs: Map<_, _> = self
1946 .attrs
1947 .iter()
1948 .filter_map(|(k, v)| {
1949 if allowed_attrs.contains(k) {
1950 Some((k.clone(), v.clone()))
1951 } else {
1952 None
1953 }
1954 })
1955 .collect();
1956
1957 let valid = EntryReduced {
1958 uuid: self.valid.uuid,
1959 effective_access,
1960 };
1961 let state = self.state.clone();
1962
1963 Entry {
1964 valid,
1965 state,
1966 attrs: f_attrs,
1967 }
1968 }
1969
1970 pub fn to_tombstone(&self, cid: Cid) -> Entry<EntryInvalid, EntryCommitted> {
1972 let mut ecstate = self.valid.ecstate.clone();
1973 let mut attrs_new: Eattrs = Map::new();
1975
1976 let class_ava = vs_iutf8![EntryClass::Object.into(), EntryClass::Tombstone.into()];
1977 let last_mod_ava = vs_cid![cid.clone()];
1978 let created_ava = vs_cid![cid.clone()];
1979
1980 attrs_new.insert(Attribute::Uuid, vs_uuid![self.get_uuid()]);
1981 attrs_new.insert(Attribute::Class, class_ava);
1982 attrs_new.insert(Attribute::LastModifiedCid, last_mod_ava);
1983 attrs_new.insert(Attribute::CreatedAtCid, created_ava);
1984
1985 ecstate.tombstone(&cid);
1987
1988 Entry {
1989 valid: EntryInvalid { cid, ecstate },
1990 state: self.state.clone(),
1991 attrs: attrs_new,
1992 }
1993 }
1994
1995 pub fn into_valid(self, ecstate: EntryChangeState) -> Entry<EntryValid, EntryCommitted> {
1997 Entry {
1998 valid: EntryValid {
1999 uuid: self.valid.uuid,
2000 ecstate,
2001 },
2002 state: self.state,
2003 attrs: self.attrs,
2004 }
2005 }
2006
2007 pub fn verify(
2008 &self,
2009 schema: &dyn SchemaTransaction,
2010 results: &mut Vec<Result<(), ConsistencyError>>,
2011 ) {
2012 self.valid
2013 .ecstate
2014 .verify(schema, &self.attrs, self.state.id, results);
2015 }
2016}
2017
2018impl<STATE> Entry<EntryValid, STATE> {
2019 fn validate(&self, schema: &dyn SchemaTransaction) -> Result<(), SchemaError> {
2020 let schema_classes = schema.get_classes();
2021 let schema_attributes = schema.get_attributes();
2022
2023 trace!(?self.attrs, "Entry::validate -> target");
2025
2026 if !self.attribute_pres(Attribute::Class) {
2028 return Err(SchemaError::NoClassFound);
2030 }
2031
2032 if self.attribute_equality(Attribute::Class, &EntryClass::Conflict.into()) {
2033 trace!("Skipping schema validation on conflict entry");
2035 return Ok(());
2036 };
2037
2038 let recycled = self.attribute_equality(Attribute::Class, &EntryClass::Recycled.into());
2040
2041 let extensible =
2044 self.attribute_equality(Attribute::Class, &EntryClass::ExtensibleObject.into());
2045
2046 let entry_classes = self.get_ava_set(Attribute::Class).ok_or_else(|| {
2047 admin_debug!("Attribute '{}' missing from entry", Attribute::Class);
2048 SchemaError::NoClassFound
2049 })?;
2050 let mut invalid_classes = Vec::with_capacity(0);
2051
2052 let mut classes: Vec<&SchemaClass> = Vec::with_capacity(entry_classes.len());
2053
2054 let entry_classes = if let Some(ec) = entry_classes.as_iutf8_set() {
2057 ec.iter()
2058 .for_each(|s| match schema_classes.get(s.as_str()) {
2059 Some(x) => classes.push(x),
2060 None => {
2061 admin_debug!("invalid class: {:?}", s);
2062 invalid_classes.push(s.to_string())
2063 }
2064 });
2065 ec
2066 } else {
2067 admin_debug!("corrupt class attribute");
2068 return Err(SchemaError::NoClassFound);
2069 };
2070
2071 if !invalid_classes.is_empty() {
2072 return Err(SchemaError::InvalidClass(invalid_classes));
2073 };
2074
2075 let supplements_classes: Vec<_> = classes
2079 .iter()
2080 .flat_map(|cls| cls.systemsupplements.iter().chain(cls.supplements.iter()))
2081 .collect();
2082
2083 let valid_supplements = if supplements_classes.is_empty() {
2085 true
2087 } else {
2088 supplements_classes
2089 .iter()
2090 .any(|class| entry_classes.contains(class.as_str()))
2091 };
2092
2093 if !valid_supplements {
2094 warn!(
2095 "Validation error, the following possible supplement classes are missing - {:?}",
2096 supplements_classes
2097 );
2098 let supplements_classes = supplements_classes.iter().map(|s| s.to_string()).collect();
2099 return Err(SchemaError::SupplementsNotSatisfied(supplements_classes));
2100 }
2101
2102 let excludes_classes: Vec<_> = classes
2103 .iter()
2104 .flat_map(|cls| cls.systemexcludes.iter().chain(cls.excludes.iter()))
2105 .collect();
2106
2107 let mut invalid_excludes = Vec::with_capacity(0);
2108
2109 excludes_classes.iter().for_each(|class| {
2110 if entry_classes.contains(class.as_str()) {
2111 invalid_excludes.push(class.to_string())
2112 }
2113 });
2114
2115 if !invalid_excludes.is_empty() {
2116 admin_warn!(
2117 "Validation error, the following excluded classes are present - {:?}",
2118 invalid_excludes
2119 );
2120 return Err(SchemaError::ExcludesNotSatisfied(invalid_excludes));
2121 }
2122
2123 let must: Result<Vec<&SchemaAttribute>, _> = classes
2136 .iter()
2137 .flat_map(|cls| cls.systemmust.iter().chain(cls.must.iter()))
2139 .map(|s| {
2140 schema_attributes.get(s).ok_or(SchemaError::Corrupted)
2143 })
2144 .collect();
2145
2146 let must = must?;
2147
2148 let mut missing_must = Vec::with_capacity(0);
2151 for attr in must.iter() {
2152 let avas = self.get_ava_set(&attr.name);
2153 if avas.is_none() {
2154 missing_must.push(attr.name.clone());
2155 }
2156 }
2157
2158 if !missing_must.is_empty() {
2159 admin_warn!(
2160 "Validation error, the following required ({}) (must) attributes are missing - {:?}",
2161 self.get_display_id(), missing_must
2162 );
2163 if !recycled {
2169 return Err(SchemaError::MissingMustAttribute(missing_must));
2170 }
2171 }
2172
2173 if extensible {
2174 self.attrs.iter().try_for_each(|(attr_name, avas)| {
2175 match schema_attributes.get(attr_name) {
2176 Some(a_schema) => {
2177 if a_schema.phantom {
2180 admin_warn!(
2181 "Rejecting attempt to add phantom attribute to extensible object: {}",
2182 attr_name
2183 );
2184 Err(SchemaError::PhantomAttribute(attr_name.to_string()))
2185 } else {
2186 a_schema.validate_ava(attr_name, avas)
2187 }
2189 }
2190 None => {
2191 admin_error!(
2192 "Invalid Attribute {}, undefined in schema_attributes",
2193 attr_name.to_string()
2194 );
2195 Err(SchemaError::InvalidAttribute(
2196 attr_name.to_string()
2197 ))
2198 }
2199 }
2200 })?;
2201 } else {
2202 let may: Result<Map<&Attribute, &SchemaAttribute>, _> = classes
2210 .iter()
2211 .flat_map(|cls| {
2213 trace!(?cls);
2214 cls.systemmust
2215 .iter()
2216 .chain(cls.must.iter())
2217 .chain(cls.systemmay.iter())
2218 .chain(cls.may.iter())
2219 })
2220 .map(|s| {
2221 Ok((s, schema_attributes.get(s).ok_or(SchemaError::Corrupted)?))
2224 })
2225 .collect();
2226
2227 let may = may?;
2228
2229 self.attrs.iter().try_for_each(|(attr_name, avas)| {
2238 match may.get(attr_name) {
2239 Some(a_schema) => {
2240 a_schema.validate_ava(attr_name, avas)
2243 }
2245 None => {
2246 admin_error!(
2247 "{} {} - not found in the list of valid attributes for this set of classes {:?} - valid attributes are {:?}",
2248
2249 attr_name.as_str(),
2250 self.get_display_id(),
2251 entry_classes.iter().collect::<Vec<_>>(),
2252 may.keys().collect::<Vec<_>>()
2253 );
2254 Err(SchemaError::AttributeNotValidForClass(
2255 attr_name.to_string()
2256 ))
2257 }
2258 }
2259 })?;
2260 }
2261
2262 Ok(())
2264 }
2265
2266 pub fn seal(mut self, schema: &dyn SchemaTransaction) -> Entry<EntrySealed, STATE> {
2267 let EntryValid { uuid, mut ecstate } = self.valid;
2268
2269 ecstate.retain(|k, _| schema.is_replicated(k));
2273
2274 let last_mod_cid = ecstate.get_max_cid();
2276 let cv = vs_cid![last_mod_cid.clone()];
2277 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
2278
2279 let create_at_cid = ecstate.at();
2283 let cv = vs_cid![create_at_cid.clone()];
2284 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
2285
2286 Entry {
2287 valid: EntrySealed { uuid, ecstate },
2288 state: self.state,
2289 attrs: self.attrs,
2290 }
2291 }
2292
2293 pub fn get_uuid(&self) -> Uuid {
2294 self.valid.uuid
2295 }
2296}
2297
2298impl<STATE> GetUuid for Entry<EntrySealed, STATE>
2299where
2300 STATE: Clone,
2301{
2302 fn get_uuid(&self) -> Uuid {
2303 self.valid.uuid
2304 }
2305}
2306
2307impl<STATE> Entry<EntrySealed, STATE>
2308where
2309 STATE: Clone,
2310{
2311 pub fn invalidate(mut self, cid: Cid, trim_cid: &Cid) -> Entry<EntryInvalid, STATE> {
2312 for vs in self.attrs.values_mut() {
2314 vs.trim(trim_cid);
2315 }
2316
2317 let last_mod_cid = self.valid.ecstate.get_max_cid();
2325 let cv = vs_cid![last_mod_cid.clone()];
2326 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
2327
2328 let create_at_cid = self.valid.ecstate.at();
2329 let cv = vs_cid![create_at_cid.clone()];
2330 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
2331
2332 Entry {
2333 valid: EntryInvalid {
2334 cid,
2335 ecstate: self.valid.ecstate,
2336 },
2337 state: self.state,
2338 attrs: self.attrs,
2339 }
2340 }
2341
2342 pub fn get_uuid(&self) -> Uuid {
2343 self.valid.uuid
2344 }
2345
2346 pub fn get_changestate(&self) -> &EntryChangeState {
2347 &self.valid.ecstate
2348 }
2349
2350 pub(crate) fn entry_changed_excluding_attribute<A: AsRef<Attribute>>(
2354 &self,
2355 attr: A,
2356 cid: &Cid,
2357 ) -> bool {
2358 let attr_ref = attr.as_ref();
2359
2360 use crate::repl::entry::State;
2361
2362 match self.get_changestate().current() {
2363 State::Live { at: _, changes } => {
2364 changes.iter().any(|(change_attr, change_id)| {
2365 change_id >= cid &&
2366 *change_attr != *attr_ref &&
2367 *change_attr != Attribute::LastModifiedCid
2369 })
2370 }
2371 State::Tombstone { at } => at == cid,
2372 }
2373 }
2374
2375 #[cfg(test)]
2379 pub(crate) fn into_invalid(mut self) -> Entry<EntryInvalid, STATE> {
2380 let cid = Cid::new_zero();
2381 self.set_last_changed(cid.clone());
2382
2383 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
2384
2385 Entry {
2386 valid: EntryInvalid { cid, ecstate },
2387 state: self.state,
2388 attrs: self.attrs,
2389 }
2390 }
2391}
2392
2393impl GetUuid for Entry<EntryReduced, EntryCommitted> {
2394 fn get_uuid(&self) -> Uuid {
2395 self.valid.uuid
2396 }
2397}
2398
2399impl Entry<EntryReduced, EntryCommitted> {
2400 pub fn get_uuid(&self) -> Uuid {
2401 self.valid.uuid
2402 }
2403
2404 pub fn to_pe(&self, qs: &mut QueryServerReadTransaction) -> Result<ProtoEntry, OperationError> {
2406 let attrs: Result<_, _> = self
2408 .attrs
2409 .iter()
2410 .map(|(k, vs)| qs.resolve_valueset(vs).map(|pvs| (k.to_string(), pvs)))
2411 .collect();
2412 Ok(ProtoEntry { attrs: attrs? })
2413 }
2414
2415 pub fn to_scim_kanidm<'a, TXN>(
2416 &self,
2417 read_txn: &mut TXN,
2418 ) -> Result<ScimEntryKanidm, OperationError>
2419 where
2420 TXN: QueryServerTransaction<'a>,
2421 {
2422 let result: Result<BTreeMap<Attribute, ScimValueKanidm>, OperationError> = self
2423 .attrs
2424 .iter()
2425 .filter(|(k, _vs)| **k != Attribute::Uuid)
2427 .filter_map(|(k, vs)| {
2428 let opt_resolve_status = vs.to_scim_value();
2429 let res_opt_scim_value = match opt_resolve_status {
2430 None => Ok(None),
2431 Some(ScimResolveStatus::Resolved(scim_value_kani)) => Ok(Some(scim_value_kani)),
2432 Some(ScimResolveStatus::NeedsResolution(scim_value_interim)) => {
2433 read_txn.resolve_scim_interim(scim_value_interim)
2434 }
2435 };
2436 res_opt_scim_value
2437 .transpose()
2438 .map(|scim_res| scim_res.map(|scim_value| (k.clone(), scim_value)))
2439 })
2440 .collect();
2441
2442 let attrs = result?;
2443
2444 let ext_access_check = self.valid.effective_access.as_ref().map(|eff_acc| {
2445 let ident = eff_acc.ident;
2446 let delete = eff_acc.delete;
2447 let search = (&eff_acc.search).into();
2448 let modify_present = (&eff_acc.modify_pres).into();
2449 let modify_remove = (&eff_acc.modify_rem).into();
2450
2451 ScimEffectiveAccess {
2452 ident,
2453 delete,
2454 search,
2455 modify_present,
2456 modify_remove,
2457 }
2458 });
2459
2460 let id = self.get_uuid();
2461
2462 let schemas = Vec::with_capacity(0);
2465
2466 Ok(ScimEntryKanidm {
2467 header: ScimEntryHeader {
2468 schemas,
2469 id,
2470 external_id: None,
2472 meta: None,
2475 },
2476 ext_access_check,
2477 attrs,
2478 })
2479 }
2480
2481 pub fn to_ldap(
2483 &self,
2484 qs: &mut QueryServerReadTransaction,
2485 basedn: &str,
2486 all_attrs: bool,
2488 l_attrs: &[String],
2491 ) -> Result<LdapSearchResultEntry, OperationError> {
2492 let rdn = qs.uuid_to_rdn(self.get_uuid())?;
2493
2494 let dn = format!("{rdn},{basedn}");
2495
2496 let attr_map: Result<Map<&str, Vec<Vec<u8>>>, _> = self
2501 .attrs
2502 .iter()
2503 .map(|(k, vs)| {
2504 qs.resolve_valueset_ldap(vs, basedn)
2505 .map(|pvs| (k.as_str(), pvs))
2506 })
2507 .collect();
2508 let attr_map = attr_map?;
2509
2510 let attr_names: Vec<(&str, &str)> = if all_attrs {
2513 self.attrs
2515 .keys()
2516 .map(|k| (k.as_str(), k.as_str()))
2517 .chain(
2518 l_attrs
2519 .iter()
2520 .map(|k| (k.as_str(), ldap_vattr_map(k.as_str()).unwrap_or(k.as_str()))),
2521 )
2522 .collect()
2523 } else {
2524 l_attrs
2526 .iter()
2527 .map(|k| (k.as_str(), ldap_vattr_map(k.as_str()).unwrap_or(k.as_str())))
2528 .collect()
2529 };
2530
2531 let attributes: Vec<_> = attr_names
2533 .into_iter()
2534 .filter_map(|(ldap_a, kani_a)| {
2535 match ldap_a {
2537 LDAP_ATTR_DN => Some(LdapPartialAttribute {
2538 atype: LDAP_ATTR_DN.to_string(),
2539 vals: vec![dn.as_bytes().to_vec()],
2540 }),
2541 LDAP_ATTR_ENTRYDN => Some(LdapPartialAttribute {
2542 atype: LDAP_ATTR_ENTRYDN.to_string(),
2543 vals: vec![dn.as_bytes().to_vec()],
2544 }),
2545 LDAP_ATTR_MAIL_PRIMARY | LDAP_ATTR_EMAIL_PRIMARY => {
2546 attr_map.get(kani_a).map(|pvs| LdapPartialAttribute {
2547 atype: ldap_a.to_string(),
2548 vals: pvs
2549 .first()
2550 .map(|first| vec![first.clone()])
2551 .unwrap_or_default(),
2552 })
2553 }
2554 LDAP_ATTR_MAIL_ALTERNATIVE | LDAP_ATTR_EMAIL_ALTERNATIVE => {
2555 attr_map.get(kani_a).map(|pvs| LdapPartialAttribute {
2556 atype: ldap_a.to_string(),
2557 vals: pvs
2558 .split_first()
2559 .map(|(_, rest)| rest.to_vec())
2560 .unwrap_or_default(),
2561 })
2562 }
2563 ATTR_HOME_DIRECTORY => Some(LdapPartialAttribute {
2564 atype: ATTR_HOME_DIRECTORY.to_string(),
2565 vals: vec![format!("/home/{}", self.get_uuid()).into_bytes()],
2566 }),
2567 _ => attr_map.get(kani_a).map(|pvs| LdapPartialAttribute {
2568 atype: ldap_a.to_string(),
2569 vals: pvs.clone(),
2570 }),
2571 }
2572 })
2573 .collect();
2574
2575 Ok(LdapSearchResultEntry { dn, attributes })
2576 }
2577}
2578
2579impl<VALID, STATE> Entry<VALID, STATE> {
2580 fn add_ava_int(&mut self, attr: Attribute, value: Value) -> bool {
2585 if let Some(vs) = self.attrs.get_mut(&attr) {
2586 let r = vs.insert_checked(value);
2587 debug_assert!(r.is_ok());
2588 r.unwrap_or(false)
2590 } else {
2591 #[allow(clippy::expect_used)]
2592 let vs = valueset::from_value_iter(std::iter::once(value))
2593 .expect("Unable to fail - non-zero iter, and single value type!");
2594 self.attrs.insert(attr, vs);
2595 true
2597 }
2598 }
2600
2601 fn set_ava_iter_int<T>(&mut self, attr: Attribute, iter: T)
2603 where
2604 T: IntoIterator<Item = Value>,
2605 {
2606 let Ok(vs) = valueset::from_value_iter(iter.into_iter()) else {
2607 trace!("set_ava_iter_int - empty from_value_iter, skipping");
2608 return;
2609 };
2610
2611 if let Some(existing_vs) = self.attrs.get_mut(&attr) {
2612 let _ = existing_vs.merge(&vs);
2614 } else {
2615 self.attrs.insert(attr, vs);
2617 }
2618 }
2619
2620 #[cfg(test)]
2622 fn set_last_changed(&mut self, cid: Cid) {
2623 let cv = vs_cid![cid.clone()];
2624 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
2625 let cv = vs_cid![cid];
2626 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
2627 }
2628
2629 pub(crate) fn get_display_id(&self) -> String {
2630 self.attrs
2631 .get(&Attribute::Spn)
2632 .map(|vs| vs.to_proto_string_clone_iter())
2633 .or_else(|| {
2634 self.attrs
2635 .get(&Attribute::Uuid)
2636 .map(|vs| vs.to_proto_string_clone_iter())
2637 })
2638 .and_then(|mut string_iter| string_iter.next())
2640 .unwrap_or_else(|| "no entry id available".to_string())
2641 }
2642
2643 pub fn has_class(&self, class: &EntryClass) -> bool {
2644 self.get_ava_set(Attribute::Class)
2645 .and_then(|vs| vs.as_iutf8_set())
2646 .map(|set| {
2647 let class_name: &str = class.into();
2648 set.contains(class_name)
2649 })
2650 .unwrap_or_default()
2651 }
2652
2653 pub fn attr_keys(&self) -> impl Iterator<Item = &Attribute> {
2654 self.attrs.keys()
2655 }
2656
2657 pub fn get_ava_names(&self) -> impl Iterator<Item = &str> {
2659 self.attrs.keys().map(|a| a.as_str())
2661 }
2662
2663 pub fn get_ava(&self) -> &Eattrs {
2665 &self.attrs
2666 }
2667
2668 pub fn get_ava_iter(&self) -> impl Iterator<Item = (&Attribute, &ValueSet)> {
2669 self.attrs.iter()
2670 }
2671
2672 pub fn get_ava_set<A: AsRef<Attribute>>(&self, attr: A) -> Option<&ValueSet> {
2674 self.attrs.get(attr.as_ref())
2675 }
2676
2677 pub fn get_ava_refer<A: AsRef<Attribute>>(&self, attr: A) -> Option<&BTreeSet<Uuid>> {
2678 self.get_ava_set(attr).and_then(|vs| vs.as_refer_set())
2679 }
2680
2681 pub fn get_ava_as_iutf8_iter<A: AsRef<Attribute>>(
2682 &self,
2683 attr: A,
2684 ) -> Option<impl Iterator<Item = &str>> {
2685 self.get_ava_set(attr).and_then(|vs| vs.as_iutf8_iter())
2686 }
2687
2688 pub fn get_ava_as_iutf8<A: AsRef<Attribute>>(&self, attr: A) -> Option<&BTreeSet<String>> {
2689 self.get_ava_set(attr).and_then(|vs| vs.as_iutf8_set())
2690 }
2691
2692 pub fn get_ava_as_image<A: AsRef<Attribute>>(&self, attr: A) -> Option<&HashSet<ImageValue>> {
2693 self.get_ava_set(attr).and_then(|vs| vs.as_imageset())
2694 }
2695
2696 pub fn get_ava_single_image<A: AsRef<Attribute>>(&self, attr: A) -> Option<ImageValue> {
2697 let images = self.get_ava_set(attr).and_then(|vs| vs.as_imageset())?;
2698 images.iter().next().cloned()
2699 }
2700
2701 pub fn get_ava_single_credential_type<A: AsRef<Attribute>>(
2702 &self,
2703 attr: A,
2704 ) -> Option<CredentialType> {
2705 self.get_ava_set(attr)
2706 .and_then(|vs| vs.to_credentialtype_single())
2707 }
2708
2709 pub fn get_ava_as_oauthscopes<A: AsRef<Attribute>>(
2710 &self,
2711 attr: A,
2712 ) -> Option<impl Iterator<Item = &str>> {
2713 self.get_ava_set(attr)
2714 .and_then(|vs| vs.as_oauthscope_iter())
2715 }
2716
2717 pub fn get_ava_as_oauthscopemaps<A: AsRef<Attribute>>(
2718 &self,
2719 attr: A,
2720 ) -> Option<&std::collections::BTreeMap<Uuid, std::collections::BTreeSet<String>>> {
2721 self.get_ava_set(attr).and_then(|vs| vs.as_oauthscopemap())
2722 }
2723
2724 pub fn get_ava_as_intenttokens<A: AsRef<Attribute>>(
2725 &self,
2726 attr: A,
2727 ) -> Option<&std::collections::BTreeMap<String, IntentTokenState>> {
2728 self.get_ava_set(attr)
2729 .and_then(|vs| vs.as_intenttoken_map())
2730 }
2731
2732 pub fn get_ava_as_session_map<A: AsRef<Attribute>>(
2733 &self,
2734 attr: A,
2735 ) -> Option<&std::collections::BTreeMap<Uuid, Session>> {
2736 self.get_ava_set(attr).and_then(|vs| vs.as_session_map())
2737 }
2738
2739 pub fn get_ava_as_apitoken_map<A: AsRef<Attribute>>(
2740 &self,
2741 attr: A,
2742 ) -> Option<&std::collections::BTreeMap<Uuid, ApiToken>> {
2743 self.get_ava_set(attr).and_then(|vs| vs.as_apitoken_map())
2744 }
2745
2746 pub fn get_ava_as_oauth2session_map<A: AsRef<Attribute>>(
2747 &self,
2748 attr: A,
2749 ) -> Option<&std::collections::BTreeMap<Uuid, Oauth2Session>> {
2750 self.get_ava_set(attr)
2751 .and_then(|vs| vs.as_oauth2session_map())
2752 }
2753
2754 pub fn get_ava_as_s256_set<A: AsRef<Attribute>>(
2755 &self,
2756 attr: A,
2757 ) -> Option<&std::collections::BTreeSet<Sha256Output>> {
2758 self.get_ava_set(attr).and_then(|vs| vs.as_s256_set())
2759 }
2760
2761 pub fn get_ava_iter_iname<A: AsRef<Attribute>>(
2763 &self,
2764 attr: A,
2765 ) -> Option<impl Iterator<Item = &str>> {
2766 self.get_ava_set(attr).and_then(|vs| vs.as_iname_iter())
2767 }
2768
2769 pub fn get_ava_iter_iutf8<A: AsRef<Attribute>>(
2771 &self,
2772 attr: A,
2773 ) -> Option<impl Iterator<Item = &str>> {
2774 self.get_ava_set(attr).and_then(|vs| vs.as_iutf8_iter())
2775 }
2776
2777 pub fn get_ava_as_refuuid<A: AsRef<Attribute>>(
2779 &self,
2780 attr: A,
2781 ) -> Option<Box<dyn Iterator<Item = Uuid> + '_>> {
2782 self.get_ava_set(attr).and_then(|vs| vs.as_ref_uuid_iter())
2784 }
2785
2786 pub fn get_ava_iter_sshpubkeys<A: AsRef<Attribute>>(
2788 &self,
2789 attr: A,
2790 ) -> Option<impl Iterator<Item = String> + '_> {
2791 self.get_ava_set(attr)
2792 .and_then(|vs| vs.as_sshpubkey_string_iter())
2793 }
2794
2795 pub fn get_ava_single<A: AsRef<Attribute>>(&self, attr: A) -> Option<Value> {
2801 self.get_ava_set(attr).and_then(|vs| vs.to_value_single())
2802 }
2803
2804 pub fn get_ava_single_proto_string<A: AsRef<Attribute>>(&self, attr: A) -> Option<String> {
2805 self.get_ava_set(attr)
2806 .and_then(|vs| vs.to_proto_string_single())
2807 }
2808
2809 pub fn get_ava_single_bool<A: AsRef<Attribute>>(&self, attr: A) -> Option<bool> {
2811 self.get_ava_set(attr).and_then(|vs| vs.to_bool_single())
2812 }
2813
2814 pub fn get_ava_single_uint32<A: AsRef<Attribute>>(&self, attr: A) -> Option<u32> {
2816 self.get_ava_set(attr).and_then(|vs| vs.to_uint32_single())
2817 }
2818
2819 pub fn get_ava_single_syntax<A: AsRef<Attribute>>(&self, attr: A) -> Option<SyntaxType> {
2821 self.get_ava_set(attr)
2822 .and_then(|vs| vs.to_syntaxtype_single())
2823 }
2824
2825 pub fn get_ava_single_credential<A: AsRef<Attribute>>(&self, attr: A) -> Option<&Credential> {
2827 self.get_ava_set(attr)
2828 .and_then(|vs| vs.to_credential_single())
2829 }
2830
2831 pub fn get_ava_passkeys<A: AsRef<Attribute>>(
2833 &self,
2834 attr: A,
2835 ) -> Option<&BTreeMap<Uuid, (String, PasskeyV4)>> {
2836 self.get_ava_set(attr).and_then(|vs| vs.as_passkey_map())
2837 }
2838
2839 pub fn get_ava_attestedpasskeys<A: AsRef<Attribute>>(
2841 &self,
2842 attr: A,
2843 ) -> Option<&BTreeMap<Uuid, (String, AttestedPasskeyV4)>> {
2844 self.get_ava_set(attr)
2845 .and_then(|vs| vs.as_attestedpasskey_map())
2846 }
2847
2848 pub fn get_ava_uihint<A: AsRef<Attribute>>(&self, attr: A) -> Option<&BTreeSet<UiHint>> {
2850 self.get_ava_set(attr).and_then(|vs| vs.as_uihint_set())
2851 }
2852
2853 pub fn get_ava_single_secret<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2855 self.get_ava_set(attr).and_then(|vs| vs.to_secret_single())
2856 }
2857
2858 pub fn get_ava_single_datetime<A: AsRef<Attribute>>(&self, attr: A) -> Option<OffsetDateTime> {
2860 self.get_ava_set(attr)
2861 .and_then(|vs| vs.to_datetime_single())
2862 }
2863
2864 pub(crate) fn get_ava_single_utf8<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2866 self.get_ava_set(attr).and_then(|vs| vs.to_utf8_single())
2867 }
2868
2869 pub(crate) fn get_ava_single_iutf8<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2871 self.get_ava_set(attr).and_then(|vs| vs.to_iutf8_single())
2872 }
2873
2874 pub(crate) fn get_ava_single_iname<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2876 self.get_ava_set(attr).and_then(|vs| vs.to_iname_single())
2877 }
2878
2879 pub fn get_ava_single_url<A: AsRef<Attribute>>(&self, attr: A) -> Option<&Url> {
2881 self.get_ava_set(attr).and_then(|vs| vs.to_url_single())
2882 }
2883
2884 pub fn get_ava_single_uuid<A: AsRef<Attribute>>(&self, attr: A) -> Option<Uuid> {
2885 self.get_ava_set(attr).and_then(|vs| vs.to_uuid_single())
2886 }
2887
2888 pub fn get_ava_single_refer<A: AsRef<Attribute>>(&self, attr: A) -> Option<Uuid> {
2889 self.get_ava_set(attr).and_then(|vs| vs.to_refer_single())
2890 }
2891
2892 pub fn get_ava_mail_primary<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2893 self.get_ava_set(attr)
2894 .and_then(|vs| vs.to_email_address_primary_str())
2895 }
2896
2897 pub fn get_ava_iter_mail<A: AsRef<Attribute>>(
2898 &self,
2899 attr: A,
2900 ) -> Option<impl Iterator<Item = &str>> {
2901 self.get_ava_set(attr).and_then(|vs| vs.as_email_str_iter())
2902 }
2903
2904 pub fn get_ava_single_protofilter<A: AsRef<Attribute>>(&self, attr: A) -> Option<&ProtoFilter> {
2906 self.get_ava_set(attr)
2907 .and_then(|vs| vs.to_json_filter_single())
2908 }
2909
2910 pub fn get_ava_single_private_binary<A: AsRef<Attribute>>(&self, attr: A) -> Option<&[u8]> {
2911 self.get_ava_set(attr)
2912 .and_then(|vs| vs.to_private_binary_single())
2913 }
2914
2915 pub fn get_ava_single_jws_key_es256<A: AsRef<Attribute>>(
2916 &self,
2917 attr: A,
2918 ) -> Option<&JwsEs256Signer> {
2919 self.get_ava_set(attr)
2920 .and_then(|vs| vs.to_jws_key_es256_single())
2921 }
2922
2923 pub fn get_ava_webauthn_attestation_ca_list<A: AsRef<Attribute>>(
2924 &self,
2925 attr: A,
2926 ) -> Option<&AttestationCaList> {
2927 self.get_ava_set(attr)
2928 .and_then(|vs| vs.as_webauthn_attestation_ca_list())
2929 }
2930
2931 pub fn get_ava_application_password<A: AsRef<Attribute>>(
2932 &self,
2933 attr: A,
2934 ) -> Option<&BTreeMap<Uuid, Vec<ApplicationPassword>>> {
2935 self.get_ava_set(attr)
2936 .and_then(|vs| vs.as_application_password_map())
2937 }
2938
2939 pub(crate) fn generate_spn(&self, domain_name: &str) -> Option<ValueSet> {
2941 if let Some(name) = self.get_ava_single_iname(Attribute::Name) {
2942 return Some(ValueSetSpn::new((name.into(), domain_name.into())));
2944 }
2945
2946 let spn_set = self.get_ava_set(Attribute::Spn)?;
2948
2949 if spn_set.syntax() == SyntaxType::SecurityPrincipalName {
2950 Some(spn_set.clone())
2952 } else if let Some(name) = spn_set.to_iname_single() {
2953 Some(ValueSetSpn::new((name.into(), domain_name.into())))
2955 } else {
2956 None
2958 }
2959 }
2960
2961 pub fn attribute_pres<A: AsRef<Attribute>>(&self, attr: A) -> bool {
2963 self.attrs.contains_key(attr.as_ref())
2964 }
2965
2966 pub fn attribute_equality<A: AsRef<Attribute>>(&self, attr: A, value: &PartialValue) -> bool {
2969 match self.attrs.get(attr.as_ref()) {
2974 Some(v_list) => v_list.contains(value),
2975 None => false,
2976 }
2977 }
2978
2979 pub fn attribute_substring<A: AsRef<Attribute>>(
2982 &self,
2983 attr: A,
2984 subvalue: &PartialValue,
2985 ) -> bool {
2986 self.get_ava_set(attr)
2987 .map(|vset| vset.substring(subvalue))
2988 .unwrap_or(false)
2989 }
2990
2991 pub fn attribute_startswith<A: AsRef<Attribute>>(
2994 &self,
2995 attr: A,
2996 subvalue: &PartialValue,
2997 ) -> bool {
2998 self.get_ava_set(attr)
2999 .map(|vset| vset.startswith(subvalue))
3000 .unwrap_or(false)
3001 }
3002
3003 pub fn attribute_endswith<A: AsRef<Attribute>>(
3006 &self,
3007 attr: A,
3008 subvalue: &PartialValue,
3009 ) -> bool {
3010 self.get_ava_set(attr)
3011 .map(|vset| vset.endswith(subvalue))
3012 .unwrap_or(false)
3013 }
3014
3015 pub fn attribute_lessthan<A: AsRef<Attribute>>(
3018 &self,
3019 attr: A,
3020 subvalue: &PartialValue,
3021 ) -> bool {
3022 self.get_ava_set(attr)
3023 .map(|vset| vset.lessthan(subvalue))
3024 .unwrap_or(false)
3025 }
3026
3027 #[inline(always)]
3032 #[instrument(level = "trace", name = "entry::entry_match_no_index", skip(self))]
3033 pub fn entry_match_no_index(&self, filter: &Filter<FilterValidResolved>) -> bool {
3035 self.entry_match_no_index_inner(filter.to_inner())
3036 }
3037
3038 fn entry_match_no_index_inner(&self, filter: &FilterResolved) -> bool {
3042 match filter {
3045 FilterResolved::Eq(attr, value, _) => self.attribute_equality(attr, value),
3046 FilterResolved::Cnt(attr, subvalue, _) => self.attribute_substring(attr, subvalue),
3047 FilterResolved::Stw(attr, subvalue, _) => self.attribute_startswith(attr, subvalue),
3048 FilterResolved::Enw(attr, subvalue, _) => self.attribute_endswith(attr, subvalue),
3049 FilterResolved::Pres(attr, _) => self.attribute_pres(attr),
3050 FilterResolved::LessThan(attr, subvalue, _) => self.attribute_lessthan(attr, subvalue),
3051 FilterResolved::Or(l, _) => l.iter().any(|f| self.entry_match_no_index_inner(f)),
3053 FilterResolved::And(l, _) => l.iter().all(|f| self.entry_match_no_index_inner(f)),
3055 FilterResolved::Inclusion(_, _) => {
3056 false
3060 }
3061 FilterResolved::AndNot(f, _) => !self.entry_match_no_index_inner(f),
3062 FilterResolved::Invalid(_) => false,
3063 }
3064 }
3065
3066 pub fn filter_from_attrs(&self, attrs: &[Attribute]) -> Option<Filter<FilterInvalid>> {
3069 let mut pairs: Vec<(Attribute, PartialValue)> = Vec::with_capacity(0);
3081
3082 for attr in attrs {
3083 match self.attrs.get(attr) {
3084 Some(values) => values
3085 .to_partialvalue_iter()
3086 .for_each(|pv| pairs.push((attr.clone(), pv))),
3087 None => return None,
3088 }
3089 }
3090
3091 let res: Vec<FC> = pairs
3092 .into_iter()
3093 .map(|(attr, pv)| FC::Eq(attr, pv))
3094 .collect();
3095 Some(filter_all!(f_and(res)))
3096 }
3097
3098 pub fn gen_modlist_assert(
3101 &self,
3102 schema: &dyn SchemaTransaction,
3103 ) -> Result<ModifyList<ModifyInvalid>, SchemaError> {
3104 let mut mods = ModifyList::new();
3108
3109 for (k, vs) in self.attrs.iter() {
3110 if *k == Attribute::Uuid {
3126 continue;
3127 }
3128 match schema.is_multivalue(k) {
3130 Ok(r) => {
3131 if !r ||
3134 *k == Attribute::AcpReceiverGroup ||
3137 *k == Attribute::AcpCreateAttr ||
3138 *k == Attribute::AcpCreateClass ||
3139 *k == Attribute::AcpModifyPresentAttr ||
3140 *k == Attribute::AcpModifyRemovedAttr ||
3141 *k == Attribute::AcpModifyClass ||
3142 *k == Attribute::SystemMust ||
3143 *k == Attribute::SystemMay
3144 {
3145 mods.push_mod(Modify::Purged(k.clone()));
3146 }
3147 }
3148 Err(e) => return Err(e),
3150 }
3151 for v in vs.to_value_iter() {
3152 mods.push_mod(Modify::Present(k.clone(), v.clone()));
3153 }
3154 }
3155
3156 Ok(mods)
3157 }
3158
3159 pub fn mask_recycled_ts(&self) -> Option<&Self> {
3162 match self.attrs.get(&Attribute::Class) {
3164 Some(cls) => {
3165 if cls.contains(&EntryClass::Tombstone.to_partialvalue())
3166 || cls.contains(&EntryClass::Recycled.to_partialvalue())
3167 {
3168 None
3169 } else {
3170 Some(self)
3171 }
3172 }
3173 None => Some(self),
3174 }
3175 }
3176
3177 pub fn mask_recycled(&self) -> Option<&Self> {
3180 match self.attrs.get(&Attribute::Class) {
3182 Some(cls) => {
3183 if cls.contains(&EntryClass::Recycled.to_partialvalue()) {
3184 None
3185 } else {
3186 Some(self)
3187 }
3188 }
3189 None => Some(self),
3190 }
3191 }
3192
3193 pub fn mask_tombstone(&self) -> Option<&Self> {
3196 match self.attrs.get(&Attribute::Class) {
3198 Some(cls) => {
3199 if cls.contains(&EntryClass::Tombstone.to_partialvalue()) {
3200 None
3201 } else {
3202 Some(self)
3203 }
3204 }
3205 None => Some(self),
3206 }
3207 }
3208}
3209
3210impl<STATE> Entry<EntryInvalid, STATE>
3211where
3212 STATE: Clone,
3213{
3214 pub fn add_ava(&mut self, attr: Attribute, value: Value) {
3219 self.valid.ecstate.change_ava(&self.valid.cid, &attr);
3220 self.add_ava_int(attr, value);
3221 }
3222
3223 pub fn add_ava_if_not_exist<A: AsRef<Attribute>>(&mut self, attr: A, value: Value) {
3224 let attr_ref = attr.as_ref();
3225 if self.add_ava_int(attr_ref.clone(), value) {
3227 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3229 }
3230 }
3231
3232 fn assert_ava<A: AsRef<Attribute>>(
3233 &mut self,
3234 attr: A,
3235 value: &PartialValue,
3236 ) -> Result<(), OperationError> {
3237 self.valid
3238 .ecstate
3239 .change_ava(&self.valid.cid, attr.as_ref());
3240
3241 if self.attribute_equality(attr, value) {
3242 Ok(())
3243 } else {
3244 Err(OperationError::ModifyAssertionFailed)
3245 }
3246 }
3247
3248 pub(crate) fn remove_ava<A: AsRef<Attribute>>(&mut self, attr: A, value: &PartialValue) {
3251 let attr_ref = attr.as_ref();
3252 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3253
3254 let rm = if let Some(vs) = self.attrs.get_mut(attr_ref) {
3255 vs.remove(value, &self.valid.cid);
3256 vs.is_empty()
3257 } else {
3258 false
3259 };
3260 if rm {
3261 self.attrs.remove(attr_ref);
3262 };
3263 }
3264
3265 pub(crate) fn remove_avas<A: AsRef<Attribute>>(
3266 &mut self,
3267 attr: A,
3268 values: &BTreeSet<PartialValue>,
3269 ) {
3270 let attr_ref = attr.as_ref();
3271 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3272
3273 let rm = if let Some(vs) = self.attrs.get_mut(attr_ref) {
3274 values.iter().for_each(|k| {
3275 vs.remove(k, &self.valid.cid);
3276 });
3277 vs.is_empty()
3278 } else {
3279 false
3280 };
3281 if rm {
3282 self.attrs.remove(attr_ref);
3283 };
3284 }
3285
3286 pub(crate) fn purge_ava<A: AsRef<Attribute>>(&mut self, attr: A) {
3289 let attr_ref = attr.as_ref();
3290 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3291 let can_remove = self
3294 .attrs
3295 .get_mut(attr_ref)
3296 .map(|vs| vs.purge(&self.valid.cid))
3297 .unwrap_or_default();
3299 if can_remove {
3300 self.attrs.remove(attr_ref);
3301 }
3302 }
3303
3304 pub fn pop_ava<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<ValueSet> {
3306 let attr_ref = attr.as_ref();
3307 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3308
3309 let mut vs = self.attrs.remove(attr_ref)?;
3310 if vs.purge(&self.valid.cid) {
3311 Some(vs)
3313 } else {
3314 let r_vs = vs.clone();
3316 self.attrs.insert(attr_ref.clone(), vs);
3317 Some(r_vs)
3318 }
3319 }
3320
3321 #[cfg(test)]
3326 pub(crate) fn force_trim_ava<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<ValueSet> {
3327 self.valid
3328 .ecstate
3329 .change_ava(&self.valid.cid, attr.as_ref());
3330 self.attrs.remove(attr.as_ref())
3331 }
3332
3333 pub fn set_ava<T>(&mut self, attr: &Attribute, iter: T)
3336 where
3337 T: Clone + IntoIterator<Item = Value>,
3338 {
3339 self.purge_ava(attr);
3340 self.set_ava_iter_int(attr.clone(), iter)
3341 }
3342
3343 pub fn set_ava_set(&mut self, attr: &Attribute, vs: ValueSet) {
3346 self.purge_ava(attr);
3347 if let Some(existing_vs) = self.attrs.get_mut(attr) {
3348 let _ = existing_vs.merge(&vs);
3349 } else {
3350 self.attrs.insert(attr.clone(), vs);
3351 }
3352 }
3353
3354 pub fn merge_ava_set(&mut self, attr: &Attribute, vs: ValueSet) -> Result<(), OperationError> {
3357 self.valid.ecstate.change_ava(&self.valid.cid, attr);
3358 if let Some(existing_vs) = self.attrs.get_mut(attr) {
3359 existing_vs.merge(&vs)
3360 } else {
3361 self.attrs.insert(attr.clone(), vs);
3362 Ok(())
3363 }
3364 }
3365
3366 pub fn apply_modlist(
3368 &mut self,
3369 modlist: &ModifyList<ModifyValid>,
3370 ) -> Result<(), OperationError> {
3371 for modify in modlist {
3372 match modify {
3373 Modify::Present(attr, value) => {
3374 self.add_ava(attr.clone(), value.clone());
3375 }
3376 Modify::Removed(attr, value) => {
3377 self.remove_ava(attr, value);
3378 }
3379 Modify::Purged(attr) => {
3380 self.purge_ava(attr);
3381 }
3382 Modify::Assert(attr, value) => {
3383 self.assert_ava(attr, value).inspect_err(|_e| {
3384 error!("Modification assertion was not met. {} {:?}", attr, value);
3385 })?;
3386 }
3387 Modify::Set(attr, valueset) => self.set_ava_set(attr, valueset.clone()),
3388 }
3389 }
3390 Ok(())
3391 }
3392}
3393
3394impl<VALID, STATE> PartialEq for Entry<VALID, STATE> {
3395 fn eq(&self, rhs: &Entry<VALID, STATE>) -> bool {
3396 compare_attrs(&self.attrs, &rhs.attrs)
3407 }
3408}
3409
3410pub(crate) fn entry_init_fn<T>(args: T) -> EntryInitNew
3412where
3413 T: IntoIterator<Item = (Attribute, Value)>,
3414{
3415 let mut entry: EntryInitNew = Entry::new();
3416 args.into_iter().for_each(|(k, v)| entry.add_ava(k, v));
3417 entry
3418}
3419
3420#[cfg(test)]
3421mod tests {
3422 use crate::prelude::*;
3423 use std::collections::BTreeSet as Set;
3424
3425 use hashbrown::HashMap;
3426
3427 use crate::be::{IdxKey, IdxSlope};
3428 use crate::entry::{Entry, EntryInit, EntryInvalid, EntryNew};
3429 use crate::modify::{Modify, ModifyList};
3430 use crate::value::{IndexType, PartialValue, Value};
3431
3432 #[test]
3433 fn test_entry_basic() {
3434 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3435
3436 e.add_ava(Attribute::UserId, Value::from("william"));
3437 }
3438
3439 #[test]
3440 fn test_entry_dup_value() {
3441 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3448 e.add_ava(Attribute::UserId, Value::from("william"));
3449 e.add_ava(Attribute::UserId, Value::from("william"));
3450
3451 let values = e.get_ava_set(Attribute::UserId).expect("Failed to get ava");
3452 assert_eq!(values.len(), 1)
3454 }
3455
3456 #[test]
3457 fn test_entry_pres() {
3458 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3459 e.add_ava(Attribute::UserId, Value::from("william"));
3460
3461 assert!(e.attribute_pres(Attribute::UserId));
3462 assert!(!e.attribute_pres(Attribute::Name));
3463 }
3464
3465 #[test]
3466 fn test_entry_equality() {
3467 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3468
3469 e.add_ava(Attribute::UserId, Value::from("william"));
3470
3471 assert!(e.attribute_equality(Attribute::UserId, &PartialValue::new_utf8s("william")));
3472 assert!(!e.attribute_equality(Attribute::UserId, &PartialValue::new_utf8s("test")));
3473 assert!(!e.attribute_equality(Attribute::NonExist, &PartialValue::new_utf8s("william")));
3474 assert!(!e.attribute_equality(Attribute::UserId, &PartialValue::new_iutf8("william")));
3476 }
3477
3478 #[test]
3479 fn test_entry_substring() {
3480 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3481
3482 e.add_ava(Attribute::UserId, Value::from("william"));
3483
3484 assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("william")));
3485 assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("will")));
3486 assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("liam")));
3487 assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("lli")));
3488 assert!(!e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("llim")));
3489 assert!(!e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("bob")));
3490 assert!(!e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("wl")));
3491
3492 assert!(e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("will")));
3493 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("liam")));
3494 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("lli")));
3495 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("llim")));
3496 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("bob")));
3497 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("wl")));
3498
3499 assert!(e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("liam")));
3500 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("will")));
3501 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("lli")));
3502 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("llim")));
3503 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("bob")));
3504 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("wl")));
3505 }
3506
3507 #[test]
3508 fn test_entry_lessthan() {
3509 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3510
3511 let pv2 = PartialValue::new_uint32(2);
3512 let pv8 = PartialValue::new_uint32(8);
3513 let pv10 = PartialValue::new_uint32(10);
3514 let pv15 = PartialValue::new_uint32(15);
3515
3516 e1.add_ava(Attribute::TestAttr, Value::new_uint32(10));
3517
3518 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv2));
3519 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv8));
3520 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv10));
3521 assert!(e1.attribute_lessthan(Attribute::TestAttr, &pv15));
3522
3523 e1.add_ava(Attribute::TestAttr, Value::new_uint32(8));
3524
3525 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv2));
3526 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv8));
3527 assert!(e1.attribute_lessthan(Attribute::TestAttr, &pv10));
3528 assert!(e1.attribute_lessthan(Attribute::TestAttr, &pv15));
3529 }
3530
3531 #[test]
3532 fn test_entry_apply_modlist() {
3533 let mut e: Entry<EntryInvalid, EntryNew> = Entry::new().into_invalid_new();
3535
3536 e.add_ava(Attribute::UserId, Value::from("william"));
3537
3538 let present_single_mods = ModifyList::new_valid_list(vec![Modify::Present(
3539 Attribute::Attr,
3540 Value::new_iutf8("value"),
3541 )]);
3542
3543 assert!(e.apply_modlist(&present_single_mods).is_ok());
3544
3545 assert!(e.attribute_equality(Attribute::UserId, &PartialValue::new_utf8s("william")));
3547 assert!(e.attribute_equality(Attribute::Attr, &PartialValue::new_iutf8("value")));
3548
3549 let present_multivalue_mods = ModifyList::new_valid_list(vec![
3551 Modify::Present(Attribute::Class, Value::new_iutf8("test")),
3552 Modify::Present(Attribute::Class, Value::new_iutf8("multi_test")),
3553 ]);
3554
3555 assert!(e.apply_modlist(&present_multivalue_mods).is_ok());
3556
3557 assert!(e.attribute_equality(Attribute::Class, &PartialValue::new_iutf8("test")));
3558 assert!(e.attribute_equality(Attribute::Class, &PartialValue::new_iutf8("multi_test")));
3559
3560 let purge_single_mods = ModifyList::new_valid_list(vec![Modify::Purged(Attribute::Attr)]);
3562
3563 assert!(e.apply_modlist(&purge_single_mods).is_ok());
3564
3565 assert!(!e.attribute_pres(Attribute::Attr));
3566
3567 let purge_multi_mods = ModifyList::new_valid_list(vec![Modify::Purged(Attribute::Class)]);
3568
3569 assert!(e.apply_modlist(&purge_multi_mods).is_ok());
3570
3571 assert!(!e.attribute_pres(Attribute::Class));
3572
3573 let purge_empty_mods = purge_single_mods;
3574
3575 assert!(e.apply_modlist(&purge_empty_mods).is_ok());
3576
3577 let remove_mods = ModifyList::new_valid_list(vec![Modify::Removed(
3579 Attribute::Attr,
3580 PartialValue::new_iutf8("value"),
3581 )]);
3582
3583 assert!(e.apply_modlist(&present_single_mods).is_ok());
3584 assert!(e.attribute_equality(Attribute::Attr, &PartialValue::new_iutf8("value")));
3585 assert!(e.apply_modlist(&remove_mods).is_ok());
3586 assert!(!e.attrs.contains_key(&Attribute::Attr));
3587
3588 let remove_empty_mods = remove_mods;
3589
3590 assert!(e.apply_modlist(&remove_empty_mods).is_ok());
3591
3592 assert!(!e.attrs.contains_key(&Attribute::Attr));
3593 }
3594
3595 #[test]
3596 fn test_entry_idx_diff() {
3597 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3598 e1.add_ava(Attribute::UserId, Value::from("william"));
3599 let mut e1_mod = e1.clone();
3600 e1_mod.add_ava(Attribute::Extra, Value::from("test"));
3601
3602 let e1 = e1.into_sealed_committed();
3603 let e1_mod = e1_mod.into_sealed_committed();
3604
3605 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3606 e2.add_ava(Attribute::UserId, Value::from("claire"));
3607 let e2 = e2.into_sealed_committed();
3608
3609 let mut idxmeta = HashMap::with_capacity(8);
3610 idxmeta.insert(
3611 IdxKey {
3612 attr: Attribute::UserId,
3613 itype: IndexType::Equality,
3614 },
3615 IdxSlope::MAX,
3616 );
3617 idxmeta.insert(
3618 IdxKey {
3619 attr: Attribute::UserId,
3620 itype: IndexType::Presence,
3621 },
3622 IdxSlope::MAX,
3623 );
3624 idxmeta.insert(
3625 IdxKey {
3626 attr: Attribute::Extra,
3627 itype: IndexType::Equality,
3628 },
3629 IdxSlope::MAX,
3630 );
3631
3632 let r1 = Entry::idx_diff(&idxmeta, None, None);
3634 eprintln!("{r1:?}");
3635 assert_eq!(r1, Vec::with_capacity(0));
3636
3637 let mut del_r = Entry::idx_diff(&idxmeta, Some(&e1), None);
3639 del_r.sort_unstable();
3640 eprintln!("del_r {del_r:?}");
3641 assert!(
3642 del_r[0]
3643 == Err((
3644 &Attribute::UserId,
3645 IndexType::Equality,
3646 "william".to_string()
3647 ))
3648 );
3649 assert!(del_r[1] == Err((&Attribute::UserId, IndexType::Presence, "_".to_string())));
3650
3651 let mut add_r = Entry::idx_diff(&idxmeta, None, Some(&e1));
3653 add_r.sort_unstable();
3654 eprintln!("{add_r:?}");
3655 assert!(
3656 add_r[0]
3657 == Ok((
3658 &Attribute::UserId,
3659 IndexType::Equality,
3660 "william".to_string()
3661 ))
3662 );
3663 assert!(add_r[1] == Ok((&Attribute::UserId, IndexType::Presence, "_".to_string())));
3664
3665 let no_r = Entry::idx_diff(&idxmeta, Some(&e1), Some(&e1));
3669 assert!(no_r.is_empty());
3670
3671 let add_a_r = Entry::idx_diff(&idxmeta, Some(&e1), Some(&e1_mod));
3673 assert!(add_a_r[0] == Ok((&Attribute::Extra, IndexType::Equality, "test".to_string())));
3674
3675 let del_a_r = Entry::idx_diff(&idxmeta, Some(&e1_mod), Some(&e1));
3677 assert!(del_a_r[0] == Err((&Attribute::Extra, IndexType::Equality, "test".to_string())));
3678
3679 let mut chg_r = Entry::idx_diff(&idxmeta, Some(&e1), Some(&e2));
3681 chg_r.sort_unstable();
3682 eprintln!("{chg_r:?}");
3683 assert!(
3684 chg_r[1]
3685 == Err((
3686 &Attribute::UserId,
3687 IndexType::Equality,
3688 "william".to_string()
3689 ))
3690 );
3691
3692 assert!(
3693 chg_r[0]
3694 == Ok((
3695 &Attribute::UserId,
3696 IndexType::Equality,
3697 "claire".to_string()
3698 ))
3699 );
3700 }
3701
3702 #[test]
3703 fn test_entry_mask_recycled_ts() {
3704 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3705 e1.add_ava(Attribute::Class, EntryClass::Person.to_value());
3706 let e1 = e1.into_sealed_committed();
3707 assert!(e1.mask_recycled_ts().is_some());
3708
3709 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3710 e2.add_ava(Attribute::Class, EntryClass::Person.to_value());
3711 e2.add_ava(Attribute::Class, EntryClass::Recycled.into());
3712 let e2 = e2.into_sealed_committed();
3713 assert!(e2.mask_recycled_ts().is_none());
3714
3715 let mut e3: Entry<EntryInit, EntryNew> = Entry::new();
3716 e3.add_ava(Attribute::Class, EntryClass::Tombstone.into());
3717 let e3 = e3.into_sealed_committed();
3718 assert!(e3.mask_recycled_ts().is_none());
3719 }
3720
3721 #[test]
3722 fn test_entry_idx_name2uuid_diff() {
3723 let r = Entry::idx_name2uuid_diff(None, None);
3725 assert_eq!(r, (None, None));
3726
3727 {
3729 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3730 e.add_ava(Attribute::Class, EntryClass::Person.to_value());
3731 let e = e.into_sealed_committed();
3732
3733 assert!(Entry::idx_name2uuid_diff(None, Some(&e)) == (Some(Set::new()), None));
3734 }
3735
3736 {
3737 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3738 e.add_ava(Attribute::Class, EntryClass::Person.to_value());
3739 e.add_ava(Attribute::GidNumber, Value::new_uint32(1300));
3740 e.add_ava(Attribute::Name, Value::new_iname("testperson"));
3741 e.add_ava(
3742 Attribute::Spn,
3743 Value::new_spn_str("testperson", "example.com"),
3744 );
3745 e.add_ava(
3746 Attribute::Uuid,
3747 Value::Uuid(uuid!("9fec0398-c46c-4df4-9df5-b0016f7d563f")),
3748 );
3749 let e = e.into_sealed_committed();
3750
3751 assert!(
3753 Entry::idx_name2uuid_diff(None, Some(&e))
3754 == (
3755 Some(btreeset![
3756 "1300".to_string(),
3757 "testperson".to_string(),
3758 "testperson@example.com".to_string()
3759 ]),
3760 None
3761 )
3762 );
3763 assert!(
3766 Entry::idx_name2uuid_diff(Some(&e), None)
3767 == (
3768 None,
3769 Some(btreeset![
3770 "1300".to_string(),
3771 "testperson".to_string(),
3772 "testperson@example.com".to_string()
3773 ])
3774 )
3775 );
3776
3777 assert!(
3779 Entry::idx_name2uuid_diff(Some(&e), Some(&e))
3780 == (Some(Set::new()), Some(Set::new()))
3781 );
3782 }
3783 {
3786 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3787 e1.add_ava(Attribute::Class, EntryClass::Person.to_value());
3788 e1.add_ava(
3789 Attribute::Spn,
3790 Value::new_spn_str("testperson", "example.com"),
3791 );
3792 let e1 = e1.into_sealed_committed();
3793
3794 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3795 e2.add_ava(Attribute::Class, EntryClass::Person.to_value());
3796 e2.add_ava(Attribute::Name, Value::new_iname("testperson"));
3797 e2.add_ava(
3798 Attribute::Spn,
3799 Value::new_spn_str("testperson", "example.com"),
3800 );
3801 let e2 = e2.into_sealed_committed();
3802
3803 assert!(
3805 Entry::idx_name2uuid_diff(Some(&e1), Some(&e2))
3806 == (Some(btreeset!["testperson".to_string()]), Some(Set::new()))
3807 );
3808
3809 assert!(
3811 Entry::idx_name2uuid_diff(Some(&e2), Some(&e1))
3812 == (Some(Set::new()), Some(btreeset!["testperson".to_string()]))
3813 );
3814 }
3815
3816 {
3818 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3819 e1.add_ava(Attribute::Class, EntryClass::Person.to_value());
3820 e1.add_ava(
3821 Attribute::Spn,
3822 Value::new_spn_str("testperson", "example.com"),
3823 );
3824 let e1 = e1.into_sealed_committed();
3825
3826 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3827 e2.add_ava(Attribute::Class, EntryClass::Person.to_value());
3828 e2.add_ava(
3829 Attribute::Spn,
3830 Value::new_spn_str("renameperson", "example.com"),
3831 );
3832 let e2 = e2.into_sealed_committed();
3833
3834 assert!(
3835 Entry::idx_name2uuid_diff(Some(&e1), Some(&e2))
3836 == (
3837 Some(btreeset!["renameperson@example.com".to_string()]),
3838 Some(btreeset!["testperson@example.com".to_string()])
3839 )
3840 );
3841 }
3842 }
3843
3844 #[test]
3845 fn test_entry_idx_uuid2spn_diff() {
3846 assert!(Entry::idx_uuid2spn_diff(None, None).is_none());
3847
3848 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3849 e1.add_ava(
3850 Attribute::Spn,
3851 Value::new_spn_str("testperson", "example.com"),
3852 );
3853 let e1 = e1.into_sealed_committed();
3854
3855 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3856 e2.add_ava(
3857 Attribute::Spn,
3858 Value::new_spn_str("renameperson", "example.com"),
3859 );
3860 let e2 = e2.into_sealed_committed();
3861
3862 assert!(
3863 Entry::idx_uuid2spn_diff(None, Some(&e1))
3864 == Some(Ok(Value::new_spn_str("testperson", "example.com")))
3865 );
3866 assert!(Entry::idx_uuid2spn_diff(Some(&e1), None) == Some(Err(())));
3867 assert!(Entry::idx_uuid2spn_diff(Some(&e1), Some(&e1)).is_none());
3868 assert!(
3869 Entry::idx_uuid2spn_diff(Some(&e1), Some(&e2))
3870 == Some(Ok(Value::new_spn_str("renameperson", "example.com")))
3871 );
3872 }
3873
3874 #[test]
3875 fn test_entry_idx_uuid2rdn_diff() {
3876 assert!(Entry::idx_uuid2rdn_diff(None, None).is_none());
3877
3878 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3879 e1.add_ava(
3880 Attribute::Spn,
3881 Value::new_spn_str("testperson", "example.com"),
3882 );
3883 let e1 = e1.into_sealed_committed();
3884
3885 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3886 e2.add_ava(
3887 Attribute::Spn,
3888 Value::new_spn_str("renameperson", "example.com"),
3889 );
3890 let e2 = e2.into_sealed_committed();
3891
3892 assert!(
3893 Entry::idx_uuid2rdn_diff(None, Some(&e1))
3894 == Some(Ok("spn=testperson@example.com".to_string()))
3895 );
3896 assert!(Entry::idx_uuid2rdn_diff(Some(&e1), None) == Some(Err(())));
3897 assert!(Entry::idx_uuid2rdn_diff(Some(&e1), Some(&e1)).is_none());
3898 assert!(
3899 Entry::idx_uuid2rdn_diff(Some(&e1), Some(&e2))
3900 == Some(Ok("spn=renameperson@example.com".to_string()))
3901 );
3902 }
3903}