1use std::cmp::Ordering;
28pub use std::collections::BTreeSet as Set;
29use std::collections::{BTreeMap as Map, BTreeMap, BTreeSet};
30use std::sync::Arc;
31
32use crate::be::dbentry::{DbEntry, DbEntryVers};
33use crate::be::dbvalue::DbValueSetV2;
34use crate::be::{IdxKey, IdxSlope};
35use crate::credential::apppwd::ApplicationPassword;
36use crate::credential::Credential;
37use crate::filter::{Filter, FilterInvalid, FilterResolved, FilterValidResolved};
38use crate::idm::ldap::ldap_vattr_map;
39use crate::modify::{Modify, ModifyInvalid, ModifyList, ModifyValid};
40use crate::prelude::*;
41use crate::repl::cid::Cid;
42use crate::repl::entry::EntryChangeState;
43use crate::repl::proto::{ReplEntryV1, ReplIncrementalEntryV1};
44use crate::server::access::AccessEffectivePermission;
45use compact_jwt::JwsEs256Signer;
46use hashbrown::{HashMap, HashSet};
47use kanidm_proto::internal::ImageValue;
48use kanidm_proto::internal::{
49 ConsistencyError, Filter as ProtoFilter, OperationError, SchemaError, UiHint,
50};
51use kanidm_proto::scim_v1::server::ScimEffectiveAccess;
52use kanidm_proto::v1::Entry as ProtoEntry;
53use ldap3_proto::simple::{LdapPartialAttribute, LdapSearchResultEntry};
54use openssl::ec::EcKey;
55use openssl::pkey::{Private, Public};
56use time::OffsetDateTime;
57use tracing::trace;
58use uuid::Uuid;
59use webauthn_rs::prelude::{
60 AttestationCaList, AttestedPasskey as AttestedPasskeyV4, Passkey as PasskeyV4,
61};
62
63use crate::schema::{SchemaAttribute, SchemaClass, SchemaTransaction};
64use crate::value::{
65 ApiToken, CredentialType, IndexType, IntentTokenState, Oauth2Session, PartialValue, Session,
66 SyntaxType, Value,
67};
68use crate::valueset::{self, ScimResolveStatus, ValueSet};
69
70pub type EntryInitNew = Entry<EntryInit, EntryNew>;
71pub type EntryInvalidNew = Entry<EntryInvalid, EntryNew>;
72pub type EntryRefreshNew = Entry<EntryRefresh, EntryNew>;
73pub type EntrySealedNew = Entry<EntrySealed, EntryNew>;
74pub type EntryValidCommitted = Entry<EntryValid, EntryCommitted>;
75pub type EntrySealedCommitted = Entry<EntrySealed, EntryCommitted>;
76pub type EntryInvalidCommitted = Entry<EntryInvalid, EntryCommitted>;
77pub type EntryReducedCommitted = Entry<EntryReduced, EntryCommitted>;
78pub type EntryTuple = (Arc<EntrySealedCommitted>, EntryInvalidCommitted);
79
80pub type EntryIncrementalNew = Entry<EntryIncremental, EntryNew>;
81pub type EntryIncrementalCommitted = Entry<EntryIncremental, EntryCommitted>;
82
83#[derive(Clone, Debug)]
96pub struct EntryNew; #[derive(Clone, Debug)]
100pub struct EntryCommitted {
101 id: u64,
102}
103
104#[derive(Clone, Debug)]
105pub struct EntryInit;
106
107#[derive(Clone, Debug)]
114pub struct EntryInvalid {
115 cid: Cid,
116 ecstate: EntryChangeState,
117}
118
119#[derive(Clone, Debug)]
121pub struct EntryRefresh {
122 ecstate: EntryChangeState,
123}
124
125#[derive(Clone, Debug)]
127pub struct EntryIncremental {
128 uuid: Uuid,
130 ecstate: EntryChangeState,
131}
132
133#[derive(Clone, Debug)]
139pub struct EntryValid {
140 uuid: Uuid,
142 ecstate: EntryChangeState,
143}
144
145#[derive(Clone, Debug)]
152pub struct EntrySealed {
153 uuid: Uuid,
154 ecstate: EntryChangeState,
155}
156
157#[derive(Clone, Debug)]
163pub struct EntryReduced {
164 uuid: Uuid,
165 effective_access: Option<Box<AccessEffectivePermission>>,
166}
167
168pub type Eattrs = Map<Attribute, ValueSet>;
171
172pub trait GetUuid {
173 fn get_uuid(&self) -> Uuid;
174}
175
176pub trait Committed {}
177
178impl Committed for EntrySealed {}
179impl Committed for EntryReduced {}
180
181pub(crate) fn compare_attrs(left: &Eattrs, right: &Eattrs) -> bool {
182 let allkeys: Set<&Attribute> = left
185 .keys()
186 .chain(right.keys())
187 .filter(|k| *k != &Attribute::LastModifiedCid && *k != &Attribute::CreatedAtCid)
188 .collect();
189
190 allkeys.into_iter().all(|k| {
191 let left_vs = left.get(k);
193 let right_vs = right.get(k);
194 let r = match (left_vs, right_vs) {
195 (Some(l), Some(r)) => l.eq(r),
196 _ => false,
197 };
198 if !r {
199 trace!(?k, ?left_vs, ?right_vs, "compare_attrs_allkeys");
200 }
201 r
202 })
203}
204
205pub struct Entry<VALID, STATE> {
232 valid: VALID,
233 state: STATE,
234 attrs: Eattrs,
236}
237
238impl<VALID, STATE> std::fmt::Debug for Entry<VALID, STATE>
239where
240 STATE: std::fmt::Debug,
241 VALID: std::fmt::Debug,
242{
243 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
244 f.debug_struct("Entry<EntrySealed, _>")
245 .field("state", &self.state)
246 .field("valid", &self.valid)
247 .field("attrs", &self.attrs)
248 .finish()
249 }
250}
251
252impl<STATE> std::fmt::Display for Entry<EntrySealed, STATE>
253where
254 STATE: Clone,
255{
256 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
257 write!(f, "{}", self.get_uuid())
258 }
259}
260
261impl<STATE> std::fmt::Display for Entry<EntryInit, STATE>
262where
263 STATE: Clone,
264{
265 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
266 write!(f, "Entry in initial state")
267 }
268}
269
270impl<STATE> Entry<EntryInit, STATE>
271where
272 STATE: Clone,
273{
274 pub fn get_uuid(&self) -> Option<Uuid> {
276 self.attrs
277 .get(&Attribute::Uuid)
278 .and_then(|vs| vs.to_uuid_single())
279 }
280}
281
282impl Default for Entry<EntryInit, EntryNew> {
283 fn default() -> Self {
284 Self::new()
285 }
286}
287
288impl Entry<EntryInit, EntryNew> {
289 pub fn new() -> Self {
290 Entry {
291 valid: EntryInit,
293 state: EntryNew,
294 attrs: Map::new(),
295 }
297 }
298
299 pub fn from_proto_entry(
302 e: &ProtoEntry,
303 qs: &mut QueryServerWriteTransaction,
304 ) -> Result<Self, OperationError> {
305 trace!("from_proto_entry");
306 let map2: Result<Eattrs, OperationError> = e
313 .attrs
314 .iter()
315 .filter(|(_, v)| !v.is_empty())
316 .map(|(k, v)| {
317 trace!(?k, ?v, "attribute");
318 let attr_nk = Attribute::from(k.as_str());
319 let nv = valueset::from_result_value_iter(
320 v.iter().map(|vr| qs.clone_value(&attr_nk, vr)),
321 );
322 trace!(?nv, "new valueset transform");
323 match nv {
324 Ok(nvi) => Ok((attr_nk, nvi)),
325 Err(e) => Err(e),
326 }
327 })
328 .collect();
329
330 let x = map2?;
331
332 Ok(Entry {
333 state: EntryNew,
334 valid: EntryInit,
335 attrs: x,
336 })
337 }
338
339 #[instrument(level = "debug", skip_all)]
342 pub fn from_proto_entry_str(
343 es: &str,
344 qs: &mut QueryServerWriteTransaction,
345 ) -> Result<Self, OperationError> {
346 if cfg!(test) {
347 if es.len() > 256 {
348 let (dsp_es, _) = es.split_at(255);
349 trace!("Parsing -> {}...", dsp_es);
350 } else {
351 trace!("Parsing -> {}", es);
352 }
353 }
354 let pe: ProtoEntry = serde_json::from_str(es).map_err(|e| {
356 admin_error!(?e, "SerdeJson Failure");
359 OperationError::SerdeJsonError
360 })?;
361 Self::from_proto_entry(&pe, qs)
363 }
364
365 pub fn assign_cid(
368 mut self,
369 cid: Cid,
370 schema: &dyn SchemaTransaction,
371 ) -> Entry<EntryInvalid, EntryNew> {
372 let ecstate = EntryChangeState::new(&cid, &self.attrs, schema);
378
379 let cv = vs_cid![cid.clone()];
382 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
383 let cv = vs_cid![cid.clone()];
384 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
385
386 Entry {
387 valid: EntryInvalid { cid, ecstate },
388 state: EntryNew,
389 attrs: self.attrs,
390 }
391 }
392
393 pub fn compare(&self, rhs: &Entry<EntrySealed, EntryCommitted>) -> bool {
395 compare_attrs(&self.attrs, &rhs.attrs)
396 }
397
398 #[cfg(test)]
402 pub fn into_invalid_new(mut self) -> Entry<EntryInvalid, EntryNew> {
403 let cid = Cid::new_zero();
404 self.set_last_changed(cid.clone());
405
406 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
407
408 Entry {
409 valid: EntryInvalid { cid, ecstate },
410 state: EntryNew,
411 attrs: self.attrs,
412 }
413 }
414
415 #[cfg(test)]
419 pub fn into_valid_new(mut self) -> Entry<EntryValid, EntryNew> {
420 let cid = Cid::new_zero();
421 self.set_last_changed(cid.clone());
422 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
423
424 Entry {
425 valid: EntryValid {
426 ecstate,
427 uuid: self.get_uuid().expect("Invalid uuid"),
428 },
429 state: EntryNew,
430 attrs: self.attrs,
431 }
432 }
433
434 #[cfg(test)]
438 pub fn into_sealed_committed(mut self) -> Entry<EntrySealed, EntryCommitted> {
439 let cid = Cid::new_zero();
440 self.set_last_changed(cid.clone());
441 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
442 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
443 Entry {
444 valid: EntrySealed { uuid, ecstate },
445 state: EntryCommitted { id: 0 },
446 attrs: self.attrs,
447 }
448 }
449
450 #[cfg(test)]
454 pub fn into_sealed_new(mut self) -> Entry<EntrySealed, EntryNew> {
455 let cid = Cid::new_zero();
456 self.set_last_changed(cid.clone());
457 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
458
459 Entry {
460 valid: EntrySealed {
461 uuid: self.get_uuid().expect("Invalid uuid"),
462 ecstate,
463 },
464 state: EntryNew,
465 attrs: self.attrs,
466 }
467 }
468
469 pub fn add_ava(&mut self, attr: Attribute, value: Value) {
475 self.add_ava_int(attr, value);
476 }
477
478 pub fn remove_ava(&mut self, attr: &Attribute) {
479 self.attrs.remove(attr);
480 }
481
482 pub fn set_ava<T>(&mut self, attr: Attribute, iter: T)
484 where
485 T: IntoIterator<Item = Value>,
486 {
487 self.set_ava_iter_int(attr, iter);
488 }
489
490 pub fn get_ava_mut<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<&mut ValueSet> {
491 self.attrs.get_mut(attr.as_ref())
492 }
493}
494
495impl From<SchemaAttribute> for EntryInitNew {
496 fn from(value: SchemaAttribute) -> Self {
497 EntryInitNew::from(&value)
498 }
499}
500
501impl From<&SchemaAttribute> for EntryInitNew {
502 fn from(s: &SchemaAttribute) -> Self {
503 let mut attrs = Eattrs::new();
505 attrs.insert(Attribute::AttributeName, vs_iutf8![s.name.as_str()]);
506 attrs.insert(Attribute::Description, vs_utf8![s.description.to_owned()]);
507 attrs.insert(Attribute::Uuid, vs_uuid![s.uuid]);
508 attrs.insert(Attribute::MultiValue, vs_bool![s.multivalue]);
509 attrs.insert(Attribute::Phantom, vs_bool![s.phantom]);
510 attrs.insert(Attribute::SyncAllowed, vs_bool![s.sync_allowed]);
511 attrs.insert(Attribute::Replicated, vs_bool![s.replicated.into()]);
512 attrs.insert(Attribute::Unique, vs_bool![s.unique]);
513 attrs.insert(Attribute::Indexed, vs_bool![s.indexed]);
514 attrs.insert(Attribute::Syntax, vs_syntax![s.syntax]);
515 attrs.insert(
516 Attribute::Class,
517 vs_iutf8![
518 EntryClass::Object.into(),
519 EntryClass::System.into(),
520 EntryClass::AttributeType.into()
521 ],
522 );
523
524 Entry {
527 valid: EntryInit,
528 state: EntryNew,
529 attrs,
530 }
531 }
532}
533
534impl From<SchemaClass> for EntryInitNew {
535 fn from(value: SchemaClass) -> Self {
536 EntryInitNew::from(&value)
537 }
538}
539
540impl From<&SchemaClass> for EntryInitNew {
541 fn from(s: &SchemaClass) -> Self {
542 let mut attrs = Eattrs::new();
543 attrs.insert(Attribute::ClassName, vs_iutf8![s.name.as_str()]);
544 attrs.insert(Attribute::Description, vs_utf8![s.description.to_owned()]);
545 attrs.insert(Attribute::SyncAllowed, vs_bool![s.sync_allowed]);
546 attrs.insert(Attribute::Uuid, vs_uuid![s.uuid]);
547 attrs.insert(
548 Attribute::Class,
549 vs_iutf8![
550 EntryClass::Object.into(),
551 EntryClass::System.into(),
552 EntryClass::ClassType.into()
553 ],
554 );
555
556 let vs_systemmay = ValueSetIutf8::from_iter(s.systemmay.iter().map(|sm| sm.as_str()));
557 if let Some(vs) = vs_systemmay {
558 attrs.insert(Attribute::SystemMay, vs);
559 }
560
561 let vs_systemmust = ValueSetIutf8::from_iter(s.systemmust.iter().map(|sm| sm.as_str()));
562 if let Some(vs) = vs_systemmust {
563 attrs.insert(Attribute::SystemMust, vs);
564 }
565
566 let vs_systemexcludes =
567 ValueSetIutf8::from_iter(s.systemexcludes.iter().map(|sm| sm.as_str()));
568 if let Some(vs) = vs_systemexcludes {
569 attrs.insert(Attribute::SystemExcludes, vs);
570 }
571
572 let vs_systemsupplements =
573 ValueSetIutf8::from_iter(s.systemsupplements.iter().map(|sm| sm.as_str()));
574 if let Some(vs) = vs_systemsupplements {
575 attrs.insert(Attribute::SystemSupplements, vs);
576 }
577
578 Entry {
579 valid: EntryInit,
580 state: EntryNew,
581 attrs,
582 }
583 }
584}
585
586impl Entry<EntryRefresh, EntryNew> {
587 pub fn from_repl_entry_v1(repl_entry: ReplEntryV1) -> Result<Self, OperationError> {
588 let (ecstate, mut attrs) = repl_entry.rehydrate()?;
590
591 let last_mod_cid = ecstate.get_max_cid();
594 let cv = vs_cid![last_mod_cid.clone()];
595 let _ = attrs.insert(Attribute::LastModifiedCid, cv);
596
597 let create_at_cid = ecstate.at();
598 let cv = vs_cid![create_at_cid.clone()];
599 let _ = attrs.insert(Attribute::CreatedAtCid, cv);
600
601 Ok(Entry {
602 valid: EntryRefresh { ecstate },
603 state: EntryNew,
604 attrs,
605 })
606 }
607}
608
609impl<STATE> Entry<EntryRefresh, STATE> {
610 pub fn validate(
611 self,
612 schema: &dyn SchemaTransaction,
613 ) -> Result<Entry<EntryValid, STATE>, SchemaError> {
614 let uuid: Uuid = self
615 .attrs
616 .get(&Attribute::Uuid)
617 .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
618 .and_then(|vs| {
619 vs.to_uuid_single()
620 .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
621 })?;
622
623 let ne = Entry {
625 valid: EntryValid {
626 uuid,
627 ecstate: self.valid.ecstate,
628 },
629 state: self.state,
630 attrs: self.attrs,
631 };
632
633 ne.validate(schema).map(|()| ne)
634 }
635}
636
637impl<STATE> Entry<EntryIncremental, STATE> {
638 pub fn get_uuid(&self) -> Uuid {
639 self.valid.uuid
640 }
641}
642
643impl Entry<EntryIncremental, EntryNew> {
644 fn stub_ecstate(&self) -> EntryChangeState {
645 self.valid.ecstate.stub()
646 }
647
648 pub fn rehydrate(repl_inc_entry: ReplIncrementalEntryV1) -> Result<Self, OperationError> {
649 let (uuid, ecstate, attrs) = repl_inc_entry.rehydrate()?;
650
651 Ok(Entry {
652 valid: EntryIncremental { uuid, ecstate },
653 state: EntryNew,
654 attrs,
655 })
656 }
657
658 pub(crate) fn is_add_conflict(&self, db_entry: &EntrySealedCommitted) -> bool {
659 use crate::repl::entry::State;
660 debug_assert_eq!(self.valid.uuid, db_entry.valid.uuid);
661 let self_cs = &self.valid.ecstate;
663 let db_cs = db_entry.get_changestate();
664
665 match (self_cs.current(), db_cs.current()) {
667 (State::Live { at: at_left, .. }, State::Live { at: at_right, .. }) => {
668 at_left != at_right
669 }
670 _ => false,
672 }
673 }
674
675 pub(crate) fn resolve_add_conflict(
676 &self,
677 cid: &Cid,
678 db_ent: &EntrySealedCommitted,
679 ) -> (Option<EntrySealedNew>, EntryIncrementalCommitted) {
680 use crate::repl::entry::State;
681 debug_assert_eq!(self.valid.uuid, db_ent.valid.uuid);
682 let self_cs = &self.valid.ecstate;
683 let db_cs = db_ent.get_changestate();
684
685 match (self_cs.current(), db_cs.current()) {
686 (
687 State::Live {
688 at: at_left,
689 changes: _changes_left,
690 },
691 State::Live {
692 at: at_right,
693 changes: _changes_right,
694 },
695 ) => {
696 debug_assert!(at_left != at_right);
697 if at_left > at_right {
708 trace!("RI > DE, return DE");
709 (
710 None,
711 Entry {
712 valid: EntryIncremental {
713 uuid: db_ent.valid.uuid,
714 ecstate: db_cs.clone(),
715 },
716 state: EntryCommitted {
717 id: db_ent.state.id,
718 },
719 attrs: db_ent.attrs.clone(),
720 },
721 )
722 }
723 else {
733 trace!("RI < DE, return RI");
734 let conflict = if at_right.s_uuid == cid.s_uuid {
736 trace!("Origin process conflict entry");
737 let mut cnf_ent = Entry {
740 valid: EntryInvalid {
741 cid: cid.clone(),
742 ecstate: db_cs.clone(),
743 },
744 state: EntryNew,
745 attrs: db_ent.attrs.clone(),
746 };
747
748 cnf_ent.add_ava(Attribute::SourceUuid, Value::Uuid(db_ent.valid.uuid));
750
751 let new_uuid = Uuid::new_v4();
753 cnf_ent.purge_ava(Attribute::Uuid);
754 cnf_ent.add_ava(Attribute::Uuid, Value::Uuid(new_uuid));
755 cnf_ent.add_ava(Attribute::Class, EntryClass::Recycled.into());
756 cnf_ent.add_ava(Attribute::Class, EntryClass::Conflict.into());
757
758 let cv = vs_cid![cid.clone()];
762 let _ = cnf_ent.attrs.insert(Attribute::LastModifiedCid, cv);
763 let cv = vs_cid![cid.clone()];
765 let _ = cnf_ent.attrs.insert(Attribute::CreatedAtCid, cv);
766
767 let Entry {
771 valid: EntryInvalid { cid: _, ecstate },
772 state,
773 attrs,
774 } = cnf_ent;
775
776 let cnf_ent = Entry {
777 valid: EntrySealed {
778 uuid: new_uuid,
779 ecstate,
780 },
781 state,
782 attrs,
783 };
784
785 Some(cnf_ent)
786 } else {
787 None
788 };
789
790 let mut attrs = self.attrs.clone();
794 let ecstate = self_cs.clone();
795
796 let last_mod_cid = ecstate.get_max_cid();
797 let cv = vs_cid![last_mod_cid.clone()];
798 let _ = attrs.insert(Attribute::LastModifiedCid, cv);
799
800 let create_at_cid = ecstate.at();
801 let cv = vs_cid![create_at_cid.clone()];
802 let _ = attrs.insert(Attribute::CreatedAtCid, cv);
803
804 (
805 conflict,
806 Entry {
807 valid: EntryIncremental {
808 uuid: self.valid.uuid,
809 ecstate,
810 },
811 state: EntryCommitted {
812 id: db_ent.state.id,
813 },
814 attrs,
815 },
816 )
817 }
818 }
819 _ => unreachable!(),
821 }
822 }
823
824 pub(crate) fn merge_state(
825 &self,
826 db_ent: &EntrySealedCommitted,
827 schema: &dyn SchemaTransaction,
828 trim_cid: &Cid,
829 ) -> EntryIncrementalCommitted {
830 use crate::repl::entry::State;
831
832 debug_assert_eq!(self.valid.uuid, db_ent.valid.uuid);
834
835 let self_cs = &self.valid.ecstate;
838 let db_cs = db_ent.get_changestate();
839
840 match (self_cs.current(), db_cs.current()) {
841 (
842 State::Live {
843 at: at_left,
844 changes: changes_left,
845 },
846 State::Live {
847 at: at_right,
848 changes: changes_right,
849 },
850 ) => {
851 debug_assert_eq!(at_left, at_right);
852 let mut attr_set: Vec<_> =
857 changes_left.keys().chain(changes_right.keys()).collect();
858 attr_set.shrink_to_fit();
859 attr_set.sort_unstable();
860 attr_set.dedup();
861
862 let mut changes = BTreeMap::default();
864 let mut eattrs = Eattrs::default();
865
866 for attr_name in attr_set.into_iter() {
868 match (changes_left.get(attr_name), changes_right.get(attr_name)) {
869 (Some(cid_left), Some(cid_right)) => {
870 let take_left = cid_left > cid_right;
877
878 match (self.attrs.get(attr_name), db_ent.attrs.get(attr_name)) {
879 (Some(vs_left), Some(vs_right)) if take_left => {
880 changes.insert(attr_name.clone(), cid_left.clone());
881 #[allow(clippy::todo)]
882 if let Some(merged_attr_state) =
883 vs_left.repl_merge_valueset(vs_right, trim_cid)
884 {
885 eattrs.insert(attr_name.clone(), merged_attr_state);
888 } else {
889 eattrs.insert(attr_name.clone(), vs_left.clone());
890 }
891 }
892 (Some(vs_left), Some(vs_right)) => {
893 changes.insert(attr_name.clone(), cid_right.clone());
894 #[allow(clippy::todo)]
895 if let Some(merged_attr_state) =
896 vs_right.repl_merge_valueset(vs_left, trim_cid)
897 {
898 eattrs.insert(attr_name.clone(), merged_attr_state);
901 } else {
902 eattrs.insert(attr_name.clone(), vs_right.clone());
903 }
904 }
905 (Some(vs_left), None) if take_left => {
906 changes.insert(attr_name.clone(), cid_left.clone());
907 eattrs.insert(attr_name.clone(), vs_left.clone());
908 }
909 (Some(_vs_left), None) => {
910 changes.insert(attr_name.clone(), cid_right.clone());
911 }
913 (None, Some(_vs_right)) if take_left => {
914 changes.insert(attr_name.clone(), cid_left.clone());
915 }
917 (None, Some(vs_right)) => {
918 changes.insert(attr_name.clone(), cid_right.clone());
919 eattrs.insert(attr_name.clone(), vs_right.clone());
920 }
921 (None, None) if take_left => {
922 changes.insert(attr_name.clone(), cid_left.clone());
923 }
925 (None, None) => {
926 changes.insert(attr_name.clone(), cid_right.clone());
927 }
929 }
930 }
932 (Some(cid_left), None) => {
933 changes.insert(attr_name.clone(), cid_left.clone());
935 if let Some(valueset) = self.attrs.get(attr_name) {
936 eattrs.insert(attr_name.clone(), valueset.clone());
937 }
938 }
939 (None, Some(cid_right)) => {
940 changes.insert(attr_name.clone(), cid_right.clone());
942 if let Some(valueset) = db_ent.attrs.get(attr_name) {
943 eattrs.insert(attr_name.clone(), valueset.clone());
944 }
945 }
946 (None, None) => {
947 debug_assert!(false);
949 }
950 }
951 }
952
953 let mut ecstate = EntryChangeState::build(State::Live {
954 at: at_left.clone(),
955 changes,
956 });
957
958 ecstate.retain(|k, _| schema.is_replicated(k));
962
963 let cv = vs_cid![ecstate.get_max_cid().clone()];
964 let _ = eattrs.insert(Attribute::LastModifiedCid, cv);
965
966 let cv = vs_cid![ecstate.at().clone()];
967 let _ = eattrs.insert(Attribute::CreatedAtCid, cv);
968
969 Entry {
970 valid: EntryIncremental {
971 uuid: self.valid.uuid,
972 ecstate,
973 },
974 state: EntryCommitted {
975 id: db_ent.state.id,
976 },
977 attrs: eattrs,
978 }
979 }
980 (State::Tombstone { at: left_at }, State::Live { .. }) => {
981 let mut attrs_new: Eattrs = Map::new();
985 let class_ava = vs_iutf8![EntryClass::Object.into(), EntryClass::Tombstone.into()];
986 let last_mod_ava = vs_cid![left_at.clone()];
987 let created_ava = vs_cid![left_at.clone()];
988
989 attrs_new.insert(Attribute::Uuid, vs_uuid![self.valid.uuid]);
990 attrs_new.insert(Attribute::Class, class_ava);
991 attrs_new.insert(Attribute::LastModifiedCid, last_mod_ava);
992 attrs_new.insert(Attribute::CreatedAtCid, created_ava);
993
994 Entry {
995 valid: EntryIncremental {
996 uuid: self.valid.uuid,
997 ecstate: self.valid.ecstate.clone(),
998 },
999 state: EntryCommitted {
1000 id: db_ent.state.id,
1001 },
1002 attrs: attrs_new,
1003 }
1004 }
1005 (State::Live { .. }, State::Tombstone { .. }) => {
1006 Entry {
1014 valid: EntryIncremental {
1015 uuid: db_ent.valid.uuid,
1016 ecstate: db_ent.valid.ecstate.clone(),
1017 },
1018 state: EntryCommitted {
1019 id: db_ent.state.id,
1020 },
1021 attrs: db_ent.attrs.clone(),
1022 }
1023 }
1024 (State::Tombstone { at: left_at }, State::Tombstone { at: right_at }) => {
1025 let (at, ecstate) = if left_at < right_at {
1030 (left_at, self.valid.ecstate.clone())
1031 } else {
1032 (right_at, db_ent.valid.ecstate.clone())
1033 };
1034
1035 let mut attrs_new: Eattrs = Map::new();
1036 let class_ava = vs_iutf8![EntryClass::Object.into(), EntryClass::Tombstone.into()];
1037 let last_mod_ava = vs_cid![at.clone()];
1038 let created_ava = vs_cid![at.clone()];
1039
1040 attrs_new.insert(Attribute::Uuid, vs_uuid![db_ent.valid.uuid]);
1041 attrs_new.insert(Attribute::Class, class_ava);
1042 attrs_new.insert(Attribute::LastModifiedCid, last_mod_ava);
1043 attrs_new.insert(Attribute::CreatedAtCid, created_ava);
1044
1045 Entry {
1046 valid: EntryIncremental {
1047 uuid: db_ent.valid.uuid,
1048 ecstate,
1049 },
1050 state: EntryCommitted {
1051 id: db_ent.state.id,
1052 },
1053 attrs: attrs_new,
1054 }
1055 }
1056 }
1057 }
1058}
1059
1060impl Entry<EntryIncremental, EntryCommitted> {
1061 pub(crate) fn validate_repl(self, schema: &dyn SchemaTransaction) -> EntryValidCommitted {
1062 let mut ne = Entry {
1067 valid: EntryValid {
1068 uuid: self.valid.uuid,
1069 ecstate: self.valid.ecstate,
1070 },
1071 state: self.state,
1072 attrs: self.attrs,
1073 };
1074
1075 if let Err(e) = ne.validate(schema) {
1076 warn!(uuid = ?self.valid.uuid, err = ?e, "Entry failed schema check, moving to a conflict state");
1077 ne.add_ava_int(Attribute::Class, EntryClass::Recycled.into());
1078 ne.add_ava_int(Attribute::Class, EntryClass::Conflict.into());
1079 ne.add_ava_int(Attribute::SourceUuid, Value::Uuid(self.valid.uuid));
1080 }
1081 ne
1082 }
1083}
1084
1085impl<STATE> Entry<EntryInvalid, STATE> {
1086 pub(crate) fn get_uuid(&self) -> Option<Uuid> {
1087 self.attrs
1088 .get(&Attribute::Uuid)
1089 .and_then(|vs| vs.to_uuid_single())
1090 }
1091
1092 pub fn validate(
1095 self,
1096 schema: &dyn SchemaTransaction,
1097 ) -> Result<Entry<EntryValid, STATE>, SchemaError> {
1098 let uuid: Uuid = self
1099 .attrs
1100 .get(&Attribute::Uuid)
1101 .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
1102 .and_then(|vs| {
1103 vs.to_uuid_single()
1104 .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
1105 })?;
1106
1107 let ne = Entry {
1109 valid: EntryValid {
1110 uuid,
1111 ecstate: self.valid.ecstate,
1112 },
1113 state: self.state,
1114 attrs: self.attrs,
1115 };
1116
1117 ne.validate(schema).map(|()| ne)
1118 }
1119
1120 pub(crate) fn get_ava_refer_mut<A: AsRef<Attribute>>(
1124 &mut self,
1125 attr: A,
1126 ) -> Option<&mut BTreeSet<Uuid>> {
1127 self.attrs
1128 .get_mut(attr.as_ref())
1129 .and_then(|vs| vs.as_refer_set_mut())
1130 }
1131}
1132
1133impl<VALID, STATE> Clone for Entry<VALID, STATE>
1134where
1135 VALID: Clone,
1136 STATE: Clone,
1137{
1138 fn clone(&self) -> Entry<VALID, STATE> {
1140 Entry {
1141 valid: self.valid.clone(),
1142 state: self.state.clone(),
1143 attrs: self.attrs.clone(),
1144 }
1145 }
1146}
1147
1148impl Entry<EntryInvalid, EntryCommitted> {
1149 #[cfg(test)]
1153 pub fn into_valid_new(self) -> Entry<EntryValid, EntryNew> {
1154 let uuid = self.get_uuid().expect("Invalid uuid");
1155 Entry {
1156 valid: EntryValid {
1157 uuid,
1158 ecstate: self.valid.ecstate,
1159 },
1160 state: EntryNew,
1161 attrs: self.attrs,
1162 }
1163 }
1164
1165 pub fn to_recycled(mut self) -> Self {
1167 self.add_ava(Attribute::Class, EntryClass::Recycled.into());
1169
1170 Entry {
1174 valid: self.valid,
1175 state: self.state,
1176 attrs: self.attrs,
1177 }
1178 }
1179
1180 pub fn to_conflict<T>(&mut self, iter: T)
1182 where
1183 T: IntoIterator<Item = Uuid>,
1184 {
1185 self.add_ava(Attribute::Class, EntryClass::Recycled.into());
1186 self.add_ava(Attribute::Class, EntryClass::Conflict.into());
1187 for source_uuid in iter {
1189 self.add_ava(Attribute::SourceUuid, Value::Uuid(source_uuid));
1190 }
1191 }
1192
1193 pub fn to_revived(mut self) -> Self {
1195 self.remove_ava(Attribute::Class, &EntryClass::Recycled.into());
1197 self.remove_ava(Attribute::Class, &EntryClass::Conflict.into());
1198 self.purge_ava(Attribute::SourceUuid);
1199 self.purge_ava(Attribute::RecycledDirectMemberOf);
1200
1201 Entry {
1205 valid: self.valid,
1206 state: self.state,
1207 attrs: self.attrs,
1208 }
1209 }
1210}
1211impl Entry<EntryInvalid, EntryNew> {
1214 #[cfg(test)]
1217 pub fn into_init_new(self) -> Entry<EntryInit, EntryNew> {
1218 Entry {
1219 valid: EntryInit,
1220 state: EntryNew,
1221 attrs: self.attrs,
1222 }
1223 }
1224
1225 #[cfg(test)]
1229 pub fn into_valid_new(self) -> Entry<EntryValid, EntryNew> {
1230 let uuid = self.get_uuid().expect("Invalid uuid");
1231 Entry {
1232 valid: EntryValid {
1233 uuid,
1234 ecstate: self.valid.ecstate,
1235 },
1236 state: EntryNew,
1237 attrs: self.attrs,
1238 }
1239 }
1240
1241 #[cfg(test)]
1245 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1246 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
1247 Entry {
1248 valid: EntrySealed {
1249 uuid,
1250 ecstate: self.valid.ecstate,
1251 },
1252 state: EntryCommitted { id: 0 },
1253 attrs: self.attrs,
1254 }
1255 }
1256
1257 #[cfg(test)]
1261 pub fn into_valid_committed(self) -> Entry<EntryValid, EntryCommitted> {
1262 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
1263 Entry {
1264 valid: EntryValid {
1265 uuid,
1266 ecstate: self.valid.ecstate,
1267 },
1268 state: EntryCommitted { id: 0 },
1269 attrs: self.attrs,
1270 }
1271 }
1272}
1273
1274impl Entry<EntryInvalid, EntryCommitted> {
1275 #[cfg(test)]
1279 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1280 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
1281 Entry {
1282 valid: EntrySealed {
1283 uuid,
1284 ecstate: self.valid.ecstate,
1285 },
1286 state: self.state,
1287 attrs: self.attrs,
1288 }
1289 }
1290}
1291
1292impl Entry<EntrySealed, EntryNew> {
1293 #[cfg(test)]
1297 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1298 Entry {
1299 valid: self.valid,
1300 state: EntryCommitted { id: 0 },
1301 attrs: self.attrs,
1302 }
1303 }
1304
1305 pub fn into_sealed_committed_id(self, id: u64) -> Entry<EntrySealed, EntryCommitted> {
1308 Entry {
1309 valid: self.valid,
1310 state: EntryCommitted { id },
1311 attrs: self.attrs,
1312 }
1313 }
1314
1315 pub fn compare(&self, rhs: &Entry<EntrySealed, EntryNew>) -> bool {
1316 compare_attrs(&self.attrs, &rhs.attrs)
1317 }
1318}
1319
1320type IdxDiff<'a> =
1321 Vec<Result<(&'a Attribute, IndexType, String), (&'a Attribute, IndexType, String)>>;
1322
1323impl<VALID> Entry<VALID, EntryCommitted> {
1324 pub fn get_id(&self) -> u64 {
1326 self.state.id
1327 }
1328}
1329
1330impl<STATE> Entry<EntrySealed, STATE> {
1331 pub fn into_init(self) -> Entry<EntryInit, STATE> {
1332 Entry {
1333 valid: EntryInit,
1334 state: self.state,
1335 attrs: self.attrs,
1336 }
1337 }
1338}
1339
1340impl Entry<EntrySealed, EntryCommitted> {
1341 #[cfg(test)]
1342 pub(crate) fn get_last_changed(&self) -> Cid {
1343 self.valid.ecstate.get_max_cid().clone()
1344 }
1345
1346 #[cfg(test)]
1348 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1349 self
1351 }
1352
1353 pub(crate) fn stub_sealed_committed_id(
1354 id: u64,
1355 ctx_ent: &EntryIncrementalNew,
1356 ) -> EntrySealedCommitted {
1357 let uuid = ctx_ent.get_uuid();
1358 let ecstate = ctx_ent.stub_ecstate();
1359
1360 Entry {
1361 valid: EntrySealed { uuid, ecstate },
1362 state: EntryCommitted { id },
1363 attrs: Default::default(),
1364 }
1365 }
1366
1367 pub fn insert_claim(&mut self, value: &str) {
1370 self.add_ava_int(Attribute::Claim, Value::new_iutf8(value));
1371 }
1372
1373 pub fn compare(&self, rhs: &Entry<EntrySealed, EntryCommitted>) -> bool {
1374 compare_attrs(&self.attrs, &rhs.attrs)
1375 }
1376
1377 pub fn to_dbentry(&self) -> DbEntry {
1379 DbEntry {
1382 ent: DbEntryVers::V3 {
1383 changestate: self.valid.ecstate.to_db_changestate(),
1384 attrs: self
1385 .attrs
1386 .iter()
1387 .map(|(k, vs)| {
1388 let dbvs: DbValueSetV2 = vs.to_db_valueset_v2();
1389 (k.clone(), dbvs)
1390 })
1391 .collect(),
1392 },
1393 }
1394 }
1395
1396 #[inline]
1397 fn get_name2uuid_cands(&self) -> Set<String> {
1400 let cands = [Attribute::Spn, Attribute::Name, Attribute::GidNumber];
1406 cands
1407 .iter()
1408 .filter_map(|cand| {
1409 self.attrs
1410 .get(cand)
1411 .map(|vs| vs.to_proto_string_clone_iter())
1412 })
1413 .flatten()
1414 .collect()
1415 }
1416
1417 #[inline]
1418 fn get_externalid2uuid(&self) -> Option<String> {
1421 self.attrs
1422 .get(&Attribute::SyncExternalId)
1423 .and_then(|vs| vs.to_proto_string_single())
1424 }
1425
1426 #[inline]
1427 pub(crate) fn get_uuid2spn(&self) -> Value {
1430 self.attrs
1431 .get(&Attribute::Spn)
1432 .and_then(|vs| vs.to_value_single())
1433 .or_else(|| {
1434 self.attrs
1435 .get(&Attribute::Name)
1436 .and_then(|vs| vs.to_value_single())
1437 })
1438 .unwrap_or_else(|| Value::Uuid(self.get_uuid()))
1439 }
1440
1441 #[inline]
1442 pub(crate) fn get_uuid2rdn(&self) -> String {
1446 self.attrs
1447 .get(&Attribute::Spn)
1448 .and_then(|vs| vs.to_proto_string_single().map(|v| format!("spn={v}")))
1449 .or_else(|| {
1450 self.attrs
1451 .get(&Attribute::Name)
1452 .and_then(|vs| vs.to_proto_string_single().map(|v| format!("name={v}")))
1453 })
1454 .unwrap_or_else(|| format!("uuid={}", self.get_uuid().as_hyphenated()))
1455 }
1456
1457 pub(crate) fn idx_name2uuid_diff(
1460 pre: Option<&Self>,
1461 post: Option<&Self>,
1462 ) -> (
1463 Option<Set<String>>,
1465 Option<Set<String>>,
1467 ) {
1468 match (pre, post) {
1470 (None, None) => {
1471 (None, None)
1473 }
1474 (None, Some(b)) => {
1475 (Some(b.get_name2uuid_cands()), None)
1478 }
1479 (Some(a), None) => {
1480 (None, Some(a.get_name2uuid_cands()))
1482 }
1483 (Some(a), Some(b)) => {
1484 let pre_set = a.get_name2uuid_cands();
1485 let post_set = b.get_name2uuid_cands();
1486
1487 let add_set: Set<_> = post_set.difference(&pre_set).cloned().collect();
1489 let rem_set: Set<_> = pre_set.difference(&post_set).cloned().collect();
1491 (Some(add_set), Some(rem_set))
1492 }
1493 }
1494 }
1495
1496 pub(crate) fn idx_externalid2uuid_diff(
1498 pre: Option<&Self>,
1499 post: Option<&Self>,
1500 ) -> (Option<String>, Option<String>) {
1501 match (pre, post) {
1502 (None, None) => {
1503 (None, None)
1505 }
1506 (None, Some(b)) => {
1507 (b.get_externalid2uuid(), None)
1509 }
1510 (Some(a), None) => {
1511 (None, a.get_externalid2uuid())
1513 }
1514 (Some(a), Some(b)) => {
1515 let ia = a.get_externalid2uuid();
1516 let ib = b.get_externalid2uuid();
1517 if ia != ib {
1518 (ib, ia)
1521 } else {
1522 (None, None)
1524 }
1525 }
1526 }
1527 }
1528
1529 pub(crate) fn idx_uuid2spn_diff(
1532 pre: Option<&Self>,
1533 post: Option<&Self>,
1534 ) -> Option<Result<Value, ()>> {
1535 match (pre, post) {
1536 (None, None) => {
1537 None
1539 }
1540 (None, Some(b)) => {
1541 Some(Ok(b.get_uuid2spn()))
1543 }
1544 (Some(_a), None) => {
1545 Some(Err(()))
1547 }
1548 (Some(a), Some(b)) => {
1549 let ia = a.get_uuid2spn();
1550 let ib = b.get_uuid2spn();
1551 if ia != ib {
1552 Some(Ok(ib))
1554 } else {
1555 None
1557 }
1558 }
1559 }
1560 }
1561
1562 pub(crate) fn idx_uuid2rdn_diff(
1565 pre: Option<&Self>,
1566 post: Option<&Self>,
1567 ) -> Option<Result<String, ()>> {
1568 match (pre, post) {
1569 (None, None) => {
1570 None
1572 }
1573 (None, Some(b)) => {
1574 Some(Ok(b.get_uuid2rdn()))
1576 }
1577 (Some(_a), None) => {
1578 Some(Err(()))
1580 }
1581 (Some(a), Some(b)) => {
1582 let ia = a.get_uuid2rdn();
1583 let ib = b.get_uuid2rdn();
1584 if ia != ib {
1585 Some(Ok(ib))
1587 } else {
1588 None
1590 }
1591 }
1592 }
1593 }
1594
1595 pub(crate) fn idx_diff<'a>(
1598 idxmeta: &'a HashMap<IdxKey, IdxSlope>,
1599 pre: Option<&Self>,
1600 post: Option<&Self>,
1601 ) -> IdxDiff<'a> {
1602 match (pre, post) {
1607 (None, None) => {
1608 Vec::with_capacity(0)
1610 }
1611 (Some(pre_e), None) => {
1612 idxmeta
1614 .keys()
1615 .flat_map(|ikey| {
1616 match pre_e.get_ava_set(&ikey.attr) {
1617 None => Vec::with_capacity(0),
1618 Some(vs) => {
1619 let changes: Vec<Result<_, _>> = match ikey.itype {
1620 IndexType::Equality => {
1621 vs.generate_idx_eq_keys()
1623 .into_iter()
1624 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1625 .collect()
1626 }
1627 IndexType::Presence => {
1628 vec![Err((&ikey.attr, ikey.itype, "_".to_string()))]
1629 }
1630 IndexType::SubString => vs
1631 .generate_idx_sub_keys()
1632 .into_iter()
1633 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1634 .collect(),
1635 };
1636 changes
1637 }
1638 }
1639 })
1640 .collect()
1641 }
1642 (None, Some(post_e)) => {
1643 idxmeta
1645 .keys()
1646 .flat_map(|ikey| {
1647 match post_e.get_ava_set(&ikey.attr) {
1648 None => Vec::with_capacity(0),
1649 Some(vs) => {
1650 let changes: Vec<Result<_, _>> = match ikey.itype {
1651 IndexType::Equality => vs
1652 .generate_idx_eq_keys()
1653 .into_iter()
1654 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1655 .collect(),
1656 IndexType::Presence => {
1657 vec![Ok((&ikey.attr, ikey.itype, "_".to_string()))]
1658 }
1659 IndexType::SubString => vs
1660 .generate_idx_sub_keys()
1661 .into_iter()
1662 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1663 .collect(),
1664 };
1665 changes
1668 }
1669 }
1670 })
1671 .collect()
1672 }
1673 (Some(pre_e), Some(post_e)) => {
1674 assert_eq!(pre_e.state.id, post_e.state.id);
1675 idxmeta
1676 .keys()
1677 .flat_map(|ikey| {
1678 match (
1679 pre_e.get_ava_set(&ikey.attr),
1680 post_e.get_ava_set(&ikey.attr),
1681 ) {
1682 (None, None) => {
1683 Vec::with_capacity(0)
1685 }
1686 (Some(pre_vs), None) => {
1687 let changes: Vec<Result<_, _>> = match ikey.itype {
1689 IndexType::Equality => {
1690 pre_vs
1693 .generate_idx_eq_keys()
1694 .into_iter()
1695 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1696 .collect()
1697 }
1698 IndexType::Presence => {
1699 vec![Err((&ikey.attr, ikey.itype, "_".to_string()))]
1700 }
1701 IndexType::SubString => pre_vs
1702 .generate_idx_sub_keys()
1703 .into_iter()
1704 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1705 .collect(),
1706 };
1707 changes
1708 }
1709 (None, Some(post_vs)) => {
1710 let changes: Vec<Result<_, _>> = match ikey.itype {
1712 IndexType::Equality => {
1713 post_vs
1716 .generate_idx_eq_keys()
1717 .into_iter()
1718 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1719 .collect()
1720 }
1721 IndexType::Presence => {
1722 vec![Ok((&ikey.attr, ikey.itype, "_".to_string()))]
1723 }
1724 IndexType::SubString => post_vs
1725 .generate_idx_sub_keys()
1726 .into_iter()
1727 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1728 .collect(),
1729 };
1730 changes
1731 }
1732 (Some(pre_vs), Some(post_vs)) => {
1733 let (mut pre_idx_keys, mut post_idx_keys) = match ikey.itype {
1735 IndexType::Equality => (
1736 pre_vs.generate_idx_eq_keys(),
1737 post_vs.generate_idx_eq_keys(),
1738 ),
1739 IndexType::Presence => {
1740 (Vec::with_capacity(0), Vec::with_capacity(0))
1742 }
1743 IndexType::SubString => (
1744 pre_vs.generate_idx_sub_keys(),
1745 post_vs.generate_idx_sub_keys(),
1746 ),
1747 };
1748
1749 let sz = if pre_idx_keys.len() > post_idx_keys.len() {
1750 pre_idx_keys.len()
1751 } else {
1752 post_idx_keys.len()
1753 };
1754
1755 let mut added_vs = Vec::with_capacity(sz);
1756 let mut removed_vs = Vec::with_capacity(sz);
1757
1758 if sz > 0 {
1759 pre_idx_keys.sort_unstable();
1760 post_idx_keys.sort_unstable();
1761
1762 let mut pre_iter = pre_idx_keys.iter();
1763 let mut post_iter = post_idx_keys.iter();
1764
1765 let mut pre = pre_iter.next();
1766 let mut post = post_iter.next();
1767
1768 loop {
1769 match (pre, post) {
1770 (Some(a), Some(b)) => {
1771 match a.cmp(b) {
1772 Ordering::Less => {
1773 removed_vs.push(a.clone());
1774 pre = pre_iter.next();
1775 }
1776 Ordering::Equal => {
1777 pre = pre_iter.next();
1779 post = post_iter.next();
1780 }
1781 Ordering::Greater => {
1782 added_vs.push(b.clone());
1783 post = post_iter.next();
1784 }
1785 }
1786 }
1787 (Some(a), None) => {
1788 removed_vs.push(a.clone());
1789 pre = pre_iter.next();
1790 }
1791 (None, Some(b)) => {
1792 added_vs.push(b.clone());
1793 post = post_iter.next();
1794 }
1795 (None, None) => {
1796 break;
1797 }
1798 }
1799 }
1800 } let mut diff =
1803 Vec::with_capacity(removed_vs.len() + added_vs.len());
1804
1805 match ikey.itype {
1806 IndexType::SubString | IndexType::Equality => {
1807 removed_vs
1808 .into_iter()
1809 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1810 .for_each(|v| diff.push(v));
1811 added_vs
1812 .into_iter()
1813 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1814 .for_each(|v| diff.push(v));
1815 }
1816 IndexType::Presence => {
1817 }
1819 };
1820 diff
1822 }
1823 }
1824 })
1825 .collect()
1826 }
1828 }
1829 }
1830
1831 pub fn from_dbentry(db_e: DbEntry, id: u64) -> Option<Self> {
1832 let (attrs, ecstate) = match db_e.ent {
1835 DbEntryVers::V3 { changestate, attrs } => {
1836 let ecstate = EntryChangeState::from_db_changestate(changestate);
1837
1838 let r_attrs = attrs
1839 .into_iter()
1840 .filter(|(_k, vs)| !vs.is_empty())
1842 .map(|(k, dbvs)| {
1843 valueset::from_db_valueset_v2(dbvs)
1844 .map(|vs: ValueSet| (k, vs))
1845 .map_err(|e| {
1846 error!(?e, "from_dbentry failed");
1847 })
1848 })
1849 .collect::<Result<Eattrs, ()>>()
1850 .ok()?;
1851
1852 (r_attrs, ecstate)
1853 }
1854 };
1855
1856 let uuid = attrs
1857 .get(&Attribute::Uuid)
1858 .and_then(|vs| vs.to_uuid_single())?;
1859
1860 Some(Entry {
1861 valid: EntrySealed { uuid, ecstate },
1862 state: EntryCommitted { id },
1863 attrs,
1864 })
1865 }
1866
1867 #[cfg(test)]
1875 pub(crate) fn into_reduced(self) -> Entry<EntryReduced, EntryCommitted> {
1876 Entry {
1877 valid: EntryReduced {
1878 uuid: self.valid.uuid,
1879 effective_access: None,
1880 },
1881 state: self.state,
1882 attrs: self.attrs,
1883 }
1884 }
1885
1886 pub fn reduce_attributes(
1889 &self,
1890 allowed_attrs: &BTreeSet<Attribute>,
1891 effective_access: Option<Box<AccessEffectivePermission>>,
1892 ) -> Entry<EntryReduced, EntryCommitted> {
1893 let f_attrs: Map<_, _> = self
1895 .attrs
1896 .iter()
1897 .filter_map(|(k, v)| {
1898 if allowed_attrs.contains(k) {
1899 Some((k.clone(), v.clone()))
1900 } else {
1901 None
1902 }
1903 })
1904 .collect();
1905
1906 let valid = EntryReduced {
1907 uuid: self.valid.uuid,
1908 effective_access,
1909 };
1910 let state = self.state.clone();
1911
1912 Entry {
1913 valid,
1914 state,
1915 attrs: f_attrs,
1916 }
1917 }
1918
1919 pub fn to_tombstone(&self, cid: Cid) -> Entry<EntryInvalid, EntryCommitted> {
1921 let mut ecstate = self.valid.ecstate.clone();
1922 let mut attrs_new: Eattrs = Map::new();
1924
1925 let class_ava = vs_iutf8![EntryClass::Object.into(), EntryClass::Tombstone.into()];
1926 let last_mod_ava = vs_cid![cid.clone()];
1927 let created_ava = vs_cid![cid.clone()];
1928
1929 attrs_new.insert(Attribute::Uuid, vs_uuid![self.get_uuid()]);
1930 attrs_new.insert(Attribute::Class, class_ava);
1931 attrs_new.insert(Attribute::LastModifiedCid, last_mod_ava);
1932 attrs_new.insert(Attribute::CreatedAtCid, created_ava);
1933
1934 ecstate.tombstone(&cid);
1936
1937 Entry {
1938 valid: EntryInvalid { cid, ecstate },
1939 state: self.state.clone(),
1940 attrs: attrs_new,
1941 }
1942 }
1943
1944 pub fn into_valid(self, ecstate: EntryChangeState) -> Entry<EntryValid, EntryCommitted> {
1946 Entry {
1947 valid: EntryValid {
1948 uuid: self.valid.uuid,
1949 ecstate,
1950 },
1951 state: self.state,
1952 attrs: self.attrs,
1953 }
1954 }
1955
1956 pub fn verify(
1957 &self,
1958 schema: &dyn SchemaTransaction,
1959 results: &mut Vec<Result<(), ConsistencyError>>,
1960 ) {
1961 self.valid
1962 .ecstate
1963 .verify(schema, &self.attrs, self.state.id, results);
1964 }
1965}
1966
1967impl<STATE> Entry<EntryValid, STATE> {
1968 fn validate(&self, schema: &dyn SchemaTransaction) -> Result<(), SchemaError> {
1969 let schema_classes = schema.get_classes();
1970 let schema_attributes = schema.get_attributes();
1971
1972 trace!(?self.attrs, "Entry::validate -> target");
1974
1975 if !self.attribute_pres(Attribute::Class) {
1977 return Err(SchemaError::NoClassFound);
1979 }
1980
1981 if self.attribute_equality(Attribute::Class, &EntryClass::Conflict.into()) {
1982 trace!("Skipping schema validation on conflict entry");
1984 return Ok(());
1985 };
1986
1987 let recycled = self.attribute_equality(Attribute::Class, &EntryClass::Recycled.into());
1989
1990 let extensible =
1993 self.attribute_equality(Attribute::Class, &EntryClass::ExtensibleObject.into());
1994
1995 let entry_classes = self.get_ava_set(Attribute::Class).ok_or_else(|| {
1996 admin_debug!("Attribute '{}' missing from entry", Attribute::Class);
1997 SchemaError::NoClassFound
1998 })?;
1999 let mut invalid_classes = Vec::with_capacity(0);
2000
2001 let mut classes: Vec<&SchemaClass> = Vec::with_capacity(entry_classes.len());
2002
2003 let entry_classes = if let Some(ec) = entry_classes.as_iutf8_set() {
2006 ec.iter()
2007 .for_each(|s| match schema_classes.get(s.as_str()) {
2008 Some(x) => classes.push(x),
2009 None => {
2010 admin_debug!("invalid class: {:?}", s);
2011 invalid_classes.push(s.to_string())
2012 }
2013 });
2014 ec
2015 } else {
2016 admin_debug!("corrupt class attribute");
2017 return Err(SchemaError::NoClassFound);
2018 };
2019
2020 if !invalid_classes.is_empty() {
2021 return Err(SchemaError::InvalidClass(invalid_classes));
2022 };
2023
2024 let supplements_classes: Vec<_> = classes
2028 .iter()
2029 .flat_map(|cls| cls.systemsupplements.iter().chain(cls.supplements.iter()))
2030 .collect();
2031
2032 let valid_supplements = if supplements_classes.is_empty() {
2034 true
2036 } else {
2037 supplements_classes
2038 .iter()
2039 .any(|class| entry_classes.contains(class.as_str()))
2040 };
2041
2042 if !valid_supplements {
2043 warn!(
2044 "Validation error, the following possible supplement classes are missing - {:?}",
2045 supplements_classes
2046 );
2047 let supplements_classes = supplements_classes.iter().map(|s| s.to_string()).collect();
2048 return Err(SchemaError::SupplementsNotSatisfied(supplements_classes));
2049 }
2050
2051 let excludes_classes: Vec<_> = classes
2052 .iter()
2053 .flat_map(|cls| cls.systemexcludes.iter().chain(cls.excludes.iter()))
2054 .collect();
2055
2056 let mut invalid_excludes = Vec::with_capacity(0);
2057
2058 excludes_classes.iter().for_each(|class| {
2059 if entry_classes.contains(class.as_str()) {
2060 invalid_excludes.push(class.to_string())
2061 }
2062 });
2063
2064 if !invalid_excludes.is_empty() {
2065 admin_warn!(
2066 "Validation error, the following excluded classes are present - {:?}",
2067 invalid_excludes
2068 );
2069 return Err(SchemaError::ExcludesNotSatisfied(invalid_excludes));
2070 }
2071
2072 let must: Result<Vec<&SchemaAttribute>, _> = classes
2085 .iter()
2086 .flat_map(|cls| cls.systemmust.iter().chain(cls.must.iter()))
2088 .map(|s| {
2089 schema_attributes.get(s).ok_or(SchemaError::Corrupted)
2092 })
2093 .collect();
2094
2095 let must = must?;
2096
2097 let mut missing_must = Vec::with_capacity(0);
2100 for attr in must.iter() {
2101 let avas = self.get_ava_set(&attr.name);
2102 if avas.is_none() {
2103 missing_must.push(attr.name.clone());
2104 }
2105 }
2106
2107 if !missing_must.is_empty() {
2108 admin_warn!(
2109 "Validation error, the following required ({}) (must) attributes are missing - {:?}",
2110 self.get_display_id(), missing_must
2111 );
2112 if !recycled {
2118 return Err(SchemaError::MissingMustAttribute(missing_must));
2119 }
2120 }
2121
2122 if extensible {
2123 self.attrs.iter().try_for_each(|(attr_name, avas)| {
2124 match schema_attributes.get(attr_name) {
2125 Some(a_schema) => {
2126 if a_schema.phantom {
2129 admin_warn!(
2130 "Rejecting attempt to add phantom attribute to extensible object: {}",
2131 attr_name
2132 );
2133 Err(SchemaError::PhantomAttribute(attr_name.to_string()))
2134 } else {
2135 a_schema.validate_ava(attr_name, avas)
2136 }
2138 }
2139 None => {
2140 admin_error!(
2141 "Invalid Attribute {}, undefined in schema_attributes",
2142 attr_name.to_string()
2143 );
2144 Err(SchemaError::InvalidAttribute(
2145 attr_name.to_string()
2146 ))
2147 }
2148 }
2149 })?;
2150 } else {
2151 let may: Result<Map<&Attribute, &SchemaAttribute>, _> = classes
2159 .iter()
2160 .flat_map(|cls| {
2162 trace!(?cls);
2163 cls.systemmust
2164 .iter()
2165 .chain(cls.must.iter())
2166 .chain(cls.systemmay.iter())
2167 .chain(cls.may.iter())
2168 })
2169 .map(|s| {
2170 Ok((s, schema_attributes.get(s).ok_or(SchemaError::Corrupted)?))
2173 })
2174 .collect();
2175
2176 let may = may?;
2177
2178 self.attrs.iter().try_for_each(|(attr_name, avas)| {
2187 match may.get(attr_name) {
2188 Some(a_schema) => {
2189 a_schema.validate_ava(attr_name, avas)
2192 }
2194 None => {
2195 admin_error!(
2196 "{} {} - not found in the list of valid attributes for this set of classes {:?} - valid attributes are {:?}",
2197
2198 attr_name.as_str(),
2199 self.get_display_id(),
2200 entry_classes.iter().collect::<Vec<_>>(),
2201 may.keys().collect::<Vec<_>>()
2202 );
2203 Err(SchemaError::AttributeNotValidForClass(
2204 attr_name.to_string()
2205 ))
2206 }
2207 }
2208 })?;
2209 }
2210
2211 Ok(())
2213 }
2214
2215 pub fn seal(mut self, schema: &dyn SchemaTransaction) -> Entry<EntrySealed, STATE> {
2216 let EntryValid { uuid, mut ecstate } = self.valid;
2217
2218 ecstate.retain(|k, _| schema.is_replicated(k));
2222
2223 let last_mod_cid = ecstate.get_max_cid();
2225 let cv = vs_cid![last_mod_cid.clone()];
2226 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
2227
2228 let create_at_cid = ecstate.at();
2232 let cv = vs_cid![create_at_cid.clone()];
2233 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
2234
2235 Entry {
2236 valid: EntrySealed { uuid, ecstate },
2237 state: self.state,
2238 attrs: self.attrs,
2239 }
2240 }
2241
2242 pub fn get_uuid(&self) -> Uuid {
2243 self.valid.uuid
2244 }
2245}
2246
2247impl<STATE> GetUuid for Entry<EntrySealed, STATE>
2248where
2249 STATE: Clone,
2250{
2251 fn get_uuid(&self) -> Uuid {
2252 self.valid.uuid
2253 }
2254}
2255
2256impl<STATE> Entry<EntrySealed, STATE>
2257where
2258 STATE: Clone,
2259{
2260 pub fn invalidate(mut self, cid: Cid, trim_cid: &Cid) -> Entry<EntryInvalid, STATE> {
2261 for vs in self.attrs.values_mut() {
2263 vs.trim(trim_cid);
2264 }
2265
2266 let last_mod_cid = self.valid.ecstate.get_max_cid();
2274 let cv = vs_cid![last_mod_cid.clone()];
2275 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
2276
2277 let create_at_cid = self.valid.ecstate.at();
2278 let cv = vs_cid![create_at_cid.clone()];
2279 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
2280
2281 Entry {
2282 valid: EntryInvalid {
2283 cid,
2284 ecstate: self.valid.ecstate,
2285 },
2286 state: self.state,
2287 attrs: self.attrs,
2288 }
2289 }
2290
2291 pub fn get_uuid(&self) -> Uuid {
2292 self.valid.uuid
2293 }
2294
2295 pub fn get_changestate(&self) -> &EntryChangeState {
2296 &self.valid.ecstate
2297 }
2298
2299 pub(crate) fn entry_changed_excluding_attribute<A: AsRef<Attribute>>(
2303 &self,
2304 attr: A,
2305 cid: &Cid,
2306 ) -> bool {
2307 let attr_ref = attr.as_ref();
2308
2309 use crate::repl::entry::State;
2310
2311 match self.get_changestate().current() {
2312 State::Live { at: _, changes } => {
2313 changes.iter().any(|(change_attr, change_id)| {
2314 change_id >= cid &&
2315 *change_attr != *attr_ref &&
2316 *change_attr != Attribute::LastModifiedCid
2318 })
2319 }
2320 State::Tombstone { at } => at == cid,
2321 }
2322 }
2323
2324 #[cfg(test)]
2328 pub(crate) fn into_invalid(mut self) -> Entry<EntryInvalid, STATE> {
2329 let cid = Cid::new_zero();
2330 self.set_last_changed(cid.clone());
2331
2332 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
2333
2334 Entry {
2335 valid: EntryInvalid { cid, ecstate },
2336 state: self.state,
2337 attrs: self.attrs,
2338 }
2339 }
2340}
2341
2342impl GetUuid for Entry<EntryReduced, EntryCommitted> {
2343 fn get_uuid(&self) -> Uuid {
2344 self.valid.uuid
2345 }
2346}
2347
2348impl Entry<EntryReduced, EntryCommitted> {
2349 pub fn get_uuid(&self) -> Uuid {
2350 self.valid.uuid
2351 }
2352
2353 pub fn to_pe(&self, qs: &mut QueryServerReadTransaction) -> Result<ProtoEntry, OperationError> {
2355 let attrs: Result<_, _> = self
2357 .attrs
2358 .iter()
2359 .map(|(k, vs)| qs.resolve_valueset(vs).map(|pvs| (k.to_string(), pvs)))
2360 .collect();
2361 Ok(ProtoEntry { attrs: attrs? })
2362 }
2363
2364 pub fn to_scim_kanidm<'a, TXN>(
2365 &self,
2366 read_txn: &mut TXN,
2367 ) -> Result<ScimEntryKanidm, OperationError>
2368 where
2369 TXN: QueryServerTransaction<'a>,
2370 {
2371 let result: Result<BTreeMap<Attribute, ScimValueKanidm>, OperationError> = self
2372 .attrs
2373 .iter()
2374 .filter(|(k, _vs)| **k != Attribute::Uuid)
2376 .filter_map(|(k, vs)| {
2377 let opt_resolve_status = vs.to_scim_value();
2378 let res_opt_scim_value = match opt_resolve_status {
2379 None => Ok(None),
2380 Some(ScimResolveStatus::Resolved(scim_value_kani)) => Ok(Some(scim_value_kani)),
2381 Some(ScimResolveStatus::NeedsResolution(scim_value_interim)) => {
2382 read_txn.resolve_scim_interim(scim_value_interim)
2383 }
2384 };
2385 res_opt_scim_value
2386 .transpose()
2387 .map(|scim_res| scim_res.map(|scim_value| (k.clone(), scim_value)))
2388 })
2389 .collect();
2390
2391 let attrs = result?;
2392
2393 let ext_access_check = self.valid.effective_access.as_ref().map(|eff_acc| {
2394 let ident = eff_acc.ident;
2395 let delete = eff_acc.delete;
2396 let search = (&eff_acc.search).into();
2397 let modify_present = (&eff_acc.modify_pres).into();
2398 let modify_remove = (&eff_acc.modify_rem).into();
2399
2400 ScimEffectiveAccess {
2401 ident,
2402 delete,
2403 search,
2404 modify_present,
2405 modify_remove,
2406 }
2407 });
2408
2409 let id = self.get_uuid();
2410
2411 let schemas = Vec::with_capacity(0);
2414
2415 Ok(ScimEntryKanidm {
2416 header: ScimEntryHeader {
2417 schemas,
2418 id,
2419 external_id: None,
2421 meta: None,
2424 },
2425 ext_access_check,
2426 attrs,
2427 })
2428 }
2429
2430 pub fn to_ldap(
2432 &self,
2433 qs: &mut QueryServerReadTransaction,
2434 basedn: &str,
2435 all_attrs: bool,
2437 l_attrs: &[String],
2440 ) -> Result<LdapSearchResultEntry, OperationError> {
2441 let rdn = qs.uuid_to_rdn(self.get_uuid())?;
2442
2443 let dn = format!("{rdn},{basedn}");
2444
2445 let attr_map: Result<Map<&str, Vec<Vec<u8>>>, _> = self
2450 .attrs
2451 .iter()
2452 .map(|(k, vs)| {
2453 qs.resolve_valueset_ldap(vs, basedn)
2454 .map(|pvs| (k.as_str(), pvs))
2455 })
2456 .collect();
2457 let attr_map = attr_map?;
2458
2459 let attr_names: Vec<(&str, &str)> = if all_attrs {
2462 self.attrs
2464 .keys()
2465 .map(|k| (k.as_str(), k.as_str()))
2466 .chain(
2467 l_attrs
2468 .iter()
2469 .map(|k| (k.as_str(), ldap_vattr_map(k.as_str()).unwrap_or(k.as_str()))),
2470 )
2471 .collect()
2472 } else {
2473 l_attrs
2475 .iter()
2476 .map(|k| (k.as_str(), ldap_vattr_map(k.as_str()).unwrap_or(k.as_str())))
2477 .collect()
2478 };
2479
2480 let attributes: Vec<_> = attr_names
2482 .into_iter()
2483 .filter_map(|(ldap_a, kani_a)| {
2484 match ldap_a {
2486 LDAP_ATTR_DN => Some(LdapPartialAttribute {
2487 atype: LDAP_ATTR_DN.to_string(),
2488 vals: vec![dn.as_bytes().to_vec()],
2489 }),
2490 LDAP_ATTR_ENTRYDN => Some(LdapPartialAttribute {
2491 atype: LDAP_ATTR_ENTRYDN.to_string(),
2492 vals: vec![dn.as_bytes().to_vec()],
2493 }),
2494 LDAP_ATTR_MAIL_PRIMARY | LDAP_ATTR_EMAIL_PRIMARY => {
2495 attr_map.get(kani_a).map(|pvs| LdapPartialAttribute {
2496 atype: ldap_a.to_string(),
2497 vals: pvs
2498 .first()
2499 .map(|first| vec![first.clone()])
2500 .unwrap_or_default(),
2501 })
2502 }
2503 LDAP_ATTR_MAIL_ALTERNATIVE | LDAP_ATTR_EMAIL_ALTERNATIVE => {
2504 attr_map.get(kani_a).map(|pvs| LdapPartialAttribute {
2505 atype: ldap_a.to_string(),
2506 vals: pvs
2507 .split_first()
2508 .map(|(_, rest)| rest.to_vec())
2509 .unwrap_or_default(),
2510 })
2511 }
2512 _ => attr_map.get(kani_a).map(|pvs| LdapPartialAttribute {
2513 atype: ldap_a.to_string(),
2514 vals: pvs.clone(),
2515 }),
2516 }
2517 })
2518 .collect();
2519
2520 Ok(LdapSearchResultEntry { dn, attributes })
2521 }
2522}
2523
2524impl<VALID, STATE> Entry<VALID, STATE> {
2526 fn add_ava_int(&mut self, attr: Attribute, value: Value) -> bool {
2531 if let Some(vs) = self.attrs.get_mut(&attr) {
2532 let r = vs.insert_checked(value);
2533 debug_assert!(r.is_ok());
2534 r.unwrap_or(false)
2536 } else {
2537 #[allow(clippy::expect_used)]
2538 let vs = valueset::from_value_iter(std::iter::once(value))
2539 .expect("Unable to fail - non-zero iter, and single value type!");
2540 self.attrs.insert(attr, vs);
2541 true
2543 }
2544 }
2546
2547 fn set_ava_iter_int<T>(&mut self, attr: Attribute, iter: T)
2549 where
2550 T: IntoIterator<Item = Value>,
2551 {
2552 let Ok(vs) = valueset::from_value_iter(iter.into_iter()) else {
2553 trace!("set_ava_iter_int - empty from_value_iter, skipping");
2554 return;
2555 };
2556
2557 if let Some(existing_vs) = self.attrs.get_mut(&attr) {
2558 let _ = existing_vs.merge(&vs);
2560 } else {
2561 self.attrs.insert(attr, vs);
2563 }
2564 }
2565
2566 #[cfg(test)]
2568 fn set_last_changed(&mut self, cid: Cid) {
2569 let cv = vs_cid![cid.clone()];
2570 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
2571 let cv = vs_cid![cid];
2572 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
2573 }
2574
2575 pub(crate) fn get_display_id(&self) -> String {
2576 self.attrs
2577 .get(&Attribute::Spn)
2578 .and_then(|vs| vs.to_value_single())
2579 .or_else(|| {
2580 self.attrs
2581 .get(&Attribute::Name)
2582 .and_then(|vs| vs.to_value_single())
2583 })
2584 .or_else(|| {
2585 self.attrs
2586 .get(&Attribute::Uuid)
2587 .and_then(|vs| vs.to_value_single())
2588 })
2589 .map(|value| value.to_proto_string_clone())
2590 .unwrap_or_else(|| "no entry id available".to_string())
2591 }
2592
2593 pub fn get_ava_names(&self) -> impl Iterator<Item = &str> {
2595 self.attrs.keys().map(|a| a.as_str())
2597 }
2598
2599 pub fn get_ava(&self) -> &Eattrs {
2601 &self.attrs
2602 }
2603
2604 pub fn get_ava_iter(&self) -> impl Iterator<Item = (&Attribute, &ValueSet)> {
2605 self.attrs.iter()
2606 }
2607
2608 pub fn get_ava_set<A: AsRef<Attribute>>(&self, attr: A) -> Option<&ValueSet> {
2610 self.attrs.get(attr.as_ref())
2611 }
2612
2613 pub fn get_ava_refer<A: AsRef<Attribute>>(&self, attr: A) -> Option<&BTreeSet<Uuid>> {
2614 self.get_ava_set(attr).and_then(|vs| vs.as_refer_set())
2615 }
2616
2617 pub fn get_ava_as_iutf8_iter<A: AsRef<Attribute>>(
2618 &self,
2619 attr: A,
2620 ) -> Option<impl Iterator<Item = &str>> {
2621 self.get_ava_set(attr).and_then(|vs| vs.as_iutf8_iter())
2622 }
2623
2624 pub fn get_ava_as_iutf8<A: AsRef<Attribute>>(&self, attr: A) -> Option<&BTreeSet<String>> {
2625 self.get_ava_set(attr).and_then(|vs| vs.as_iutf8_set())
2626 }
2627
2628 pub fn get_ava_as_image<A: AsRef<Attribute>>(&self, attr: A) -> Option<&HashSet<ImageValue>> {
2629 self.get_ava_set(attr).and_then(|vs| vs.as_imageset())
2630 }
2631
2632 pub fn get_ava_single_image<A: AsRef<Attribute>>(&self, attr: A) -> Option<ImageValue> {
2633 let images = self.get_ava_set(attr).and_then(|vs| vs.as_imageset())?;
2634 images.iter().next().cloned()
2635 }
2636
2637 pub fn get_ava_single_credential_type<A: AsRef<Attribute>>(
2638 &self,
2639 attr: A,
2640 ) -> Option<CredentialType> {
2641 self.get_ava_set(attr)
2642 .and_then(|vs| vs.to_credentialtype_single())
2643 }
2644
2645 pub fn get_ava_as_oauthscopes<A: AsRef<Attribute>>(
2646 &self,
2647 attr: A,
2648 ) -> Option<impl Iterator<Item = &str>> {
2649 self.get_ava_set(attr)
2650 .and_then(|vs| vs.as_oauthscope_iter())
2651 }
2652
2653 pub fn get_ava_as_oauthscopemaps<A: AsRef<Attribute>>(
2654 &self,
2655 attr: A,
2656 ) -> Option<&std::collections::BTreeMap<Uuid, std::collections::BTreeSet<String>>> {
2657 self.get_ava_set(attr).and_then(|vs| vs.as_oauthscopemap())
2658 }
2659
2660 pub fn get_ava_as_intenttokens<A: AsRef<Attribute>>(
2661 &self,
2662 attr: A,
2663 ) -> Option<&std::collections::BTreeMap<String, IntentTokenState>> {
2664 self.get_ava_set(attr)
2665 .and_then(|vs| vs.as_intenttoken_map())
2666 }
2667
2668 pub fn get_ava_as_session_map<A: AsRef<Attribute>>(
2669 &self,
2670 attr: A,
2671 ) -> Option<&std::collections::BTreeMap<Uuid, Session>> {
2672 self.get_ava_set(attr).and_then(|vs| vs.as_session_map())
2673 }
2674
2675 pub fn get_ava_as_apitoken_map<A: AsRef<Attribute>>(
2676 &self,
2677 attr: A,
2678 ) -> Option<&std::collections::BTreeMap<Uuid, ApiToken>> {
2679 self.get_ava_set(attr).and_then(|vs| vs.as_apitoken_map())
2680 }
2681
2682 pub fn get_ava_as_oauth2session_map<A: AsRef<Attribute>>(
2683 &self,
2684 attr: A,
2685 ) -> Option<&std::collections::BTreeMap<Uuid, Oauth2Session>> {
2686 self.get_ava_set(attr)
2687 .and_then(|vs| vs.as_oauth2session_map())
2688 }
2689
2690 pub fn get_ava_iter_iname<A: AsRef<Attribute>>(
2692 &self,
2693 attr: A,
2694 ) -> Option<impl Iterator<Item = &str>> {
2695 self.get_ava_set(attr).and_then(|vs| vs.as_iname_iter())
2696 }
2697
2698 pub fn get_ava_iter_iutf8<A: AsRef<Attribute>>(
2700 &self,
2701 attr: A,
2702 ) -> Option<impl Iterator<Item = &str>> {
2703 self.get_ava_set(attr).and_then(|vs| vs.as_iutf8_iter())
2704 }
2705
2706 pub fn get_ava_as_refuuid<A: AsRef<Attribute>>(
2708 &self,
2709 attr: A,
2710 ) -> Option<Box<dyn Iterator<Item = Uuid> + '_>> {
2711 self.get_ava_set(attr).and_then(|vs| vs.as_ref_uuid_iter())
2713 }
2714
2715 pub fn get_ava_iter_sshpubkeys<A: AsRef<Attribute>>(
2717 &self,
2718 attr: A,
2719 ) -> Option<impl Iterator<Item = String> + '_> {
2720 self.get_ava_set(attr)
2721 .and_then(|vs| vs.as_sshpubkey_string_iter())
2722 }
2723
2724 pub fn get_ava_single<A: AsRef<Attribute>>(&self, attr: A) -> Option<Value> {
2730 self.get_ava_set(attr).and_then(|vs| vs.to_value_single())
2731 }
2732
2733 pub fn get_ava_single_proto_string<A: AsRef<Attribute>>(&self, attr: A) -> Option<String> {
2734 self.get_ava_set(attr)
2735 .and_then(|vs| vs.to_proto_string_single())
2736 }
2737
2738 pub fn get_ava_single_bool<A: AsRef<Attribute>>(&self, attr: A) -> Option<bool> {
2740 self.get_ava_set(attr).and_then(|vs| vs.to_bool_single())
2741 }
2742
2743 pub fn get_ava_single_uint32<A: AsRef<Attribute>>(&self, attr: A) -> Option<u32> {
2745 self.get_ava_set(attr).and_then(|vs| vs.to_uint32_single())
2746 }
2747
2748 pub fn get_ava_single_syntax<A: AsRef<Attribute>>(&self, attr: A) -> Option<SyntaxType> {
2750 self.get_ava_set(attr)
2751 .and_then(|vs| vs.to_syntaxtype_single())
2752 }
2753
2754 pub fn get_ava_single_credential<A: AsRef<Attribute>>(&self, attr: A) -> Option<&Credential> {
2756 self.get_ava_set(attr)
2757 .and_then(|vs| vs.to_credential_single())
2758 }
2759
2760 pub fn get_ava_passkeys<A: AsRef<Attribute>>(
2762 &self,
2763 attr: A,
2764 ) -> Option<&BTreeMap<Uuid, (String, PasskeyV4)>> {
2765 self.get_ava_set(attr).and_then(|vs| vs.as_passkey_map())
2766 }
2767
2768 pub fn get_ava_attestedpasskeys<A: AsRef<Attribute>>(
2770 &self,
2771 attr: A,
2772 ) -> Option<&BTreeMap<Uuid, (String, AttestedPasskeyV4)>> {
2773 self.get_ava_set(attr)
2774 .and_then(|vs| vs.as_attestedpasskey_map())
2775 }
2776
2777 pub fn get_ava_uihint<A: AsRef<Attribute>>(&self, attr: A) -> Option<&BTreeSet<UiHint>> {
2779 self.get_ava_set(attr).and_then(|vs| vs.as_uihint_set())
2780 }
2781
2782 pub fn get_ava_single_secret<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2784 self.get_ava_set(attr).and_then(|vs| vs.to_secret_single())
2785 }
2786
2787 pub fn get_ava_single_datetime<A: AsRef<Attribute>>(&self, attr: A) -> Option<OffsetDateTime> {
2789 self.get_ava_set(attr)
2790 .and_then(|vs| vs.to_datetime_single())
2791 }
2792
2793 pub(crate) fn get_ava_single_utf8<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2795 self.get_ava_set(attr).and_then(|vs| vs.to_utf8_single())
2796 }
2797
2798 pub(crate) fn get_ava_single_iutf8<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2800 self.get_ava_set(attr).and_then(|vs| vs.to_iutf8_single())
2801 }
2802
2803 pub(crate) fn get_ava_single_iname<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2805 self.get_ava_set(attr).and_then(|vs| vs.to_iname_single())
2806 }
2807
2808 pub fn get_ava_single_url<A: AsRef<Attribute>>(&self, attr: A) -> Option<&Url> {
2810 self.get_ava_set(attr).and_then(|vs| vs.to_url_single())
2811 }
2812
2813 pub fn get_ava_single_uuid<A: AsRef<Attribute>>(&self, attr: A) -> Option<Uuid> {
2814 self.get_ava_set(attr).and_then(|vs| vs.to_uuid_single())
2815 }
2816
2817 pub fn get_ava_single_refer<A: AsRef<Attribute>>(&self, attr: A) -> Option<Uuid> {
2818 self.get_ava_set(attr).and_then(|vs| vs.to_refer_single())
2819 }
2820
2821 pub fn get_ava_mail_primary<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2822 self.get_ava_set(attr)
2823 .and_then(|vs| vs.to_email_address_primary_str())
2824 }
2825
2826 pub fn get_ava_iter_mail<A: AsRef<Attribute>>(
2827 &self,
2828 attr: A,
2829 ) -> Option<impl Iterator<Item = &str>> {
2830 self.get_ava_set(attr).and_then(|vs| vs.as_email_str_iter())
2831 }
2832
2833 pub fn get_ava_single_protofilter<A: AsRef<Attribute>>(&self, attr: A) -> Option<&ProtoFilter> {
2835 self.get_ava_set(attr)
2836 .and_then(|vs| vs.to_json_filter_single())
2837 }
2838
2839 pub fn get_ava_single_private_binary<A: AsRef<Attribute>>(&self, attr: A) -> Option<&[u8]> {
2840 self.get_ava_set(attr)
2841 .and_then(|vs| vs.to_private_binary_single())
2842 }
2843
2844 pub fn get_ava_single_jws_key_es256<A: AsRef<Attribute>>(
2845 &self,
2846 attr: A,
2847 ) -> Option<&JwsEs256Signer> {
2848 self.get_ava_set(attr)
2849 .and_then(|vs| vs.to_jws_key_es256_single())
2850 }
2851
2852 pub fn get_ava_single_eckey_private<A: AsRef<Attribute>>(
2853 &self,
2854 attr: A,
2855 ) -> Option<&EcKey<Private>> {
2856 self.get_ava_set(attr)
2857 .and_then(|vs| vs.to_eckey_private_single())
2858 }
2859
2860 pub fn get_ava_single_eckey_public<A: AsRef<Attribute>>(
2861 &self,
2862 attr: A,
2863 ) -> Option<&EcKey<Public>> {
2864 self.get_ava_set(attr)
2865 .and_then(|vs| vs.to_eckey_public_single())
2866 }
2867
2868 pub fn get_ava_webauthn_attestation_ca_list<A: AsRef<Attribute>>(
2869 &self,
2870 attr: A,
2871 ) -> Option<&AttestationCaList> {
2872 self.get_ava_set(attr)
2873 .and_then(|vs| vs.as_webauthn_attestation_ca_list())
2874 }
2875
2876 pub fn get_ava_application_password<A: AsRef<Attribute>>(
2877 &self,
2878 attr: A,
2879 ) -> Option<&BTreeMap<Uuid, Vec<ApplicationPassword>>> {
2880 self.get_ava_set(attr)
2881 .and_then(|vs| vs.as_application_password_map())
2882 }
2883
2884 pub(crate) fn generate_spn(&self, domain_name: &str) -> Option<Value> {
2886 self.get_ava_single_iname(Attribute::Name)
2887 .map(|name| Value::new_spn_str(name, domain_name))
2888 }
2889
2890 pub fn attribute_pres<A: AsRef<Attribute>>(&self, attr: A) -> bool {
2892 self.attrs.contains_key(attr.as_ref())
2893 }
2894
2895 pub fn attribute_equality<A: AsRef<Attribute>>(&self, attr: A, value: &PartialValue) -> bool {
2898 match self.attrs.get(attr.as_ref()) {
2903 Some(v_list) => v_list.contains(value),
2904 None => false,
2905 }
2906 }
2907
2908 pub fn attribute_substring<A: AsRef<Attribute>>(
2911 &self,
2912 attr: A,
2913 subvalue: &PartialValue,
2914 ) -> bool {
2915 self.get_ava_set(attr)
2916 .map(|vset| vset.substring(subvalue))
2917 .unwrap_or(false)
2918 }
2919
2920 pub fn attribute_startswith<A: AsRef<Attribute>>(
2923 &self,
2924 attr: A,
2925 subvalue: &PartialValue,
2926 ) -> bool {
2927 self.get_ava_set(attr)
2928 .map(|vset| vset.startswith(subvalue))
2929 .unwrap_or(false)
2930 }
2931
2932 pub fn attribute_endswith<A: AsRef<Attribute>>(
2935 &self,
2936 attr: A,
2937 subvalue: &PartialValue,
2938 ) -> bool {
2939 self.get_ava_set(attr)
2940 .map(|vset| vset.endswith(subvalue))
2941 .unwrap_or(false)
2942 }
2943
2944 pub fn attribute_lessthan<A: AsRef<Attribute>>(
2947 &self,
2948 attr: A,
2949 subvalue: &PartialValue,
2950 ) -> bool {
2951 self.get_ava_set(attr)
2952 .map(|vset| vset.lessthan(subvalue))
2953 .unwrap_or(false)
2954 }
2955
2956 #[inline(always)]
2961 #[instrument(level = "trace", name = "entry::entry_match_no_index", skip(self))]
2962 pub fn entry_match_no_index(&self, filter: &Filter<FilterValidResolved>) -> bool {
2964 self.entry_match_no_index_inner(filter.to_inner())
2965 }
2966
2967 fn entry_match_no_index_inner(&self, filter: &FilterResolved) -> bool {
2971 match filter {
2974 FilterResolved::Eq(attr, value, _) => self.attribute_equality(attr, value),
2975 FilterResolved::Cnt(attr, subvalue, _) => self.attribute_substring(attr, subvalue),
2976 FilterResolved::Stw(attr, subvalue, _) => self.attribute_startswith(attr, subvalue),
2977 FilterResolved::Enw(attr, subvalue, _) => self.attribute_endswith(attr, subvalue),
2978 FilterResolved::Pres(attr, _) => self.attribute_pres(attr),
2979 FilterResolved::LessThan(attr, subvalue, _) => self.attribute_lessthan(attr, subvalue),
2980 FilterResolved::Or(l, _) => l.iter().any(|f| self.entry_match_no_index_inner(f)),
2982 FilterResolved::And(l, _) => l.iter().all(|f| self.entry_match_no_index_inner(f)),
2984 FilterResolved::Inclusion(_, _) => {
2985 false
2989 }
2990 FilterResolved::AndNot(f, _) => !self.entry_match_no_index_inner(f),
2991 FilterResolved::Invalid(_) => false,
2992 }
2993 }
2994
2995 pub fn filter_from_attrs(&self, attrs: &[Attribute]) -> Option<Filter<FilterInvalid>> {
2998 let mut pairs: Vec<(Attribute, PartialValue)> = Vec::with_capacity(0);
3010
3011 for attr in attrs {
3012 match self.attrs.get(attr) {
3013 Some(values) => values
3014 .to_partialvalue_iter()
3015 .for_each(|pv| pairs.push((attr.clone(), pv))),
3016 None => return None,
3017 }
3018 }
3019
3020 let res: Vec<FC> = pairs
3021 .into_iter()
3022 .map(|(attr, pv)| FC::Eq(attr, pv))
3023 .collect();
3024 Some(filter_all!(f_and(res)))
3025 }
3026
3027 pub fn gen_modlist_assert(
3030 &self,
3031 schema: &dyn SchemaTransaction,
3032 ) -> Result<ModifyList<ModifyInvalid>, SchemaError> {
3033 let mut mods = ModifyList::new();
3037
3038 for (k, vs) in self.attrs.iter() {
3039 if *k == Attribute::Uuid {
3055 continue;
3056 }
3057 match schema.is_multivalue(k) {
3059 Ok(r) => {
3060 if !r ||
3063 *k == Attribute::AcpReceiverGroup ||
3066 *k == Attribute::AcpCreateAttr ||
3067 *k == Attribute::AcpCreateClass ||
3068 *k == Attribute::AcpModifyPresentAttr ||
3069 *k == Attribute::AcpModifyRemovedAttr ||
3070 *k == Attribute::AcpModifyClass ||
3071 *k == Attribute::SystemMust ||
3072 *k == Attribute::SystemMay
3073 {
3074 mods.push_mod(Modify::Purged(k.clone()));
3075 }
3076 }
3077 Err(e) => return Err(e),
3079 }
3080 for v in vs.to_value_iter() {
3081 mods.push_mod(Modify::Present(k.clone(), v.clone()));
3082 }
3083 }
3084
3085 Ok(mods)
3086 }
3087
3088 pub fn mask_recycled_ts(&self) -> Option<&Self> {
3091 match self.attrs.get(&Attribute::Class) {
3093 Some(cls) => {
3094 if cls.contains(&EntryClass::Tombstone.to_partialvalue())
3095 || cls.contains(&EntryClass::Recycled.to_partialvalue())
3096 {
3097 None
3098 } else {
3099 Some(self)
3100 }
3101 }
3102 None => Some(self),
3103 }
3104 }
3105
3106 pub fn mask_recycled(&self) -> Option<&Self> {
3109 match self.attrs.get(&Attribute::Class) {
3111 Some(cls) => {
3112 if cls.contains(&EntryClass::Recycled.to_partialvalue()) {
3113 None
3114 } else {
3115 Some(self)
3116 }
3117 }
3118 None => Some(self),
3119 }
3120 }
3121
3122 pub fn mask_tombstone(&self) -> Option<&Self> {
3125 match self.attrs.get(&Attribute::Class) {
3127 Some(cls) => {
3128 if cls.contains(&EntryClass::Tombstone.to_partialvalue()) {
3129 None
3130 } else {
3131 Some(self)
3132 }
3133 }
3134 None => Some(self),
3135 }
3136 }
3137}
3138
3139impl<STATE> Entry<EntryInvalid, STATE>
3140where
3141 STATE: Clone,
3142{
3143 pub fn add_ava(&mut self, attr: Attribute, value: Value) {
3148 self.valid.ecstate.change_ava(&self.valid.cid, &attr);
3149 self.add_ava_int(attr, value);
3150 }
3151
3152 pub fn add_ava_if_not_exist<A: AsRef<Attribute>>(&mut self, attr: A, value: Value) {
3153 let attr_ref = attr.as_ref();
3154 if self.add_ava_int(attr_ref.clone(), value) {
3156 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3158 }
3159 }
3160
3161 fn assert_ava<A: AsRef<Attribute>>(
3162 &mut self,
3163 attr: A,
3164 value: &PartialValue,
3165 ) -> Result<(), OperationError> {
3166 self.valid
3167 .ecstate
3168 .change_ava(&self.valid.cid, attr.as_ref());
3169
3170 if self.attribute_equality(attr, value) {
3171 Ok(())
3172 } else {
3173 Err(OperationError::ModifyAssertionFailed)
3174 }
3175 }
3176
3177 pub(crate) fn remove_ava<A: AsRef<Attribute>>(&mut self, attr: A, value: &PartialValue) {
3180 let attr_ref = attr.as_ref();
3181 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3182
3183 let rm = if let Some(vs) = self.attrs.get_mut(attr_ref) {
3184 vs.remove(value, &self.valid.cid);
3185 vs.is_empty()
3186 } else {
3187 false
3188 };
3189 if rm {
3190 self.attrs.remove(attr_ref);
3191 };
3192 }
3193
3194 pub(crate) fn remove_avas<A: AsRef<Attribute>>(
3195 &mut self,
3196 attr: A,
3197 values: &BTreeSet<PartialValue>,
3198 ) {
3199 let attr_ref = attr.as_ref();
3200 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3201
3202 let rm = if let Some(vs) = self.attrs.get_mut(attr_ref) {
3203 values.iter().for_each(|k| {
3204 vs.remove(k, &self.valid.cid);
3205 });
3206 vs.is_empty()
3207 } else {
3208 false
3209 };
3210 if rm {
3211 self.attrs.remove(attr_ref);
3212 };
3213 }
3214
3215 pub(crate) fn purge_ava<A: AsRef<Attribute>>(&mut self, attr: A) {
3218 let attr_ref = attr.as_ref();
3219 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3220 let can_remove = self
3223 .attrs
3224 .get_mut(attr_ref)
3225 .map(|vs| vs.purge(&self.valid.cid))
3226 .unwrap_or_default();
3228 if can_remove {
3229 self.attrs.remove(attr_ref);
3230 }
3231 }
3232
3233 pub fn pop_ava<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<ValueSet> {
3235 let attr_ref = attr.as_ref();
3236 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3237
3238 let mut vs = self.attrs.remove(attr_ref)?;
3239 if vs.purge(&self.valid.cid) {
3240 Some(vs)
3242 } else {
3243 let r_vs = vs.clone();
3245 self.attrs.insert(attr_ref.clone(), vs);
3246 Some(r_vs)
3247 }
3248 }
3249
3250 #[cfg(test)]
3255 pub(crate) fn force_trim_ava<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<ValueSet> {
3256 self.valid
3257 .ecstate
3258 .change_ava(&self.valid.cid, attr.as_ref());
3259 self.attrs.remove(attr.as_ref())
3260 }
3261
3262 pub fn set_ava<T>(&mut self, attr: &Attribute, iter: T)
3265 where
3266 T: Clone + IntoIterator<Item = Value>,
3267 {
3268 self.purge_ava(attr);
3269 self.set_ava_iter_int(attr.clone(), iter)
3270 }
3271
3272 pub fn set_ava_set(&mut self, attr: &Attribute, vs: ValueSet) {
3275 self.purge_ava(attr);
3276 if let Some(existing_vs) = self.attrs.get_mut(attr) {
3277 let _ = existing_vs.merge(&vs);
3278 } else {
3279 self.attrs.insert(attr.clone(), vs);
3280 }
3281 }
3282
3283 pub fn merge_ava_set(&mut self, attr: &Attribute, vs: ValueSet) -> Result<(), OperationError> {
3286 self.valid.ecstate.change_ava(&self.valid.cid, attr);
3287 if let Some(existing_vs) = self.attrs.get_mut(attr) {
3288 existing_vs.merge(&vs)
3289 } else {
3290 self.attrs.insert(attr.clone(), vs);
3291 Ok(())
3292 }
3293 }
3294
3295 pub fn apply_modlist(
3297 &mut self,
3298 modlist: &ModifyList<ModifyValid>,
3299 ) -> Result<(), OperationError> {
3300 for modify in modlist {
3301 match modify {
3302 Modify::Present(attr, value) => {
3303 self.add_ava(attr.clone(), value.clone());
3304 }
3305 Modify::Removed(attr, value) => {
3306 self.remove_ava(attr, value);
3307 }
3308 Modify::Purged(attr) => {
3309 self.purge_ava(attr);
3310 }
3311 Modify::Assert(attr, value) => {
3312 self.assert_ava(attr, value).inspect_err(|_e| {
3313 error!("Modification assertion was not met. {} {:?}", attr, value);
3314 })?;
3315 }
3316 Modify::Set(attr, valueset) => self.set_ava_set(attr, valueset.clone()),
3317 }
3318 }
3319 Ok(())
3320 }
3321}
3322
3323impl<VALID, STATE> PartialEq for Entry<VALID, STATE> {
3324 fn eq(&self, rhs: &Entry<VALID, STATE>) -> bool {
3325 compare_attrs(&self.attrs, &rhs.attrs)
3336 }
3337}
3338
3339#[cfg(test)]
3340mod tests {
3341 use crate::prelude::*;
3342 use std::collections::BTreeSet as Set;
3343
3344 use hashbrown::HashMap;
3345
3346 use crate::be::{IdxKey, IdxSlope};
3347 use crate::entry::{Entry, EntryInit, EntryInvalid, EntryNew};
3348 use crate::modify::{Modify, ModifyList};
3349 use crate::value::{IndexType, PartialValue, Value};
3350
3351 #[test]
3352 fn test_entry_basic() {
3353 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3354
3355 e.add_ava(Attribute::UserId, Value::from("william"));
3356 }
3357
3358 #[test]
3359 fn test_entry_dup_value() {
3360 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3367 e.add_ava(Attribute::UserId, Value::from("william"));
3368 e.add_ava(Attribute::UserId, Value::from("william"));
3369
3370 let values = e.get_ava_set(Attribute::UserId).expect("Failed to get ava");
3371 assert_eq!(values.len(), 1)
3373 }
3374
3375 #[test]
3376 fn test_entry_pres() {
3377 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3378 e.add_ava(Attribute::UserId, Value::from("william"));
3379
3380 assert!(e.attribute_pres(Attribute::UserId));
3381 assert!(!e.attribute_pres(Attribute::Name));
3382 }
3383
3384 #[test]
3385 fn test_entry_equality() {
3386 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3387
3388 e.add_ava(Attribute::UserId, Value::from("william"));
3389
3390 assert!(e.attribute_equality(Attribute::UserId, &PartialValue::new_utf8s("william")));
3391 assert!(!e.attribute_equality(Attribute::UserId, &PartialValue::new_utf8s("test")));
3392 assert!(!e.attribute_equality(Attribute::NonExist, &PartialValue::new_utf8s("william")));
3393 assert!(!e.attribute_equality(Attribute::UserId, &PartialValue::new_iutf8("william")));
3395 }
3396
3397 #[test]
3398 fn test_entry_substring() {
3399 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3400
3401 e.add_ava(Attribute::UserId, Value::from("william"));
3402
3403 assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("william")));
3404 assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("will")));
3405 assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("liam")));
3406 assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("lli")));
3407 assert!(!e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("llim")));
3408 assert!(!e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("bob")));
3409 assert!(!e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("wl")));
3410
3411 assert!(e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("will")));
3412 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("liam")));
3413 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("lli")));
3414 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("llim")));
3415 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("bob")));
3416 assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("wl")));
3417
3418 assert!(e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("liam")));
3419 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("will")));
3420 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("lli")));
3421 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("llim")));
3422 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("bob")));
3423 assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("wl")));
3424 }
3425
3426 #[test]
3427 fn test_entry_lessthan() {
3428 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3429
3430 let pv2 = PartialValue::new_uint32(2);
3431 let pv8 = PartialValue::new_uint32(8);
3432 let pv10 = PartialValue::new_uint32(10);
3433 let pv15 = PartialValue::new_uint32(15);
3434
3435 e1.add_ava(Attribute::TestAttr, Value::new_uint32(10));
3436
3437 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv2));
3438 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv8));
3439 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv10));
3440 assert!(e1.attribute_lessthan(Attribute::TestAttr, &pv15));
3441
3442 e1.add_ava(Attribute::TestAttr, Value::new_uint32(8));
3443
3444 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv2));
3445 assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv8));
3446 assert!(e1.attribute_lessthan(Attribute::TestAttr, &pv10));
3447 assert!(e1.attribute_lessthan(Attribute::TestAttr, &pv15));
3448 }
3449
3450 #[test]
3451 fn test_entry_apply_modlist() {
3452 let mut e: Entry<EntryInvalid, EntryNew> = Entry::new().into_invalid_new();
3454
3455 e.add_ava(Attribute::UserId, Value::from("william"));
3456
3457 let present_single_mods = ModifyList::new_valid_list(vec![Modify::Present(
3458 Attribute::Attr,
3459 Value::new_iutf8("value"),
3460 )]);
3461
3462 assert!(e.apply_modlist(&present_single_mods).is_ok());
3463
3464 assert!(e.attribute_equality(Attribute::UserId, &PartialValue::new_utf8s("william")));
3466 assert!(e.attribute_equality(Attribute::Attr, &PartialValue::new_iutf8("value")));
3467
3468 let present_multivalue_mods = ModifyList::new_valid_list(vec![
3470 Modify::Present(Attribute::Class, Value::new_iutf8("test")),
3471 Modify::Present(Attribute::Class, Value::new_iutf8("multi_test")),
3472 ]);
3473
3474 assert!(e.apply_modlist(&present_multivalue_mods).is_ok());
3475
3476 assert!(e.attribute_equality(Attribute::Class, &PartialValue::new_iutf8("test")));
3477 assert!(e.attribute_equality(Attribute::Class, &PartialValue::new_iutf8("multi_test")));
3478
3479 let purge_single_mods = ModifyList::new_valid_list(vec![Modify::Purged(Attribute::Attr)]);
3481
3482 assert!(e.apply_modlist(&purge_single_mods).is_ok());
3483
3484 assert!(!e.attribute_pres(Attribute::Attr));
3485
3486 let purge_multi_mods = ModifyList::new_valid_list(vec![Modify::Purged(Attribute::Class)]);
3487
3488 assert!(e.apply_modlist(&purge_multi_mods).is_ok());
3489
3490 assert!(!e.attribute_pres(Attribute::Class));
3491
3492 let purge_empty_mods = purge_single_mods;
3493
3494 assert!(e.apply_modlist(&purge_empty_mods).is_ok());
3495
3496 let remove_mods = ModifyList::new_valid_list(vec![Modify::Removed(
3498 Attribute::Attr,
3499 PartialValue::new_iutf8("value"),
3500 )]);
3501
3502 assert!(e.apply_modlist(&present_single_mods).is_ok());
3503 assert!(e.attribute_equality(Attribute::Attr, &PartialValue::new_iutf8("value")));
3504 assert!(e.apply_modlist(&remove_mods).is_ok());
3505 assert!(!e.attrs.contains_key(&Attribute::Attr));
3506
3507 let remove_empty_mods = remove_mods;
3508
3509 assert!(e.apply_modlist(&remove_empty_mods).is_ok());
3510
3511 assert!(!e.attrs.contains_key(&Attribute::Attr));
3512 }
3513
3514 #[test]
3515 fn test_entry_idx_diff() {
3516 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3517 e1.add_ava(Attribute::UserId, Value::from("william"));
3518 let mut e1_mod = e1.clone();
3519 e1_mod.add_ava(Attribute::Extra, Value::from("test"));
3520
3521 let e1 = e1.into_sealed_committed();
3522 let e1_mod = e1_mod.into_sealed_committed();
3523
3524 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3525 e2.add_ava(Attribute::UserId, Value::from("claire"));
3526 let e2 = e2.into_sealed_committed();
3527
3528 let mut idxmeta = HashMap::with_capacity(8);
3529 idxmeta.insert(
3530 IdxKey {
3531 attr: Attribute::UserId,
3532 itype: IndexType::Equality,
3533 },
3534 IdxSlope::MAX,
3535 );
3536 idxmeta.insert(
3537 IdxKey {
3538 attr: Attribute::UserId,
3539 itype: IndexType::Presence,
3540 },
3541 IdxSlope::MAX,
3542 );
3543 idxmeta.insert(
3544 IdxKey {
3545 attr: Attribute::Extra,
3546 itype: IndexType::Equality,
3547 },
3548 IdxSlope::MAX,
3549 );
3550
3551 let r1 = Entry::idx_diff(&idxmeta, None, None);
3553 eprintln!("{r1:?}");
3554 assert_eq!(r1, Vec::with_capacity(0));
3555
3556 let mut del_r = Entry::idx_diff(&idxmeta, Some(&e1), None);
3558 del_r.sort_unstable();
3559 eprintln!("del_r {del_r:?}");
3560 assert!(
3561 del_r[0]
3562 == Err((
3563 &Attribute::UserId,
3564 IndexType::Equality,
3565 "william".to_string()
3566 ))
3567 );
3568 assert!(del_r[1] == Err((&Attribute::UserId, IndexType::Presence, "_".to_string())));
3569
3570 let mut add_r = Entry::idx_diff(&idxmeta, None, Some(&e1));
3572 add_r.sort_unstable();
3573 eprintln!("{add_r:?}");
3574 assert!(
3575 add_r[0]
3576 == Ok((
3577 &Attribute::UserId,
3578 IndexType::Equality,
3579 "william".to_string()
3580 ))
3581 );
3582 assert!(add_r[1] == Ok((&Attribute::UserId, IndexType::Presence, "_".to_string())));
3583
3584 let no_r = Entry::idx_diff(&idxmeta, Some(&e1), Some(&e1));
3588 assert!(no_r.is_empty());
3589
3590 let add_a_r = Entry::idx_diff(&idxmeta, Some(&e1), Some(&e1_mod));
3592 assert!(add_a_r[0] == Ok((&Attribute::Extra, IndexType::Equality, "test".to_string())));
3593
3594 let del_a_r = Entry::idx_diff(&idxmeta, Some(&e1_mod), Some(&e1));
3596 assert!(del_a_r[0] == Err((&Attribute::Extra, IndexType::Equality, "test".to_string())));
3597
3598 let mut chg_r = Entry::idx_diff(&idxmeta, Some(&e1), Some(&e2));
3600 chg_r.sort_unstable();
3601 eprintln!("{chg_r:?}");
3602 assert!(
3603 chg_r[1]
3604 == Err((
3605 &Attribute::UserId,
3606 IndexType::Equality,
3607 "william".to_string()
3608 ))
3609 );
3610
3611 assert!(
3612 chg_r[0]
3613 == Ok((
3614 &Attribute::UserId,
3615 IndexType::Equality,
3616 "claire".to_string()
3617 ))
3618 );
3619 }
3620
3621 #[test]
3622 fn test_entry_mask_recycled_ts() {
3623 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3624 e1.add_ava(Attribute::Class, EntryClass::Person.to_value());
3625 let e1 = e1.into_sealed_committed();
3626 assert!(e1.mask_recycled_ts().is_some());
3627
3628 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3629 e2.add_ava(Attribute::Class, EntryClass::Person.to_value());
3630 e2.add_ava(Attribute::Class, EntryClass::Recycled.into());
3631 let e2 = e2.into_sealed_committed();
3632 assert!(e2.mask_recycled_ts().is_none());
3633
3634 let mut e3: Entry<EntryInit, EntryNew> = Entry::new();
3635 e3.add_ava(Attribute::Class, EntryClass::Tombstone.into());
3636 let e3 = e3.into_sealed_committed();
3637 assert!(e3.mask_recycled_ts().is_none());
3638 }
3639
3640 #[test]
3641 fn test_entry_idx_name2uuid_diff() {
3642 let r = Entry::idx_name2uuid_diff(None, None);
3644 assert_eq!(r, (None, None));
3645
3646 {
3648 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3649 e.add_ava(Attribute::Class, EntryClass::Person.to_value());
3650 let e = e.into_sealed_committed();
3651
3652 assert!(Entry::idx_name2uuid_diff(None, Some(&e)) == (Some(Set::new()), None));
3653 }
3654
3655 {
3656 let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3657 e.add_ava(Attribute::Class, EntryClass::Person.to_value());
3658 e.add_ava(Attribute::GidNumber, Value::new_uint32(1300));
3659 e.add_ava(Attribute::Name, Value::new_iname("testperson"));
3660 e.add_ava(
3661 Attribute::Spn,
3662 Value::new_spn_str("testperson", "example.com"),
3663 );
3664 e.add_ava(
3665 Attribute::Uuid,
3666 Value::Uuid(uuid!("9fec0398-c46c-4df4-9df5-b0016f7d563f")),
3667 );
3668 let e = e.into_sealed_committed();
3669
3670 assert!(
3672 Entry::idx_name2uuid_diff(None, Some(&e))
3673 == (
3674 Some(btreeset![
3675 "1300".to_string(),
3676 "testperson".to_string(),
3677 "testperson@example.com".to_string()
3678 ]),
3679 None
3680 )
3681 );
3682 assert!(
3685 Entry::idx_name2uuid_diff(Some(&e), None)
3686 == (
3687 None,
3688 Some(btreeset![
3689 "1300".to_string(),
3690 "testperson".to_string(),
3691 "testperson@example.com".to_string()
3692 ])
3693 )
3694 );
3695
3696 assert!(
3698 Entry::idx_name2uuid_diff(Some(&e), Some(&e))
3699 == (Some(Set::new()), Some(Set::new()))
3700 );
3701 }
3702 {
3705 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3706 e1.add_ava(Attribute::Class, EntryClass::Person.to_value());
3707 e1.add_ava(
3708 Attribute::Spn,
3709 Value::new_spn_str("testperson", "example.com"),
3710 );
3711 let e1 = e1.into_sealed_committed();
3712
3713 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3714 e2.add_ava(Attribute::Class, EntryClass::Person.to_value());
3715 e2.add_ava(Attribute::Name, Value::new_iname("testperson"));
3716 e2.add_ava(
3717 Attribute::Spn,
3718 Value::new_spn_str("testperson", "example.com"),
3719 );
3720 let e2 = e2.into_sealed_committed();
3721
3722 assert!(
3724 Entry::idx_name2uuid_diff(Some(&e1), Some(&e2))
3725 == (Some(btreeset!["testperson".to_string()]), Some(Set::new()))
3726 );
3727
3728 assert!(
3730 Entry::idx_name2uuid_diff(Some(&e2), Some(&e1))
3731 == (Some(Set::new()), Some(btreeset!["testperson".to_string()]))
3732 );
3733 }
3734
3735 {
3737 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3738 e1.add_ava(Attribute::Class, EntryClass::Person.to_value());
3739 e1.add_ava(
3740 Attribute::Spn,
3741 Value::new_spn_str("testperson", "example.com"),
3742 );
3743 let e1 = e1.into_sealed_committed();
3744
3745 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3746 e2.add_ava(Attribute::Class, EntryClass::Person.to_value());
3747 e2.add_ava(
3748 Attribute::Spn,
3749 Value::new_spn_str("renameperson", "example.com"),
3750 );
3751 let e2 = e2.into_sealed_committed();
3752
3753 assert!(
3754 Entry::idx_name2uuid_diff(Some(&e1), Some(&e2))
3755 == (
3756 Some(btreeset!["renameperson@example.com".to_string()]),
3757 Some(btreeset!["testperson@example.com".to_string()])
3758 )
3759 );
3760 }
3761 }
3762
3763 #[test]
3764 fn test_entry_idx_uuid2spn_diff() {
3765 assert!(Entry::idx_uuid2spn_diff(None, None).is_none());
3766
3767 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3768 e1.add_ava(
3769 Attribute::Spn,
3770 Value::new_spn_str("testperson", "example.com"),
3771 );
3772 let e1 = e1.into_sealed_committed();
3773
3774 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3775 e2.add_ava(
3776 Attribute::Spn,
3777 Value::new_spn_str("renameperson", "example.com"),
3778 );
3779 let e2 = e2.into_sealed_committed();
3780
3781 assert!(
3782 Entry::idx_uuid2spn_diff(None, Some(&e1))
3783 == Some(Ok(Value::new_spn_str("testperson", "example.com")))
3784 );
3785 assert!(Entry::idx_uuid2spn_diff(Some(&e1), None) == Some(Err(())));
3786 assert!(Entry::idx_uuid2spn_diff(Some(&e1), Some(&e1)).is_none());
3787 assert!(
3788 Entry::idx_uuid2spn_diff(Some(&e1), Some(&e2))
3789 == Some(Ok(Value::new_spn_str("renameperson", "example.com")))
3790 );
3791 }
3792
3793 #[test]
3794 fn test_entry_idx_uuid2rdn_diff() {
3795 assert!(Entry::idx_uuid2rdn_diff(None, None).is_none());
3796
3797 let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3798 e1.add_ava(
3799 Attribute::Spn,
3800 Value::new_spn_str("testperson", "example.com"),
3801 );
3802 let e1 = e1.into_sealed_committed();
3803
3804 let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3805 e2.add_ava(
3806 Attribute::Spn,
3807 Value::new_spn_str("renameperson", "example.com"),
3808 );
3809 let e2 = e2.into_sealed_committed();
3810
3811 assert!(
3812 Entry::idx_uuid2rdn_diff(None, Some(&e1))
3813 == Some(Ok("spn=testperson@example.com".to_string()))
3814 );
3815 assert!(Entry::idx_uuid2rdn_diff(Some(&e1), None) == Some(Err(())));
3816 assert!(Entry::idx_uuid2rdn_diff(Some(&e1), Some(&e1)).is_none());
3817 assert!(
3818 Entry::idx_uuid2rdn_diff(Some(&e1), Some(&e2))
3819 == Some(Ok("spn=renameperson@example.com".to_string()))
3820 );
3821 }
3822}