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 openssl::ec::EcKey;
57use openssl::pkey::{Private, Public};
58use std::cmp::Ordering;
59pub use std::collections::BTreeSet as Set;
60use std::collections::{BTreeMap as Map, BTreeMap, BTreeSet};
61use std::sync::Arc;
62use time::OffsetDateTime;
63use tracing::trace;
64use uuid::Uuid;
65use webauthn_rs::prelude::{
66 AttestationCaList, AttestedPasskey as AttestedPasskeyV4, Passkey as PasskeyV4,
67};
68
69pub type EntryInitNew = Entry<EntryInit, EntryNew>;
70pub type EntryInvalidNew = Entry<EntryInvalid, EntryNew>;
71pub type EntryRefreshNew = Entry<EntryRefresh, EntryNew>;
72pub type EntrySealedNew = Entry<EntrySealed, EntryNew>;
73pub type EntryValidCommitted = Entry<EntryValid, EntryCommitted>;
74pub type EntrySealedCommitted = Entry<EntrySealed, EntryCommitted>;
75pub type EntryInvalidCommitted = Entry<EntryInvalid, EntryCommitted>;
76pub type EntryReducedCommitted = Entry<EntryReduced, EntryCommitted>;
77pub type EntryTuple = (Arc<EntrySealedCommitted>, EntryInvalidCommitted);
78
79pub type EntryIncrementalNew = Entry<EntryIncremental, EntryNew>;
80pub type EntryIncrementalCommitted = Entry<EntryIncremental, EntryCommitted>;
81
82#[derive(Clone, Debug)]
95pub struct EntryNew; #[derive(Clone, Debug)]
99pub struct EntryCommitted {
100 id: u64,
101}
102
103#[derive(Clone, Debug)]
104pub struct EntryInit;
105
106#[derive(Clone, Debug)]
113pub struct EntryInvalid {
114 cid: Cid,
115 ecstate: EntryChangeState,
116}
117
118#[derive(Clone, Debug)]
120pub struct EntryRefresh {
121 ecstate: EntryChangeState,
122}
123
124#[derive(Clone, Debug)]
126pub struct EntryIncremental {
127 uuid: Uuid,
129 ecstate: EntryChangeState,
130}
131
132#[derive(Clone, Debug)]
138pub struct EntryValid {
139 uuid: Uuid,
141 ecstate: EntryChangeState,
142}
143
144#[derive(Clone, Debug)]
151pub struct EntrySealed {
152 uuid: Uuid,
153 ecstate: EntryChangeState,
154}
155
156#[derive(Clone, Debug)]
162pub struct EntryReduced {
163 uuid: Uuid,
164 effective_access: Option<Box<AccessEffectivePermission>>,
165}
166
167pub type Eattrs = Map<Attribute, ValueSet>;
170
171pub trait GetUuid {
172 fn get_uuid(&self) -> Uuid;
173}
174
175pub trait Committed {}
176
177impl Committed for EntrySealed {}
178impl Committed for EntryReduced {}
179
180pub(crate) fn compare_attrs(left: &Eattrs, right: &Eattrs) -> bool {
181 let allkeys: Set<&Attribute> = left
184 .keys()
185 .chain(right.keys())
186 .filter(|k| *k != &Attribute::LastModifiedCid && *k != &Attribute::CreatedAtCid)
187 .collect();
188
189 allkeys.into_iter().all(|k| {
190 let left_vs = left.get(k);
192 let right_vs = right.get(k);
193 let r = match (left_vs, right_vs) {
194 (Some(l), Some(r)) => l.eq(r),
195 _ => false,
196 };
197 if !r {
198 trace!(?k, ?left_vs, ?right_vs, "compare_attrs_allkeys");
199 }
200 r
201 })
202}
203
204pub struct Entry<VALID, STATE> {
231 valid: VALID,
232 state: STATE,
233 attrs: Eattrs,
235}
236
237impl<VALID, STATE> std::fmt::Debug for Entry<VALID, STATE>
238where
239 STATE: std::fmt::Debug,
240 VALID: std::fmt::Debug,
241{
242 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
243 f.debug_struct("Entry<EntrySealed, _>")
244 .field("state", &self.state)
245 .field("valid", &self.valid)
246 .field("attrs", &self.attrs)
247 .finish()
248 }
249}
250
251impl<STATE> std::fmt::Display for Entry<EntrySealed, STATE>
252where
253 STATE: Clone,
254{
255 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
256 write!(f, "{}", self.get_uuid())
257 }
258}
259
260impl<STATE> std::fmt::Display for Entry<EntryInit, STATE>
261where
262 STATE: Clone,
263{
264 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
265 write!(f, "Entry in initial state")
266 }
267}
268
269impl<STATE> Entry<EntryInit, STATE>
270where
271 STATE: Clone,
272{
273 pub fn get_uuid(&self) -> Option<Uuid> {
275 self.attrs
276 .get(&Attribute::Uuid)
277 .and_then(|vs| vs.to_uuid_single())
278 }
279}
280
281impl Default for Entry<EntryInit, EntryNew> {
282 fn default() -> Self {
283 Self::new()
284 }
285}
286
287impl FromIterator<(Attribute, ValueSet)> for EntryInitNew {
288 fn from_iter<I: IntoIterator<Item = (Attribute, ValueSet)>>(iter: I) -> Self {
289 let attrs = Eattrs::from_iter(iter);
290
291 Entry {
292 valid: EntryInit,
293 state: EntryNew,
294 attrs,
295 }
296 }
297}
298
299impl Entry<EntryInit, EntryNew> {
300 pub fn new() -> Self {
301 Entry {
302 valid: EntryInit,
304 state: EntryNew,
305 attrs: Map::new(),
306 }
307 }
308
309 pub fn from_proto_entry(
312 e: &ProtoEntry,
313 qs: &mut QueryServerWriteTransaction,
314 ) -> Result<Self, OperationError> {
315 trace!("from_proto_entry");
316 let map2: Result<Eattrs, OperationError> = e
323 .attrs
324 .iter()
325 .filter(|(_, v)| !v.is_empty())
326 .map(|(k, v)| {
327 trace!(?k, ?v, "attribute");
328 let attr_nk = Attribute::from(k.as_str());
329 let nv = valueset::from_result_value_iter(
330 v.iter().map(|vr| qs.clone_value(&attr_nk, vr)),
331 );
332 trace!(?nv, "new valueset transform");
333 match nv {
334 Ok(nvi) => Ok((attr_nk, nvi)),
335 Err(e) => Err(e),
336 }
337 })
338 .collect();
339
340 let x = map2?;
341
342 Ok(Entry {
343 state: EntryNew,
344 valid: EntryInit,
345 attrs: x,
346 })
347 }
348
349 #[instrument(level = "debug", skip_all)]
352 pub fn from_proto_entry_str(
353 es: &str,
354 qs: &mut QueryServerWriteTransaction,
355 ) -> Result<Self, OperationError> {
356 if cfg!(test) {
357 if es.len() > 256 {
358 let (dsp_es, _) = es.split_at(255);
359 trace!("Parsing -> {}...", dsp_es);
360 } else {
361 trace!("Parsing -> {}", es);
362 }
363 }
364 let pe: ProtoEntry = serde_json::from_str(es).map_err(|e| {
366 admin_error!(?e, "SerdeJson Failure");
369 OperationError::SerdeJsonError
370 })?;
371 Self::from_proto_entry(&pe, qs)
373 }
374
375 pub fn assign_cid(
378 mut self,
379 cid: Cid,
380 schema: &dyn SchemaTransaction,
381 ) -> Entry<EntryInvalid, EntryNew> {
382 let ecstate = EntryChangeState::new(&cid, &self.attrs, schema);
388
389 let cv = vs_cid![cid.clone()];
392 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
393 let cv = vs_cid![cid.clone()];
394 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
395
396 Entry {
397 valid: EntryInvalid { cid, ecstate },
398 state: EntryNew,
399 attrs: self.attrs,
400 }
401 }
402
403 pub fn compare(&self, rhs: &Entry<EntrySealed, EntryCommitted>) -> bool {
405 compare_attrs(&self.attrs, &rhs.attrs)
406 }
407
408 #[cfg(test)]
412 pub fn into_invalid_new(mut self) -> Entry<EntryInvalid, EntryNew> {
413 let cid = Cid::new_zero();
414 self.set_last_changed(cid.clone());
415
416 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
417
418 Entry {
419 valid: EntryInvalid { cid, ecstate },
420 state: EntryNew,
421 attrs: self.attrs,
422 }
423 }
424
425 #[cfg(test)]
429 pub fn into_valid_new(mut self) -> Entry<EntryValid, EntryNew> {
430 let cid = Cid::new_zero();
431 self.set_last_changed(cid.clone());
432 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
433
434 Entry {
435 valid: EntryValid {
436 ecstate,
437 uuid: self.get_uuid().expect("Invalid uuid"),
438 },
439 state: EntryNew,
440 attrs: self.attrs,
441 }
442 }
443
444 #[cfg(test)]
448 pub fn into_sealed_committed(mut self) -> Entry<EntrySealed, EntryCommitted> {
449 let cid = Cid::new_zero();
450 self.set_last_changed(cid.clone());
451 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
452 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
453 Entry {
454 valid: EntrySealed { uuid, ecstate },
455 state: EntryCommitted { id: 0 },
456 attrs: self.attrs,
457 }
458 }
459
460 #[cfg(test)]
464 pub fn into_sealed_new(mut self) -> Entry<EntrySealed, EntryNew> {
465 let cid = Cid::new_zero();
466 self.set_last_changed(cid.clone());
467 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
468
469 Entry {
470 valid: EntrySealed {
471 uuid: self.get_uuid().expect("Invalid uuid"),
472 ecstate,
473 },
474 state: EntryNew,
475 attrs: self.attrs,
476 }
477 }
478
479 pub fn add_ava(&mut self, attr: Attribute, value: Value) {
485 self.add_ava_int(attr, value);
486 }
487
488 pub fn remove_ava(&mut self, attr: &Attribute) {
489 self.attrs.remove(attr);
490 }
491
492 pub fn set_ava_set(&mut self, attr: &Attribute, vs: ValueSet) {
494 self.attrs.insert(attr.clone(), vs);
495 }
496
497 pub fn set_ava<T>(&mut self, attr: Attribute, iter: T)
499 where
500 T: IntoIterator<Item = Value>,
501 {
502 self.set_ava_iter_int(attr, iter);
503 }
504
505 pub fn get_ava_mut<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<&mut ValueSet> {
506 self.attrs.get_mut(attr.as_ref())
507 }
508}
509
510impl From<SchemaAttribute> for EntryInitNew {
511 fn from(value: SchemaAttribute) -> Self {
512 EntryInitNew::from(&value)
513 }
514}
515
516impl From<&SchemaAttribute> for EntryInitNew {
517 fn from(s: &SchemaAttribute) -> Self {
518 let mut attrs = Eattrs::new();
520 attrs.insert(Attribute::AttributeName, vs_iutf8![s.name.as_str()]);
521 attrs.insert(Attribute::Description, vs_utf8![s.description.to_owned()]);
522 attrs.insert(Attribute::Uuid, vs_uuid![s.uuid]);
523 attrs.insert(Attribute::MultiValue, vs_bool![s.multivalue]);
524 attrs.insert(Attribute::Phantom, vs_bool![s.phantom]);
525 attrs.insert(Attribute::SyncAllowed, vs_bool![s.sync_allowed]);
526 attrs.insert(Attribute::Replicated, vs_bool![s.replicated.into()]);
527 attrs.insert(Attribute::Unique, vs_bool![s.unique]);
528 attrs.insert(Attribute::Indexed, vs_bool![s.indexed]);
529 attrs.insert(Attribute::Syntax, vs_syntax![s.syntax]);
530 attrs.insert(
531 Attribute::Class,
532 vs_iutf8![
533 EntryClass::Object.into(),
534 EntryClass::System.into(),
535 EntryClass::AttributeType.into()
536 ],
537 );
538
539 Entry {
542 valid: EntryInit,
543 state: EntryNew,
544 attrs,
545 }
546 }
547}
548
549impl From<SchemaClass> for EntryInitNew {
550 fn from(value: SchemaClass) -> Self {
551 EntryInitNew::from(&value)
552 }
553}
554
555impl From<&SchemaClass> for EntryInitNew {
556 fn from(s: &SchemaClass) -> Self {
557 let mut attrs = Eattrs::new();
558 attrs.insert(Attribute::ClassName, vs_iutf8![s.name.as_str()]);
559 attrs.insert(Attribute::Description, vs_utf8![s.description.to_owned()]);
560 attrs.insert(Attribute::SyncAllowed, vs_bool![s.sync_allowed]);
561 attrs.insert(Attribute::Uuid, vs_uuid![s.uuid]);
562 attrs.insert(
563 Attribute::Class,
564 vs_iutf8![
565 EntryClass::Object.into(),
566 EntryClass::System.into(),
567 EntryClass::ClassType.into()
568 ],
569 );
570
571 let vs_systemmay = ValueSetIutf8::from_iter(s.systemmay.iter().map(|sm| sm.as_str()));
572 if let Some(vs) = vs_systemmay {
573 attrs.insert(Attribute::SystemMay, vs);
574 }
575
576 let vs_systemmust = ValueSetIutf8::from_iter(s.systemmust.iter().map(|sm| sm.as_str()));
577 if let Some(vs) = vs_systemmust {
578 attrs.insert(Attribute::SystemMust, vs);
579 }
580
581 let vs_systemexcludes =
582 ValueSetIutf8::from_iter(s.systemexcludes.iter().map(|sm| sm.as_str()));
583 if let Some(vs) = vs_systemexcludes {
584 attrs.insert(Attribute::SystemExcludes, vs);
585 }
586
587 let vs_systemsupplements =
588 ValueSetIutf8::from_iter(s.systemsupplements.iter().map(|sm| sm.as_str()));
589 if let Some(vs) = vs_systemsupplements {
590 attrs.insert(Attribute::SystemSupplements, vs);
591 }
592
593 Entry {
594 valid: EntryInit,
595 state: EntryNew,
596 attrs,
597 }
598 }
599}
600
601impl Entry<EntryRefresh, EntryNew> {
602 pub fn from_repl_entry_v1(repl_entry: ReplEntryV1) -> Result<Self, OperationError> {
603 let (ecstate, mut attrs) = repl_entry.rehydrate()?;
605
606 let last_mod_cid = ecstate.get_max_cid();
609 let cv = vs_cid![last_mod_cid.clone()];
610 let _ = attrs.insert(Attribute::LastModifiedCid, cv);
611
612 let create_at_cid = ecstate.at();
613 let cv = vs_cid![create_at_cid.clone()];
614 let _ = attrs.insert(Attribute::CreatedAtCid, cv);
615
616 Ok(Entry {
617 valid: EntryRefresh { ecstate },
618 state: EntryNew,
619 attrs,
620 })
621 }
622}
623
624impl<STATE> Entry<EntryRefresh, STATE> {
625 pub fn validate(
626 self,
627 schema: &dyn SchemaTransaction,
628 ) -> Result<Entry<EntryValid, STATE>, SchemaError> {
629 let uuid: Uuid = self
630 .attrs
631 .get(&Attribute::Uuid)
632 .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
633 .and_then(|vs| {
634 vs.to_uuid_single()
635 .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
636 })?;
637
638 let ne = Entry {
640 valid: EntryValid {
641 uuid,
642 ecstate: self.valid.ecstate,
643 },
644 state: self.state,
645 attrs: self.attrs,
646 };
647
648 ne.validate(schema).map(|()| ne)
649 }
650}
651
652impl<STATE> Entry<EntryIncremental, STATE> {
653 pub fn get_uuid(&self) -> Uuid {
654 self.valid.uuid
655 }
656}
657
658impl Entry<EntryIncremental, EntryNew> {
659 fn stub_ecstate(&self) -> EntryChangeState {
660 self.valid.ecstate.stub()
661 }
662
663 pub fn rehydrate(repl_inc_entry: ReplIncrementalEntryV1) -> Result<Self, OperationError> {
664 let (uuid, ecstate, attrs) = repl_inc_entry.rehydrate()?;
665
666 Ok(Entry {
667 valid: EntryIncremental { uuid, ecstate },
668 state: EntryNew,
669 attrs,
670 })
671 }
672
673 pub(crate) fn is_add_conflict(&self, db_entry: &EntrySealedCommitted) -> bool {
674 use crate::repl::entry::State;
675 debug_assert_eq!(self.valid.uuid, db_entry.valid.uuid);
676 let self_cs = &self.valid.ecstate;
678 let db_cs = db_entry.get_changestate();
679
680 match (self_cs.current(), db_cs.current()) {
682 (State::Live { at: at_left, .. }, State::Live { at: at_right, .. }) => {
683 at_left != at_right
684 }
685 _ => false,
687 }
688 }
689
690 pub(crate) fn resolve_add_conflict(
691 &self,
692 cid: &Cid,
693 db_ent: &EntrySealedCommitted,
694 ) -> (Option<EntrySealedNew>, EntryIncrementalCommitted) {
695 use crate::repl::entry::State;
696 debug_assert_eq!(self.valid.uuid, db_ent.valid.uuid);
697 let self_cs = &self.valid.ecstate;
698 let db_cs = db_ent.get_changestate();
699
700 match (self_cs.current(), db_cs.current()) {
701 (
702 State::Live {
703 at: at_left,
704 changes: _changes_left,
705 },
706 State::Live {
707 at: at_right,
708 changes: _changes_right,
709 },
710 ) => {
711 debug_assert!(at_left != at_right);
712 if at_left > at_right {
723 trace!("RI > DE, return DE");
724 (
725 None,
726 Entry {
727 valid: EntryIncremental {
728 uuid: db_ent.valid.uuid,
729 ecstate: db_cs.clone(),
730 },
731 state: EntryCommitted {
732 id: db_ent.state.id,
733 },
734 attrs: db_ent.attrs.clone(),
735 },
736 )
737 }
738 else {
748 trace!("RI < DE, return RI");
749 let conflict = if at_right.s_uuid == cid.s_uuid {
751 trace!("Origin process conflict entry");
752 let mut cnf_ent = Entry {
755 valid: EntryInvalid {
756 cid: cid.clone(),
757 ecstate: db_cs.clone(),
758 },
759 state: EntryNew,
760 attrs: db_ent.attrs.clone(),
761 };
762
763 cnf_ent.add_ava(Attribute::SourceUuid, Value::Uuid(db_ent.valid.uuid));
765
766 let new_uuid = Uuid::new_v4();
768 cnf_ent.purge_ava(Attribute::Uuid);
769 cnf_ent.add_ava(Attribute::Uuid, Value::Uuid(new_uuid));
770 cnf_ent.add_ava(Attribute::Class, EntryClass::Recycled.into());
771 cnf_ent.add_ava(Attribute::Class, EntryClass::Conflict.into());
772
773 let cv = vs_cid![cid.clone()];
777 let _ = cnf_ent.attrs.insert(Attribute::LastModifiedCid, cv);
778 let cv = vs_cid![cid.clone()];
780 let _ = cnf_ent.attrs.insert(Attribute::CreatedAtCid, cv);
781
782 let Entry {
786 valid: EntryInvalid { cid: _, ecstate },
787 state,
788 attrs,
789 } = cnf_ent;
790
791 let cnf_ent = Entry {
792 valid: EntrySealed {
793 uuid: new_uuid,
794 ecstate,
795 },
796 state,
797 attrs,
798 };
799
800 Some(cnf_ent)
801 } else {
802 None
803 };
804
805 let mut attrs = self.attrs.clone();
809 let ecstate = self_cs.clone();
810
811 let last_mod_cid = ecstate.get_max_cid();
812 let cv = vs_cid![last_mod_cid.clone()];
813 let _ = attrs.insert(Attribute::LastModifiedCid, cv);
814
815 let create_at_cid = ecstate.at();
816 let cv = vs_cid![create_at_cid.clone()];
817 let _ = attrs.insert(Attribute::CreatedAtCid, cv);
818
819 (
820 conflict,
821 Entry {
822 valid: EntryIncremental {
823 uuid: self.valid.uuid,
824 ecstate,
825 },
826 state: EntryCommitted {
827 id: db_ent.state.id,
828 },
829 attrs,
830 },
831 )
832 }
833 }
834 _ => unreachable!(),
836 }
837 }
838
839 pub(crate) fn merge_state(
840 &self,
841 db_ent: &EntrySealedCommitted,
842 schema: &dyn SchemaTransaction,
843 trim_cid: &Cid,
844 ) -> EntryIncrementalCommitted {
845 use crate::repl::entry::State;
846
847 debug_assert_eq!(self.valid.uuid, db_ent.valid.uuid);
849
850 let self_cs = &self.valid.ecstate;
853 let db_cs = db_ent.get_changestate();
854
855 match (self_cs.current(), db_cs.current()) {
856 (
857 State::Live {
858 at: at_left,
859 changes: changes_left,
860 },
861 State::Live {
862 at: at_right,
863 changes: changes_right,
864 },
865 ) => {
866 debug_assert_eq!(at_left, at_right);
867 let mut attr_set: Vec<_> =
872 changes_left.keys().chain(changes_right.keys()).collect();
873 attr_set.shrink_to_fit();
874 attr_set.sort_unstable();
875 attr_set.dedup();
876
877 let mut changes = BTreeMap::default();
879 let mut eattrs = Eattrs::default();
880
881 for attr_name in attr_set.into_iter() {
883 match (changes_left.get(attr_name), changes_right.get(attr_name)) {
884 (Some(cid_left), Some(cid_right)) => {
885 let take_left = cid_left > cid_right;
892
893 match (self.attrs.get(attr_name), db_ent.attrs.get(attr_name)) {
894 (Some(vs_left), Some(vs_right)) if take_left => {
895 changes.insert(attr_name.clone(), cid_left.clone());
896 #[allow(clippy::todo)]
897 if let Some(merged_attr_state) =
898 vs_left.repl_merge_valueset(vs_right, trim_cid)
899 {
900 eattrs.insert(attr_name.clone(), merged_attr_state);
903 } else {
904 eattrs.insert(attr_name.clone(), vs_left.clone());
905 }
906 }
907 (Some(vs_left), Some(vs_right)) => {
908 changes.insert(attr_name.clone(), cid_right.clone());
909 #[allow(clippy::todo)]
910 if let Some(merged_attr_state) =
911 vs_right.repl_merge_valueset(vs_left, trim_cid)
912 {
913 eattrs.insert(attr_name.clone(), merged_attr_state);
916 } else {
917 eattrs.insert(attr_name.clone(), vs_right.clone());
918 }
919 }
920 (Some(vs_left), None) if take_left => {
921 changes.insert(attr_name.clone(), cid_left.clone());
922 eattrs.insert(attr_name.clone(), vs_left.clone());
923 }
924 (Some(_vs_left), None) => {
925 changes.insert(attr_name.clone(), cid_right.clone());
926 }
928 (None, Some(_vs_right)) if take_left => {
929 changes.insert(attr_name.clone(), cid_left.clone());
930 }
932 (None, Some(vs_right)) => {
933 changes.insert(attr_name.clone(), cid_right.clone());
934 eattrs.insert(attr_name.clone(), vs_right.clone());
935 }
936 (None, None) if take_left => {
937 changes.insert(attr_name.clone(), cid_left.clone());
938 }
940 (None, None) => {
941 changes.insert(attr_name.clone(), cid_right.clone());
942 }
944 }
945 }
947 (Some(cid_left), None) => {
948 changes.insert(attr_name.clone(), cid_left.clone());
950 if let Some(valueset) = self.attrs.get(attr_name) {
951 eattrs.insert(attr_name.clone(), valueset.clone());
952 }
953 }
954 (None, Some(cid_right)) => {
955 changes.insert(attr_name.clone(), cid_right.clone());
957 if let Some(valueset) = db_ent.attrs.get(attr_name) {
958 eattrs.insert(attr_name.clone(), valueset.clone());
959 }
960 }
961 (None, None) => {
962 debug_assert!(false);
964 }
965 }
966 }
967
968 let mut ecstate = EntryChangeState::build(State::Live {
969 at: at_left.clone(),
970 changes,
971 });
972
973 ecstate.retain(|k, _| schema.is_replicated(k));
977
978 let cv = vs_cid![ecstate.get_max_cid().clone()];
979 let _ = eattrs.insert(Attribute::LastModifiedCid, cv);
980
981 let cv = vs_cid![ecstate.at().clone()];
982 let _ = eattrs.insert(Attribute::CreatedAtCid, cv);
983
984 Entry {
985 valid: EntryIncremental {
986 uuid: self.valid.uuid,
987 ecstate,
988 },
989 state: EntryCommitted {
990 id: db_ent.state.id,
991 },
992 attrs: eattrs,
993 }
994 }
995 (State::Tombstone { at: left_at }, State::Live { .. }) => {
996 let mut attrs_new: Eattrs = Map::new();
1000 let class_ava = vs_iutf8![EntryClass::Object.into(), EntryClass::Tombstone.into()];
1001 let last_mod_ava = vs_cid![left_at.clone()];
1002 let created_ava = vs_cid![left_at.clone()];
1003
1004 attrs_new.insert(Attribute::Uuid, vs_uuid![self.valid.uuid]);
1005 attrs_new.insert(Attribute::Class, class_ava);
1006 attrs_new.insert(Attribute::LastModifiedCid, last_mod_ava);
1007 attrs_new.insert(Attribute::CreatedAtCid, created_ava);
1008
1009 Entry {
1010 valid: EntryIncremental {
1011 uuid: self.valid.uuid,
1012 ecstate: self.valid.ecstate.clone(),
1013 },
1014 state: EntryCommitted {
1015 id: db_ent.state.id,
1016 },
1017 attrs: attrs_new,
1018 }
1019 }
1020 (State::Live { .. }, State::Tombstone { .. }) => {
1021 Entry {
1029 valid: EntryIncremental {
1030 uuid: db_ent.valid.uuid,
1031 ecstate: db_ent.valid.ecstate.clone(),
1032 },
1033 state: EntryCommitted {
1034 id: db_ent.state.id,
1035 },
1036 attrs: db_ent.attrs.clone(),
1037 }
1038 }
1039 (State::Tombstone { at: left_at }, State::Tombstone { at: right_at }) => {
1040 let (at, ecstate) = if left_at < right_at {
1045 (left_at, self.valid.ecstate.clone())
1046 } else {
1047 (right_at, db_ent.valid.ecstate.clone())
1048 };
1049
1050 let mut attrs_new: Eattrs = Map::new();
1051 let class_ava = vs_iutf8![EntryClass::Object.into(), EntryClass::Tombstone.into()];
1052 let last_mod_ava = vs_cid![at.clone()];
1053 let created_ava = vs_cid![at.clone()];
1054
1055 attrs_new.insert(Attribute::Uuid, vs_uuid![db_ent.valid.uuid]);
1056 attrs_new.insert(Attribute::Class, class_ava);
1057 attrs_new.insert(Attribute::LastModifiedCid, last_mod_ava);
1058 attrs_new.insert(Attribute::CreatedAtCid, created_ava);
1059
1060 Entry {
1061 valid: EntryIncremental {
1062 uuid: db_ent.valid.uuid,
1063 ecstate,
1064 },
1065 state: EntryCommitted {
1066 id: db_ent.state.id,
1067 },
1068 attrs: attrs_new,
1069 }
1070 }
1071 }
1072 }
1073}
1074
1075impl Entry<EntryIncremental, EntryCommitted> {
1076 pub(crate) fn validate_repl(self, schema: &dyn SchemaTransaction) -> EntryValidCommitted {
1077 let mut ne = Entry {
1082 valid: EntryValid {
1083 uuid: self.valid.uuid,
1084 ecstate: self.valid.ecstate,
1085 },
1086 state: self.state,
1087 attrs: self.attrs,
1088 };
1089
1090 if let Err(e) = ne.validate(schema) {
1091 warn!(uuid = ?self.valid.uuid, err = ?e, "Entry failed schema check, moving to a conflict state");
1092 ne.add_ava_int(Attribute::Class, EntryClass::Recycled.into());
1093 ne.add_ava_int(Attribute::Class, EntryClass::Conflict.into());
1094 ne.add_ava_int(Attribute::SourceUuid, Value::Uuid(self.valid.uuid));
1095 }
1096 ne
1097 }
1098}
1099
1100impl<STATE> Entry<EntryInvalid, STATE> {
1101 pub(crate) fn get_uuid(&self) -> Option<Uuid> {
1102 self.attrs
1103 .get(&Attribute::Uuid)
1104 .and_then(|vs| vs.to_uuid_single())
1105 }
1106
1107 pub fn validate(
1110 self,
1111 schema: &dyn SchemaTransaction,
1112 ) -> Result<Entry<EntryValid, STATE>, SchemaError> {
1113 let uuid: Uuid = self
1114 .attrs
1115 .get(&Attribute::Uuid)
1116 .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
1117 .and_then(|vs| {
1118 vs.to_uuid_single()
1119 .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
1120 })?;
1121
1122 let ne = Entry {
1124 valid: EntryValid {
1125 uuid,
1126 ecstate: self.valid.ecstate,
1127 },
1128 state: self.state,
1129 attrs: self.attrs,
1130 };
1131
1132 ne.validate(schema).map(|()| ne)
1133 }
1134
1135 pub(crate) fn get_ava_refer_mut<A: AsRef<Attribute>>(
1139 &mut self,
1140 attr: A,
1141 ) -> Option<&mut BTreeSet<Uuid>> {
1142 self.get_ava_mut(attr).and_then(|vs| vs.as_refer_set_mut())
1143 }
1144
1145 pub(crate) fn get_ava_mut<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<&mut ValueSet> {
1146 let attr_ref = attr.as_ref();
1147 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
1148 self.attrs.get_mut(attr.as_ref())
1149 }
1150}
1151
1152impl<VALID, STATE> Clone for Entry<VALID, STATE>
1153where
1154 VALID: Clone,
1155 STATE: Clone,
1156{
1157 fn clone(&self) -> Entry<VALID, STATE> {
1159 Entry {
1160 valid: self.valid.clone(),
1161 state: self.state.clone(),
1162 attrs: self.attrs.clone(),
1163 }
1164 }
1165}
1166
1167impl Entry<EntryInvalid, EntryCommitted> {
1168 #[cfg(test)]
1172 pub fn into_valid_new(self) -> Entry<EntryValid, EntryNew> {
1173 let uuid = self.get_uuid().expect("Invalid uuid");
1174 Entry {
1175 valid: EntryValid {
1176 uuid,
1177 ecstate: self.valid.ecstate,
1178 },
1179 state: EntryNew,
1180 attrs: self.attrs,
1181 }
1182 }
1183
1184 pub fn to_recycled(mut self) -> Self {
1186 self.add_ava(Attribute::Class, EntryClass::Recycled.into());
1188
1189 Entry {
1193 valid: self.valid,
1194 state: self.state,
1195 attrs: self.attrs,
1196 }
1197 }
1198
1199 pub fn to_conflict<T>(&mut self, iter: T)
1201 where
1202 T: IntoIterator<Item = Uuid>,
1203 {
1204 self.add_ava(Attribute::Class, EntryClass::Recycled.into());
1205 self.add_ava(Attribute::Class, EntryClass::Conflict.into());
1206 for source_uuid in iter {
1208 self.add_ava(Attribute::SourceUuid, Value::Uuid(source_uuid));
1209 }
1210 }
1211
1212 pub fn to_revived(mut self) -> Self {
1214 self.remove_ava(Attribute::Class, &EntryClass::Recycled.into());
1216 self.remove_ava(Attribute::Class, &EntryClass::Conflict.into());
1217
1218 self.purge_ava(Attribute::CascadeDeleted);
1219 self.purge_ava(Attribute::SourceUuid);
1220 self.purge_ava(Attribute::RecycledDirectMemberOf);
1221
1222 Entry {
1226 valid: self.valid,
1227 state: self.state,
1228 attrs: self.attrs,
1229 }
1230 }
1231}
1232impl Entry<EntryInvalid, EntryNew> {
1235 #[cfg(test)]
1238 pub fn into_init_new(self) -> Entry<EntryInit, EntryNew> {
1239 Entry {
1240 valid: EntryInit,
1241 state: EntryNew,
1242 attrs: self.attrs,
1243 }
1244 }
1245
1246 #[cfg(test)]
1250 pub fn into_valid_new(self) -> Entry<EntryValid, EntryNew> {
1251 let uuid = self.get_uuid().expect("Invalid uuid");
1252 Entry {
1253 valid: EntryValid {
1254 uuid,
1255 ecstate: self.valid.ecstate,
1256 },
1257 state: EntryNew,
1258 attrs: self.attrs,
1259 }
1260 }
1261
1262 #[cfg(test)]
1266 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1267 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
1268 Entry {
1269 valid: EntrySealed {
1270 uuid,
1271 ecstate: self.valid.ecstate,
1272 },
1273 state: EntryCommitted { id: 0 },
1274 attrs: self.attrs,
1275 }
1276 }
1277
1278 #[cfg(test)]
1282 pub fn into_valid_committed(self) -> Entry<EntryValid, EntryCommitted> {
1283 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
1284 Entry {
1285 valid: EntryValid {
1286 uuid,
1287 ecstate: self.valid.ecstate,
1288 },
1289 state: EntryCommitted { id: 0 },
1290 attrs: self.attrs,
1291 }
1292 }
1293}
1294
1295impl Entry<EntryInvalid, EntryCommitted> {
1296 #[cfg(test)]
1300 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1301 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
1302 Entry {
1303 valid: EntrySealed {
1304 uuid,
1305 ecstate: self.valid.ecstate,
1306 },
1307 state: self.state,
1308 attrs: self.attrs,
1309 }
1310 }
1311}
1312
1313impl Entry<EntrySealed, EntryNew> {
1314 #[cfg(test)]
1318 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1319 Entry {
1320 valid: self.valid,
1321 state: EntryCommitted { id: 0 },
1322 attrs: self.attrs,
1323 }
1324 }
1325
1326 pub fn into_sealed_committed_id(self, id: u64) -> Entry<EntrySealed, EntryCommitted> {
1329 Entry {
1330 valid: self.valid,
1331 state: EntryCommitted { id },
1332 attrs: self.attrs,
1333 }
1334 }
1335
1336 pub fn compare(&self, rhs: &Entry<EntrySealed, EntryNew>) -> bool {
1337 compare_attrs(&self.attrs, &rhs.attrs)
1338 }
1339}
1340
1341type IdxDiff<'a> =
1342 Vec<Result<(&'a Attribute, IndexType, String), (&'a Attribute, IndexType, String)>>;
1343
1344impl<VALID> Entry<VALID, EntryCommitted> {
1345 pub fn get_id(&self) -> u64 {
1347 self.state.id
1348 }
1349}
1350
1351impl<STATE> Entry<EntrySealed, STATE> {
1352 pub fn into_init(self) -> Entry<EntryInit, STATE> {
1353 Entry {
1354 valid: EntryInit,
1355 state: self.state,
1356 attrs: self.attrs,
1357 }
1358 }
1359}
1360
1361impl Entry<EntrySealed, EntryCommitted> {
1362 #[cfg(test)]
1363 pub(crate) fn get_last_changed(&self) -> Cid {
1364 self.valid.ecstate.get_max_cid().clone()
1365 }
1366
1367 #[cfg(test)]
1369 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1370 self
1372 }
1373
1374 pub(crate) fn stub_sealed_committed_id(
1375 id: u64,
1376 ctx_ent: &EntryIncrementalNew,
1377 ) -> EntrySealedCommitted {
1378 let uuid = ctx_ent.get_uuid();
1379 let ecstate = ctx_ent.stub_ecstate();
1380
1381 Entry {
1382 valid: EntrySealed { uuid, ecstate },
1383 state: EntryCommitted { id },
1384 attrs: Default::default(),
1385 }
1386 }
1387
1388 pub fn insert_claim(&mut self, value: &str) {
1391 self.add_ava_int(Attribute::Claim, Value::new_iutf8(value));
1392 }
1393
1394 pub fn compare(&self, rhs: &Entry<EntrySealed, EntryCommitted>) -> bool {
1395 compare_attrs(&self.attrs, &rhs.attrs)
1396 }
1397
1398 pub fn to_dbentry(&self) -> DbEntry {
1400 DbEntry {
1403 ent: DbEntryVers::V3 {
1404 changestate: self.valid.ecstate.to_db_changestate(),
1405 attrs: self
1406 .attrs
1407 .iter()
1408 .map(|(k, vs)| {
1409 let dbvs: DbValueSetV2 = vs.to_db_valueset_v2();
1410 (k.clone(), dbvs)
1411 })
1412 .collect(),
1413 },
1414 }
1415 }
1416
1417 #[inline]
1418 fn get_name2uuid_cands(&self) -> Set<String> {
1421 let cands = [Attribute::Spn, Attribute::Name, Attribute::GidNumber];
1427 cands
1428 .iter()
1429 .filter_map(|cand| {
1430 self.attrs
1431 .get(cand)
1432 .map(|vs| vs.to_proto_string_clone_iter())
1433 })
1434 .flatten()
1435 .collect()
1436 }
1437
1438 #[inline]
1439 fn get_externalid2uuid(&self) -> Option<String> {
1442 self.attrs
1443 .get(&Attribute::SyncExternalId)
1444 .and_then(|vs| vs.to_proto_string_single())
1445 }
1446
1447 #[inline]
1448 pub(crate) fn get_uuid2spn(&self) -> Value {
1451 self.attrs
1452 .get(&Attribute::Spn)
1453 .and_then(|vs| vs.to_value_single())
1454 .or_else(|| {
1455 self.attrs
1456 .get(&Attribute::Name)
1457 .and_then(|vs| vs.to_value_single())
1458 })
1459 .unwrap_or_else(|| Value::Uuid(self.get_uuid()))
1460 }
1461
1462 #[inline]
1463 pub(crate) fn get_uuid2rdn(&self) -> String {
1467 self.attrs
1468 .get(&Attribute::Spn)
1469 .and_then(|vs| vs.to_proto_string_single().map(|v| format!("spn={v}")))
1470 .or_else(|| {
1471 self.attrs
1472 .get(&Attribute::Name)
1473 .and_then(|vs| vs.to_proto_string_single().map(|v| format!("name={v}")))
1474 })
1475 .unwrap_or_else(|| format!("uuid={}", self.get_uuid().as_hyphenated()))
1476 }
1477
1478 pub(crate) fn idx_name2uuid_diff(
1481 pre: Option<&Self>,
1482 post: Option<&Self>,
1483 ) -> (
1484 Option<Set<String>>,
1486 Option<Set<String>>,
1488 ) {
1489 match (pre, post) {
1491 (None, None) => {
1492 (None, None)
1494 }
1495 (None, Some(b)) => {
1496 (Some(b.get_name2uuid_cands()), None)
1499 }
1500 (Some(a), None) => {
1501 (None, Some(a.get_name2uuid_cands()))
1503 }
1504 (Some(a), Some(b)) => {
1505 let pre_set = a.get_name2uuid_cands();
1506 let post_set = b.get_name2uuid_cands();
1507
1508 let add_set: Set<_> = post_set.difference(&pre_set).cloned().collect();
1510 let rem_set: Set<_> = pre_set.difference(&post_set).cloned().collect();
1512 (Some(add_set), Some(rem_set))
1513 }
1514 }
1515 }
1516
1517 pub(crate) fn idx_externalid2uuid_diff(
1519 pre: Option<&Self>,
1520 post: Option<&Self>,
1521 ) -> (Option<String>, Option<String>) {
1522 match (pre, post) {
1523 (None, None) => {
1524 (None, None)
1526 }
1527 (None, Some(b)) => {
1528 (b.get_externalid2uuid(), None)
1530 }
1531 (Some(a), None) => {
1532 (None, a.get_externalid2uuid())
1534 }
1535 (Some(a), Some(b)) => {
1536 let ia = a.get_externalid2uuid();
1537 let ib = b.get_externalid2uuid();
1538 if ia != ib {
1539 (ib, ia)
1542 } else {
1543 (None, None)
1545 }
1546 }
1547 }
1548 }
1549
1550 pub(crate) fn idx_uuid2spn_diff(
1553 pre: Option<&Self>,
1554 post: Option<&Self>,
1555 ) -> Option<Result<Value, ()>> {
1556 match (pre, post) {
1557 (None, None) => {
1558 None
1560 }
1561 (None, Some(b)) => {
1562 Some(Ok(b.get_uuid2spn()))
1564 }
1565 (Some(_a), None) => {
1566 Some(Err(()))
1568 }
1569 (Some(a), Some(b)) => {
1570 let ia = a.get_uuid2spn();
1571 let ib = b.get_uuid2spn();
1572 if ia != ib {
1573 Some(Ok(ib))
1575 } else {
1576 None
1578 }
1579 }
1580 }
1581 }
1582
1583 pub(crate) fn idx_uuid2rdn_diff(
1586 pre: Option<&Self>,
1587 post: Option<&Self>,
1588 ) -> Option<Result<String, ()>> {
1589 match (pre, post) {
1590 (None, None) => {
1591 None
1593 }
1594 (None, Some(b)) => {
1595 Some(Ok(b.get_uuid2rdn()))
1597 }
1598 (Some(_a), None) => {
1599 Some(Err(()))
1601 }
1602 (Some(a), Some(b)) => {
1603 let ia = a.get_uuid2rdn();
1604 let ib = b.get_uuid2rdn();
1605 if ia != ib {
1606 Some(Ok(ib))
1608 } else {
1609 None
1611 }
1612 }
1613 }
1614 }
1615
1616 pub(crate) fn idx_diff<'a>(
1619 idxmeta: &'a HashMap<IdxKey, IdxSlope>,
1620 pre: Option<&Self>,
1621 post: Option<&Self>,
1622 ) -> IdxDiff<'a> {
1623 match (pre, post) {
1628 (None, None) => {
1629 Vec::with_capacity(0)
1631 }
1632 (Some(pre_e), None) => {
1633 idxmeta
1635 .keys()
1636 .flat_map(|ikey| {
1637 match pre_e.get_ava_set(&ikey.attr) {
1638 None => Vec::with_capacity(0),
1639 Some(vs) => {
1640 let changes: Vec<Result<_, _>> = match ikey.itype {
1641 IndexType::Equality => {
1642 vs.generate_idx_eq_keys()
1644 .into_iter()
1645 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1646 .collect()
1647 }
1648 IndexType::Presence => {
1649 vec![Err((&ikey.attr, ikey.itype, "_".to_string()))]
1650 }
1651 IndexType::SubString => vs
1652 .generate_idx_sub_keys()
1653 .into_iter()
1654 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1655 .collect(),
1656 IndexType::Ordering => vs
1657 .generate_idx_ord_keys()
1658 .into_iter()
1659 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1660 .collect(),
1661 };
1662 changes
1663 }
1664 }
1665 })
1666 .collect()
1667 }
1668 (None, Some(post_e)) => {
1669 idxmeta
1671 .keys()
1672 .flat_map(|ikey| {
1673 match post_e.get_ava_set(&ikey.attr) {
1674 None => Vec::with_capacity(0),
1675 Some(vs) => {
1676 let changes: Vec<Result<_, _>> = match ikey.itype {
1677 IndexType::Equality => vs
1678 .generate_idx_eq_keys()
1679 .into_iter()
1680 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1681 .collect(),
1682 IndexType::Presence => {
1683 vec![Ok((&ikey.attr, ikey.itype, "_".to_string()))]
1684 }
1685 IndexType::SubString => vs
1686 .generate_idx_sub_keys()
1687 .into_iter()
1688 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1689 .collect(),
1690 IndexType::Ordering => vs
1691 .generate_idx_ord_keys()
1692 .into_iter()
1693 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1694 .collect(),
1695 };
1696 changes
1699 }
1700 }
1701 })
1702 .collect()
1703 }
1704 (Some(pre_e), Some(post_e)) => {
1705 assert_eq!(pre_e.state.id, post_e.state.id);
1706 idxmeta
1707 .keys()
1708 .flat_map(|ikey| {
1709 match (
1710 pre_e.get_ava_set(&ikey.attr),
1711 post_e.get_ava_set(&ikey.attr),
1712 ) {
1713 (None, None) => {
1714 Vec::with_capacity(0)
1716 }
1717 (Some(pre_vs), None) => {
1718 let changes: Vec<Result<_, _>> = match ikey.itype {
1720 IndexType::Equality => {
1721 pre_vs
1724 .generate_idx_eq_keys()
1725 .into_iter()
1726 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1727 .collect()
1728 }
1729 IndexType::Presence => {
1730 vec![Err((&ikey.attr, ikey.itype, "_".to_string()))]
1731 }
1732 IndexType::SubString => pre_vs
1733 .generate_idx_sub_keys()
1734 .into_iter()
1735 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1736 .collect(),
1737 IndexType::Ordering => pre_vs
1738 .generate_idx_ord_keys()
1739 .into_iter()
1740 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1741 .collect(),
1742 };
1743 changes
1744 }
1745 (None, Some(post_vs)) => {
1746 let changes: Vec<Result<_, _>> = match ikey.itype {
1748 IndexType::Equality => {
1749 post_vs
1752 .generate_idx_eq_keys()
1753 .into_iter()
1754 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1755 .collect()
1756 }
1757 IndexType::Presence => {
1758 vec![Ok((&ikey.attr, ikey.itype, "_".to_string()))]
1759 }
1760 IndexType::SubString => post_vs
1761 .generate_idx_sub_keys()
1762 .into_iter()
1763 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1764 .collect(),
1765 IndexType::Ordering => post_vs
1766 .generate_idx_ord_keys()
1767 .into_iter()
1768 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1769 .collect(),
1770 };
1771 changes
1772 }
1773 (Some(pre_vs), Some(post_vs)) => {
1774 let (mut pre_idx_keys, mut post_idx_keys) = match ikey.itype {
1776 IndexType::Equality => (
1777 pre_vs.generate_idx_eq_keys(),
1778 post_vs.generate_idx_eq_keys(),
1779 ),
1780 IndexType::Presence => {
1781 (Vec::with_capacity(0), Vec::with_capacity(0))
1783 }
1784 IndexType::SubString => (
1785 pre_vs.generate_idx_sub_keys(),
1786 post_vs.generate_idx_sub_keys(),
1787 ),
1788 IndexType::Ordering => (
1789 pre_vs.generate_idx_ord_keys(),
1790 post_vs.generate_idx_ord_keys(),
1791 ),
1792 };
1793
1794 let sz = if pre_idx_keys.len() > post_idx_keys.len() {
1795 pre_idx_keys.len()
1796 } else {
1797 post_idx_keys.len()
1798 };
1799
1800 let mut added_vs = Vec::with_capacity(sz);
1801 let mut removed_vs = Vec::with_capacity(sz);
1802
1803 if sz > 0 {
1804 pre_idx_keys.sort_unstable();
1805 post_idx_keys.sort_unstable();
1806
1807 let mut pre_iter = pre_idx_keys.iter();
1808 let mut post_iter = post_idx_keys.iter();
1809
1810 let mut pre = pre_iter.next();
1811 let mut post = post_iter.next();
1812
1813 loop {
1814 match (pre, post) {
1815 (Some(a), Some(b)) => {
1816 match a.cmp(b) {
1817 Ordering::Less => {
1818 removed_vs.push(a.clone());
1819 pre = pre_iter.next();
1820 }
1821 Ordering::Equal => {
1822 pre = pre_iter.next();
1824 post = post_iter.next();
1825 }
1826 Ordering::Greater => {
1827 added_vs.push(b.clone());
1828 post = post_iter.next();
1829 }
1830 }
1831 }
1832 (Some(a), None) => {
1833 removed_vs.push(a.clone());
1834 pre = pre_iter.next();
1835 }
1836 (None, Some(b)) => {
1837 added_vs.push(b.clone());
1838 post = post_iter.next();
1839 }
1840 (None, None) => {
1841 break;
1842 }
1843 }
1844 }
1845 } let mut diff =
1848 Vec::with_capacity(removed_vs.len() + added_vs.len());
1849
1850 match ikey.itype {
1851 IndexType::SubString
1852 | IndexType::Ordering
1853 | IndexType::Equality => {
1854 removed_vs
1855 .into_iter()
1856 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1857 .for_each(|v| diff.push(v));
1858 added_vs
1859 .into_iter()
1860 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1861 .for_each(|v| diff.push(v));
1862 }
1863 IndexType::Presence => {
1864 }
1866 };
1867 diff
1869 }
1870 }
1871 })
1872 .collect()
1873 }
1875 }
1876 }
1877
1878 pub fn from_dbentry(db_e: DbEntry, id: u64) -> Option<Self> {
1879 let (attrs, ecstate) = match db_e.ent {
1882 DbEntryVers::V3 { changestate, attrs } => {
1883 let ecstate = EntryChangeState::from_db_changestate(changestate);
1884
1885 let r_attrs = attrs
1886 .into_iter()
1887 .filter(|(_k, vs)| !vs.is_empty())
1889 .map(|(k, dbvs)| {
1890 valueset::from_db_valueset_v2(dbvs)
1891 .map(|vs: ValueSet| (k, vs))
1892 .map_err(|e| {
1893 error!(?e, "from_dbentry failed");
1894 })
1895 })
1896 .collect::<Result<Eattrs, ()>>()
1897 .ok()?;
1898
1899 (r_attrs, ecstate)
1900 }
1901 };
1902
1903 let uuid = attrs
1904 .get(&Attribute::Uuid)
1905 .and_then(|vs| vs.to_uuid_single())?;
1906
1907 Some(Entry {
1908 valid: EntrySealed { uuid, ecstate },
1909 state: EntryCommitted { id },
1910 attrs,
1911 })
1912 }
1913
1914 #[cfg(test)]
1922 pub(crate) fn into_reduced(self) -> Entry<EntryReduced, EntryCommitted> {
1923 Entry {
1924 valid: EntryReduced {
1925 uuid: self.valid.uuid,
1926 effective_access: None,
1927 },
1928 state: self.state,
1929 attrs: self.attrs,
1930 }
1931 }
1932
1933 pub fn reduce_attributes(
1936 &self,
1937 allowed_attrs: &BTreeSet<Attribute>,
1938 effective_access: Option<Box<AccessEffectivePermission>>,
1939 ) -> Entry<EntryReduced, EntryCommitted> {
1940 let f_attrs: Map<_, _> = self
1942 .attrs
1943 .iter()
1944 .filter_map(|(k, v)| {
1945 if allowed_attrs.contains(k) {
1946 Some((k.clone(), v.clone()))
1947 } else {
1948 None
1949 }
1950 })
1951 .collect();
1952
1953 let valid = EntryReduced {
1954 uuid: self.valid.uuid,
1955 effective_access,
1956 };
1957 let state = self.state.clone();
1958
1959 Entry {
1960 valid,
1961 state,
1962 attrs: f_attrs,
1963 }
1964 }
1965
1966 pub fn to_tombstone(&self, cid: Cid) -> Entry<EntryInvalid, EntryCommitted> {
1968 let mut ecstate = self.valid.ecstate.clone();
1969 let mut attrs_new: Eattrs = Map::new();
1971
1972 let class_ava = vs_iutf8![EntryClass::Object.into(), EntryClass::Tombstone.into()];
1973 let last_mod_ava = vs_cid![cid.clone()];
1974 let created_ava = vs_cid![cid.clone()];
1975
1976 attrs_new.insert(Attribute::Uuid, vs_uuid![self.get_uuid()]);
1977 attrs_new.insert(Attribute::Class, class_ava);
1978 attrs_new.insert(Attribute::LastModifiedCid, last_mod_ava);
1979 attrs_new.insert(Attribute::CreatedAtCid, created_ava);
1980
1981 ecstate.tombstone(&cid);
1983
1984 Entry {
1985 valid: EntryInvalid { cid, ecstate },
1986 state: self.state.clone(),
1987 attrs: attrs_new,
1988 }
1989 }
1990
1991 pub fn into_valid(self, ecstate: EntryChangeState) -> Entry<EntryValid, EntryCommitted> {
1993 Entry {
1994 valid: EntryValid {
1995 uuid: self.valid.uuid,
1996 ecstate,
1997 },
1998 state: self.state,
1999 attrs: self.attrs,
2000 }
2001 }
2002
2003 pub fn verify(
2004 &self,
2005 schema: &dyn SchemaTransaction,
2006 results: &mut Vec<Result<(), ConsistencyError>>,
2007 ) {
2008 self.valid
2009 .ecstate
2010 .verify(schema, &self.attrs, self.state.id, results);
2011 }
2012}
2013
2014impl<STATE> Entry<EntryValid, STATE> {
2015 fn validate(&self, schema: &dyn SchemaTransaction) -> Result<(), SchemaError> {
2016 let schema_classes = schema.get_classes();
2017 let schema_attributes = schema.get_attributes();
2018
2019 trace!(?self.attrs, "Entry::validate -> target");
2021
2022 if !self.attribute_pres(Attribute::Class) {
2024 return Err(SchemaError::NoClassFound);
2026 }
2027
2028 if self.attribute_equality(Attribute::Class, &EntryClass::Conflict.into()) {
2029 trace!("Skipping schema validation on conflict entry");
2031 return Ok(());
2032 };
2033
2034 let recycled = self.attribute_equality(Attribute::Class, &EntryClass::Recycled.into());
2036
2037 let extensible =
2040 self.attribute_equality(Attribute::Class, &EntryClass::ExtensibleObject.into());
2041
2042 let entry_classes = self.get_ava_set(Attribute::Class).ok_or_else(|| {
2043 admin_debug!("Attribute '{}' missing from entry", Attribute::Class);
2044 SchemaError::NoClassFound
2045 })?;
2046 let mut invalid_classes = Vec::with_capacity(0);
2047
2048 let mut classes: Vec<&SchemaClass> = Vec::with_capacity(entry_classes.len());
2049
2050 let entry_classes = if let Some(ec) = entry_classes.as_iutf8_set() {
2053 ec.iter()
2054 .for_each(|s| match schema_classes.get(s.as_str()) {
2055 Some(x) => classes.push(x),
2056 None => {
2057 admin_debug!("invalid class: {:?}", s);
2058 invalid_classes.push(s.to_string())
2059 }
2060 });
2061 ec
2062 } else {
2063 admin_debug!("corrupt class attribute");
2064 return Err(SchemaError::NoClassFound);
2065 };
2066
2067 if !invalid_classes.is_empty() {
2068 return Err(SchemaError::InvalidClass(invalid_classes));
2069 };
2070
2071 let supplements_classes: Vec<_> = classes
2075 .iter()
2076 .flat_map(|cls| cls.systemsupplements.iter().chain(cls.supplements.iter()))
2077 .collect();
2078
2079 let valid_supplements = if supplements_classes.is_empty() {
2081 true
2083 } else {
2084 supplements_classes
2085 .iter()
2086 .any(|class| entry_classes.contains(class.as_str()))
2087 };
2088
2089 if !valid_supplements {
2090 warn!(
2091 "Validation error, the following possible supplement classes are missing - {:?}",
2092 supplements_classes
2093 );
2094 let supplements_classes = supplements_classes.iter().map(|s| s.to_string()).collect();
2095 return Err(SchemaError::SupplementsNotSatisfied(supplements_classes));
2096 }
2097
2098 let excludes_classes: Vec<_> = classes
2099 .iter()
2100 .flat_map(|cls| cls.systemexcludes.iter().chain(cls.excludes.iter()))
2101 .collect();
2102
2103 let mut invalid_excludes = Vec::with_capacity(0);
2104
2105 excludes_classes.iter().for_each(|class| {
2106 if entry_classes.contains(class.as_str()) {
2107 invalid_excludes.push(class.to_string())
2108 }
2109 });
2110
2111 if !invalid_excludes.is_empty() {
2112 admin_warn!(
2113 "Validation error, the following excluded classes are present - {:?}",
2114 invalid_excludes
2115 );
2116 return Err(SchemaError::ExcludesNotSatisfied(invalid_excludes));
2117 }
2118
2119 let must: Result<Vec<&SchemaAttribute>, _> = classes
2132 .iter()
2133 .flat_map(|cls| cls.systemmust.iter().chain(cls.must.iter()))
2135 .map(|s| {
2136 schema_attributes.get(s).ok_or(SchemaError::Corrupted)
2139 })
2140 .collect();
2141
2142 let must = must?;
2143
2144 let mut missing_must = Vec::with_capacity(0);
2147 for attr in must.iter() {
2148 let avas = self.get_ava_set(&attr.name);
2149 if avas.is_none() {
2150 missing_must.push(attr.name.clone());
2151 }
2152 }
2153
2154 if !missing_must.is_empty() {
2155 admin_warn!(
2156 "Validation error, the following required ({}) (must) attributes are missing - {:?}",
2157 self.get_display_id(), missing_must
2158 );
2159 if !recycled {
2165 return Err(SchemaError::MissingMustAttribute(missing_must));
2166 }
2167 }
2168
2169 if extensible {
2170 self.attrs.iter().try_for_each(|(attr_name, avas)| {
2171 match schema_attributes.get(attr_name) {
2172 Some(a_schema) => {
2173 if a_schema.phantom {
2176 admin_warn!(
2177 "Rejecting attempt to add phantom attribute to extensible object: {}",
2178 attr_name
2179 );
2180 Err(SchemaError::PhantomAttribute(attr_name.to_string()))
2181 } else {
2182 a_schema.validate_ava(attr_name, avas)
2183 }
2185 }
2186 None => {
2187 admin_error!(
2188 "Invalid Attribute {}, undefined in schema_attributes",
2189 attr_name.to_string()
2190 );
2191 Err(SchemaError::InvalidAttribute(
2192 attr_name.to_string()
2193 ))
2194 }
2195 }
2196 })?;
2197 } else {
2198 let may: Result<Map<&Attribute, &SchemaAttribute>, _> = classes
2206 .iter()
2207 .flat_map(|cls| {
2209 trace!(?cls);
2210 cls.systemmust
2211 .iter()
2212 .chain(cls.must.iter())
2213 .chain(cls.systemmay.iter())
2214 .chain(cls.may.iter())
2215 })
2216 .map(|s| {
2217 Ok((s, schema_attributes.get(s).ok_or(SchemaError::Corrupted)?))
2220 })
2221 .collect();
2222
2223 let may = may?;
2224
2225 self.attrs.iter().try_for_each(|(attr_name, avas)| {
2234 match may.get(attr_name) {
2235 Some(a_schema) => {
2236 a_schema.validate_ava(attr_name, avas)
2239 }
2241 None => {
2242 admin_error!(
2243 "{} {} - not found in the list of valid attributes for this set of classes {:?} - valid attributes are {:?}",
2244
2245 attr_name.as_str(),
2246 self.get_display_id(),
2247 entry_classes.iter().collect::<Vec<_>>(),
2248 may.keys().collect::<Vec<_>>()
2249 );
2250 Err(SchemaError::AttributeNotValidForClass(
2251 attr_name.to_string()
2252 ))
2253 }
2254 }
2255 })?;
2256 }
2257
2258 Ok(())
2260 }
2261
2262 pub fn seal(mut self, schema: &dyn SchemaTransaction) -> Entry<EntrySealed, STATE> {
2263 let EntryValid { uuid, mut ecstate } = self.valid;
2264
2265 ecstate.retain(|k, _| schema.is_replicated(k));
2269
2270 let last_mod_cid = ecstate.get_max_cid();
2272 let cv = vs_cid![last_mod_cid.clone()];
2273 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
2274
2275 let create_at_cid = ecstate.at();
2279 let cv = vs_cid![create_at_cid.clone()];
2280 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
2281
2282 Entry {
2283 valid: EntrySealed { uuid, ecstate },
2284 state: self.state,
2285 attrs: self.attrs,
2286 }
2287 }
2288
2289 pub fn get_uuid(&self) -> Uuid {
2290 self.valid.uuid
2291 }
2292}
2293
2294impl<STATE> GetUuid for Entry<EntrySealed, STATE>
2295where
2296 STATE: Clone,
2297{
2298 fn get_uuid(&self) -> Uuid {
2299 self.valid.uuid
2300 }
2301}
2302
2303impl<STATE> Entry<EntrySealed, STATE>
2304where
2305 STATE: Clone,
2306{
2307 pub fn invalidate(mut self, cid: Cid, trim_cid: &Cid) -> Entry<EntryInvalid, STATE> {
2308 for vs in self.attrs.values_mut() {
2310 vs.trim(trim_cid);
2311 }
2312
2313 let last_mod_cid = self.valid.ecstate.get_max_cid();
2321 let cv = vs_cid![last_mod_cid.clone()];
2322 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
2323
2324 let create_at_cid = self.valid.ecstate.at();
2325 let cv = vs_cid![create_at_cid.clone()];
2326 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
2327
2328 Entry {
2329 valid: EntryInvalid {
2330 cid,
2331 ecstate: self.valid.ecstate,
2332 },
2333 state: self.state,
2334 attrs: self.attrs,
2335 }
2336 }
2337
2338 pub fn get_uuid(&self) -> Uuid {
2339 self.valid.uuid
2340 }
2341
2342 pub fn get_changestate(&self) -> &EntryChangeState {
2343 &self.valid.ecstate
2344 }
2345
2346 pub(crate) fn entry_changed_excluding_attribute<A: AsRef<Attribute>>(
2350 &self,
2351 attr: A,
2352 cid: &Cid,
2353 ) -> bool {
2354 let attr_ref = attr.as_ref();
2355
2356 use crate::repl::entry::State;
2357
2358 match self.get_changestate().current() {
2359 State::Live { at: _, changes } => {
2360 changes.iter().any(|(change_attr, change_id)| {
2361 change_id >= cid &&
2362 *change_attr != *attr_ref &&
2363 *change_attr != Attribute::LastModifiedCid
2365 })
2366 }
2367 State::Tombstone { at } => at == cid,
2368 }
2369 }
2370
2371 #[cfg(test)]
2375 pub(crate) fn into_invalid(mut self) -> Entry<EntryInvalid, STATE> {
2376 let cid = Cid::new_zero();
2377 self.set_last_changed(cid.clone());
2378
2379 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
2380
2381 Entry {
2382 valid: EntryInvalid { cid, ecstate },
2383 state: self.state,
2384 attrs: self.attrs,
2385 }
2386 }
2387}
2388
2389impl GetUuid for Entry<EntryReduced, EntryCommitted> {
2390 fn get_uuid(&self) -> Uuid {
2391 self.valid.uuid
2392 }
2393}
2394
2395impl Entry<EntryReduced, EntryCommitted> {
2396 pub fn get_uuid(&self) -> Uuid {
2397 self.valid.uuid
2398 }
2399
2400 pub fn to_pe(&self, qs: &mut QueryServerReadTransaction) -> Result<ProtoEntry, OperationError> {
2402 let attrs: Result<_, _> = self
2404 .attrs
2405 .iter()
2406 .map(|(k, vs)| qs.resolve_valueset(vs).map(|pvs| (k.to_string(), pvs)))
2407 .collect();
2408 Ok(ProtoEntry { attrs: attrs? })
2409 }
2410
2411 pub fn to_scim_kanidm<'a, TXN>(
2412 &self,
2413 read_txn: &mut TXN,
2414 ) -> Result<ScimEntryKanidm, OperationError>
2415 where
2416 TXN: QueryServerTransaction<'a>,
2417 {
2418 let result: Result<BTreeMap<Attribute, ScimValueKanidm>, OperationError> = self
2419 .attrs
2420 .iter()
2421 .filter(|(k, _vs)| **k != Attribute::Uuid)
2423 .filter_map(|(k, vs)| {
2424 let opt_resolve_status = vs.to_scim_value();
2425 let res_opt_scim_value = match opt_resolve_status {
2426 None => Ok(None),
2427 Some(ScimResolveStatus::Resolved(scim_value_kani)) => Ok(Some(scim_value_kani)),
2428 Some(ScimResolveStatus::NeedsResolution(scim_value_interim)) => {
2429 read_txn.resolve_scim_interim(scim_value_interim)
2430 }
2431 };
2432 res_opt_scim_value
2433 .transpose()
2434 .map(|scim_res| scim_res.map(|scim_value| (k.clone(), scim_value)))
2435 })
2436 .collect();
2437
2438 let attrs = result?;
2439
2440 let ext_access_check = self.valid.effective_access.as_ref().map(|eff_acc| {
2441 let ident = eff_acc.ident;
2442 let delete = eff_acc.delete;
2443 let search = (&eff_acc.search).into();
2444 let modify_present = (&eff_acc.modify_pres).into();
2445 let modify_remove = (&eff_acc.modify_rem).into();
2446
2447 ScimEffectiveAccess {
2448 ident,
2449 delete,
2450 search,
2451 modify_present,
2452 modify_remove,
2453 }
2454 });
2455
2456 let id = self.get_uuid();
2457
2458 let schemas = Vec::with_capacity(0);
2461
2462 Ok(ScimEntryKanidm {
2463 header: ScimEntryHeader {
2464 schemas,
2465 id,
2466 external_id: None,
2468 meta: None,
2471 },
2472 ext_access_check,
2473 attrs,
2474 })
2475 }
2476
2477 pub fn to_ldap(
2479 &self,
2480 qs: &mut QueryServerReadTransaction,
2481 basedn: &str,
2482 all_attrs: bool,
2484 l_attrs: &[String],
2487 ) -> Result<LdapSearchResultEntry, OperationError> {
2488 let rdn = qs.uuid_to_rdn(self.get_uuid())?;
2489
2490 let dn = format!("{rdn},{basedn}");
2491
2492 let attr_map: Result<Map<&str, Vec<Vec<u8>>>, _> = self
2497 .attrs
2498 .iter()
2499 .map(|(k, vs)| {
2500 qs.resolve_valueset_ldap(vs, basedn)
2501 .map(|pvs| (k.as_str(), pvs))
2502 })
2503 .collect();
2504 let attr_map = attr_map?;
2505
2506 let attr_names: Vec<(&str, &str)> = if all_attrs {
2509 self.attrs
2511 .keys()
2512 .map(|k| (k.as_str(), k.as_str()))
2513 .chain(
2514 l_attrs
2515 .iter()
2516 .map(|k| (k.as_str(), ldap_vattr_map(k.as_str()).unwrap_or(k.as_str()))),
2517 )
2518 .collect()
2519 } else {
2520 l_attrs
2522 .iter()
2523 .map(|k| (k.as_str(), ldap_vattr_map(k.as_str()).unwrap_or(k.as_str())))
2524 .collect()
2525 };
2526
2527 let attributes: Vec<_> = attr_names
2529 .into_iter()
2530 .filter_map(|(ldap_a, kani_a)| {
2531 match ldap_a {
2533 LDAP_ATTR_DN => Some(LdapPartialAttribute {
2534 atype: LDAP_ATTR_DN.to_string(),
2535 vals: vec![dn.as_bytes().to_vec()],
2536 }),
2537 LDAP_ATTR_ENTRYDN => Some(LdapPartialAttribute {
2538 atype: LDAP_ATTR_ENTRYDN.to_string(),
2539 vals: vec![dn.as_bytes().to_vec()],
2540 }),
2541 LDAP_ATTR_MAIL_PRIMARY | LDAP_ATTR_EMAIL_PRIMARY => {
2542 attr_map.get(kani_a).map(|pvs| LdapPartialAttribute {
2543 atype: ldap_a.to_string(),
2544 vals: pvs
2545 .first()
2546 .map(|first| vec![first.clone()])
2547 .unwrap_or_default(),
2548 })
2549 }
2550 LDAP_ATTR_MAIL_ALTERNATIVE | LDAP_ATTR_EMAIL_ALTERNATIVE => {
2551 attr_map.get(kani_a).map(|pvs| LdapPartialAttribute {
2552 atype: ldap_a.to_string(),
2553 vals: pvs
2554 .split_first()
2555 .map(|(_, rest)| rest.to_vec())
2556 .unwrap_or_default(),
2557 })
2558 }
2559 _ => attr_map.get(kani_a).map(|pvs| LdapPartialAttribute {
2560 atype: ldap_a.to_string(),
2561 vals: pvs.clone(),
2562 }),
2563 }
2564 })
2565 .collect();
2566
2567 Ok(LdapSearchResultEntry { dn, attributes })
2568 }
2569}
2570
2571impl<VALID, STATE> Entry<VALID, STATE> {
2573 fn add_ava_int(&mut self, attr: Attribute, value: Value) -> bool {
2578 if let Some(vs) = self.attrs.get_mut(&attr) {
2579 let r = vs.insert_checked(value);
2580 debug_assert!(r.is_ok());
2581 r.unwrap_or(false)
2583 } else {
2584 #[allow(clippy::expect_used)]
2585 let vs = valueset::from_value_iter(std::iter::once(value))
2586 .expect("Unable to fail - non-zero iter, and single value type!");
2587 self.attrs.insert(attr, vs);
2588 true
2590 }
2591 }
2593
2594 fn set_ava_iter_int<T>(&mut self, attr: Attribute, iter: T)
2596 where
2597 T: IntoIterator<Item = Value>,
2598 {
2599 let Ok(vs) = valueset::from_value_iter(iter.into_iter()) else {
2600 trace!("set_ava_iter_int - empty from_value_iter, skipping");
2601 return;
2602 };
2603
2604 if let Some(existing_vs) = self.attrs.get_mut(&attr) {
2605 let _ = existing_vs.merge(&vs);
2607 } else {
2608 self.attrs.insert(attr, vs);
2610 }
2611 }
2612
2613 #[cfg(test)]
2615 fn set_last_changed(&mut self, cid: Cid) {
2616 let cv = vs_cid![cid.clone()];
2617 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
2618 let cv = vs_cid![cid];
2619 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
2620 }
2621
2622 pub(crate) fn get_display_id(&self) -> String {
2623 self.attrs
2624 .get(&Attribute::Spn)
2625 .and_then(|vs| vs.to_value_single())
2626 .or_else(|| {
2627 self.attrs
2628 .get(&Attribute::Name)
2629 .and_then(|vs| vs.to_value_single())
2630 })
2631 .or_else(|| {
2632 self.attrs
2633 .get(&Attribute::Uuid)
2634 .and_then(|vs| vs.to_value_single())
2635 })
2636 .map(|value| value.to_proto_string_clone())
2637 .unwrap_or_else(|| "no entry id available".to_string())
2638 }
2639
2640 pub fn has_class(&self, class: &EntryClass) -> bool {
2641 self.get_ava_set(Attribute::Class)
2642 .and_then(|vs| vs.as_iutf8_set())
2643 .map(|set| {
2644 let class_name: &str = class.into();
2645 set.contains(class_name)
2646 })
2647 .unwrap_or_default()
2648 }
2649
2650 pub fn get_ava_names(&self) -> impl Iterator<Item = &str> {
2652 self.attrs.keys().map(|a| a.as_str())
2654 }
2655
2656 pub fn get_ava(&self) -> &Eattrs {
2658 &self.attrs
2659 }
2660
2661 pub fn get_ava_iter(&self) -> impl Iterator<Item = (&Attribute, &ValueSet)> {
2662 self.attrs.iter()
2663 }
2664
2665 pub fn get_ava_set<A: AsRef<Attribute>>(&self, attr: A) -> Option<&ValueSet> {
2667 self.attrs.get(attr.as_ref())
2668 }
2669
2670 pub fn get_ava_refer<A: AsRef<Attribute>>(&self, attr: A) -> Option<&BTreeSet<Uuid>> {
2671 self.get_ava_set(attr).and_then(|vs| vs.as_refer_set())
2672 }
2673
2674 pub fn get_ava_as_iutf8_iter<A: AsRef<Attribute>>(
2675 &self,
2676 attr: A,
2677 ) -> Option<impl Iterator<Item = &str>> {
2678 self.get_ava_set(attr).and_then(|vs| vs.as_iutf8_iter())
2679 }
2680
2681 pub fn get_ava_as_iutf8<A: AsRef<Attribute>>(&self, attr: A) -> Option<&BTreeSet<String>> {
2682 self.get_ava_set(attr).and_then(|vs| vs.as_iutf8_set())
2683 }
2684
2685 pub fn get_ava_as_image<A: AsRef<Attribute>>(&self, attr: A) -> Option<&HashSet<ImageValue>> {
2686 self.get_ava_set(attr).and_then(|vs| vs.as_imageset())
2687 }
2688
2689 pub fn get_ava_single_image<A: AsRef<Attribute>>(&self, attr: A) -> Option<ImageValue> {
2690 let images = self.get_ava_set(attr).and_then(|vs| vs.as_imageset())?;
2691 images.iter().next().cloned()
2692 }
2693
2694 pub fn get_ava_single_credential_type<A: AsRef<Attribute>>(
2695 &self,
2696 attr: A,
2697 ) -> Option<CredentialType> {
2698 self.get_ava_set(attr)
2699 .and_then(|vs| vs.to_credentialtype_single())
2700 }
2701
2702 pub fn get_ava_as_oauthscopes<A: AsRef<Attribute>>(
2703 &self,
2704 attr: A,
2705 ) -> Option<impl Iterator<Item = &str>> {
2706 self.get_ava_set(attr)
2707 .and_then(|vs| vs.as_oauthscope_iter())
2708 }
2709
2710 pub fn get_ava_as_oauthscopemaps<A: AsRef<Attribute>>(
2711 &self,
2712 attr: A,
2713 ) -> Option<&std::collections::BTreeMap<Uuid, std::collections::BTreeSet<String>>> {
2714 self.get_ava_set(attr).and_then(|vs| vs.as_oauthscopemap())
2715 }
2716
2717 pub fn get_ava_as_intenttokens<A: AsRef<Attribute>>(
2718 &self,
2719 attr: A,
2720 ) -> Option<&std::collections::BTreeMap<String, IntentTokenState>> {
2721 self.get_ava_set(attr)
2722 .and_then(|vs| vs.as_intenttoken_map())
2723 }
2724
2725 pub fn get_ava_as_session_map<A: AsRef<Attribute>>(
2726 &self,
2727 attr: A,
2728 ) -> Option<&std::collections::BTreeMap<Uuid, Session>> {
2729 self.get_ava_set(attr).and_then(|vs| vs.as_session_map())
2730 }
2731
2732 pub fn get_ava_as_apitoken_map<A: AsRef<Attribute>>(
2733 &self,
2734 attr: A,
2735 ) -> Option<&std::collections::BTreeMap<Uuid, ApiToken>> {
2736 self.get_ava_set(attr).and_then(|vs| vs.as_apitoken_map())
2737 }
2738
2739 pub fn get_ava_as_oauth2session_map<A: AsRef<Attribute>>(
2740 &self,
2741 attr: A,
2742 ) -> Option<&std::collections::BTreeMap<Uuid, Oauth2Session>> {
2743 self.get_ava_set(attr)
2744 .and_then(|vs| vs.as_oauth2session_map())
2745 }
2746
2747 pub fn get_ava_as_s256_set<A: AsRef<Attribute>>(
2748 &self,
2749 attr: A,
2750 ) -> Option<&std::collections::BTreeSet<Sha256Output>> {
2751 self.get_ava_set(attr).and_then(|vs| vs.as_s256_set())
2752 }
2753
2754 pub fn get_ava_iter_iname<A: AsRef<Attribute>>(
2756 &self,
2757 attr: A,
2758 ) -> Option<impl Iterator<Item = &str>> {
2759 self.get_ava_set(attr).and_then(|vs| vs.as_iname_iter())
2760 }
2761
2762 pub fn get_ava_iter_iutf8<A: AsRef<Attribute>>(
2764 &self,
2765 attr: A,
2766 ) -> Option<impl Iterator<Item = &str>> {
2767 self.get_ava_set(attr).and_then(|vs| vs.as_iutf8_iter())
2768 }
2769
2770 pub fn get_ava_as_refuuid<A: AsRef<Attribute>>(
2772 &self,
2773 attr: A,
2774 ) -> Option<Box<dyn Iterator<Item = Uuid> + '_>> {
2775 self.get_ava_set(attr).and_then(|vs| vs.as_ref_uuid_iter())
2777 }
2778
2779 pub fn get_ava_iter_sshpubkeys<A: AsRef<Attribute>>(
2781 &self,
2782 attr: A,
2783 ) -> Option<impl Iterator<Item = String> + '_> {
2784 self.get_ava_set(attr)
2785 .and_then(|vs| vs.as_sshpubkey_string_iter())
2786 }
2787
2788 pub fn get_ava_single<A: AsRef<Attribute>>(&self, attr: A) -> Option<Value> {
2794 self.get_ava_set(attr).and_then(|vs| vs.to_value_single())
2795 }
2796
2797 pub fn get_ava_single_proto_string<A: AsRef<Attribute>>(&self, attr: A) -> Option<String> {
2798 self.get_ava_set(attr)
2799 .and_then(|vs| vs.to_proto_string_single())
2800 }
2801
2802 pub fn get_ava_single_bool<A: AsRef<Attribute>>(&self, attr: A) -> Option<bool> {
2804 self.get_ava_set(attr).and_then(|vs| vs.to_bool_single())
2805 }
2806
2807 pub fn get_ava_single_uint32<A: AsRef<Attribute>>(&self, attr: A) -> Option<u32> {
2809 self.get_ava_set(attr).and_then(|vs| vs.to_uint32_single())
2810 }
2811
2812 pub fn get_ava_single_syntax<A: AsRef<Attribute>>(&self, attr: A) -> Option<SyntaxType> {
2814 self.get_ava_set(attr)
2815 .and_then(|vs| vs.to_syntaxtype_single())
2816 }
2817
2818 pub fn get_ava_single_credential<A: AsRef<Attribute>>(&self, attr: A) -> Option<&Credential> {
2820 self.get_ava_set(attr)
2821 .and_then(|vs| vs.to_credential_single())
2822 }
2823
2824 pub fn get_ava_passkeys<A: AsRef<Attribute>>(
2826 &self,
2827 attr: A,
2828 ) -> Option<&BTreeMap<Uuid, (String, PasskeyV4)>> {
2829 self.get_ava_set(attr).and_then(|vs| vs.as_passkey_map())
2830 }
2831
2832 pub fn get_ava_attestedpasskeys<A: AsRef<Attribute>>(
2834 &self,
2835 attr: A,
2836 ) -> Option<&BTreeMap<Uuid, (String, AttestedPasskeyV4)>> {
2837 self.get_ava_set(attr)
2838 .and_then(|vs| vs.as_attestedpasskey_map())
2839 }
2840
2841 pub fn get_ava_uihint<A: AsRef<Attribute>>(&self, attr: A) -> Option<&BTreeSet<UiHint>> {
2843 self.get_ava_set(attr).and_then(|vs| vs.as_uihint_set())
2844 }
2845
2846 pub fn get_ava_single_secret<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2848 self.get_ava_set(attr).and_then(|vs| vs.to_secret_single())
2849 }
2850
2851 pub fn get_ava_single_datetime<A: AsRef<Attribute>>(&self, attr: A) -> Option<OffsetDateTime> {
2853 self.get_ava_set(attr)
2854 .and_then(|vs| vs.to_datetime_single())
2855 }
2856
2857 pub(crate) fn get_ava_single_utf8<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2859 self.get_ava_set(attr).and_then(|vs| vs.to_utf8_single())
2860 }
2861
2862 pub(crate) fn get_ava_single_iutf8<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2864 self.get_ava_set(attr).and_then(|vs| vs.to_iutf8_single())
2865 }
2866
2867 pub(crate) fn get_ava_single_iname<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2869 self.get_ava_set(attr).and_then(|vs| vs.to_iname_single())
2870 }
2871
2872 pub fn get_ava_single_url<A: AsRef<Attribute>>(&self, attr: A) -> Option<&Url> {
2874 self.get_ava_set(attr).and_then(|vs| vs.to_url_single())
2875 }
2876
2877 pub fn get_ava_single_uuid<A: AsRef<Attribute>>(&self, attr: A) -> Option<Uuid> {
2878 self.get_ava_set(attr).and_then(|vs| vs.to_uuid_single())
2879 }
2880
2881 pub fn get_ava_single_refer<A: AsRef<Attribute>>(&self, attr: A) -> Option<Uuid> {
2882 self.get_ava_set(attr).and_then(|vs| vs.to_refer_single())
2883 }
2884
2885 pub fn get_ava_mail_primary<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2886 self.get_ava_set(attr)
2887 .and_then(|vs| vs.to_email_address_primary_str())
2888 }
2889
2890 pub fn get_ava_iter_mail<A: AsRef<Attribute>>(
2891 &self,
2892 attr: A,
2893 ) -> Option<impl Iterator<Item = &str>> {
2894 self.get_ava_set(attr).and_then(|vs| vs.as_email_str_iter())
2895 }
2896
2897 pub fn get_ava_single_protofilter<A: AsRef<Attribute>>(&self, attr: A) -> Option<&ProtoFilter> {
2899 self.get_ava_set(attr)
2900 .and_then(|vs| vs.to_json_filter_single())
2901 }
2902
2903 pub fn get_ava_single_private_binary<A: AsRef<Attribute>>(&self, attr: A) -> Option<&[u8]> {
2904 self.get_ava_set(attr)
2905 .and_then(|vs| vs.to_private_binary_single())
2906 }
2907
2908 pub fn get_ava_single_jws_key_es256<A: AsRef<Attribute>>(
2909 &self,
2910 attr: A,
2911 ) -> Option<&JwsEs256Signer> {
2912 self.get_ava_set(attr)
2913 .and_then(|vs| vs.to_jws_key_es256_single())
2914 }
2915
2916 pub fn get_ava_single_eckey_private<A: AsRef<Attribute>>(
2917 &self,
2918 attr: A,
2919 ) -> Option<&EcKey<Private>> {
2920 self.get_ava_set(attr)
2921 .and_then(|vs| vs.to_eckey_private_single())
2922 }
2923
2924 pub fn get_ava_single_eckey_public<A: AsRef<Attribute>>(
2925 &self,
2926 attr: A,
2927 ) -> Option<&EcKey<Public>> {
2928 self.get_ava_set(attr)
2929 .and_then(|vs| vs.to_eckey_public_single())
2930 }
2931
2932 pub fn get_ava_webauthn_attestation_ca_list<A: AsRef<Attribute>>(
2933 &self,
2934 attr: A,
2935 ) -> Option<&AttestationCaList> {
2936 self.get_ava_set(attr)
2937 .and_then(|vs| vs.as_webauthn_attestation_ca_list())
2938 }
2939
2940 pub fn get_ava_application_password<A: AsRef<Attribute>>(
2941 &self,
2942 attr: A,
2943 ) -> Option<&BTreeMap<Uuid, Vec<ApplicationPassword>>> {
2944 self.get_ava_set(attr)
2945 .and_then(|vs| vs.as_application_password_map())
2946 }
2947
2948 pub(crate) fn generate_spn(&self, domain_name: &str) -> Option<ValueSet> {
2950 if let Some(name) = self.get_ava_single_iname(Attribute::Name) {
2951 return Some(ValueSetSpn::new((name.into(), domain_name.into())));
2953 }
2954
2955 let spn_set = self.get_ava_set(Attribute::Spn)?;
2957
2958 if spn_set.syntax() == SyntaxType::SecurityPrincipalName {
2959 Some(spn_set.clone())
2961 } else if let Some(name) = spn_set.to_iname_single() {
2962 Some(ValueSetSpn::new((name.into(), domain_name.into())))
2964 } else {
2965 None
2967 }
2968 }
2969
2970 pub fn attribute_pres<A: AsRef<Attribute>>(&self, attr: A) -> bool {
2972 self.attrs.contains_key(attr.as_ref())
2973 }
2974
2975 pub fn attribute_equality<A: AsRef<Attribute>>(&self, attr: A, value: &PartialValue) -> bool {
2978 match self.attrs.get(attr.as_ref()) {
2983 Some(v_list) => v_list.contains(value),
2984 None => false,
2985 }
2986 }
2987
2988 pub fn attribute_substring<A: AsRef<Attribute>>(
2991 &self,
2992 attr: A,
2993 subvalue: &PartialValue,
2994 ) -> bool {
2995 self.get_ava_set(attr)
2996 .map(|vset| vset.substring(subvalue))
2997 .unwrap_or(false)
2998 }
2999
3000 pub fn attribute_startswith<A: AsRef<Attribute>>(
3003 &self,
3004 attr: A,
3005 subvalue: &PartialValue,
3006 ) -> bool {
3007 self.get_ava_set(attr)
3008 .map(|vset| vset.startswith(subvalue))
3009 .unwrap_or(false)
3010 }
3011
3012 pub fn attribute_endswith<A: AsRef<Attribute>>(
3015 &self,
3016 attr: A,
3017 subvalue: &PartialValue,
3018 ) -> bool {
3019 self.get_ava_set(attr)
3020 .map(|vset| vset.endswith(subvalue))
3021 .unwrap_or(false)
3022 }
3023
3024 pub fn attribute_lessthan<A: AsRef<Attribute>>(
3027 &self,
3028 attr: A,
3029 subvalue: &PartialValue,
3030 ) -> bool {
3031 self.get_ava_set(attr)
3032 .map(|vset| vset.lessthan(subvalue))
3033 .unwrap_or(false)
3034 }
3035
3036 #[inline(always)]
3041 #[instrument(level = "trace", name = "entry::entry_match_no_index", skip(self))]
3042 pub fn entry_match_no_index(&self, filter: &Filter<FilterValidResolved>) -> bool {
3044 self.entry_match_no_index_inner(filter.to_inner())
3045 }
3046
3047 fn entry_match_no_index_inner(&self, filter: &FilterResolved) -> bool {
3051 match filter {
3054 FilterResolved::Eq(attr, value, _) => self.attribute_equality(attr, value),
3055 FilterResolved::Cnt(attr, subvalue, _) => self.attribute_substring(attr, subvalue),
3056 FilterResolved::Stw(attr, subvalue, _) => self.attribute_startswith(attr, subvalue),
3057 FilterResolved::Enw(attr, subvalue, _) => self.attribute_endswith(attr, subvalue),
3058 FilterResolved::Pres(attr, _) => self.attribute_pres(attr),
3059 FilterResolved::LessThan(attr, subvalue, _) => self.attribute_lessthan(attr, subvalue),
3060 FilterResolved::Or(l, _) => l.iter().any(|f| self.entry_match_no_index_inner(f)),
3062 FilterResolved::And(l, _) => l.iter().all(|f| self.entry_match_no_index_inner(f)),
3064 FilterResolved::Inclusion(_, _) => {
3065 false
3069 }
3070 FilterResolved::AndNot(f, _) => !self.entry_match_no_index_inner(f),
3071 FilterResolved::Invalid(_) => false,
3072 }
3073 }
3074
3075 pub fn filter_from_attrs(&self, attrs: &[Attribute]) -> Option<Filter<FilterInvalid>> {
3078 let mut pairs: Vec<(Attribute, PartialValue)> = Vec::with_capacity(0);
3090
3091 for attr in attrs {
3092 match self.attrs.get(attr) {
3093 Some(values) => values
3094 .to_partialvalue_iter()
3095 .for_each(|pv| pairs.push((attr.clone(), pv))),
3096 None => return None,
3097 }
3098 }
3099
3100 let res: Vec<FC> = pairs
3101 .into_iter()
3102 .map(|(attr, pv)| FC::Eq(attr, pv))
3103 .collect();
3104 Some(filter_all!(f_and(res)))
3105 }
3106
3107 pub fn gen_modlist_assert(
3110 &self,
3111 schema: &dyn SchemaTransaction,
3112 ) -> Result<ModifyList<ModifyInvalid>, SchemaError> {
3113 let mut mods = ModifyList::new();
3117
3118 for (k, vs) in self.attrs.iter() {
3119 if *k == Attribute::Uuid {
3135 continue;
3136 }
3137 match schema.is_multivalue(k) {
3139 Ok(r) => {
3140 if !r ||
3143 *k == Attribute::AcpReceiverGroup ||
3146 *k == Attribute::AcpCreateAttr ||
3147 *k == Attribute::AcpCreateClass ||
3148 *k == Attribute::AcpModifyPresentAttr ||
3149 *k == Attribute::AcpModifyRemovedAttr ||
3150 *k == Attribute::AcpModifyClass ||
3151 *k == Attribute::SystemMust ||
3152 *k == Attribute::SystemMay
3153 {
3154 mods.push_mod(Modify::Purged(k.clone()));
3155 }
3156 }
3157 Err(e) => return Err(e),
3159 }
3160 for v in vs.to_value_iter() {
3161 mods.push_mod(Modify::Present(k.clone(), v.clone()));
3162 }
3163 }
3164
3165 Ok(mods)
3166 }
3167
3168 pub fn mask_recycled_ts(&self) -> Option<&Self> {
3171 match self.attrs.get(&Attribute::Class) {
3173 Some(cls) => {
3174 if cls.contains(&EntryClass::Tombstone.to_partialvalue())
3175 || cls.contains(&EntryClass::Recycled.to_partialvalue())
3176 {
3177 None
3178 } else {
3179 Some(self)
3180 }
3181 }
3182 None => Some(self),
3183 }
3184 }
3185
3186 pub fn mask_recycled(&self) -> Option<&Self> {
3189 match self.attrs.get(&Attribute::Class) {
3191 Some(cls) => {
3192 if cls.contains(&EntryClass::Recycled.to_partialvalue()) {
3193 None
3194 } else {
3195 Some(self)
3196 }
3197 }
3198 None => Some(self),
3199 }
3200 }
3201
3202 pub fn mask_tombstone(&self) -> Option<&Self> {
3205 match self.attrs.get(&Attribute::Class) {
3207 Some(cls) => {
3208 if cls.contains(&EntryClass::Tombstone.to_partialvalue()) {
3209 None
3210 } else {
3211 Some(self)
3212 }
3213 }
3214 None => Some(self),
3215 }
3216 }
3217}
3218
3219impl<STATE> Entry<EntryInvalid, STATE>
3220where
3221 STATE: Clone,
3222{
3223 pub fn add_ava(&mut self, attr: Attribute, value: Value) {
3228 self.valid.ecstate.change_ava(&self.valid.cid, &attr);
3229 self.add_ava_int(attr, value);
3230 }
3231
3232 pub fn add_ava_if_not_exist<A: AsRef<Attribute>>(&mut self, attr: A, value: Value) {
3233 let attr_ref = attr.as_ref();
3234 if self.add_ava_int(attr_ref.clone(), value) {
3236 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3238 }
3239 }
3240
3241 fn assert_ava<A: AsRef<Attribute>>(
3242 &mut self,
3243 attr: A,
3244 value: &PartialValue,
3245 ) -> Result<(), OperationError> {
3246 self.valid
3247 .ecstate
3248 .change_ava(&self.valid.cid, attr.as_ref());
3249
3250 if self.attribute_equality(attr, value) {
3251 Ok(())
3252 } else {
3253 Err(OperationError::ModifyAssertionFailed)
3254 }
3255 }
3256
3257 pub(crate) fn remove_ava<A: AsRef<Attribute>>(&mut self, attr: A, value: &PartialValue) {
3260 let attr_ref = attr.as_ref();
3261 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3262
3263 let rm = if let Some(vs) = self.attrs.get_mut(attr_ref) {
3264 vs.remove(value, &self.valid.cid);
3265 vs.is_empty()
3266 } else {
3267 false
3268 };
3269 if rm {
3270 self.attrs.remove(attr_ref);
3271 };
3272 }
3273
3274 pub(crate) fn remove_avas<A: AsRef<Attribute>>(
3275 &mut self,
3276 attr: A,
3277 values: &BTreeSet<PartialValue>,
3278 ) {
3279 let attr_ref = attr.as_ref();
3280 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3281
3282 let rm = if let Some(vs) = self.attrs.get_mut(attr_ref) {
3283 values.iter().for_each(|k| {
3284 vs.remove(k, &self.valid.cid);
3285 });
3286 vs.is_empty()
3287 } else {
3288 false
3289 };
3290 if rm {
3291 self.attrs.remove(attr_ref);
3292 };
3293 }
3294
3295 pub(crate) fn purge_ava<A: AsRef<Attribute>>(&mut self, attr: A) {
3298 let attr_ref = attr.as_ref();
3299 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3300 let can_remove = self
3303 .attrs
3304 .get_mut(attr_ref)
3305 .map(|vs| vs.purge(&self.valid.cid))
3306 .unwrap_or_default();
3308 if can_remove {
3309 self.attrs.remove(attr_ref);
3310 }
3311 }
3312
3313 pub fn pop_ava<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<ValueSet> {
3315 let attr_ref = attr.as_ref();
3316 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3317
3318 let mut vs = self.attrs.remove(attr_ref)?;
3319 if vs.purge(&self.valid.cid) {
3320 Some(vs)
3322 } else {
3323 let r_vs = vs.clone();
3325 self.attrs.insert(attr_ref.clone(), vs);
3326 Some(r_vs)
3327 }
3328 }
3329
3330 #[cfg(test)]
3335 pub(crate) fn force_trim_ava<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<ValueSet> {
3336 self.valid
3337 .ecstate
3338 .change_ava(&self.valid.cid, attr.as_ref());
3339 self.attrs.remove(attr.as_ref())
3340 }
3341
3342 pub fn set_ava<T>(&mut self, attr: &Attribute, iter: T)
3345 where
3346 T: Clone + IntoIterator<Item = Value>,
3347 {
3348 self.purge_ava(attr);
3349 self.set_ava_iter_int(attr.clone(), iter)
3350 }
3351
3352 pub fn set_ava_set(&mut self, attr: &Attribute, vs: ValueSet) {
3355 self.purge_ava(attr);
3356 if let Some(existing_vs) = self.attrs.get_mut(attr) {
3357 let _ = existing_vs.merge(&vs);
3358 } else {
3359 self.attrs.insert(attr.clone(), vs);
3360 }
3361 }
3362
3363 pub fn merge_ava_set(&mut self, attr: &Attribute, vs: ValueSet) -> Result<(), OperationError> {
3366 self.valid.ecstate.change_ava(&self.valid.cid, attr);
3367 if let Some(existing_vs) = self.attrs.get_mut(attr) {
3368 existing_vs.merge(&vs)
3369 } else {
3370 self.attrs.insert(attr.clone(), vs);
3371 Ok(())
3372 }
3373 }
3374
3375 pub fn apply_modlist(
3377 &mut self,
3378 modlist: &ModifyList<ModifyValid>,
3379 ) -> Result<(), OperationError> {
3380 for modify in modlist {
3381 match modify {
3382 Modify::Present(attr, value) => {
3383 self.add_ava(attr.clone(), value.clone());
3384 }
3385 Modify::Removed(attr, value) => {
3386 self.remove_ava(attr, value);
3387 }
3388 Modify::Purged(attr) => {
3389 self.purge_ava(attr);
3390 }
3391 Modify::Assert(attr, value) => {
3392 self.assert_ava(attr, value).inspect_err(|_e| {
3393 error!("Modification assertion was not met. {} {:?}", attr, value);
3394 })?;
3395 }
3396 Modify::Set(attr, valueset) => self.set_ava_set(attr, valueset.clone()),
3397 }
3398 }
3399 Ok(())
3400 }
3401}
3402
3403impl<VALID, STATE> PartialEq for Entry<VALID, STATE> {
3404 fn eq(&self, rhs: &Entry<VALID, STATE>) -> bool {
3405 compare_attrs(&self.attrs, &rhs.attrs)
3416 }
3417}
3418
3419#[cfg(test)]
3420mod tests {
3421 use crate::prelude::*;
3422 use std::collections::BTreeSet as Set;
3423
3424 use hashbrown::HashMap;
3425
3426 use crate::be::{IdxKey, IdxSlope};
3427 use crate::entry::{Entry, EntryInit, EntryInvalid, EntryNew};
3428 use crate::modify::{Modify, ModifyList};
3429 use crate::value::{IndexType, PartialValue, Value};
3430
3431 #[test]
3432 fn test_entry_basic() {
3433 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3434
3435 e.add_ava(Attribute::UserId, Value::from("william"));
3436 }
3437
3438 #[test]
3439 fn test_entry_dup_value() {
3440 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3447 e.add_ava(Attribute::UserId, Value::from("william"));
3448 e.add_ava(Attribute::UserId, Value::from("william"));
3449
3450 let values = e.get_ava_set(Attribute::UserId).expect("Failed to get ava");
3451 assert_eq!(values.len(), 1)
3453 }
3454
3455 #[test]
3456 fn test_entry_pres() {
3457 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3458 e.add_ava(Attribute::UserId, Value::from("william"));
3459
3460 assert!(e.attribute_pres(Attribute::UserId));
3461 assert!(!e.attribute_pres(Attribute::Name));
3462 }
3463
3464 #[test]
3465 fn test_entry_equality() {
3466 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3467
3468 e.add_ava(Attribute::UserId, Value::from("william"));
3469
3470 assert!(e.attribute_equality(Attribute::UserId, &PartialValue::new_utf8s("william")));
3471 assert!(!e.attribute_equality(Attribute::UserId, &PartialValue::new_utf8s("test")));
3472 assert!(!e.attribute_equality(Attribute::NonExist, &PartialValue::new_utf8s("william")));
3473 assert!(!e.attribute_equality(Attribute::UserId, &PartialValue::new_iutf8("william")));
3475 }
3476
3477 #[test]
3478 fn test_entry_substring() {
3479 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3480
3481 e.add_ava(Attribute::UserId, Value::from("william"));
3482
3483 assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("william")));
3484 assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("will")));
3485 assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("liam")));
3486 assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("lli")));
3487 assert!(!e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("llim")));
3488 assert!(!e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("bob")));
3489 assert!(!e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("wl")));
3490
3491 assert!(e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("will")));
3492 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("liam")));
3493 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("lli")));
3494 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("llim")));
3495 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("bob")));
3496 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("wl")));
3497
3498 assert!(e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("liam")));
3499 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("will")));
3500 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("lli")));
3501 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("llim")));
3502 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("bob")));
3503 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("wl")));
3504 }
3505
3506 #[test]
3507 fn test_entry_lessthan() {
3508 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3509
3510 let pv2 = PartialValue::new_uint32(2);
3511 let pv8 = PartialValue::new_uint32(8);
3512 let pv10 = PartialValue::new_uint32(10);
3513 let pv15 = PartialValue::new_uint32(15);
3514
3515 e1.add_ava(Attribute::TestAttr, Value::new_uint32(10));
3516
3517 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv2));
3518 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv8));
3519 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv10));
3520 assert!(e1.attribute_lessthan(Attribute::TestAttr, &pv15));
3521
3522 e1.add_ava(Attribute::TestAttr, Value::new_uint32(8));
3523
3524 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv2));
3525 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv8));
3526 assert!(e1.attribute_lessthan(Attribute::TestAttr, &pv10));
3527 assert!(e1.attribute_lessthan(Attribute::TestAttr, &pv15));
3528 }
3529
3530 #[test]
3531 fn test_entry_apply_modlist() {
3532 let mut e: Entry<EntryInvalid, EntryNew> = Entry::new().into_invalid_new();
3534
3535 e.add_ava(Attribute::UserId, Value::from("william"));
3536
3537 let present_single_mods = ModifyList::new_valid_list(vec![Modify::Present(
3538 Attribute::Attr,
3539 Value::new_iutf8("value"),
3540 )]);
3541
3542 assert!(e.apply_modlist(&present_single_mods).is_ok());
3543
3544 assert!(e.attribute_equality(Attribute::UserId, &PartialValue::new_utf8s("william")));
3546 assert!(e.attribute_equality(Attribute::Attr, &PartialValue::new_iutf8("value")));
3547
3548 let present_multivalue_mods = ModifyList::new_valid_list(vec![
3550 Modify::Present(Attribute::Class, Value::new_iutf8("test")),
3551 Modify::Present(Attribute::Class, Value::new_iutf8("multi_test")),
3552 ]);
3553
3554 assert!(e.apply_modlist(&present_multivalue_mods).is_ok());
3555
3556 assert!(e.attribute_equality(Attribute::Class, &PartialValue::new_iutf8("test")));
3557 assert!(e.attribute_equality(Attribute::Class, &PartialValue::new_iutf8("multi_test")));
3558
3559 let purge_single_mods = ModifyList::new_valid_list(vec![Modify::Purged(Attribute::Attr)]);
3561
3562 assert!(e.apply_modlist(&purge_single_mods).is_ok());
3563
3564 assert!(!e.attribute_pres(Attribute::Attr));
3565
3566 let purge_multi_mods = ModifyList::new_valid_list(vec![Modify::Purged(Attribute::Class)]);
3567
3568 assert!(e.apply_modlist(&purge_multi_mods).is_ok());
3569
3570 assert!(!e.attribute_pres(Attribute::Class));
3571
3572 let purge_empty_mods = purge_single_mods;
3573
3574 assert!(e.apply_modlist(&purge_empty_mods).is_ok());
3575
3576 let remove_mods = ModifyList::new_valid_list(vec![Modify::Removed(
3578 Attribute::Attr,
3579 PartialValue::new_iutf8("value"),
3580 )]);
3581
3582 assert!(e.apply_modlist(&present_single_mods).is_ok());
3583 assert!(e.attribute_equality(Attribute::Attr, &PartialValue::new_iutf8("value")));
3584 assert!(e.apply_modlist(&remove_mods).is_ok());
3585 assert!(!e.attrs.contains_key(&Attribute::Attr));
3586
3587 let remove_empty_mods = remove_mods;
3588
3589 assert!(e.apply_modlist(&remove_empty_mods).is_ok());
3590
3591 assert!(!e.attrs.contains_key(&Attribute::Attr));
3592 }
3593
3594 #[test]
3595 fn test_entry_idx_diff() {
3596 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3597 e1.add_ava(Attribute::UserId, Value::from("william"));
3598 let mut e1_mod = e1.clone();
3599 e1_mod.add_ava(Attribute::Extra, Value::from("test"));
3600
3601 let e1 = e1.into_sealed_committed();
3602 let e1_mod = e1_mod.into_sealed_committed();
3603
3604 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3605 e2.add_ava(Attribute::UserId, Value::from("claire"));
3606 let e2 = e2.into_sealed_committed();
3607
3608 let mut idxmeta = HashMap::with_capacity(8);
3609 idxmeta.insert(
3610 IdxKey {
3611 attr: Attribute::UserId,
3612 itype: IndexType::Equality,
3613 },
3614 IdxSlope::MAX,
3615 );
3616 idxmeta.insert(
3617 IdxKey {
3618 attr: Attribute::UserId,
3619 itype: IndexType::Presence,
3620 },
3621 IdxSlope::MAX,
3622 );
3623 idxmeta.insert(
3624 IdxKey {
3625 attr: Attribute::Extra,
3626 itype: IndexType::Equality,
3627 },
3628 IdxSlope::MAX,
3629 );
3630
3631 let r1 = Entry::idx_diff(&idxmeta, None, None);
3633 eprintln!("{r1:?}");
3634 assert_eq!(r1, Vec::with_capacity(0));
3635
3636 let mut del_r = Entry::idx_diff(&idxmeta, Some(&e1), None);
3638 del_r.sort_unstable();
3639 eprintln!("del_r {del_r:?}");
3640 assert!(
3641 del_r[0]
3642 == Err((
3643 &Attribute::UserId,
3644 IndexType::Equality,
3645 "william".to_string()
3646 ))
3647 );
3648 assert!(del_r[1] == Err((&Attribute::UserId, IndexType::Presence, "_".to_string())));
3649
3650 let mut add_r = Entry::idx_diff(&idxmeta, None, Some(&e1));
3652 add_r.sort_unstable();
3653 eprintln!("{add_r:?}");
3654 assert!(
3655 add_r[0]
3656 == Ok((
3657 &Attribute::UserId,
3658 IndexType::Equality,
3659 "william".to_string()
3660 ))
3661 );
3662 assert!(add_r[1] == Ok((&Attribute::UserId, IndexType::Presence, "_".to_string())));
3663
3664 let no_r = Entry::idx_diff(&idxmeta, Some(&e1), Some(&e1));
3668 assert!(no_r.is_empty());
3669
3670 let add_a_r = Entry::idx_diff(&idxmeta, Some(&e1), Some(&e1_mod));
3672 assert!(add_a_r[0] == Ok((&Attribute::Extra, IndexType::Equality, "test".to_string())));
3673
3674 let del_a_r = Entry::idx_diff(&idxmeta, Some(&e1_mod), Some(&e1));
3676 assert!(del_a_r[0] == Err((&Attribute::Extra, IndexType::Equality, "test".to_string())));
3677
3678 let mut chg_r = Entry::idx_diff(&idxmeta, Some(&e1), Some(&e2));
3680 chg_r.sort_unstable();
3681 eprintln!("{chg_r:?}");
3682 assert!(
3683 chg_r[1]
3684 == Err((
3685 &Attribute::UserId,
3686 IndexType::Equality,
3687 "william".to_string()
3688 ))
3689 );
3690
3691 assert!(
3692 chg_r[0]
3693 == Ok((
3694 &Attribute::UserId,
3695 IndexType::Equality,
3696 "claire".to_string()
3697 ))
3698 );
3699 }
3700
3701 #[test]
3702 fn test_entry_mask_recycled_ts() {
3703 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3704 e1.add_ava(Attribute::Class, EntryClass::Person.to_value());
3705 let e1 = e1.into_sealed_committed();
3706 assert!(e1.mask_recycled_ts().is_some());
3707
3708 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3709 e2.add_ava(Attribute::Class, EntryClass::Person.to_value());
3710 e2.add_ava(Attribute::Class, EntryClass::Recycled.into());
3711 let e2 = e2.into_sealed_committed();
3712 assert!(e2.mask_recycled_ts().is_none());
3713
3714 let mut e3: Entry<EntryInit, EntryNew> = Entry::new();
3715 e3.add_ava(Attribute::Class, EntryClass::Tombstone.into());
3716 let e3 = e3.into_sealed_committed();
3717 assert!(e3.mask_recycled_ts().is_none());
3718 }
3719
3720 #[test]
3721 fn test_entry_idx_name2uuid_diff() {
3722 let r = Entry::idx_name2uuid_diff(None, None);
3724 assert_eq!(r, (None, None));
3725
3726 {
3728 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3729 e.add_ava(Attribute::Class, EntryClass::Person.to_value());
3730 let e = e.into_sealed_committed();
3731
3732 assert!(Entry::idx_name2uuid_diff(None, Some(&e)) == (Some(Set::new()), None));
3733 }
3734
3735 {
3736 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3737 e.add_ava(Attribute::Class, EntryClass::Person.to_value());
3738 e.add_ava(Attribute::GidNumber, Value::new_uint32(1300));
3739 e.add_ava(Attribute::Name, Value::new_iname("testperson"));
3740 e.add_ava(
3741 Attribute::Spn,
3742 Value::new_spn_str("testperson", "example.com"),
3743 );
3744 e.add_ava(
3745 Attribute::Uuid,
3746 Value::Uuid(uuid!("9fec0398-c46c-4df4-9df5-b0016f7d563f")),
3747 );
3748 let e = e.into_sealed_committed();
3749
3750 assert!(
3752 Entry::idx_name2uuid_diff(None, Some(&e))
3753 == (
3754 Some(btreeset![
3755 "1300".to_string(),
3756 "testperson".to_string(),
3757 "testperson@example.com".to_string()
3758 ]),
3759 None
3760 )
3761 );
3762 assert!(
3765 Entry::idx_name2uuid_diff(Some(&e), None)
3766 == (
3767 None,
3768 Some(btreeset![
3769 "1300".to_string(),
3770 "testperson".to_string(),
3771 "testperson@example.com".to_string()
3772 ])
3773 )
3774 );
3775
3776 assert!(
3778 Entry::idx_name2uuid_diff(Some(&e), Some(&e))
3779 == (Some(Set::new()), Some(Set::new()))
3780 );
3781 }
3782 {
3785 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3786 e1.add_ava(Attribute::Class, EntryClass::Person.to_value());
3787 e1.add_ava(
3788 Attribute::Spn,
3789 Value::new_spn_str("testperson", "example.com"),
3790 );
3791 let e1 = e1.into_sealed_committed();
3792
3793 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3794 e2.add_ava(Attribute::Class, EntryClass::Person.to_value());
3795 e2.add_ava(Attribute::Name, Value::new_iname("testperson"));
3796 e2.add_ava(
3797 Attribute::Spn,
3798 Value::new_spn_str("testperson", "example.com"),
3799 );
3800 let e2 = e2.into_sealed_committed();
3801
3802 assert!(
3804 Entry::idx_name2uuid_diff(Some(&e1), Some(&e2))
3805 == (Some(btreeset!["testperson".to_string()]), Some(Set::new()))
3806 );
3807
3808 assert!(
3810 Entry::idx_name2uuid_diff(Some(&e2), Some(&e1))
3811 == (Some(Set::new()), Some(btreeset!["testperson".to_string()]))
3812 );
3813 }
3814
3815 {
3817 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3818 e1.add_ava(Attribute::Class, EntryClass::Person.to_value());
3819 e1.add_ava(
3820 Attribute::Spn,
3821 Value::new_spn_str("testperson", "example.com"),
3822 );
3823 let e1 = e1.into_sealed_committed();
3824
3825 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3826 e2.add_ava(Attribute::Class, EntryClass::Person.to_value());
3827 e2.add_ava(
3828 Attribute::Spn,
3829 Value::new_spn_str("renameperson", "example.com"),
3830 );
3831 let e2 = e2.into_sealed_committed();
3832
3833 assert!(
3834 Entry::idx_name2uuid_diff(Some(&e1), Some(&e2))
3835 == (
3836 Some(btreeset!["renameperson@example.com".to_string()]),
3837 Some(btreeset!["testperson@example.com".to_string()])
3838 )
3839 );
3840 }
3841 }
3842
3843 #[test]
3844 fn test_entry_idx_uuid2spn_diff() {
3845 assert!(Entry::idx_uuid2spn_diff(None, None).is_none());
3846
3847 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3848 e1.add_ava(
3849 Attribute::Spn,
3850 Value::new_spn_str("testperson", "example.com"),
3851 );
3852 let e1 = e1.into_sealed_committed();
3853
3854 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3855 e2.add_ava(
3856 Attribute::Spn,
3857 Value::new_spn_str("renameperson", "example.com"),
3858 );
3859 let e2 = e2.into_sealed_committed();
3860
3861 assert!(
3862 Entry::idx_uuid2spn_diff(None, Some(&e1))
3863 == Some(Ok(Value::new_spn_str("testperson", "example.com")))
3864 );
3865 assert!(Entry::idx_uuid2spn_diff(Some(&e1), None) == Some(Err(())));
3866 assert!(Entry::idx_uuid2spn_diff(Some(&e1), Some(&e1)).is_none());
3867 assert!(
3868 Entry::idx_uuid2spn_diff(Some(&e1), Some(&e2))
3869 == Some(Ok(Value::new_spn_str("renameperson", "example.com")))
3870 );
3871 }
3872
3873 #[test]
3874 fn test_entry_idx_uuid2rdn_diff() {
3875 assert!(Entry::idx_uuid2rdn_diff(None, None).is_none());
3876
3877 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3878 e1.add_ava(
3879 Attribute::Spn,
3880 Value::new_spn_str("testperson", "example.com"),
3881 );
3882 let e1 = e1.into_sealed_committed();
3883
3884 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3885 e2.add_ava(
3886 Attribute::Spn,
3887 Value::new_spn_str("renameperson", "example.com"),
3888 );
3889 let e2 = e2.into_sealed_committed();
3890
3891 assert!(
3892 Entry::idx_uuid2rdn_diff(None, Some(&e1))
3893 == Some(Ok("spn=testperson@example.com".to_string()))
3894 );
3895 assert!(Entry::idx_uuid2rdn_diff(Some(&e1), None) == Some(Err(())));
3896 assert!(Entry::idx_uuid2rdn_diff(Some(&e1), Some(&e1)).is_none());
3897 assert!(
3898 Entry::idx_uuid2rdn_diff(Some(&e1), Some(&e2))
3899 == Some(Ok("spn=renameperson@example.com".to_string()))
3900 );
3901 }
3902}