1use crate::be::dbentry::{DbEntry, DbEntryVers};
28use crate::be::dbvalue::DbValueSetV2;
29use crate::be::{IdxKey, IdxSlope};
30use crate::credential::apppwd::ApplicationPassword;
31use crate::credential::Credential;
32use crate::filter::{Filter, FilterInvalid, FilterResolved, FilterValidResolved};
33use crate::idm::ldap::ldap_vattr_map;
34use crate::modify::{Modify, ModifyInvalid, ModifyList, ModifyValid};
35use crate::prelude::*;
36use crate::repl::cid::Cid;
37use crate::repl::entry::EntryChangeState;
38use crate::repl::proto::{ReplEntryV1, ReplIncrementalEntryV1};
39use crate::schema::{SchemaAttribute, SchemaClass, SchemaTransaction};
40use crate::server::access::AccessEffectivePermission;
41use crate::value::{
42 ApiToken, CredentialType, IndexType, IntentTokenState, Oauth2Session, PartialValue, Session,
43 SyntaxType, Value,
44};
45use crate::valueset::{self, ScimResolveStatus, ValueSet, ValueSetSpn};
46use compact_jwt::JwsEs256Signer;
47use crypto_glue::s256::Sha256Output;
48use hashbrown::{HashMap, HashSet};
49use kanidm_proto::internal::ImageValue;
50use kanidm_proto::internal::{
51 ConsistencyError, Filter as ProtoFilter, OperationError, SchemaError, UiHint,
52};
53use kanidm_proto::scim_v1::server::ScimEffectiveAccess;
54use kanidm_proto::v1::Entry as ProtoEntry;
55use ldap3_proto::simple::{LdapPartialAttribute, LdapSearchResultEntry};
56use std::cmp::Ordering;
57use std::collections::{BTreeMap as Map, BTreeMap, BTreeSet};
58use std::sync::Arc;
59use time::OffsetDateTime;
60use tracing::trace;
61use uuid::Uuid;
62use webauthn_rs::prelude::{
63 AttestationCaList, AttestedPasskey as AttestedPasskeyV4, Passkey as PasskeyV4,
64};
65
66pub type EntryInitNew = Entry<EntryInit, EntryNew>;
67pub type EntryInvalidNew = Entry<EntryInvalid, EntryNew>;
68pub type EntryRefreshNew = Entry<EntryRefresh, EntryNew>;
69pub type EntrySealedNew = Entry<EntrySealed, EntryNew>;
70pub type EntryValidCommitted = Entry<EntryValid, EntryCommitted>;
71pub type EntrySealedCommitted = Entry<EntrySealed, EntryCommitted>;
72pub type EntryInvalidCommitted = Entry<EntryInvalid, EntryCommitted>;
73pub type EntryReducedCommitted = Entry<EntryReduced, EntryCommitted>;
74pub type EntryTuple = (Arc<EntrySealedCommitted>, EntryInvalidCommitted);
75
76pub type EntryIncrementalNew = Entry<EntryIncremental, EntryNew>;
77pub type EntryIncrementalCommitted = Entry<EntryIncremental, EntryCommitted>;
78
79#[derive(Clone, Debug)]
92pub struct EntryNew; #[derive(Clone, Debug)]
96pub struct EntryCommitted {
97 id: u64,
98}
99
100#[derive(Clone, Debug)]
101pub struct EntryInit;
102
103#[derive(Clone, Debug)]
110pub struct EntryInvalid {
111 cid: Cid,
112 ecstate: EntryChangeState,
113}
114
115#[derive(Clone, Debug)]
117pub struct EntryRefresh {
118 ecstate: EntryChangeState,
119}
120
121#[derive(Clone, Debug)]
123pub struct EntryIncremental {
124 uuid: Uuid,
126 ecstate: EntryChangeState,
127}
128
129#[derive(Clone, Debug)]
135pub struct EntryValid {
136 uuid: Uuid,
138 ecstate: EntryChangeState,
139}
140
141#[derive(Clone, Debug)]
148pub struct EntrySealed {
149 uuid: Uuid,
150 ecstate: EntryChangeState,
151}
152
153#[derive(Clone, Debug)]
159pub struct EntryReduced {
160 uuid: Uuid,
161 effective_access: Option<Box<AccessEffectivePermission>>,
162}
163
164pub type Eattrs = Map<Attribute, ValueSet>;
167
168pub trait GetUuid {
169 fn get_uuid(&self) -> Uuid;
170}
171
172pub trait Committed {}
173
174impl Committed for EntrySealed {}
175impl Committed for EntryReduced {}
176
177pub(crate) fn compare_attrs(left: &Eattrs, right: &Eattrs) -> bool {
178 let allkeys: BTreeSet<&Attribute> = left
181 .keys()
182 .chain(right.keys())
183 .filter(|k| *k != &Attribute::LastModifiedCid && *k != &Attribute::CreatedAtCid)
184 .collect();
185
186 allkeys.into_iter().all(|k| {
187 let left_vs = left.get(k);
189 let right_vs = right.get(k);
190 let r = match (left_vs, right_vs) {
191 (Some(l), Some(r)) => l.eq(r),
192 _ => false,
193 };
194 if !r {
195 trace!(?k, ?left_vs, ?right_vs, "compare_attrs_allkeys");
196 }
197 r
198 })
199}
200
201pub struct Entry<VALID, STATE> {
228 valid: VALID,
229 state: STATE,
230 attrs: Eattrs,
232}
233
234impl<VALID, STATE> std::fmt::Debug for Entry<VALID, STATE>
235where
236 STATE: std::fmt::Debug,
237 VALID: std::fmt::Debug,
238{
239 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
240 f.debug_struct("Entry<EntrySealed, _>")
241 .field("state", &self.state)
242 .field("valid", &self.valid)
243 .field("attrs", &self.attrs)
244 .finish()
245 }
246}
247
248impl<STATE> std::fmt::Display for Entry<EntrySealed, STATE>
249where
250 STATE: Clone,
251{
252 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
253 write!(f, "{}", self.get_uuid())
254 }
255}
256
257impl<STATE> std::fmt::Display for Entry<EntryInit, STATE>
258where
259 STATE: Clone,
260{
261 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
262 write!(f, "Entry in initial state")
263 }
264}
265
266impl<STATE> Entry<EntryInit, STATE>
267where
268 STATE: Clone,
269{
270 pub fn get_uuid(&self) -> Option<Uuid> {
272 self.attrs
273 .get(&Attribute::Uuid)
274 .and_then(|vs| vs.to_uuid_single())
275 }
276}
277
278impl Default for Entry<EntryInit, EntryNew> {
279 fn default() -> Self {
280 Self::new()
281 }
282}
283
284impl FromIterator<(Attribute, ValueSet)> for EntryInitNew {
285 fn from_iter<I: IntoIterator<Item = (Attribute, ValueSet)>>(iter: I) -> Self {
286 let attrs = Eattrs::from_iter(iter);
287
288 Entry {
289 valid: EntryInit,
290 state: EntryNew,
291 attrs,
292 }
293 }
294}
295
296impl Entry<EntryInit, EntryNew> {
297 pub fn new() -> Self {
298 Entry {
299 valid: EntryInit,
301 state: EntryNew,
302 attrs: Map::new(),
303 }
304 }
305
306 pub fn from_proto_entry(
309 e: &ProtoEntry,
310 qs: &mut QueryServerWriteTransaction,
311 ) -> Result<Self, OperationError> {
312 trace!("from_proto_entry");
313 let map2: Result<Eattrs, OperationError> = e
320 .attrs
321 .iter()
322 .filter(|(_, v)| !v.is_empty())
323 .map(|(k, v)| {
324 trace!(?k, ?v, "attribute");
325 let attr_nk = Attribute::from(k.as_str());
326 let nv = valueset::from_result_value_iter(
327 v.iter().map(|vr| qs.clone_value(&attr_nk, vr)),
328 );
329 trace!(?nv, "new valueset transform");
330 match nv {
331 Ok(nvi) => Ok((attr_nk, nvi)),
332 Err(e) => Err(e),
333 }
334 })
335 .collect();
336
337 let x = map2?;
338
339 Ok(Entry {
340 state: EntryNew,
341 valid: EntryInit,
342 attrs: x,
343 })
344 }
345
346 #[instrument(level = "debug", skip_all)]
349 pub fn from_proto_entry_str(
350 es: &str,
351 qs: &mut QueryServerWriteTransaction,
352 ) -> Result<Self, OperationError> {
353 if cfg!(test) {
354 if es.len() > 256 {
355 let (dsp_es, _) = es.split_at(255);
356 trace!("Parsing -> {}...", dsp_es);
357 } else {
358 trace!("Parsing -> {}", es);
359 }
360 }
361 let pe: ProtoEntry = serde_json::from_str(es).map_err(|e| {
363 admin_error!(?e, "SerdeJson Failure");
366 OperationError::SerdeJsonError
367 })?;
368 Self::from_proto_entry(&pe, qs)
370 }
371
372 pub fn assign_cid(
375 mut self,
376 cid: Cid,
377 schema: &dyn SchemaTransaction,
378 ) -> Entry<EntryInvalid, EntryNew> {
379 let ecstate = EntryChangeState::new(&cid, &self.attrs, schema);
385
386 let cv = vs_cid![cid.clone()];
389 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
390 let cv = vs_cid![cid.clone()];
391 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
392
393 Entry {
394 valid: EntryInvalid { cid, ecstate },
395 state: EntryNew,
396 attrs: self.attrs,
397 }
398 }
399
400 pub fn compare(&self, rhs: &Entry<EntrySealed, EntryCommitted>) -> bool {
402 compare_attrs(&self.attrs, &rhs.attrs)
403 }
404
405 #[cfg(test)]
409 pub fn into_invalid_new(mut self) -> Entry<EntryInvalid, EntryNew> {
410 let cid = Cid::new_zero();
411 self.set_last_changed(cid.clone());
412
413 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
414
415 Entry {
416 valid: EntryInvalid { cid, ecstate },
417 state: EntryNew,
418 attrs: self.attrs,
419 }
420 }
421
422 #[cfg(test)]
426 pub fn into_valid_new(mut self) -> Entry<EntryValid, EntryNew> {
427 let cid = Cid::new_zero();
428 self.set_last_changed(cid.clone());
429 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
430
431 Entry {
432 valid: EntryValid {
433 ecstate,
434 uuid: self.get_uuid().expect("Invalid uuid"),
435 },
436 state: EntryNew,
437 attrs: self.attrs,
438 }
439 }
440
441 #[cfg(test)]
445 pub fn into_sealed_committed(mut self) -> Entry<EntrySealed, EntryCommitted> {
446 let cid = Cid::new_zero();
447 self.set_last_changed(cid.clone());
448 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
449 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
450 Entry {
451 valid: EntrySealed { uuid, ecstate },
452 state: EntryCommitted { id: 0 },
453 attrs: self.attrs,
454 }
455 }
456
457 #[cfg(test)]
461 pub fn into_sealed_new(mut self) -> Entry<EntrySealed, EntryNew> {
462 let cid = Cid::new_zero();
463 self.set_last_changed(cid.clone());
464 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
465
466 Entry {
467 valid: EntrySealed {
468 uuid: self.get_uuid().expect("Invalid uuid"),
469 ecstate,
470 },
471 state: EntryNew,
472 attrs: self.attrs,
473 }
474 }
475
476 pub fn add_ava(&mut self, attr: Attribute, value: Value) {
482 self.add_ava_int(attr, value);
483 }
484
485 pub fn pop_ava<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<ValueSet> {
486 self.attrs.remove(attr.as_ref())
487 }
488
489 pub fn remove_ava<A: AsRef<Attribute>>(&mut self, attr: A) {
490 self.pop_ava(attr);
491 }
492
493 pub fn set_ava_set(&mut self, attr: &Attribute, vs: ValueSet) {
495 self.attrs.insert(attr.clone(), vs);
496 }
497
498 pub fn set_ava<T>(&mut self, attr: Attribute, iter: T)
500 where
501 T: IntoIterator<Item = Value>,
502 {
503 self.set_ava_iter_int(attr, iter);
504 }
505
506 pub fn get_ava_mut<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<&mut ValueSet> {
507 self.attrs.get_mut(attr.as_ref())
508 }
509}
510
511impl From<SchemaAttribute> for EntryInitNew {
512 fn from(value: SchemaAttribute) -> Self {
513 EntryInitNew::from(&value)
514 }
515}
516
517impl From<&SchemaAttribute> for EntryInitNew {
518 fn from(s: &SchemaAttribute) -> Self {
519 let mut attrs = Eattrs::new();
521 attrs.insert(Attribute::AttributeName, vs_iutf8![s.name.as_str()]);
522 attrs.insert(Attribute::Description, vs_utf8![s.description.to_owned()]);
523 attrs.insert(Attribute::Uuid, vs_uuid![s.uuid]);
524 attrs.insert(Attribute::MultiValue, vs_bool![s.multivalue]);
525 attrs.insert(Attribute::Phantom, vs_bool![s.phantom]);
526 attrs.insert(Attribute::SyncAllowed, vs_bool![s.sync_allowed]);
527 attrs.insert(Attribute::Replicated, vs_bool![s.replicated.into()]);
528 attrs.insert(Attribute::Unique, vs_bool![s.unique]);
529 attrs.insert(Attribute::Indexed, vs_bool![s.indexed]);
530 attrs.insert(Attribute::Syntax, vs_syntax![s.syntax]);
531 attrs.insert(
532 Attribute::Class,
533 vs_iutf8![
534 EntryClass::Object.into(),
535 EntryClass::System.into(),
536 EntryClass::AttributeType.into()
537 ],
538 );
539
540 Entry {
543 valid: EntryInit,
544 state: EntryNew,
545 attrs,
546 }
547 }
548}
549
550impl From<SchemaClass> for EntryInitNew {
551 fn from(value: SchemaClass) -> Self {
552 EntryInitNew::from(&value)
553 }
554}
555
556impl From<&SchemaClass> for EntryInitNew {
557 fn from(s: &SchemaClass) -> Self {
558 let mut attrs = Eattrs::new();
559 attrs.insert(Attribute::ClassName, vs_iutf8![s.name.as_str()]);
560 attrs.insert(Attribute::Description, vs_utf8![s.description.to_owned()]);
561 attrs.insert(Attribute::SyncAllowed, vs_bool![s.sync_allowed]);
562 attrs.insert(Attribute::Uuid, vs_uuid![s.uuid]);
563 attrs.insert(
564 Attribute::Class,
565 vs_iutf8![
566 EntryClass::Object.into(),
567 EntryClass::System.into(),
568 EntryClass::ClassType.into()
569 ],
570 );
571
572 let vs_systemmay = ValueSetIutf8::from_iter(s.systemmay.iter().map(|sm| sm.as_str()));
573 if let Some(vs) = vs_systemmay {
574 attrs.insert(Attribute::SystemMay, vs);
575 }
576
577 let vs_systemmust = ValueSetIutf8::from_iter(s.systemmust.iter().map(|sm| sm.as_str()));
578 if let Some(vs) = vs_systemmust {
579 attrs.insert(Attribute::SystemMust, vs);
580 }
581
582 let vs_systemexcludes =
583 ValueSetIutf8::from_iter(s.systemexcludes.iter().map(|sm| sm.as_str()));
584 if let Some(vs) = vs_systemexcludes {
585 attrs.insert(Attribute::SystemExcludes, vs);
586 }
587
588 let vs_systemsupplements =
589 ValueSetIutf8::from_iter(s.systemsupplements.iter().map(|sm| sm.as_str()));
590 if let Some(vs) = vs_systemsupplements {
591 attrs.insert(Attribute::SystemSupplements, vs);
592 }
593
594 Entry {
595 valid: EntryInit,
596 state: EntryNew,
597 attrs,
598 }
599 }
600}
601
602impl Entry<EntryRefresh, EntryNew> {
603 pub fn from_repl_entry_v1(repl_entry: ReplEntryV1) -> Result<Self, OperationError> {
604 let (ecstate, mut attrs) = repl_entry.rehydrate()?;
606
607 let last_mod_cid = ecstate.get_max_cid();
610 let cv = vs_cid![last_mod_cid.clone()];
611 let _ = attrs.insert(Attribute::LastModifiedCid, cv);
612
613 let create_at_cid = ecstate.at();
614 let cv = vs_cid![create_at_cid.clone()];
615 let _ = attrs.insert(Attribute::CreatedAtCid, cv);
616
617 Ok(Entry {
618 valid: EntryRefresh { ecstate },
619 state: EntryNew,
620 attrs,
621 })
622 }
623}
624
625impl<STATE> Entry<EntryRefresh, STATE> {
626 pub fn validate(
627 self,
628 schema: &dyn SchemaTransaction,
629 ) -> Result<Entry<EntryValid, STATE>, SchemaError> {
630 let uuid: Uuid = self
631 .attrs
632 .get(&Attribute::Uuid)
633 .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
634 .and_then(|vs| {
635 vs.to_uuid_single()
636 .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
637 })?;
638
639 let ne = Entry {
641 valid: EntryValid {
642 uuid,
643 ecstate: self.valid.ecstate,
644 },
645 state: self.state,
646 attrs: self.attrs,
647 };
648
649 ne.validate(schema).map(|()| ne)
650 }
651}
652
653impl<STATE> Entry<EntryIncremental, STATE> {
654 pub fn get_uuid(&self) -> Uuid {
655 self.valid.uuid
656 }
657}
658
659impl Entry<EntryIncremental, EntryNew> {
660 fn stub_ecstate(&self) -> EntryChangeState {
661 self.valid.ecstate.stub()
662 }
663
664 pub fn rehydrate(repl_inc_entry: ReplIncrementalEntryV1) -> Result<Self, OperationError> {
665 let (uuid, ecstate, attrs) = repl_inc_entry.rehydrate()?;
666
667 Ok(Entry {
668 valid: EntryIncremental { uuid, ecstate },
669 state: EntryNew,
670 attrs,
671 })
672 }
673
674 pub(crate) fn is_add_conflict(&self, db_entry: &EntrySealedCommitted) -> bool {
675 use crate::repl::entry::State;
676 debug_assert_eq!(self.valid.uuid, db_entry.valid.uuid);
677 let self_cs = &self.valid.ecstate;
679 let db_cs = db_entry.get_changestate();
680
681 match (self_cs.current(), db_cs.current()) {
683 (State::Live { at: at_left, .. }, State::Live { at: at_right, .. }) => {
684 at_left != at_right
685 }
686 _ => false,
688 }
689 }
690
691 pub(crate) fn resolve_add_conflict(
692 &self,
693 cid: &Cid,
694 db_ent: &EntrySealedCommitted,
695 ) -> (Option<EntrySealedNew>, EntryIncrementalCommitted) {
696 use crate::repl::entry::State;
697 debug_assert_eq!(self.valid.uuid, db_ent.valid.uuid);
698 let self_cs = &self.valid.ecstate;
699 let db_cs = db_ent.get_changestate();
700
701 match (self_cs.current(), db_cs.current()) {
702 (
703 State::Live {
704 at: at_left,
705 changes: _changes_left,
706 },
707 State::Live {
708 at: at_right,
709 changes: _changes_right,
710 },
711 ) => {
712 debug_assert!(at_left != at_right);
713 if at_left > at_right {
724 trace!("RI > DE, return DE");
725 (
726 None,
727 Entry {
728 valid: EntryIncremental {
729 uuid: db_ent.valid.uuid,
730 ecstate: db_cs.clone(),
731 },
732 state: EntryCommitted {
733 id: db_ent.state.id,
734 },
735 attrs: db_ent.attrs.clone(),
736 },
737 )
738 }
739 else {
749 trace!("RI < DE, return RI");
750 let conflict = if at_right.s_uuid == cid.s_uuid {
752 trace!("Origin process conflict entry");
753 let mut cnf_ent = Entry {
756 valid: EntryInvalid {
757 cid: cid.clone(),
758 ecstate: db_cs.clone(),
759 },
760 state: EntryNew,
761 attrs: db_ent.attrs.clone(),
762 };
763
764 cnf_ent.add_ava(Attribute::SourceUuid, Value::Uuid(db_ent.valid.uuid));
766
767 let new_uuid = Uuid::new_v4();
769 cnf_ent.purge_ava(Attribute::Uuid);
770 cnf_ent.add_ava(Attribute::Uuid, Value::Uuid(new_uuid));
771 cnf_ent.add_ava(Attribute::Class, EntryClass::Recycled.into());
772 cnf_ent.add_ava(Attribute::Class, EntryClass::Conflict.into());
773
774 let cv = vs_cid![cid.clone()];
778 let _ = cnf_ent.attrs.insert(Attribute::LastModifiedCid, cv);
779 let cv = vs_cid![cid.clone()];
781 let _ = cnf_ent.attrs.insert(Attribute::CreatedAtCid, cv);
782
783 let Entry {
787 valid: EntryInvalid { cid: _, ecstate },
788 state,
789 attrs,
790 } = cnf_ent;
791
792 let cnf_ent = Entry {
793 valid: EntrySealed {
794 uuid: new_uuid,
795 ecstate,
796 },
797 state,
798 attrs,
799 };
800
801 Some(cnf_ent)
802 } else {
803 None
804 };
805
806 let mut attrs = self.attrs.clone();
810 let ecstate = self_cs.clone();
811
812 let last_mod_cid = ecstate.get_max_cid();
813 let cv = vs_cid![last_mod_cid.clone()];
814 let _ = attrs.insert(Attribute::LastModifiedCid, cv);
815
816 let create_at_cid = ecstate.at();
817 let cv = vs_cid![create_at_cid.clone()];
818 let _ = attrs.insert(Attribute::CreatedAtCid, cv);
819
820 (
821 conflict,
822 Entry {
823 valid: EntryIncremental {
824 uuid: self.valid.uuid,
825 ecstate,
826 },
827 state: EntryCommitted {
828 id: db_ent.state.id,
829 },
830 attrs,
831 },
832 )
833 }
834 }
835 _ => unreachable!(),
839 }
840 }
841
842 pub(crate) fn merge_state(
843 &self,
844 db_ent: &EntrySealedCommitted,
845 schema: &dyn SchemaTransaction,
846 trim_cid: &Cid,
847 ) -> EntryIncrementalCommitted {
848 use crate::repl::entry::State;
849
850 debug_assert_eq!(self.valid.uuid, db_ent.valid.uuid);
852
853 let self_cs = &self.valid.ecstate;
856 let db_cs = db_ent.get_changestate();
857
858 match (self_cs.current(), db_cs.current()) {
859 (
860 State::Live {
861 at: at_left,
862 changes: changes_left,
863 },
864 State::Live {
865 at: at_right,
866 changes: changes_right,
867 },
868 ) => {
869 debug_assert_eq!(at_left, at_right);
870 let mut attr_set: Vec<_> =
875 changes_left.keys().chain(changes_right.keys()).collect();
876 attr_set.shrink_to_fit();
877 attr_set.sort_unstable();
878 attr_set.dedup();
879
880 let mut changes = BTreeMap::default();
882 let mut eattrs = Eattrs::default();
883
884 for attr_name in attr_set.into_iter() {
886 match (changes_left.get(attr_name), changes_right.get(attr_name)) {
887 (Some(cid_left), Some(cid_right)) => {
888 let take_left = cid_left > cid_right;
895
896 match (self.attrs.get(attr_name), db_ent.attrs.get(attr_name)) {
897 (Some(vs_left), Some(vs_right)) if take_left => {
898 changes.insert(attr_name.clone(), cid_left.clone());
899 #[allow(clippy::todo)]
900 if let Some(merged_attr_state) =
901 vs_left.repl_merge_valueset(vs_right, trim_cid)
902 {
903 eattrs.insert(attr_name.clone(), merged_attr_state);
906 } else {
907 eattrs.insert(attr_name.clone(), vs_left.clone());
908 }
909 }
910 (Some(vs_left), Some(vs_right)) => {
911 changes.insert(attr_name.clone(), cid_right.clone());
912 #[allow(clippy::todo)]
913 if let Some(merged_attr_state) =
914 vs_right.repl_merge_valueset(vs_left, trim_cid)
915 {
916 eattrs.insert(attr_name.clone(), merged_attr_state);
919 } else {
920 eattrs.insert(attr_name.clone(), vs_right.clone());
921 }
922 }
923 (Some(vs_left), None) if take_left => {
924 changes.insert(attr_name.clone(), cid_left.clone());
925 eattrs.insert(attr_name.clone(), vs_left.clone());
926 }
927 (Some(_vs_left), None) => {
928 changes.insert(attr_name.clone(), cid_right.clone());
929 }
931 (None, Some(_vs_right)) if take_left => {
932 changes.insert(attr_name.clone(), cid_left.clone());
933 }
935 (None, Some(vs_right)) => {
936 changes.insert(attr_name.clone(), cid_right.clone());
937 eattrs.insert(attr_name.clone(), vs_right.clone());
938 }
939 (None, None) if take_left => {
940 changes.insert(attr_name.clone(), cid_left.clone());
941 }
943 (None, None) => {
944 changes.insert(attr_name.clone(), cid_right.clone());
945 }
947 }
948 }
950 (Some(cid_left), None) => {
951 changes.insert(attr_name.clone(), cid_left.clone());
953 if let Some(valueset) = self.attrs.get(attr_name) {
954 eattrs.insert(attr_name.clone(), valueset.clone());
955 }
956 }
957 (None, Some(cid_right)) => {
958 changes.insert(attr_name.clone(), cid_right.clone());
960 if let Some(valueset) = db_ent.attrs.get(attr_name) {
961 eattrs.insert(attr_name.clone(), valueset.clone());
962 }
963 }
964 (None, None) => {
965 debug_assert!(false);
967 }
968 }
969 }
970
971 let mut ecstate = EntryChangeState::build(State::Live {
972 at: at_left.clone(),
973 changes,
974 });
975
976 ecstate.retain(|k, _| schema.is_replicated(k));
980
981 let cv = vs_cid![ecstate.get_max_cid().clone()];
982 let _ = eattrs.insert(Attribute::LastModifiedCid, cv);
983
984 let cv = vs_cid![ecstate.at().clone()];
985 let _ = eattrs.insert(Attribute::CreatedAtCid, cv);
986
987 Entry {
988 valid: EntryIncremental {
989 uuid: self.valid.uuid,
990 ecstate,
991 },
992 state: EntryCommitted {
993 id: db_ent.state.id,
994 },
995 attrs: eattrs,
996 }
997 }
998 (State::Tombstone { at: left_at }, State::Live { .. }) => {
999 let mut attrs_new: Eattrs = Map::new();
1003 let class_ava = vs_iutf8![EntryClass::Object.into(), EntryClass::Tombstone.into()];
1004 let last_mod_ava = vs_cid![left_at.clone()];
1005 let created_ava = vs_cid![left_at.clone()];
1006
1007 attrs_new.insert(Attribute::Uuid, vs_uuid![self.valid.uuid]);
1008 attrs_new.insert(Attribute::Class, class_ava);
1009 attrs_new.insert(Attribute::LastModifiedCid, last_mod_ava);
1010 attrs_new.insert(Attribute::CreatedAtCid, created_ava);
1011
1012 Entry {
1013 valid: EntryIncremental {
1014 uuid: self.valid.uuid,
1015 ecstate: self.valid.ecstate.clone(),
1016 },
1017 state: EntryCommitted {
1018 id: db_ent.state.id,
1019 },
1020 attrs: attrs_new,
1021 }
1022 }
1023 (State::Live { .. }, State::Tombstone { .. }) => {
1024 Entry {
1032 valid: EntryIncremental {
1033 uuid: db_ent.valid.uuid,
1034 ecstate: db_ent.valid.ecstate.clone(),
1035 },
1036 state: EntryCommitted {
1037 id: db_ent.state.id,
1038 },
1039 attrs: db_ent.attrs.clone(),
1040 }
1041 }
1042 (State::Tombstone { at: left_at }, State::Tombstone { at: right_at }) => {
1043 let (at, ecstate) = if left_at < right_at {
1048 (left_at, self.valid.ecstate.clone())
1049 } else {
1050 (right_at, db_ent.valid.ecstate.clone())
1051 };
1052
1053 let mut attrs_new: Eattrs = Map::new();
1054 let class_ava = vs_iutf8![EntryClass::Object.into(), EntryClass::Tombstone.into()];
1055 let last_mod_ava = vs_cid![at.clone()];
1056 let created_ava = vs_cid![at.clone()];
1057
1058 attrs_new.insert(Attribute::Uuid, vs_uuid![db_ent.valid.uuid]);
1059 attrs_new.insert(Attribute::Class, class_ava);
1060 attrs_new.insert(Attribute::LastModifiedCid, last_mod_ava);
1061 attrs_new.insert(Attribute::CreatedAtCid, created_ava);
1062
1063 Entry {
1064 valid: EntryIncremental {
1065 uuid: db_ent.valid.uuid,
1066 ecstate,
1067 },
1068 state: EntryCommitted {
1069 id: db_ent.state.id,
1070 },
1071 attrs: attrs_new,
1072 }
1073 }
1074 }
1075 }
1076}
1077
1078impl Entry<EntryIncremental, EntryCommitted> {
1079 pub(crate) fn validate_repl(self, schema: &dyn SchemaTransaction) -> EntryValidCommitted {
1080 let mut ne = Entry {
1085 valid: EntryValid {
1086 uuid: self.valid.uuid,
1087 ecstate: self.valid.ecstate,
1088 },
1089 state: self.state,
1090 attrs: self.attrs,
1091 };
1092
1093 if let Err(e) = ne.validate(schema) {
1094 warn!(uuid = ?self.valid.uuid, err = ?e, "Entry failed schema check, moving to a conflict state");
1095 ne.add_ava_int(Attribute::Class, EntryClass::Recycled.into());
1096 ne.add_ava_int(Attribute::Class, EntryClass::Conflict.into());
1097 ne.add_ava_int(Attribute::SourceUuid, Value::Uuid(self.valid.uuid));
1098 }
1099 ne
1100 }
1101}
1102
1103impl<STATE> Entry<EntryInvalid, STATE> {
1104 pub(crate) fn get_uuid(&self) -> Option<Uuid> {
1105 self.attrs
1106 .get(&Attribute::Uuid)
1107 .and_then(|vs| vs.to_uuid_single())
1108 }
1109
1110 pub fn validate(
1113 self,
1114 schema: &dyn SchemaTransaction,
1115 ) -> Result<Entry<EntryValid, STATE>, SchemaError> {
1116 let uuid: Uuid = self
1117 .attrs
1118 .get(&Attribute::Uuid)
1119 .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
1120 .and_then(|vs| {
1121 vs.to_uuid_single()
1122 .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
1123 })?;
1124
1125 let ne = Entry {
1127 valid: EntryValid {
1128 uuid,
1129 ecstate: self.valid.ecstate,
1130 },
1131 state: self.state,
1132 attrs: self.attrs,
1133 };
1134
1135 ne.validate(schema).map(|()| ne)
1136 }
1137
1138 pub(crate) fn get_ava_refer_mut<A: AsRef<Attribute>>(
1142 &mut self,
1143 attr: A,
1144 ) -> Option<&mut BTreeSet<Uuid>> {
1145 self.get_ava_mut(attr).and_then(|vs| vs.as_refer_set_mut())
1146 }
1147
1148 pub(crate) fn get_ava_mut<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<&mut ValueSet> {
1149 let attr_ref = attr.as_ref();
1150 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
1151 self.attrs.get_mut(attr.as_ref())
1152 }
1153}
1154
1155impl<VALID, STATE> Clone for Entry<VALID, STATE>
1156where
1157 VALID: Clone,
1158 STATE: Clone,
1159{
1160 fn clone(&self) -> Entry<VALID, STATE> {
1162 Entry {
1163 valid: self.valid.clone(),
1164 state: self.state.clone(),
1165 attrs: self.attrs.clone(),
1166 }
1167 }
1168}
1169
1170impl Entry<EntryInvalid, EntryCommitted> {
1171 #[cfg(test)]
1175 pub fn into_valid_new(self) -> Entry<EntryValid, EntryNew> {
1176 let uuid = self.get_uuid().expect("Invalid uuid");
1177 Entry {
1178 valid: EntryValid {
1179 uuid,
1180 ecstate: self.valid.ecstate,
1181 },
1182 state: EntryNew,
1183 attrs: self.attrs,
1184 }
1185 }
1186
1187 pub fn to_recycled(mut self) -> Self {
1189 self.add_ava(Attribute::Class, EntryClass::Recycled.into());
1191
1192 Entry {
1196 valid: self.valid,
1197 state: self.state,
1198 attrs: self.attrs,
1199 }
1200 }
1201
1202 pub fn to_conflict<T>(&mut self, iter: T)
1204 where
1205 T: IntoIterator<Item = Uuid>,
1206 {
1207 self.add_ava(Attribute::Class, EntryClass::Recycled.into());
1208 self.add_ava(Attribute::Class, EntryClass::Conflict.into());
1209 for source_uuid in iter {
1211 self.add_ava(Attribute::SourceUuid, Value::Uuid(source_uuid));
1212 }
1213 }
1214
1215 pub fn to_revived(mut self) -> Self {
1217 self.remove_ava(Attribute::Class, &EntryClass::Recycled.into());
1219 self.remove_ava(Attribute::Class, &EntryClass::Conflict.into());
1220
1221 self.purge_ava(Attribute::CascadeDeleted);
1222 self.purge_ava(Attribute::SourceUuid);
1223 self.purge_ava(Attribute::RecycledDirectMemberOf);
1224
1225 Entry {
1229 valid: self.valid,
1230 state: self.state,
1231 attrs: self.attrs,
1232 }
1233 }
1234}
1235impl Entry<EntryInvalid, EntryNew> {
1238 #[cfg(test)]
1241 pub fn into_init_new(self) -> Entry<EntryInit, EntryNew> {
1242 Entry {
1243 valid: EntryInit,
1244 state: EntryNew,
1245 attrs: self.attrs,
1246 }
1247 }
1248
1249 #[cfg(test)]
1253 pub fn into_valid_new(self) -> Entry<EntryValid, EntryNew> {
1254 let uuid = self.get_uuid().expect("Invalid uuid");
1255 Entry {
1256 valid: EntryValid {
1257 uuid,
1258 ecstate: self.valid.ecstate,
1259 },
1260 state: EntryNew,
1261 attrs: self.attrs,
1262 }
1263 }
1264
1265 #[cfg(test)]
1269 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1270 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
1271 Entry {
1272 valid: EntrySealed {
1273 uuid,
1274 ecstate: self.valid.ecstate,
1275 },
1276 state: EntryCommitted { id: 0 },
1277 attrs: self.attrs,
1278 }
1279 }
1280
1281 #[cfg(test)]
1285 pub fn into_valid_committed(self) -> Entry<EntryValid, EntryCommitted> {
1286 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
1287 Entry {
1288 valid: EntryValid {
1289 uuid,
1290 ecstate: self.valid.ecstate,
1291 },
1292 state: EntryCommitted { id: 0 },
1293 attrs: self.attrs,
1294 }
1295 }
1296}
1297
1298impl Entry<EntryInvalid, EntryCommitted> {
1299 #[cfg(test)]
1303 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1304 let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
1305 Entry {
1306 valid: EntrySealed {
1307 uuid,
1308 ecstate: self.valid.ecstate,
1309 },
1310 state: self.state,
1311 attrs: self.attrs,
1312 }
1313 }
1314}
1315
1316impl Entry<EntrySealed, EntryNew> {
1317 #[cfg(test)]
1321 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1322 Entry {
1323 valid: self.valid,
1324 state: EntryCommitted { id: 0 },
1325 attrs: self.attrs,
1326 }
1327 }
1328
1329 pub fn into_sealed_committed_id(self, id: u64) -> Entry<EntrySealed, EntryCommitted> {
1332 Entry {
1333 valid: self.valid,
1334 state: EntryCommitted { id },
1335 attrs: self.attrs,
1336 }
1337 }
1338
1339 pub fn compare(&self, rhs: &Entry<EntrySealed, EntryNew>) -> bool {
1340 compare_attrs(&self.attrs, &rhs.attrs)
1341 }
1342}
1343
1344type IdxDiff<'a> =
1345 Vec<Result<(&'a Attribute, IndexType, String), (&'a Attribute, IndexType, String)>>;
1346
1347impl<VALID> Entry<VALID, EntryCommitted> {
1348 pub fn get_id(&self) -> u64 {
1350 self.state.id
1351 }
1352}
1353
1354impl<STATE> Entry<EntrySealed, STATE> {
1355 pub fn into_init(self) -> Entry<EntryInit, STATE> {
1356 Entry {
1357 valid: EntryInit,
1358 state: self.state,
1359 attrs: self.attrs,
1360 }
1361 }
1362}
1363
1364impl Entry<EntrySealed, EntryCommitted> {
1365 #[cfg(test)]
1366 pub(crate) fn get_last_changed(&self) -> Cid {
1367 self.valid.ecstate.get_max_cid().clone()
1368 }
1369
1370 #[cfg(test)]
1372 pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1373 self
1375 }
1376
1377 pub(crate) fn stub_sealed_committed_id(
1378 id: u64,
1379 ctx_ent: &EntryIncrementalNew,
1380 ) -> EntrySealedCommitted {
1381 let uuid = ctx_ent.get_uuid();
1382 let ecstate = ctx_ent.stub_ecstate();
1383
1384 Entry {
1385 valid: EntrySealed { uuid, ecstate },
1386 state: EntryCommitted { id },
1387 attrs: Default::default(),
1388 }
1389 }
1390
1391 pub fn insert_claim(&mut self, value: &str) {
1394 self.add_ava_int(Attribute::Claim, Value::new_iutf8(value));
1395 }
1396
1397 pub fn compare(&self, rhs: &Entry<EntrySealed, EntryCommitted>) -> bool {
1398 compare_attrs(&self.attrs, &rhs.attrs)
1399 }
1400
1401 pub fn to_dbentry(&self) -> DbEntry {
1403 DbEntry {
1406 ent: DbEntryVers::V3 {
1407 changestate: self.valid.ecstate.to_db_changestate(),
1408 attrs: self
1409 .attrs
1410 .iter()
1411 .map(|(k, vs)| {
1412 let dbvs: DbValueSetV2 = vs.to_db_valueset_v2();
1413 (k.clone(), dbvs)
1414 })
1415 .collect(),
1416 },
1417 }
1418 }
1419
1420 #[inline]
1421 fn get_name2uuid_cands(&self) -> BTreeSet<String> {
1424 let cands = [Attribute::Spn, Attribute::Name, Attribute::GidNumber];
1430 cands
1431 .iter()
1432 .filter_map(|cand| {
1433 self.attrs
1434 .get(cand)
1435 .map(|vs| vs.to_proto_string_clone_iter())
1436 })
1437 .flatten()
1438 .collect()
1439 }
1440
1441 #[inline]
1442 fn get_externalid2uuid(&self) -> Option<String> {
1445 self.attrs
1446 .get(&Attribute::SyncExternalId)
1447 .and_then(|vs| vs.to_proto_string_single())
1448 }
1449
1450 #[inline]
1451 pub(crate) fn get_uuid2spn(&self) -> Value {
1454 self.attrs
1455 .get(&Attribute::Spn)
1456 .and_then(|vs| vs.to_value_single())
1457 .or_else(|| {
1458 self.attrs
1459 .get(&Attribute::Name)
1460 .and_then(|vs| vs.to_value_single())
1461 })
1462 .unwrap_or_else(|| Value::Uuid(self.get_uuid()))
1463 }
1464
1465 #[inline]
1466 pub(crate) fn get_uuid2rdn(&self) -> String {
1470 self.attrs
1471 .get(&Attribute::Spn)
1472 .and_then(|vs| vs.to_proto_string_single().map(|v| format!("spn={v}")))
1473 .or_else(|| {
1474 self.attrs
1475 .get(&Attribute::Name)
1476 .and_then(|vs| vs.to_proto_string_single().map(|v| format!("name={v}")))
1477 })
1478 .unwrap_or_else(|| format!("uuid={}", self.get_uuid().as_hyphenated()))
1479 }
1480
1481 pub(crate) fn idx_name2uuid_diff(
1484 pre: Option<&Self>,
1485 post: Option<&Self>,
1486 ) -> (
1487 Option<BTreeSet<String>>,
1489 Option<BTreeSet<String>>,
1491 ) {
1492 match (pre, post) {
1494 (None, None) => {
1495 (None, None)
1497 }
1498 (None, Some(b)) => {
1499 (Some(b.get_name2uuid_cands()), None)
1502 }
1503 (Some(a), None) => {
1504 (None, Some(a.get_name2uuid_cands()))
1506 }
1507 (Some(a), Some(b)) => {
1508 let pre_set = a.get_name2uuid_cands();
1509 let post_set = b.get_name2uuid_cands();
1510
1511 let add_set: BTreeSet<_> = post_set.difference(&pre_set).cloned().collect();
1513 let rem_set: BTreeSet<_> = pre_set.difference(&post_set).cloned().collect();
1515 (Some(add_set), Some(rem_set))
1516 }
1517 }
1518 }
1519
1520 pub(crate) fn idx_externalid2uuid_diff(
1522 pre: Option<&Self>,
1523 post: Option<&Self>,
1524 ) -> (Option<String>, Option<String>) {
1525 match (pre, post) {
1526 (None, None) => {
1527 (None, None)
1529 }
1530 (None, Some(b)) => {
1531 (b.get_externalid2uuid(), None)
1533 }
1534 (Some(a), None) => {
1535 (None, a.get_externalid2uuid())
1537 }
1538 (Some(a), Some(b)) => {
1539 let ia = a.get_externalid2uuid();
1540 let ib = b.get_externalid2uuid();
1541 if ia != ib {
1542 (ib, ia)
1545 } else {
1546 (None, None)
1548 }
1549 }
1550 }
1551 }
1552
1553 pub(crate) fn idx_uuid2spn_diff(
1556 pre: Option<&Self>,
1557 post: Option<&Self>,
1558 ) -> Option<Result<Value, ()>> {
1559 match (pre, post) {
1560 (None, None) => {
1561 None
1563 }
1564 (None, Some(b)) => {
1565 Some(Ok(b.get_uuid2spn()))
1567 }
1568 (Some(_a), None) => {
1569 Some(Err(()))
1571 }
1572 (Some(a), Some(b)) => {
1573 let ia = a.get_uuid2spn();
1574 let ib = b.get_uuid2spn();
1575 if ia != ib {
1576 Some(Ok(ib))
1578 } else {
1579 None
1581 }
1582 }
1583 }
1584 }
1585
1586 pub(crate) fn idx_uuid2rdn_diff(
1589 pre: Option<&Self>,
1590 post: Option<&Self>,
1591 ) -> Option<Result<String, ()>> {
1592 match (pre, post) {
1593 (None, None) => {
1594 None
1596 }
1597 (None, Some(b)) => {
1598 Some(Ok(b.get_uuid2rdn()))
1600 }
1601 (Some(_a), None) => {
1602 Some(Err(()))
1604 }
1605 (Some(a), Some(b)) => {
1606 let ia = a.get_uuid2rdn();
1607 let ib = b.get_uuid2rdn();
1608 if ia != ib {
1609 Some(Ok(ib))
1611 } else {
1612 None
1614 }
1615 }
1616 }
1617 }
1618
1619 pub(crate) fn idx_diff<'a>(
1622 idxmeta: &'a HashMap<IdxKey, IdxSlope>,
1623 pre: Option<&Self>,
1624 post: Option<&Self>,
1625 ) -> IdxDiff<'a> {
1626 match (pre, post) {
1631 (None, None) => {
1632 Vec::with_capacity(0)
1634 }
1635 (Some(pre_e), None) => {
1636 idxmeta
1638 .keys()
1639 .flat_map(|ikey| {
1640 match pre_e.get_ava_set(&ikey.attr) {
1641 None => Vec::with_capacity(0),
1642 Some(vs) => {
1643 let changes: Vec<Result<_, _>> = match ikey.itype {
1644 IndexType::Equality => {
1645 vs.generate_idx_eq_keys()
1647 .into_iter()
1648 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1649 .collect()
1650 }
1651 IndexType::Presence => {
1652 vec![Err((&ikey.attr, ikey.itype, "_".to_string()))]
1653 }
1654 IndexType::SubString => vs
1655 .generate_idx_sub_keys()
1656 .into_iter()
1657 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1658 .collect(),
1659 IndexType::Ordering => vs
1660 .generate_idx_ord_keys()
1661 .into_iter()
1662 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1663 .collect(),
1664 };
1665 changes
1666 }
1667 }
1668 })
1669 .collect()
1670 }
1671 (None, Some(post_e)) => {
1672 idxmeta
1674 .keys()
1675 .flat_map(|ikey| {
1676 match post_e.get_ava_set(&ikey.attr) {
1677 None => Vec::with_capacity(0),
1678 Some(vs) => {
1679 let changes: Vec<Result<_, _>> = match ikey.itype {
1680 IndexType::Equality => vs
1681 .generate_idx_eq_keys()
1682 .into_iter()
1683 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1684 .collect(),
1685 IndexType::Presence => {
1686 vec![Ok((&ikey.attr, ikey.itype, "_".to_string()))]
1687 }
1688 IndexType::SubString => vs
1689 .generate_idx_sub_keys()
1690 .into_iter()
1691 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1692 .collect(),
1693 IndexType::Ordering => vs
1694 .generate_idx_ord_keys()
1695 .into_iter()
1696 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1697 .collect(),
1698 };
1699 changes
1702 }
1703 }
1704 })
1705 .collect()
1706 }
1707 (Some(pre_e), Some(post_e)) => {
1708 assert_eq!(pre_e.state.id, post_e.state.id);
1709 idxmeta
1710 .keys()
1711 .flat_map(|ikey| {
1712 match (
1713 pre_e.get_ava_set(&ikey.attr),
1714 post_e.get_ava_set(&ikey.attr),
1715 ) {
1716 (None, None) => {
1717 Vec::with_capacity(0)
1719 }
1720 (Some(pre_vs), None) => {
1721 let changes: Vec<Result<_, _>> = match ikey.itype {
1723 IndexType::Equality => {
1724 pre_vs
1727 .generate_idx_eq_keys()
1728 .into_iter()
1729 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1730 .collect()
1731 }
1732 IndexType::Presence => {
1733 vec![Err((&ikey.attr, ikey.itype, "_".to_string()))]
1734 }
1735 IndexType::SubString => pre_vs
1736 .generate_idx_sub_keys()
1737 .into_iter()
1738 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1739 .collect(),
1740 IndexType::Ordering => pre_vs
1741 .generate_idx_ord_keys()
1742 .into_iter()
1743 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1744 .collect(),
1745 };
1746 changes
1747 }
1748 (None, Some(post_vs)) => {
1749 let changes: Vec<Result<_, _>> = match ikey.itype {
1751 IndexType::Equality => {
1752 post_vs
1755 .generate_idx_eq_keys()
1756 .into_iter()
1757 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1758 .collect()
1759 }
1760 IndexType::Presence => {
1761 vec![Ok((&ikey.attr, ikey.itype, "_".to_string()))]
1762 }
1763 IndexType::SubString => post_vs
1764 .generate_idx_sub_keys()
1765 .into_iter()
1766 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1767 .collect(),
1768 IndexType::Ordering => post_vs
1769 .generate_idx_ord_keys()
1770 .into_iter()
1771 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1772 .collect(),
1773 };
1774 changes
1775 }
1776 (Some(pre_vs), Some(post_vs)) => {
1777 let (mut pre_idx_keys, mut post_idx_keys) = match ikey.itype {
1779 IndexType::Equality => (
1780 pre_vs.generate_idx_eq_keys(),
1781 post_vs.generate_idx_eq_keys(),
1782 ),
1783 IndexType::Presence => {
1784 (Vec::with_capacity(0), Vec::with_capacity(0))
1786 }
1787 IndexType::SubString => (
1788 pre_vs.generate_idx_sub_keys(),
1789 post_vs.generate_idx_sub_keys(),
1790 ),
1791 IndexType::Ordering => (
1792 pre_vs.generate_idx_ord_keys(),
1793 post_vs.generate_idx_ord_keys(),
1794 ),
1795 };
1796
1797 let sz = if pre_idx_keys.len() > post_idx_keys.len() {
1798 pre_idx_keys.len()
1799 } else {
1800 post_idx_keys.len()
1801 };
1802
1803 let mut added_vs = Vec::with_capacity(sz);
1804 let mut removed_vs = Vec::with_capacity(sz);
1805
1806 if sz > 0 {
1807 pre_idx_keys.sort_unstable();
1808 post_idx_keys.sort_unstable();
1809
1810 let mut pre_iter = pre_idx_keys.iter();
1811 let mut post_iter = post_idx_keys.iter();
1812
1813 let mut pre = pre_iter.next();
1814 let mut post = post_iter.next();
1815
1816 loop {
1817 match (pre, post) {
1818 (Some(a), Some(b)) => {
1819 match a.cmp(b) {
1820 Ordering::Less => {
1821 removed_vs.push(a.clone());
1822 pre = pre_iter.next();
1823 }
1824 Ordering::Equal => {
1825 pre = pre_iter.next();
1827 post = post_iter.next();
1828 }
1829 Ordering::Greater => {
1830 added_vs.push(b.clone());
1831 post = post_iter.next();
1832 }
1833 }
1834 }
1835 (Some(a), None) => {
1836 removed_vs.push(a.clone());
1837 pre = pre_iter.next();
1838 }
1839 (None, Some(b)) => {
1840 added_vs.push(b.clone());
1841 post = post_iter.next();
1842 }
1843 (None, None) => {
1844 break;
1845 }
1846 }
1847 }
1848 } let mut diff =
1851 Vec::with_capacity(removed_vs.len() + added_vs.len());
1852
1853 match ikey.itype {
1854 IndexType::SubString
1855 | IndexType::Ordering
1856 | IndexType::Equality => {
1857 removed_vs
1858 .into_iter()
1859 .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1860 .for_each(|v| diff.push(v));
1861 added_vs
1862 .into_iter()
1863 .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1864 .for_each(|v| diff.push(v));
1865 }
1866 IndexType::Presence => {
1867 }
1869 };
1870 diff
1872 }
1873 }
1874 })
1875 .collect()
1876 }
1878 }
1879 }
1880
1881 pub fn from_dbentry(db_e: DbEntry, id: u64) -> Option<Self> {
1882 let (attrs, ecstate) = match db_e.ent {
1885 DbEntryVers::V3 { changestate, attrs } => {
1886 let ecstate = EntryChangeState::from_db_changestate(changestate);
1887
1888 let r_attrs = attrs
1889 .into_iter()
1890 .filter(|(_k, vs)| !vs.is_empty())
1892 .map(|(k, dbvs)| {
1893 valueset::from_db_valueset_v2(dbvs)
1894 .map(|vs: ValueSet| (k, vs))
1895 .map_err(|e| {
1896 error!(?e, "from_dbentry failed");
1897 })
1898 })
1899 .collect::<Result<Eattrs, ()>>()
1900 .ok()?;
1901
1902 (r_attrs, ecstate)
1903 }
1904 };
1905
1906 let uuid = attrs
1907 .get(&Attribute::Uuid)
1908 .and_then(|vs| vs.to_uuid_single())?;
1909
1910 Some(Entry {
1911 valid: EntrySealed { uuid, ecstate },
1912 state: EntryCommitted { id },
1913 attrs,
1914 })
1915 }
1916
1917 #[cfg(test)]
1925 pub(crate) fn into_reduced(self) -> Entry<EntryReduced, EntryCommitted> {
1926 Entry {
1927 valid: EntryReduced {
1928 uuid: self.valid.uuid,
1929 effective_access: None,
1930 },
1931 state: self.state,
1932 attrs: self.attrs,
1933 }
1934 }
1935
1936 pub fn reduce_attributes(
1939 &self,
1940 allowed_attrs: &BTreeSet<Attribute>,
1941 effective_access: Option<Box<AccessEffectivePermission>>,
1942 ) -> Entry<EntryReduced, EntryCommitted> {
1943 let f_attrs: Map<_, _> = self
1945 .attrs
1946 .iter()
1947 .filter_map(|(k, v)| {
1948 if allowed_attrs.contains(k) {
1949 Some((k.clone(), v.clone()))
1950 } else {
1951 None
1952 }
1953 })
1954 .collect();
1955
1956 let valid = EntryReduced {
1957 uuid: self.valid.uuid,
1958 effective_access,
1959 };
1960 let state = self.state.clone();
1961
1962 Entry {
1963 valid,
1964 state,
1965 attrs: f_attrs,
1966 }
1967 }
1968
1969 pub fn to_tombstone(&self, cid: Cid) -> Entry<EntryInvalid, EntryCommitted> {
1971 let mut ecstate = self.valid.ecstate.clone();
1972 let mut attrs_new: Eattrs = Map::new();
1974
1975 let class_ava = vs_iutf8![EntryClass::Object.into(), EntryClass::Tombstone.into()];
1976 let last_mod_ava = vs_cid![cid.clone()];
1977 let created_ava = vs_cid![cid.clone()];
1978
1979 attrs_new.insert(Attribute::Uuid, vs_uuid![self.get_uuid()]);
1980 attrs_new.insert(Attribute::Class, class_ava);
1981 attrs_new.insert(Attribute::LastModifiedCid, last_mod_ava);
1982 attrs_new.insert(Attribute::CreatedAtCid, created_ava);
1983
1984 ecstate.tombstone(&cid);
1986
1987 Entry {
1988 valid: EntryInvalid { cid, ecstate },
1989 state: self.state.clone(),
1990 attrs: attrs_new,
1991 }
1992 }
1993
1994 pub fn into_valid(self, ecstate: EntryChangeState) -> Entry<EntryValid, EntryCommitted> {
1996 Entry {
1997 valid: EntryValid {
1998 uuid: self.valid.uuid,
1999 ecstate,
2000 },
2001 state: self.state,
2002 attrs: self.attrs,
2003 }
2004 }
2005
2006 pub fn verify(
2007 &self,
2008 schema: &dyn SchemaTransaction,
2009 results: &mut Vec<Result<(), ConsistencyError>>,
2010 ) {
2011 self.valid
2012 .ecstate
2013 .verify(schema, &self.attrs, self.state.id, results);
2014 }
2015}
2016
2017impl<STATE> Entry<EntryValid, STATE> {
2018 fn validate(&self, schema: &dyn SchemaTransaction) -> Result<(), SchemaError> {
2019 let schema_classes = schema.get_classes();
2020 let schema_attributes = schema.get_attributes();
2021
2022 trace!(?self.attrs, "Entry::validate -> target");
2024
2025 if !self.attribute_pres(Attribute::Class) {
2027 return Err(SchemaError::NoClassFound);
2029 }
2030
2031 if self.attribute_equality(Attribute::Class, &EntryClass::Conflict.into()) {
2032 trace!("Skipping schema validation on conflict entry");
2034 return Ok(());
2035 };
2036
2037 let recycled = self.attribute_equality(Attribute::Class, &EntryClass::Recycled.into());
2039
2040 let extensible =
2043 self.attribute_equality(Attribute::Class, &EntryClass::ExtensibleObject.into());
2044
2045 let entry_classes = self.get_ava_set(Attribute::Class).ok_or_else(|| {
2046 admin_debug!("Attribute '{}' missing from entry", Attribute::Class);
2047 SchemaError::NoClassFound
2048 })?;
2049 let mut invalid_classes = Vec::with_capacity(0);
2050
2051 let mut classes: Vec<&SchemaClass> = Vec::with_capacity(entry_classes.len());
2052
2053 let entry_classes = if let Some(ec) = entry_classes.as_iutf8_set() {
2056 ec.iter()
2057 .for_each(|s| match schema_classes.get(s.as_str()) {
2058 Some(x) => classes.push(x),
2059 None => {
2060 admin_debug!("invalid class: {:?}", s);
2061 invalid_classes.push(s.to_string())
2062 }
2063 });
2064 ec
2065 } else {
2066 admin_debug!("corrupt class attribute");
2067 return Err(SchemaError::NoClassFound);
2068 };
2069
2070 if !invalid_classes.is_empty() {
2071 return Err(SchemaError::InvalidClass(invalid_classes));
2072 };
2073
2074 let supplements_classes: Vec<_> = classes
2078 .iter()
2079 .flat_map(|cls| cls.systemsupplements.iter().chain(cls.supplements.iter()))
2080 .collect();
2081
2082 let valid_supplements = if supplements_classes.is_empty() {
2084 true
2086 } else {
2087 supplements_classes
2088 .iter()
2089 .any(|class| entry_classes.contains(class.as_str()))
2090 };
2091
2092 if !valid_supplements {
2093 warn!(
2094 "Validation error, the following possible supplement classes are missing - {:?}",
2095 supplements_classes
2096 );
2097 let supplements_classes = supplements_classes.iter().map(|s| s.to_string()).collect();
2098 return Err(SchemaError::SupplementsNotSatisfied(supplements_classes));
2099 }
2100
2101 let excludes_classes: Vec<_> = classes
2102 .iter()
2103 .flat_map(|cls| cls.systemexcludes.iter().chain(cls.excludes.iter()))
2104 .collect();
2105
2106 let mut invalid_excludes = Vec::with_capacity(0);
2107
2108 excludes_classes.iter().for_each(|class| {
2109 if entry_classes.contains(class.as_str()) {
2110 invalid_excludes.push(class.to_string())
2111 }
2112 });
2113
2114 if !invalid_excludes.is_empty() {
2115 admin_warn!(
2116 "Validation error, the following excluded classes are present - {:?}",
2117 invalid_excludes
2118 );
2119 return Err(SchemaError::ExcludesNotSatisfied(invalid_excludes));
2120 }
2121
2122 let must: Result<Vec<&SchemaAttribute>, _> = classes
2135 .iter()
2136 .flat_map(|cls| cls.systemmust.iter().chain(cls.must.iter()))
2138 .map(|s| {
2139 schema_attributes.get(s).ok_or(SchemaError::Corrupted)
2142 })
2143 .collect();
2144
2145 let must = must?;
2146
2147 let mut missing_must = Vec::with_capacity(0);
2150 for attr in must.iter() {
2151 let avas = self.get_ava_set(&attr.name);
2152 if avas.is_none() {
2153 missing_must.push(attr.name.clone());
2154 }
2155 }
2156
2157 if !missing_must.is_empty() {
2158 admin_warn!(
2159 "Validation error, the following required ({}) (must) attributes are missing - {:?}",
2160 self.get_display_id(), missing_must
2161 );
2162 if !recycled {
2168 return Err(SchemaError::MissingMustAttribute(missing_must));
2169 }
2170 }
2171
2172 if extensible {
2173 self.attrs.iter().try_for_each(|(attr_name, avas)| {
2174 match schema_attributes.get(attr_name) {
2175 Some(a_schema) => {
2176 if a_schema.phantom {
2179 admin_warn!(
2180 "Rejecting attempt to add phantom attribute to extensible object: {}",
2181 attr_name
2182 );
2183 Err(SchemaError::PhantomAttribute(attr_name.to_string()))
2184 } else {
2185 a_schema.validate_ava(attr_name, avas)
2186 }
2188 }
2189 None => {
2190 admin_error!(
2191 "Invalid Attribute {}, undefined in schema_attributes",
2192 attr_name.to_string()
2193 );
2194 Err(SchemaError::InvalidAttribute(
2195 attr_name.to_string()
2196 ))
2197 }
2198 }
2199 })?;
2200 } else {
2201 let may: Result<Map<&Attribute, &SchemaAttribute>, _> = classes
2209 .iter()
2210 .flat_map(|cls| {
2212 trace!(?cls);
2213 cls.systemmust
2214 .iter()
2215 .chain(cls.must.iter())
2216 .chain(cls.systemmay.iter())
2217 .chain(cls.may.iter())
2218 })
2219 .map(|s| {
2220 Ok((s, schema_attributes.get(s).ok_or(SchemaError::Corrupted)?))
2223 })
2224 .collect();
2225
2226 let may = may?;
2227
2228 self.attrs.iter().try_for_each(|(attr_name, avas)| {
2237 match may.get(attr_name) {
2238 Some(a_schema) => {
2239 a_schema.validate_ava(attr_name, avas)
2242 }
2244 None => {
2245 admin_error!(
2246 "{} {} - not found in the list of valid attributes for this set of classes {:?} - valid attributes are {:?}",
2247
2248 attr_name.as_str(),
2249 self.get_display_id(),
2250 entry_classes.iter().collect::<Vec<_>>(),
2251 may.keys().collect::<Vec<_>>()
2252 );
2253 Err(SchemaError::AttributeNotValidForClass(
2254 attr_name.to_string()
2255 ))
2256 }
2257 }
2258 })?;
2259 }
2260
2261 Ok(())
2263 }
2264
2265 pub fn seal(mut self, schema: &dyn SchemaTransaction) -> Entry<EntrySealed, STATE> {
2266 let EntryValid { uuid, mut ecstate } = self.valid;
2267
2268 ecstate.retain(|k, _| schema.is_replicated(k));
2272
2273 let last_mod_cid = ecstate.get_max_cid();
2275 let cv = vs_cid![last_mod_cid.clone()];
2276 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
2277
2278 let create_at_cid = ecstate.at();
2282 let cv = vs_cid![create_at_cid.clone()];
2283 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
2284
2285 Entry {
2286 valid: EntrySealed { uuid, ecstate },
2287 state: self.state,
2288 attrs: self.attrs,
2289 }
2290 }
2291
2292 pub fn get_uuid(&self) -> Uuid {
2293 self.valid.uuid
2294 }
2295}
2296
2297impl<STATE> GetUuid for Entry<EntrySealed, STATE>
2298where
2299 STATE: Clone,
2300{
2301 fn get_uuid(&self) -> Uuid {
2302 self.valid.uuid
2303 }
2304}
2305
2306impl<STATE> Entry<EntrySealed, STATE>
2307where
2308 STATE: Clone,
2309{
2310 pub fn invalidate(mut self, cid: Cid, trim_cid: &Cid) -> Entry<EntryInvalid, STATE> {
2311 for vs in self.attrs.values_mut() {
2313 vs.trim(trim_cid);
2314 }
2315
2316 let last_mod_cid = self.valid.ecstate.get_max_cid();
2324 let cv = vs_cid![last_mod_cid.clone()];
2325 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
2326
2327 let create_at_cid = self.valid.ecstate.at();
2328 let cv = vs_cid![create_at_cid.clone()];
2329 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
2330
2331 Entry {
2332 valid: EntryInvalid {
2333 cid,
2334 ecstate: self.valid.ecstate,
2335 },
2336 state: self.state,
2337 attrs: self.attrs,
2338 }
2339 }
2340
2341 pub fn get_uuid(&self) -> Uuid {
2342 self.valid.uuid
2343 }
2344
2345 pub fn get_changestate(&self) -> &EntryChangeState {
2346 &self.valid.ecstate
2347 }
2348
2349 pub(crate) fn entry_changed_excluding_attribute<A: AsRef<Attribute>>(
2353 &self,
2354 attr: A,
2355 cid: &Cid,
2356 ) -> bool {
2357 let attr_ref = attr.as_ref();
2358
2359 use crate::repl::entry::State;
2360
2361 match self.get_changestate().current() {
2362 State::Live { at: _, changes } => {
2363 changes.iter().any(|(change_attr, change_id)| {
2364 change_id >= cid &&
2365 *change_attr != *attr_ref &&
2366 *change_attr != Attribute::LastModifiedCid
2368 })
2369 }
2370 State::Tombstone { at } => at == cid,
2371 }
2372 }
2373
2374 #[cfg(test)]
2378 pub(crate) fn into_invalid(mut self) -> Entry<EntryInvalid, STATE> {
2379 let cid = Cid::new_zero();
2380 self.set_last_changed(cid.clone());
2381
2382 let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
2383
2384 Entry {
2385 valid: EntryInvalid { cid, ecstate },
2386 state: self.state,
2387 attrs: self.attrs,
2388 }
2389 }
2390}
2391
2392impl GetUuid for Entry<EntryReduced, EntryCommitted> {
2393 fn get_uuid(&self) -> Uuid {
2394 self.valid.uuid
2395 }
2396}
2397
2398impl Entry<EntryReduced, EntryCommitted> {
2399 pub fn get_uuid(&self) -> Uuid {
2400 self.valid.uuid
2401 }
2402
2403 pub fn to_pe(&self, qs: &mut QueryServerReadTransaction) -> Result<ProtoEntry, OperationError> {
2405 let attrs: Result<_, _> = self
2407 .attrs
2408 .iter()
2409 .map(|(k, vs)| qs.resolve_valueset(vs).map(|pvs| (k.to_string(), pvs)))
2410 .collect();
2411 Ok(ProtoEntry { attrs: attrs? })
2412 }
2413
2414 pub fn to_scim_kanidm<'a, TXN>(
2415 &self,
2416 read_txn: &mut TXN,
2417 ) -> Result<ScimEntryKanidm, OperationError>
2418 where
2419 TXN: QueryServerTransaction<'a>,
2420 {
2421 let result: Result<BTreeMap<Attribute, ScimValueKanidm>, OperationError> = self
2422 .attrs
2423 .iter()
2424 .filter(|(k, _vs)| **k != Attribute::Uuid)
2426 .filter_map(|(k, vs)| {
2427 let opt_resolve_status = vs.to_scim_value();
2428 let res_opt_scim_value = match opt_resolve_status {
2429 None => Ok(None),
2430 Some(ScimResolveStatus::Resolved(scim_value_kani)) => Ok(Some(scim_value_kani)),
2431 Some(ScimResolveStatus::NeedsResolution(scim_value_interim)) => {
2432 read_txn.resolve_scim_interim(scim_value_interim)
2433 }
2434 };
2435 res_opt_scim_value
2436 .transpose()
2437 .map(|scim_res| scim_res.map(|scim_value| (k.clone(), scim_value)))
2438 })
2439 .collect();
2440
2441 let attrs = result?;
2442
2443 let ext_access_check = self.valid.effective_access.as_ref().map(|eff_acc| {
2444 let ident = eff_acc.ident;
2445 let delete = eff_acc.delete;
2446 let search = (&eff_acc.search).into();
2447 let modify_present = (&eff_acc.modify_pres).into();
2448 let modify_remove = (&eff_acc.modify_rem).into();
2449
2450 ScimEffectiveAccess {
2451 ident,
2452 delete,
2453 search,
2454 modify_present,
2455 modify_remove,
2456 }
2457 });
2458
2459 let id = self.get_uuid();
2460
2461 let schemas = Vec::with_capacity(0);
2464
2465 Ok(ScimEntryKanidm {
2466 header: ScimEntryHeader {
2467 schemas,
2468 id,
2469 external_id: None,
2471 meta: None,
2474 },
2475 ext_access_check,
2476 attrs,
2477 })
2478 }
2479
2480 pub fn to_ldap(
2482 &self,
2483 qs: &mut QueryServerReadTransaction,
2484 basedn: &str,
2485 all_attrs: bool,
2487 l_attrs: &[String],
2490 ) -> Result<LdapSearchResultEntry, OperationError> {
2491 let rdn = qs.uuid_to_rdn(self.get_uuid())?;
2492
2493 let dn = format!("{rdn},{basedn}");
2494
2495 let attr_map: Result<Map<&str, Vec<Vec<u8>>>, _> = self
2500 .attrs
2501 .iter()
2502 .map(|(k, vs)| {
2503 qs.resolve_valueset_ldap(vs, basedn)
2504 .map(|pvs| (k.as_str(), pvs))
2505 })
2506 .collect();
2507 let attr_map = attr_map?;
2508
2509 let attr_names: Vec<(&str, &str)> = if all_attrs {
2512 self.attrs
2514 .keys()
2515 .map(|k| (k.as_str(), k.as_str()))
2516 .chain(
2517 l_attrs
2518 .iter()
2519 .map(|k| (k.as_str(), ldap_vattr_map(k.as_str()).unwrap_or(k.as_str()))),
2520 )
2521 .collect()
2522 } else {
2523 l_attrs
2525 .iter()
2526 .map(|k| (k.as_str(), ldap_vattr_map(k.as_str()).unwrap_or(k.as_str())))
2527 .collect()
2528 };
2529
2530 let attributes: Vec<_> = attr_names
2532 .into_iter()
2533 .filter_map(|(ldap_a, kani_a)| {
2534 match ldap_a {
2536 LDAP_ATTR_DN => Some(LdapPartialAttribute {
2537 atype: LDAP_ATTR_DN.to_string(),
2538 vals: vec![dn.as_bytes().to_vec()],
2539 }),
2540 LDAP_ATTR_ENTRYDN => Some(LdapPartialAttribute {
2541 atype: LDAP_ATTR_ENTRYDN.to_string(),
2542 vals: vec![dn.as_bytes().to_vec()],
2543 }),
2544 LDAP_ATTR_MAIL_PRIMARY | LDAP_ATTR_EMAIL_PRIMARY => {
2545 attr_map.get(kani_a).map(|pvs| LdapPartialAttribute {
2546 atype: ldap_a.to_string(),
2547 vals: pvs
2548 .first()
2549 .map(|first| vec![first.clone()])
2550 .unwrap_or_default(),
2551 })
2552 }
2553 LDAP_ATTR_MAIL_ALTERNATIVE | LDAP_ATTR_EMAIL_ALTERNATIVE => {
2554 attr_map.get(kani_a).map(|pvs| LdapPartialAttribute {
2555 atype: ldap_a.to_string(),
2556 vals: pvs
2557 .split_first()
2558 .map(|(_, rest)| rest.to_vec())
2559 .unwrap_or_default(),
2560 })
2561 }
2562 ATTR_HOME_DIRECTORY => Some(LdapPartialAttribute {
2563 atype: ATTR_HOME_DIRECTORY.to_string(),
2564 vals: vec![format!("/home/{}", self.get_uuid()).into_bytes()],
2565 }),
2566 _ => attr_map.get(kani_a).map(|pvs| LdapPartialAttribute {
2567 atype: ldap_a.to_string(),
2568 vals: pvs.clone(),
2569 }),
2570 }
2571 })
2572 .collect();
2573
2574 Ok(LdapSearchResultEntry { dn, attributes })
2575 }
2576}
2577
2578impl<VALID, STATE> Entry<VALID, STATE> {
2579 fn add_ava_int(&mut self, attr: Attribute, value: Value) -> bool {
2584 if let Some(vs) = self.attrs.get_mut(&attr) {
2585 let r = vs.insert_checked(value);
2586 debug_assert!(r.is_ok());
2587 r.unwrap_or(false)
2589 } else {
2590 #[allow(clippy::expect_used)]
2591 let vs = valueset::from_value_iter(std::iter::once(value))
2592 .expect("Unable to fail - non-zero iter, and single value type!");
2593 self.attrs.insert(attr, vs);
2594 true
2596 }
2597 }
2599
2600 fn set_ava_iter_int<T>(&mut self, attr: Attribute, iter: T)
2602 where
2603 T: IntoIterator<Item = Value>,
2604 {
2605 let Ok(vs) = valueset::from_value_iter(iter.into_iter()) else {
2606 trace!("set_ava_iter_int - empty from_value_iter, skipping");
2607 return;
2608 };
2609
2610 if let Some(existing_vs) = self.attrs.get_mut(&attr) {
2611 let _ = existing_vs.merge(&vs);
2613 } else {
2614 self.attrs.insert(attr, vs);
2616 }
2617 }
2618
2619 #[cfg(test)]
2621 fn set_last_changed(&mut self, cid: Cid) {
2622 let cv = vs_cid![cid.clone()];
2623 let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
2624 let cv = vs_cid![cid];
2625 let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
2626 }
2627
2628 pub(crate) fn get_display_id(&self) -> String {
2629 self.attrs
2630 .get(&Attribute::Spn)
2631 .map(|vs| vs.to_proto_string_clone_iter())
2632 .or_else(|| {
2633 self.attrs
2634 .get(&Attribute::Uuid)
2635 .map(|vs| vs.to_proto_string_clone_iter())
2636 })
2637 .and_then(|mut string_iter| string_iter.next())
2639 .unwrap_or_else(|| "no entry id available".to_string())
2640 }
2641
2642 pub fn has_class(&self, class: &EntryClass) -> bool {
2643 self.get_ava_set(Attribute::Class)
2644 .and_then(|vs| vs.as_iutf8_set())
2645 .map(|set| {
2646 let class_name: &str = class.into();
2647 set.contains(class_name)
2648 })
2649 .unwrap_or_default()
2650 }
2651
2652 pub fn attr_keys(&self) -> impl Iterator<Item = &Attribute> {
2653 self.attrs.keys()
2654 }
2655
2656 pub fn get_ava_names(&self) -> impl Iterator<Item = &str> {
2658 self.attrs.keys().map(|a| a.as_str())
2660 }
2661
2662 pub fn get_ava(&self) -> &Eattrs {
2664 &self.attrs
2665 }
2666
2667 pub fn get_ava_iter(&self) -> impl Iterator<Item = (&Attribute, &ValueSet)> {
2668 self.attrs.iter()
2669 }
2670
2671 pub fn get_ava_set<A: AsRef<Attribute>>(&self, attr: A) -> Option<&ValueSet> {
2673 self.attrs.get(attr.as_ref())
2674 }
2675
2676 pub fn get_ava_refer<A: AsRef<Attribute>>(&self, attr: A) -> Option<&BTreeSet<Uuid>> {
2677 self.get_ava_set(attr).and_then(|vs| vs.as_refer_set())
2678 }
2679
2680 pub fn get_ava_as_iutf8_iter<A: AsRef<Attribute>>(
2681 &self,
2682 attr: A,
2683 ) -> Option<impl Iterator<Item = &str>> {
2684 self.get_ava_set(attr).and_then(|vs| vs.as_iutf8_iter())
2685 }
2686
2687 pub fn get_ava_as_iutf8<A: AsRef<Attribute>>(&self, attr: A) -> Option<&BTreeSet<String>> {
2688 self.get_ava_set(attr).and_then(|vs| vs.as_iutf8_set())
2689 }
2690
2691 pub fn get_ava_as_image<A: AsRef<Attribute>>(&self, attr: A) -> Option<&HashSet<ImageValue>> {
2692 self.get_ava_set(attr).and_then(|vs| vs.as_imageset())
2693 }
2694
2695 pub fn get_ava_single_image<A: AsRef<Attribute>>(&self, attr: A) -> Option<ImageValue> {
2696 let images = self.get_ava_set(attr).and_then(|vs| vs.as_imageset())?;
2697 images.iter().next().cloned()
2698 }
2699
2700 pub fn get_ava_single_credential_type<A: AsRef<Attribute>>(
2701 &self,
2702 attr: A,
2703 ) -> Option<CredentialType> {
2704 self.get_ava_set(attr)
2705 .and_then(|vs| vs.to_credentialtype_single())
2706 }
2707
2708 pub fn get_ava_as_oauthscopes<A: AsRef<Attribute>>(
2709 &self,
2710 attr: A,
2711 ) -> Option<impl Iterator<Item = &str>> {
2712 self.get_ava_set(attr)
2713 .and_then(|vs| vs.as_oauthscope_iter())
2714 }
2715
2716 pub fn get_ava_as_oauthscopemaps<A: AsRef<Attribute>>(
2717 &self,
2718 attr: A,
2719 ) -> Option<&std::collections::BTreeMap<Uuid, std::collections::BTreeSet<String>>> {
2720 self.get_ava_set(attr).and_then(|vs| vs.as_oauthscopemap())
2721 }
2722
2723 pub fn get_ava_as_intenttokens<A: AsRef<Attribute>>(
2724 &self,
2725 attr: A,
2726 ) -> Option<&std::collections::BTreeMap<String, IntentTokenState>> {
2727 self.get_ava_set(attr)
2728 .and_then(|vs| vs.as_intenttoken_map())
2729 }
2730
2731 pub fn get_ava_as_session_map<A: AsRef<Attribute>>(
2732 &self,
2733 attr: A,
2734 ) -> Option<&std::collections::BTreeMap<Uuid, Session>> {
2735 self.get_ava_set(attr).and_then(|vs| vs.as_session_map())
2736 }
2737
2738 pub fn get_ava_as_apitoken_map<A: AsRef<Attribute>>(
2739 &self,
2740 attr: A,
2741 ) -> Option<&std::collections::BTreeMap<Uuid, ApiToken>> {
2742 self.get_ava_set(attr).and_then(|vs| vs.as_apitoken_map())
2743 }
2744
2745 pub fn get_ava_as_oauth2session_map<A: AsRef<Attribute>>(
2746 &self,
2747 attr: A,
2748 ) -> Option<&std::collections::BTreeMap<Uuid, Oauth2Session>> {
2749 self.get_ava_set(attr)
2750 .and_then(|vs| vs.as_oauth2session_map())
2751 }
2752
2753 pub fn get_ava_as_s256_set<A: AsRef<Attribute>>(
2754 &self,
2755 attr: A,
2756 ) -> Option<&std::collections::BTreeSet<Sha256Output>> {
2757 self.get_ava_set(attr).and_then(|vs| vs.as_s256_set())
2758 }
2759
2760 pub fn get_ava_iter_iname<A: AsRef<Attribute>>(
2762 &self,
2763 attr: A,
2764 ) -> Option<impl Iterator<Item = &str>> {
2765 self.get_ava_set(attr).and_then(|vs| vs.as_iname_iter())
2766 }
2767
2768 pub fn get_ava_iter_iutf8<A: AsRef<Attribute>>(
2770 &self,
2771 attr: A,
2772 ) -> Option<impl Iterator<Item = &str>> {
2773 self.get_ava_set(attr).and_then(|vs| vs.as_iutf8_iter())
2774 }
2775
2776 pub fn get_ava_as_refuuid<A: AsRef<Attribute>>(
2778 &self,
2779 attr: A,
2780 ) -> Option<Box<dyn Iterator<Item = Uuid> + '_>> {
2781 self.get_ava_set(attr).and_then(|vs| vs.as_ref_uuid_iter())
2783 }
2784
2785 pub fn get_ava_iter_sshpubkeys<A: AsRef<Attribute>>(
2787 &self,
2788 attr: A,
2789 ) -> Option<impl Iterator<Item = String> + '_> {
2790 self.get_ava_set(attr)
2791 .and_then(|vs| vs.as_sshpubkey_string_iter())
2792 }
2793
2794 pub fn get_ava_single<A: AsRef<Attribute>>(&self, attr: A) -> Option<Value> {
2800 self.get_ava_set(attr).and_then(|vs| vs.to_value_single())
2801 }
2802
2803 pub fn get_ava_single_proto_string<A: AsRef<Attribute>>(&self, attr: A) -> Option<String> {
2804 self.get_ava_set(attr)
2805 .and_then(|vs| vs.to_proto_string_single())
2806 }
2807
2808 pub fn get_ava_single_bool<A: AsRef<Attribute>>(&self, attr: A) -> Option<bool> {
2810 self.get_ava_set(attr).and_then(|vs| vs.to_bool_single())
2811 }
2812
2813 pub fn get_ava_single_uint32<A: AsRef<Attribute>>(&self, attr: A) -> Option<u32> {
2815 self.get_ava_set(attr).and_then(|vs| vs.to_uint32_single())
2816 }
2817
2818 pub fn get_ava_single_syntax<A: AsRef<Attribute>>(&self, attr: A) -> Option<SyntaxType> {
2820 self.get_ava_set(attr)
2821 .and_then(|vs| vs.to_syntaxtype_single())
2822 }
2823
2824 pub fn get_ava_single_credential<A: AsRef<Attribute>>(&self, attr: A) -> Option<&Credential> {
2826 self.get_ava_set(attr)
2827 .and_then(|vs| vs.to_credential_single())
2828 }
2829
2830 pub fn get_ava_passkeys<A: AsRef<Attribute>>(
2832 &self,
2833 attr: A,
2834 ) -> Option<&BTreeMap<Uuid, (String, PasskeyV4)>> {
2835 self.get_ava_set(attr).and_then(|vs| vs.as_passkey_map())
2836 }
2837
2838 pub fn get_ava_attestedpasskeys<A: AsRef<Attribute>>(
2840 &self,
2841 attr: A,
2842 ) -> Option<&BTreeMap<Uuid, (String, AttestedPasskeyV4)>> {
2843 self.get_ava_set(attr)
2844 .and_then(|vs| vs.as_attestedpasskey_map())
2845 }
2846
2847 pub fn get_ava_uihint<A: AsRef<Attribute>>(&self, attr: A) -> Option<&BTreeSet<UiHint>> {
2849 self.get_ava_set(attr).and_then(|vs| vs.as_uihint_set())
2850 }
2851
2852 pub fn get_ava_single_secret<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2854 self.get_ava_set(attr).and_then(|vs| vs.to_secret_single())
2855 }
2856
2857 pub fn get_ava_single_datetime<A: AsRef<Attribute>>(&self, attr: A) -> Option<OffsetDateTime> {
2859 self.get_ava_set(attr)
2860 .and_then(|vs| vs.to_datetime_single())
2861 }
2862
2863 pub(crate) fn get_ava_single_utf8<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2865 self.get_ava_set(attr).and_then(|vs| vs.to_utf8_single())
2866 }
2867
2868 pub(crate) fn get_ava_single_iutf8<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2870 self.get_ava_set(attr).and_then(|vs| vs.to_iutf8_single())
2871 }
2872
2873 pub(crate) fn get_ava_single_iname<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2875 self.get_ava_set(attr).and_then(|vs| vs.to_iname_single())
2876 }
2877
2878 pub fn get_ava_single_url<A: AsRef<Attribute>>(&self, attr: A) -> Option<&Url> {
2880 self.get_ava_set(attr).and_then(|vs| vs.to_url_single())
2881 }
2882
2883 pub fn get_ava_single_uuid<A: AsRef<Attribute>>(&self, attr: A) -> Option<Uuid> {
2884 self.get_ava_set(attr).and_then(|vs| vs.to_uuid_single())
2885 }
2886
2887 pub fn get_ava_single_refer<A: AsRef<Attribute>>(&self, attr: A) -> Option<Uuid> {
2888 self.get_ava_set(attr).and_then(|vs| vs.to_refer_single())
2889 }
2890
2891 pub fn get_ava_mail_primary<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2892 self.get_ava_set(attr)
2893 .and_then(|vs| vs.to_email_address_primary_str())
2894 }
2895
2896 pub fn get_ava_iter_mail<A: AsRef<Attribute>>(
2897 &self,
2898 attr: A,
2899 ) -> Option<impl Iterator<Item = &str>> {
2900 self.get_ava_set(attr).and_then(|vs| vs.as_email_str_iter())
2901 }
2902
2903 pub fn get_ava_single_protofilter<A: AsRef<Attribute>>(&self, attr: A) -> Option<&ProtoFilter> {
2905 self.get_ava_set(attr)
2906 .and_then(|vs| vs.to_json_filter_single())
2907 }
2908
2909 pub fn get_ava_single_private_binary<A: AsRef<Attribute>>(&self, attr: A) -> Option<&[u8]> {
2910 self.get_ava_set(attr)
2911 .and_then(|vs| vs.to_private_binary_single())
2912 }
2913
2914 pub fn get_ava_single_jws_key_es256<A: AsRef<Attribute>>(
2915 &self,
2916 attr: A,
2917 ) -> Option<&JwsEs256Signer> {
2918 self.get_ava_set(attr)
2919 .and_then(|vs| vs.to_jws_key_es256_single())
2920 }
2921
2922 pub fn get_ava_webauthn_attestation_ca_list<A: AsRef<Attribute>>(
2923 &self,
2924 attr: A,
2925 ) -> Option<&AttestationCaList> {
2926 self.get_ava_set(attr)
2927 .and_then(|vs| vs.as_webauthn_attestation_ca_list())
2928 }
2929
2930 pub fn get_ava_application_password<A: AsRef<Attribute>>(
2931 &self,
2932 attr: A,
2933 ) -> Option<&BTreeMap<Uuid, Vec<ApplicationPassword>>> {
2934 self.get_ava_set(attr)
2935 .and_then(|vs| vs.as_application_password_map())
2936 }
2937
2938 pub(crate) fn generate_spn(&self, domain_name: &str) -> Option<ValueSet> {
2940 if let Some(name) = self.get_ava_single_iname(Attribute::Name) {
2941 return Some(ValueSetSpn::new((name.into(), domain_name.into())));
2943 }
2944
2945 let spn_set = self.get_ava_set(Attribute::Spn)?;
2947
2948 if spn_set.syntax() == SyntaxType::SecurityPrincipalName {
2949 Some(spn_set.clone())
2951 } else if let Some(name) = spn_set.to_iname_single() {
2952 Some(ValueSetSpn::new((name.into(), domain_name.into())))
2954 } else {
2955 None
2957 }
2958 }
2959
2960 pub fn attribute_pres<A: AsRef<Attribute>>(&self, attr: A) -> bool {
2962 self.attrs.contains_key(attr.as_ref())
2963 }
2964
2965 pub fn attribute_equality<A: AsRef<Attribute>>(&self, attr: A, value: &PartialValue) -> bool {
2968 match self.attrs.get(attr.as_ref()) {
2973 Some(v_list) => v_list.contains(value),
2974 None => false,
2975 }
2976 }
2977
2978 pub fn attribute_substring<A: AsRef<Attribute>>(
2981 &self,
2982 attr: A,
2983 subvalue: &PartialValue,
2984 ) -> bool {
2985 self.get_ava_set(attr)
2986 .map(|vset| vset.substring(subvalue))
2987 .unwrap_or(false)
2988 }
2989
2990 pub fn attribute_startswith<A: AsRef<Attribute>>(
2993 &self,
2994 attr: A,
2995 subvalue: &PartialValue,
2996 ) -> bool {
2997 self.get_ava_set(attr)
2998 .map(|vset| vset.startswith(subvalue))
2999 .unwrap_or(false)
3000 }
3001
3002 pub fn attribute_endswith<A: AsRef<Attribute>>(
3005 &self,
3006 attr: A,
3007 subvalue: &PartialValue,
3008 ) -> bool {
3009 self.get_ava_set(attr)
3010 .map(|vset| vset.endswith(subvalue))
3011 .unwrap_or(false)
3012 }
3013
3014 pub fn attribute_lessthan<A: AsRef<Attribute>>(
3017 &self,
3018 attr: A,
3019 subvalue: &PartialValue,
3020 ) -> bool {
3021 self.get_ava_set(attr)
3022 .map(|vset| vset.lessthan(subvalue))
3023 .unwrap_or(false)
3024 }
3025
3026 #[inline(always)]
3031 #[instrument(level = "trace", name = "entry::entry_match_no_index", skip(self))]
3032 pub fn entry_match_no_index(&self, filter: &Filter<FilterValidResolved>) -> bool {
3034 self.entry_match_no_index_inner(filter.to_inner())
3035 }
3036
3037 fn entry_match_no_index_inner(&self, filter: &FilterResolved) -> bool {
3041 match filter {
3044 FilterResolved::Eq(attr, value, _) => self.attribute_equality(attr, value),
3045 FilterResolved::Cnt(attr, subvalue, _) => self.attribute_substring(attr, subvalue),
3046 FilterResolved::Stw(attr, subvalue, _) => self.attribute_startswith(attr, subvalue),
3047 FilterResolved::Enw(attr, subvalue, _) => self.attribute_endswith(attr, subvalue),
3048 FilterResolved::Pres(attr, _) => self.attribute_pres(attr),
3049 FilterResolved::LessThan(attr, subvalue, _) => self.attribute_lessthan(attr, subvalue),
3050 FilterResolved::Or(l, _) => l.iter().any(|f| self.entry_match_no_index_inner(f)),
3052 FilterResolved::And(l, _) => l.iter().all(|f| self.entry_match_no_index_inner(f)),
3054 FilterResolved::Inclusion(_, _) => {
3055 false
3059 }
3060 FilterResolved::AndNot(f, _) => !self.entry_match_no_index_inner(f),
3061 FilterResolved::Invalid(_) => false,
3062 }
3063 }
3064
3065 pub fn filter_from_attrs(&self, attrs: &[Attribute]) -> Option<Filter<FilterInvalid>> {
3068 let mut pairs: Vec<(Attribute, PartialValue)> = Vec::with_capacity(0);
3080
3081 for attr in attrs {
3082 match self.attrs.get(attr) {
3083 Some(values) => values
3084 .to_partialvalue_iter()
3085 .for_each(|pv| pairs.push((attr.clone(), pv))),
3086 None => return None,
3087 }
3088 }
3089
3090 let res: Vec<FC> = pairs
3091 .into_iter()
3092 .map(|(attr, pv)| FC::Eq(attr, pv))
3093 .collect();
3094 Some(filter_all!(f_and(res)))
3095 }
3096
3097 pub fn gen_modlist_assert(
3100 &self,
3101 schema: &dyn SchemaTransaction,
3102 ) -> Result<ModifyList<ModifyInvalid>, SchemaError> {
3103 let mut mods = ModifyList::new();
3107
3108 for (k, vs) in self.attrs.iter() {
3109 if *k == Attribute::Uuid {
3125 continue;
3126 }
3127 match schema.is_multivalue(k) {
3129 Ok(r) => {
3130 if !r ||
3133 *k == Attribute::AcpReceiverGroup ||
3136 *k == Attribute::AcpCreateAttr ||
3137 *k == Attribute::AcpCreateClass ||
3138 *k == Attribute::AcpModifyPresentAttr ||
3139 *k == Attribute::AcpModifyRemovedAttr ||
3140 *k == Attribute::AcpModifyClass ||
3141 *k == Attribute::SystemMust ||
3142 *k == Attribute::SystemMay
3143 {
3144 mods.push_mod(Modify::Purged(k.clone()));
3145 }
3146 }
3147 Err(e) => return Err(e),
3149 }
3150 for v in vs.to_value_iter() {
3151 mods.push_mod(Modify::Present(k.clone(), v.clone()));
3152 }
3153 }
3154
3155 Ok(mods)
3156 }
3157
3158 pub fn mask_recycled_ts(&self) -> Option<&Self> {
3161 match self.attrs.get(&Attribute::Class) {
3163 Some(cls) => {
3164 if cls.contains(&EntryClass::Tombstone.to_partialvalue())
3165 || cls.contains(&EntryClass::Recycled.to_partialvalue())
3166 {
3167 None
3168 } else {
3169 Some(self)
3170 }
3171 }
3172 None => Some(self),
3173 }
3174 }
3175
3176 pub fn mask_recycled(&self) -> Option<&Self> {
3179 match self.attrs.get(&Attribute::Class) {
3181 Some(cls) => {
3182 if cls.contains(&EntryClass::Recycled.to_partialvalue()) {
3183 None
3184 } else {
3185 Some(self)
3186 }
3187 }
3188 None => Some(self),
3189 }
3190 }
3191
3192 pub fn mask_tombstone(&self) -> Option<&Self> {
3195 match self.attrs.get(&Attribute::Class) {
3197 Some(cls) => {
3198 if cls.contains(&EntryClass::Tombstone.to_partialvalue()) {
3199 None
3200 } else {
3201 Some(self)
3202 }
3203 }
3204 None => Some(self),
3205 }
3206 }
3207}
3208
3209impl<STATE> Entry<EntryInvalid, STATE>
3210where
3211 STATE: Clone,
3212{
3213 pub fn add_ava(&mut self, attr: Attribute, value: Value) {
3218 self.valid.ecstate.change_ava(&self.valid.cid, &attr);
3219 self.add_ava_int(attr, value);
3220 }
3221
3222 pub fn add_ava_if_not_exist<A: AsRef<Attribute>>(&mut self, attr: A, value: Value) {
3223 let attr_ref = attr.as_ref();
3224 if self.add_ava_int(attr_ref.clone(), value) {
3226 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3228 }
3229 }
3230
3231 fn assert_ava<A: AsRef<Attribute>>(
3232 &mut self,
3233 attr: A,
3234 value: &PartialValue,
3235 ) -> Result<(), OperationError> {
3236 self.valid
3237 .ecstate
3238 .change_ava(&self.valid.cid, attr.as_ref());
3239
3240 if self.attribute_equality(attr, value) {
3241 Ok(())
3242 } else {
3243 Err(OperationError::ModifyAssertionFailed)
3244 }
3245 }
3246
3247 pub(crate) fn remove_ava<A: AsRef<Attribute>>(&mut self, attr: A, value: &PartialValue) {
3250 let attr_ref = attr.as_ref();
3251 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3252
3253 let rm = if let Some(vs) = self.attrs.get_mut(attr_ref) {
3254 vs.remove(value, &self.valid.cid);
3255 vs.is_empty()
3256 } else {
3257 false
3258 };
3259 if rm {
3260 self.attrs.remove(attr_ref);
3261 };
3262 }
3263
3264 pub(crate) fn remove_avas<A: AsRef<Attribute>>(
3265 &mut self,
3266 attr: A,
3267 values: &BTreeSet<PartialValue>,
3268 ) {
3269 let attr_ref = attr.as_ref();
3270 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3271
3272 let rm = if let Some(vs) = self.attrs.get_mut(attr_ref) {
3273 values.iter().for_each(|k| {
3274 vs.remove(k, &self.valid.cid);
3275 });
3276 vs.is_empty()
3277 } else {
3278 false
3279 };
3280 if rm {
3281 self.attrs.remove(attr_ref);
3282 };
3283 }
3284
3285 pub(crate) fn purge_ava<A: AsRef<Attribute>>(&mut self, attr: A) {
3288 let attr_ref = attr.as_ref();
3289 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3290 let can_remove = self
3293 .attrs
3294 .get_mut(attr_ref)
3295 .map(|vs| vs.purge(&self.valid.cid))
3296 .unwrap_or_default();
3298 if can_remove {
3299 self.attrs.remove(attr_ref);
3300 }
3301 }
3302
3303 pub fn pop_ava<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<ValueSet> {
3305 let attr_ref = attr.as_ref();
3306 self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3307
3308 let mut vs = self.attrs.remove(attr_ref)?;
3309 if vs.purge(&self.valid.cid) {
3310 Some(vs)
3312 } else {
3313 let r_vs = vs.clone();
3315 self.attrs.insert(attr_ref.clone(), vs);
3316 Some(r_vs)
3317 }
3318 }
3319
3320 #[cfg(test)]
3325 pub(crate) fn force_trim_ava<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<ValueSet> {
3326 self.valid
3327 .ecstate
3328 .change_ava(&self.valid.cid, attr.as_ref());
3329 self.attrs.remove(attr.as_ref())
3330 }
3331
3332 pub fn set_ava<T>(&mut self, attr: &Attribute, iter: T)
3335 where
3336 T: Clone + IntoIterator<Item = Value>,
3337 {
3338 self.purge_ava(attr);
3339 self.set_ava_iter_int(attr.clone(), iter)
3340 }
3341
3342 pub fn set_ava_set(&mut self, attr: &Attribute, vs: ValueSet) {
3345 self.purge_ava(attr);
3346 if let Some(existing_vs) = self.attrs.get_mut(attr) {
3347 let _ = existing_vs.merge(&vs);
3348 } else {
3349 self.attrs.insert(attr.clone(), vs);
3350 }
3351 }
3352
3353 pub fn merge_ava_set(&mut self, attr: &Attribute, vs: ValueSet) -> Result<(), OperationError> {
3356 self.valid.ecstate.change_ava(&self.valid.cid, attr);
3357 if let Some(existing_vs) = self.attrs.get_mut(attr) {
3358 existing_vs.merge(&vs)
3359 } else {
3360 self.attrs.insert(attr.clone(), vs);
3361 Ok(())
3362 }
3363 }
3364
3365 pub fn apply_modlist(
3367 &mut self,
3368 modlist: &ModifyList<ModifyValid>,
3369 ) -> Result<(), OperationError> {
3370 for modify in modlist {
3371 match modify {
3372 Modify::Present(attr, value) => {
3373 self.add_ava(attr.clone(), value.clone());
3374 }
3375 Modify::Removed(attr, value) => {
3376 self.remove_ava(attr, value);
3377 }
3378 Modify::Purged(attr) => {
3379 self.purge_ava(attr);
3380 }
3381 Modify::Assert(attr, value) => {
3382 self.assert_ava(attr, value).inspect_err(|_e| {
3383 error!("Modification assertion was not met. {} {:?}", attr, value);
3384 })?;
3385 }
3386 Modify::Set(attr, valueset) => self.set_ava_set(attr, valueset.clone()),
3387 }
3388 }
3389 Ok(())
3390 }
3391}
3392
3393impl<VALID, STATE> PartialEq for Entry<VALID, STATE> {
3394 fn eq(&self, rhs: &Entry<VALID, STATE>) -> bool {
3395 compare_attrs(&self.attrs, &rhs.attrs)
3406 }
3407}
3408
3409pub(crate) fn entry_init_fn<T>(args: T) -> EntryInitNew
3411where
3412 T: IntoIterator<Item = (Attribute, Value)>,
3413{
3414 let mut entry: EntryInitNew = Entry::new();
3415 args.into_iter().for_each(|(k, v)| entry.add_ava(k, v));
3416 entry
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}