kanidmd_lib/
entry.rs

1//! Entries are the base unit of object storage in the server. This is one of the three foundational
2//! concepts along with [`filter`]s and [`schema`] that everything else builds upon.
3//!
4//! An [`Entry`] is a collection of attribute-value sets. There are sometimes called attribute value
5//! assertions, or AVAs. The attribute is a "key" and it holds 1 to infinite associated values
6//! with no ordering. An entry has many AVAs. A pseudo example, minus schema and typing:
7//!
8//! ```text
9//! Entry {
10//!   "name": ["william"],
11//!   "uuid": ["..."],
12//!   "mail": ["maila@example.com", "mailb@example.com"],
13//! };
14//! ```
15//!
16//! There are three rules for entries:
17//! * Must have an AVA for UUID containing a single value.
18//! * Any AVA with zero values will be removed.
19//! * AVAs are stored with no sorting.
20//!
21//! For more, see the [`Entry`] type.
22//!
23//! [`Entry`]: struct.Entry.html
24//! [`filter`]: ../filter/index.html
25//! [`schema`]: ../schema/index.html
26
27use 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// Entry should have a lifecycle of types. This is Raw (modifiable) and Entry (verified).
84// This way, we can move between them, but only certain actions are possible on either
85// This means modifications happen on Raw, but to move to Entry, you schema normalise.
86// Vice versa, you can for free, move to Raw, but you lose the validation.
87
88// Because this is type system it's "free" in the end, and means we force validation
89// at the correct and required points of the entries life.
90
91// This is specifically important for the commit to the backend, as we only want to
92// commit validated types.
93
94// Has never been in the DB, so doesn't have an ID.
95#[derive(Clone, Debug)]
96pub struct EntryNew; // new
97
98// It's been in the DB, so it has an id
99#[derive(Clone, Debug)]
100pub struct EntryCommitted {
101    id: u64,
102}
103
104#[derive(Clone, Debug)]
105pub struct EntryInit;
106
107/*  |
108 *  | Init comes from a proto entry, it's new.
109 *  | We add the current Cid before we allow mods.
110 *  V
111 */
112
113#[derive(Clone, Debug)]
114pub struct EntryInvalid {
115    cid: Cid,
116    ecstate: EntryChangeState,
117}
118
119// Alternate path - this entry came from a full refresh, and already has an entry change state.
120#[derive(Clone, Debug)]
121pub struct EntryRefresh {
122    ecstate: EntryChangeState,
123}
124
125// Alternate path - this entry came from an incremental replication.
126#[derive(Clone, Debug)]
127pub struct EntryIncremental {
128    // Must have a uuid, else we can't proceed at all.
129    uuid: Uuid,
130    ecstate: EntryChangeState,
131}
132
133/*  |
134 *  | The changes made within this entry are validated by the schema.
135 *  V
136 */
137
138#[derive(Clone, Debug)]
139pub struct EntryValid {
140    // Asserted with schema, so we know it has a UUID now ...
141    uuid: Uuid,
142    ecstate: EntryChangeState,
143}
144
145/*  |
146 *  | The changes are extracted into the changelog as needed, creating a
147 *  | stable database entry.
148 *  V
149 */
150
151#[derive(Clone, Debug)]
152pub struct EntrySealed {
153    uuid: Uuid,
154    ecstate: EntryChangeState,
155}
156
157/*  |
158 *  | The entry has access controls applied to reduce what is yielded to a client
159 *  V
160 */
161
162#[derive(Clone, Debug)]
163pub struct EntryReduced {
164    uuid: Uuid,
165    effective_access: Option<Box<AccessEffectivePermission>>,
166}
167
168// One day this is going to be Map<Attribute, ValueSet> - @yaleman
169// Today is that day - @firstyear
170pub 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    // We can't shortcut based on len because cid mod may not be present.
183    // Build the set of all keys between both.
184    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        // Both must be Some, and both must have the same interiors.
192        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
205/// Entry is the core data storage type of the server. Almost every aspect of the server is
206/// designed to read, handle and manipulate entries.
207///
208/// Entries store attribute value assertions, or AVA. These are sets of key-values.
209///
210/// Entries have a lifecycle within a single operation, and as part of replication.
211/// The lifecycle for operations is defined through state and valid types. Each entry has a pair
212/// Of these types at anytime. The first is the AVA [`schema`] and [`access`] control assertion
213/// state. This is represented by the type `VALID` as one of `EntryValid`, `EntryInvalid` or
214/// `EntryReduced`. Every entry starts as `EntryInvalid`, and when checked by the schema for
215/// correctness, transitions to `EntryValid`. While an entry is `EntryValid` it can not be
216/// altered - you must invalidate it to `EntryInvalid`, then modify, then check again.
217/// An entry that has had access controls applied moves from `EntryValid` to `EntryReduced`,
218/// to show that the AVAs have reduced to the valid read set of the current [`event`] user.
219///
220/// The second type of `STATE` represents the database commit state and internal db ID's. A
221/// new entry that has never been committed is `EntryNew`, but an entry that has been retrieved
222/// from the database is `EntryCommitted`. This affects the operations you can apply IE modify
223/// or delete.
224///
225/// These types exist to prevent at compile time, mishandling of Entries, to ensure they are always
226/// handled with the correct lifecycles and processes.
227///
228/// [`schema`]: ../schema/index.html
229/// [`access`]: ../access/index.html
230/// [`event`]: ../event/index.html
231pub struct Entry<VALID, STATE> {
232    valid: VALID,
233    state: STATE,
234    // We may need to change this to Set to allow borrow of Value -> PartialValue for lookups.
235    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    /// Get the uuid of this entry.
275    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            // This means NEVER COMMITTED
292            valid: EntryInit,
293            state: EntryNew,
294            attrs: Map::new(),
295            // attrs: Map::with_capacity(32),
296        }
297    }
298
299    /// Consume a Protocol Entry from JSON, and validate and process the data into an internal
300    /// [`Entry`] type.
301    pub fn from_proto_entry(
302        e: &ProtoEntry,
303        qs: &mut QueryServerWriteTransaction,
304    ) -> Result<Self, OperationError> {
305        trace!("from_proto_entry");
306        // Why not the trait? In the future we may want to extend
307        // this with server aware functions for changes of the
308        // incoming data.
309
310        // Somehow we need to take the tree of e attrs, and convert
311        // all ref types to our types ...
312        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    /// Given a proto entry in JSON formed as a serialised string, processed that string
340    /// into an Entry.
341    #[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        // str -> Proto entry
355        let pe: ProtoEntry = serde_json::from_str(es).map_err(|e| {
356            // We probably shouldn't print ES here because that would allow users
357            // to inject content into our logs :)
358            admin_error!(?e, "SerdeJson Failure");
359            OperationError::SerdeJsonError
360        })?;
361        // now call from_proto_entry
362        Self::from_proto_entry(&pe, qs)
363    }
364
365    /// Assign the Change Identifier to this Entry, allowing it to be modified and then
366    /// written to the `Backend`
367    pub fn assign_cid(
368        mut self,
369        cid: Cid,
370        schema: &dyn SchemaTransaction,
371    ) -> Entry<EntryInvalid, EntryNew> {
372        /*
373         * Create the change log. This must be the last thing BEFORE we return!
374         * This is because we need to capture the set_last_changed attribute in
375         * the create transition.
376         */
377        let ecstate = EntryChangeState::new(&cid, &self.attrs, schema);
378
379        // Since the entry is now created, and modified here, we set the initial CID
380        // values.
381        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    /// Compare this entry to another.
394    pub fn compare(&self, rhs: &Entry<EntrySealed, EntryCommitted>) -> bool {
395        compare_attrs(&self.attrs, &rhs.attrs)
396    }
397
398    /// ⚠️  This function bypasses the db commit and creates invalid replication metadata.
399    /// The entry it creates can never be replicated.
400    /// This is a TEST ONLY method and will never be exposed in production.
401    #[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    /// ⚠️  This function bypasses the db commit and creates invalid replication metadata.
416    /// The entry it creates can never be replicated.
417    /// This is a TEST ONLY method and will never be exposed in production.
418    #[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    /// ⚠️  This function bypasses the db commit, assigns fake db ids, and invalid replication metadata.
435    /// The entry it creates can never be committed safely or replicated.
436    /// This is a TEST ONLY method and will never be exposed in production.
437    #[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    /// ⚠️  This function bypasses the db commit and creates invalid replication metadata.
451    /// The entry it creates can never be replicated.
452    /// This is a TEST ONLY method and will never be exposed in production.
453    #[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    //              ⚠️   replication safety  ⚠️
470    // These functions are SAFE because they occur in the EntryInit
471    // state, which precedes the generation of the initial Create
472    // event for the attribute.
473    /// Add an attribute-value-assertion to this Entry.
474    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    /// Replace the existing content of an attribute set of this Entry, with a new set of Values.
483    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        // Build the Map of the attributes
504        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        // Insert stuff.
525
526        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        // From the entry, we have to rebuild the ecstate and the attrs.
589        let (ecstate, mut attrs) = repl_entry.rehydrate()?;
590
591        // During seal, these values will be re-written, but we need them present for
592        // schema validation.
593        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        // Build the new valid entry ...
624        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        // This is a conflict if the state 'at' is not identical
662        let self_cs = &self.valid.ecstate;
663        let db_cs = db_entry.get_changestate();
664
665        // Can only add conflict on live entries.
666        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            // Tombstone will always overwrite.
671            _ => 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                // Determine which of the entries must become the conflict
698                // and which will now persist. There are three possible cases.
699                //
700                // 1. The incoming ReplIncremental is after DBentry. This means RI is the
701                //    conflicting node. We take no action and just return the db_ent
702                //    as the valid state.
703                //
704                //    Since we are returning the existing database entry, we already have
705                //    locally applies the needed LastModifiedCid and CreatedAtCid. We
706                //    can proceed with no other changes.
707                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                //
724                // 2. The incoming ReplIncremental is before DBentry. This means our
725                //    DE is the conflicting note. There are now two choices:
726                //    a.  We are the origin of the DE, and thus must create the conflict
727                //        entry for replication (to guarantee single create)
728                //    b.  We are not the origin of the DE and so do not create a conflict
729                //        entry.
730                //    In both cases we update the DE with the state of RI after we have
731                //    followed the above logic.
732                else {
733                    trace!("RI < DE, return RI");
734                    // Are we the origin?
735                    let conflict = if at_right.s_uuid == cid.s_uuid {
736                        trace!("Origin process conflict entry");
737                        // We are making a new entry!
738
739                        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                        // Move the current uuid to source_uuid
749                        cnf_ent.add_ava(Attribute::SourceUuid, Value::Uuid(db_ent.valid.uuid));
750
751                        // We need to make a random uuid in the conflict gen process.
752                        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                        // Bypass add_ava here so that we don't update the ecstate with the
759                        // metadata of these attrs.
760                        // Setup the last changed to now.
761                        let cv = vs_cid![cid.clone()];
762                        let _ = cnf_ent.attrs.insert(Attribute::LastModifiedCid, cv);
763                        // Set the created_at to now, since we are creating a new conflict entry here.
764                        let cv = vs_cid![cid.clone()];
765                        let _ = cnf_ent.attrs.insert(Attribute::CreatedAtCid, cv);
766
767                        // Now we have to internally bypass some states.
768                        // This is okay because conflict entries aren't subject
769                        // to schema anyway.
770                        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                    // Since we are going to make the incoming node, we need to now
791                    // populate it's last-mod and created attributes.
792
793                    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            // Can never get here due to is_add_conflict above.
820            _ => 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        // Paranoid check.
833        debug_assert_eq!(self.valid.uuid, db_ent.valid.uuid);
834
835        // First, determine if either side is a tombstone. This is needed so that only
836        // when both sides are live
837        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                // Given the current db entry, compare and merge our attributes to
853                // form a resultant entry attr and ecstate
854                //
855                // To shortcut this we dedup the attr set and then iterate.
856                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                // Make a new ecstate and attrs set.
863                let mut changes = BTreeMap::default();
864                let mut eattrs = Eattrs::default();
865
866                // Now we have the set of attrs from both sides. Lets see what state they are in!
867                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                            // This is the normal / usual and most "fun" case. Here we need to determine
871                            // which side is latest and then do a valueset merge. This is also
872                            // needing schema awareness depending on the attribute!
873                            //
874                            // The behaviour is very dependent on the state of the attributes and
875                            // if they exist.
876                            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                                        // NOTE: This is for special attr types that need to merge
886                                        // rather than choose content.
887                                        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                                        // NOTE: This is for special attr types that need to merge
899                                        // rather than choose content.
900                                        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                                    // Taking right, nothing to do due to no attr.
912                                }
913                                (None, Some(_vs_right)) if take_left => {
914                                    changes.insert(attr_name.clone(), cid_left.clone());
915                                    // Taking left, nothing to do due to no attr.
916                                }
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                                    // Taking left, nothing to do due to no attr.
924                                }
925                                (None, None) => {
926                                    changes.insert(attr_name.clone(), cid_right.clone());
927                                    // Taking right, nothing to do due to no attr.
928                                }
929                            }
930                            // End attr merging
931                        }
932                        (Some(cid_left), None) => {
933                            // Keep the value on the left.
934                            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                            // Keep the value on the right.
941                            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                            // Should be impossible! At least one side or the other must have a change.
948                            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                // Similar to the process of "seal", remove anything that isn't
959                // replicated from the ecstate (should be a no-op), and then update
960                // the created/mod cid's.
961                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                // We have to generate the attrs here, since on replication
982                // we just send the tombstone ecstate rather than attrs. Our
983                // db stub also lacks these attributes too.
984                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                // Our current DB entry is a tombstone - ignore the incoming live
1007                // entry and just retain our DB tombstone.
1008                //
1009                // Note we don't need to gen the attrs here since if a stub was made then
1010                // we'd be live:live. To be in live:ts, then our db entry MUST exist and
1011                // must be a ts.
1012
1013                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                // WARNING - this differs from the other tombstone check cases
1026                // lower of the two AT values. This way replicas always have the
1027                // earliest TS value. It's a rare case but needs handling.
1028
1029                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        // Unlike the other method of schema validation, we can't return an error
1063        // here when schema fails - we need to in-place move the entry to a
1064        // conflict state so that the replication can proceed.
1065
1066        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    /// Validate that this entry and its attribute-value sets are conformant to the system's'
1093    /// schema and the relevant syntaxes.
1094    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        // Build the new valid entry ...
1108        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    /// Access a reference set in a directly mutable form. This is "safe" because
1121    /// referential integrity will check the values added are valid, and because
1122    /// this is strongly typed it can't violate syntax.
1123    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    // Dirty modifiable state. Works on any other state to dirty them.
1139    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    /// ⚠️  This function bypasses the schema validation and can panic if uuid is not found.
1150    /// The entry it creates can never be committed safely or replicated.
1151    /// This is a TEST ONLY method and will never be exposed in production.
1152    #[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    /// Convert this entry into a recycled entry, that is "in the recycle bin".
1166    pub fn to_recycled(mut self) -> Self {
1167        // This will put the modify ahead of the recycle transition.
1168        self.add_ava(Attribute::Class, EntryClass::Recycled.into());
1169
1170        // Change state repl doesn't need this flag
1171        // self.valid.ecstate.recycled(&self.valid.cid);
1172
1173        Entry {
1174            valid: self.valid,
1175            state: self.state,
1176            attrs: self.attrs,
1177        }
1178    }
1179
1180    /// Convert this entry into a conflict, declaring what entries it conflicted against.
1181    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        // Add all the source uuids we conflicted against.
1188        for source_uuid in iter {
1189            self.add_ava(Attribute::SourceUuid, Value::Uuid(source_uuid));
1190        }
1191    }
1192
1193    /// Extract this entry from the recycle bin into a live state.
1194    pub fn to_revived(mut self) -> Self {
1195        // This will put the modify ahead of the revive transition.
1196        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        // Change state repl doesn't need this flag
1202        // self.valid.ecstate.revive(&self.valid.cid);
1203
1204        Entry {
1205            valid: self.valid,
1206            state: self.state,
1207            attrs: self.attrs,
1208        }
1209    }
1210}
1211// Both invalid states can be reached from "entry -> invalidate"
1212
1213impl Entry<EntryInvalid, EntryNew> {
1214    /// This function steps back from EntryInvalid to EntryInit.
1215    /// This is a TEST ONLY method and will never be exposed in production.
1216    #[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    /// ⚠️  This function bypasses the schema validation and can panic if uuid is not found.
1226    /// The entry it creates can never be committed safely or replicated.
1227    /// This is a TEST ONLY method and will never be exposed in production.
1228    #[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    /// ⚠️  This function bypasses the db commit, assigns fake db ids, and assigns an invalid uuid.
1242    /// The entry it creates can never be committed safely or replicated.
1243    /// This is a TEST ONLY method and will never be exposed in production.
1244    #[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    /// ⚠️  This function bypasses the schema validation and assigns a fake uuid.
1258    /// The entry it creates can never be committed safely or replicated.
1259    /// This is a TEST ONLY method and will never be exposed in production.
1260    #[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    /// ⚠️  This function bypasses the schema validation and assigns a fake uuid.
1276    /// The entry it creates can never be committed safely or replicated.
1277    /// This is a TEST ONLY method and will never be exposed in production.
1278    #[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    /// ⚠️  This function bypasses schema validation and assigns an invalid uuid.
1294    /// The entry it creates can never be committed safely or replicated.
1295    /// This is a TEST ONLY method and will never be exposed in production.
1296    #[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    /// Given this validated and sealed entry, process it with a `Backend` ID number so that it
1306    /// can be then serialised to the database.
1307    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    /// If this entry has ever been committed to disk, retrieve its database id number.
1325    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    /// State transititon to allow self to self for certain test macros.
1347    #[cfg(test)]
1348    pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1349        // NO-OP to satisfy macros.
1350        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    /// Insert a claim to this entry. This claim can NOT be persisted to disk, this is only
1368    /// used during a single Event session.
1369    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    /// Serialise this entry to its Database format ready for storage.
1378    pub fn to_dbentry(&self) -> DbEntry {
1379        // In the future this will do extra work to process uuid
1380        // into "attributes" suitable for dbentry storage.
1381        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    /// Given this entry, extract the set of strings that can uniquely identify this for authentication
1398    /// purposes. These strings are then indexed.
1399    fn get_name2uuid_cands(&self) -> Set<String> {
1400        // The cands are:
1401        // * spn
1402        // * name
1403        // * gidnumber
1404
1405        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    /// Given this entry, extract the set of strings that can externally identify this
1419    /// entry for sync purposes. These strings are then indexed.
1420    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    /// Given this entry, extract its primary security principal name, or if not present
1428    /// extract its name, and if that's not present, extract its uuid.
1429    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    /// Given this entry, determine its relative distinguished named for LDAP compatibility.
1443    ///
1444    /// See also - `get_display_id`
1445    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    /// Generate the required values for a name2uuid index. IE this is
1458    /// ALL possible names this entry COULD be known uniquely by!
1459    pub(crate) fn idx_name2uuid_diff(
1460        pre: Option<&Self>,
1461        post: Option<&Self>,
1462    ) -> (
1463        // Add
1464        Option<Set<String>>,
1465        // Remove
1466        Option<Set<String>>,
1467    ) {
1468        // needs to return gid for posix conversion
1469        match (pre, post) {
1470            (None, None) => {
1471                // No action required
1472                (None, None)
1473            }
1474            (None, Some(b)) => {
1475                // We are adding this entry (or restoring it),
1476                // so we need to add the values.
1477                (Some(b.get_name2uuid_cands()), None)
1478            }
1479            (Some(a), None) => {
1480                // Removing the entry, remove all values.
1481                (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                // what is in post, but not pre (added)
1488                let add_set: Set<_> = post_set.difference(&pre_set).cloned().collect();
1489                // what is in pre, but not post (removed)
1490                let rem_set: Set<_> = pre_set.difference(&post_set).cloned().collect();
1491                (Some(add_set), Some(rem_set))
1492            }
1493        }
1494    }
1495
1496    /// Generate the required values for externalid2uuid.
1497    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                // no action
1504                (None, None)
1505            }
1506            (None, Some(b)) => {
1507                // add
1508                (b.get_externalid2uuid(), None)
1509            }
1510            (Some(a), None) => {
1511                // remove
1512                (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                    // Note, we swap these since ib is the new post state
1519                    // we want to add, and ia is what we remove.
1520                    (ib, ia)
1521                } else {
1522                    // no action
1523                    (None, None)
1524                }
1525            }
1526        }
1527    }
1528
1529    /// Generate a differential between a previous and current entry state, and what changes this
1530    /// means for the current set of spn's for this uuid.
1531    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                // no action
1538                None
1539            }
1540            (None, Some(b)) => {
1541                // add
1542                Some(Ok(b.get_uuid2spn()))
1543            }
1544            (Some(_a), None) => {
1545                // remove
1546                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                    // Add (acts as replace)
1553                    Some(Ok(ib))
1554                } else {
1555                    // no action
1556                    None
1557                }
1558            }
1559        }
1560    }
1561
1562    /// Generate a differential between a previous and current entry state, and what changes this
1563    /// means for the current set of LDAP relative distinguished names.
1564    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                // no action
1571                None
1572            }
1573            (None, Some(b)) => {
1574                // add
1575                Some(Ok(b.get_uuid2rdn()))
1576            }
1577            (Some(_a), None) => {
1578                // remove
1579                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                    // Add (acts as replace)
1586                    Some(Ok(ib))
1587                } else {
1588                    // no action
1589                    None
1590                }
1591            }
1592        }
1593    }
1594
1595    /// Given the previous and current state of this entry, determine the indexing differential
1596    /// that needs to be applied. i.e. what indexes must be created, modified and removed.
1597    pub(crate) fn idx_diff<'a>(
1598        idxmeta: &'a HashMap<IdxKey, IdxSlope>,
1599        pre: Option<&Self>,
1600        post: Option<&Self>,
1601    ) -> IdxDiff<'a> {
1602        // We yield a list of Result, where Ok() means "add",
1603        // and Err() means "remove".
1604        // the value inside the result, is a tuple of attr, itype, idx_key
1605
1606        match (pre, post) {
1607            (None, None) => {
1608                // if both are none, yield empty list.
1609                Vec::with_capacity(0)
1610            }
1611            (Some(pre_e), None) => {
1612                // If we are none (?), yield our pre-state as removals.
1613                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                                        // We generate these keys out of the valueset now.
1622                                        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                // If the pre-state is none, yield our additions.
1644                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                                // For each value
1666                                //
1667                                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                                // Neither have it, do nothing.
1684                                Vec::with_capacity(0)
1685                            }
1686                            (Some(pre_vs), None) => {
1687                                // It existed before, but not anymore
1688                                let changes: Vec<Result<_, _>> = match ikey.itype {
1689                                    IndexType::Equality => {
1690                                        // Turn each idx_key to the tuple of
1691                                        // changes.
1692                                        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                                // It was added now.
1711                                let changes: Vec<Result<_, _>> = match ikey.itype {
1712                                    IndexType::Equality => {
1713                                        // Turn each idx_key to the tuple of
1714                                        // changes.
1715                                        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                                // it exists in both, we need to work out the difference within the attr.
1734                                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                                        // No action - we still are "present", so nothing to do!
1741                                        (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                                                        // In both - no action needed.
1778                                                        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                                } // end sz > 0
1801
1802                                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                                        // No action - we still are "present", so nothing to do!
1818                                    }
1819                                };
1820                                // Return the diff
1821                                diff
1822                            }
1823                        }
1824                    })
1825                    .collect()
1826                // End diff of the entries
1827            }
1828        }
1829    }
1830
1831    pub fn from_dbentry(db_e: DbEntry, id: u64) -> Option<Self> {
1832        // Convert attrs from db format to value
1833
1834        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                    // Skip anything empty as new VS can't deal with it.
1841                    .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    /// ⚠️  This function bypasses the access control validation logic and should NOT
1868    /// be used without special care and attention to ensure that no private data
1869    /// is leaked incorrectly to clients. Generally this is ONLY used inside of
1870    /// the access control processing functions which correctly applies the reduction
1871    /// steps.
1872    ///
1873    /// This is a TEST ONLY method and will never be exposed in production.
1874    #[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    /// Given a set of attributes that are allowed to be seen on this entry, process and remove
1887    /// all other values that are NOT allowed in this query.
1888    pub fn reduce_attributes(
1889        &self,
1890        allowed_attrs: &BTreeSet<Attribute>,
1891        effective_access: Option<Box<AccessEffectivePermission>>,
1892    ) -> Entry<EntryReduced, EntryCommitted> {
1893        // Remove all attrs from our tree that are NOT in the allowed set.
1894        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    /// Convert this recycled entry, into a tombstone ready for reaping.
1920    pub fn to_tombstone(&self, cid: Cid) -> Entry<EntryInvalid, EntryCommitted> {
1921        let mut ecstate = self.valid.ecstate.clone();
1922        // Duplicate this to a tombstone entry
1923        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        // ⚠️  No return from this point!
1935        ecstate.tombstone(&cid);
1936
1937        Entry {
1938            valid: EntryInvalid { cid, ecstate },
1939            state: self.state.clone(),
1940            attrs: attrs_new,
1941        }
1942    }
1943
1944    /// Given a current transaction change identifier, mark this entry as valid and committed.
1945    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        // Now validate it!
1973        trace!(?self.attrs, "Entry::validate -> target");
1974
1975        // First, check we have class on the object ....
1976        if !self.attribute_pres(Attribute::Class) {
1977            // lrequest_error!("Missing attribute class");
1978            return Err(SchemaError::NoClassFound);
1979        }
1980
1981        if self.attribute_equality(Attribute::Class, &EntryClass::Conflict.into()) {
1982            // Conflict entries are exempt from schema enforcement. Return true.
1983            trace!("Skipping schema validation on conflict entry");
1984            return Ok(());
1985        };
1986
1987        // Are we in the recycle bin? We soften some checks if we are.
1988        let recycled = self.attribute_equality(Attribute::Class, &EntryClass::Recycled.into());
1989
1990        // Do we have extensible? We still validate syntax of attrs but don't
1991        // check for valid object structures.
1992        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        // We need to keep the btreeset of entry classes here so we can check the
2004        // requires and excludes.
2005        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        // Now determine the set of excludes and requires we have, and then
2025        // assert we don't violate them.
2026
2027        let supplements_classes: Vec<_> = classes
2028            .iter()
2029            .flat_map(|cls| cls.systemsupplements.iter().chain(cls.supplements.iter()))
2030            .collect();
2031
2032        // So long as one supplement is present we can continue.
2033        let valid_supplements = if supplements_classes.is_empty() {
2034            // No need to check.
2035            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        // What this is really doing is taking a set of classes, and building an
2073        // "overall" class that describes this exact object for checking. IE we
2074        // build a super must/may set from the small class must/may sets.
2075
2076        //   for each class
2077        //      add systemmust/must and systemmay/may to their lists
2078        //      add anything from must also into may
2079
2080        // Now from the set of valid classes make a list of must/may
2081        //
2082        // NOTE: We still need this on extensible, because we still need to satisfy
2083        // our other must conditions as well!
2084        let must: Result<Vec<&SchemaAttribute>, _> = classes
2085            .iter()
2086            // Join our class systemmmust + must into one iter
2087            .flat_map(|cls| cls.systemmust.iter().chain(cls.must.iter()))
2088            .map(|s| {
2089                // This should NOT fail - if it does, it means our schema is
2090                // in an invalid state!
2091                schema_attributes.get(s).ok_or(SchemaError::Corrupted)
2092            })
2093            .collect();
2094
2095        let must = must?;
2096
2097        // Check that all must are inplace
2098        //   for each attr in must, check it's present on our ent
2099        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            // We if are in the recycle bin, we don't hard error here. This can occur when
2113            // a migration occurs and we delete an acp, and then the related group. Because
2114            // this would trigger refint which purges the acp_receiver_group, then this
2115            // must value becomes unsatisfiable. So here we soften the check for recycled
2116            // entries because they are in a "nebulous" state anyway.
2117            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                            // Now, for each type we do a *full* check of the syntax
2127                            // and validity of the ava.
2128                            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                                // .map_err(|e| lrequest_error!("Failed to validate: {}", attr_name);)
2137                            }
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            // Note - we do NOT need to check phantom attributes here because they are
2152            // not allowed to exist in the class, which means a phantom attribute can't
2153            // be in the may/must set, and would FAIL our normal checks anyway.
2154
2155            // The set of "may" is a combination of may and must, since we have already
2156            // asserted that all must requirements are fulfilled. This allows us to
2157            // perform extended attribute checking in a single pass.
2158            let may: Result<Map<&Attribute, &SchemaAttribute>, _> = classes
2159                .iter()
2160                // Join our class systemmmust + must + systemmay + may into one.
2161                .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                    // This should NOT fail - if it does, it means our schema is
2171                    // in an invalid state!
2172                    Ok((s, schema_attributes.get(s).ok_or(SchemaError::Corrupted)?))
2173                })
2174                .collect();
2175
2176            let may = may?;
2177
2178            // TODO #70: Error needs to say what is missing
2179            // We need to return *all* missing attributes, not just the first error
2180            // we find. This will probably take a rewrite of the function definition
2181            // to return a result<_, vec<schemaerror>> and for the schema errors to take
2182            // information about what is invalid. It's pretty nontrivial.
2183
2184            // Check that any other attributes are in may
2185            //   for each attr on the object, check it's in the may+must set
2186            self.attrs.iter().try_for_each(|(attr_name, avas)| {
2187                    match may.get(attr_name) {
2188                        Some(a_schema) => {
2189                            // Now, for each type we do a *full* check of the syntax
2190                            // and validity of the ava.
2191                            a_schema.validate_ava(attr_name, avas)
2192                            // .map_err(|e| lrequest_error!("Failed to validate: {}", attr_name);
2193                        }
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        // Well, we got here, so okay!
2212        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        // Remove anything from the ecstate that is not a replicated attribute in the schema.
2219        // This is to allow ecstate equality to work, but also to just prevent ruv updates and
2220        // replicating things that only touched or changed phantom attrs.
2221        ecstate.retain(|k, _| schema.is_replicated(k));
2222
2223        // Update the last changed time.
2224        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        // Update created-at time. This is needed for migrations currently. It could
2229        // be alternately in the entry create path, but it makes more sense here as
2230        // we get the create_at time from the replication metadata
2231        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        // Trim attributes that require it. For most this is a no-op.
2262        for vs in self.attrs.values_mut() {
2263            vs.trim(trim_cid);
2264        }
2265
2266        // During migration to the new modified/created cid system, we need to account
2267        // for entries that don't have this yet. Normally we would apply this in seal()
2268        // to the current CID. At this point we enter in the expected value from the
2269        // entry. Note, we don't set last mod to cid yet, we leave that to seal() so that
2270        // if this entry is excluded later in the change, we haven't tainted anything, or
2271        // so that if the change only applies to non-replicated attrs we haven't mucked
2272        // up the value.
2273        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    /// Determine if any attribute of this entry changed excluding the attribute named.
2300    /// This allows for detection of entry changes unless the change was to a specific
2301    /// attribute.
2302    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                    // This always changes, and could throw off other detections.
2317                    *change_attr != Attribute::LastModifiedCid
2318                })
2319            }
2320            State::Tombstone { at } => at == cid,
2321        }
2322    }
2323
2324    /// ⚠️  - Invalidate an entry by resetting it's change state to time-zero. This entry
2325    /// can never be replicated after this.
2326    /// This is a TEST ONLY method and will never be exposed in production.
2327    #[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    /// Transform this reduced entry into a JSON protocol form that can be sent to clients.
2354    pub fn to_pe(&self, qs: &mut QueryServerReadTransaction) -> Result<ProtoEntry, OperationError> {
2355        // Turn values -> Strings.
2356        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            // We want to skip some attributes as they are already in the header.
2375            .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        // Not sure how I want to handle this yet, I think we need some schema changes
2412        // to achieve this.
2413        let schemas = Vec::with_capacity(0);
2414
2415        Ok(ScimEntryKanidm {
2416            header: ScimEntryHeader {
2417                schemas,
2418                id,
2419                // TODO: Should be spn / name or uuid.
2420                external_id: None,
2421                // TODO - this one will be useful in future, but we need to change
2422                // entry to store some extra metadata.
2423                meta: None,
2424            },
2425            ext_access_check,
2426            attrs,
2427        })
2428    }
2429
2430    /// Transform this reduced entry into an LDAP form that can be sent to clients.
2431    pub fn to_ldap(
2432        &self,
2433        qs: &mut QueryServerReadTransaction,
2434        basedn: &str,
2435        // Did the client request all attributes?
2436        all_attrs: bool,
2437        // Did the ldap client request any sperific attribute names? If so,
2438        // we need to remap everything to match.
2439        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        // Everything in our attrs set is "what was requested". So we can transform that now
2446        // so they are all in "ldap forms" which makes our next stage a bit easier.
2447
2448        // Stage 1 - transform our results to a map of kani attr -> ldap value.
2449        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        // Stage 2 - transform and get all our attr - names out that we need to return.
2460        //                  ldap a, kani a
2461        let attr_names: Vec<(&str, &str)> = if all_attrs {
2462            // Join the set of attr keys, and our requested attrs.
2463            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            // Just get the requested ones.
2474            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        // Stage 3 - given our map, generate the final result.
2481        let attributes: Vec<_> = attr_names
2482            .into_iter()
2483            .filter_map(|(ldap_a, kani_a)| {
2484                // In some special cases, we may need to transform or rewrite the values.
2485                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
2524// impl<STATE> Entry<EntryValid, STATE> {
2525impl<VALID, STATE> Entry<VALID, STATE> {
2526    /// This internally adds an AVA to the entry. If the entry was newly added, then true is returned.
2527    /// If the value already existed, or was unable to be added, false is returned. Alternately,
2528    /// you can think of this boolean as "if a write occurred to the structure", true indicating that
2529    /// a change occurred.
2530    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            // Default to the value not being present if wrong typed.
2535            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            // The attribute did not exist before.
2542            true
2543        }
2544        // Doesn't matter if it already exists, equality will replace.
2545    }
2546
2547    /// Overwrite the current set of values for an attribute, with this new set.
2548    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            // This is the suboptimal path. This can only exist in rare cases.
2559            let _ = existing_vs.merge(&vs);
2560        } else {
2561            // Normally this is what's taken.
2562            self.attrs.insert(attr, vs);
2563        }
2564    }
2565
2566    /// Update the last_changed flag of this entry to the given change identifier.
2567    #[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    /// Get an iterator over the current set of attribute names that this entry contains.
2594    pub fn get_ava_names(&self) -> impl Iterator<Item = &str> {
2595        // Get the set of all attribute names in the entry
2596        self.attrs.keys().map(|a| a.as_str())
2597    }
2598
2599    /// Get an iterator over the current set of values for an attribute name.
2600    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    /// Return a reference to the current set of values that are associated to this attribute.
2609    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    /// If possible, return an iterator over the set of values transformed into a `&str`.
2691    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    /// If possible, return an iterator over the set of values transformed into a `&str`.
2699    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    /// If possible, return an iterator over the set of values transformed into a `Uuid`.
2707    pub fn get_ava_as_refuuid<A: AsRef<Attribute>>(
2708        &self,
2709        attr: A,
2710    ) -> Option<Box<dyn Iterator<Item = Uuid> + '_>> {
2711        // If any value is NOT a reference, it's filtered out.
2712        self.get_ava_set(attr).and_then(|vs| vs.as_ref_uuid_iter())
2713    }
2714
2715    /// If possible, return an iterator over the set of ssh key values transformed into a `&str`.
2716    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    // These are special types to allow returning typed values from
2725    // an entry, if we "know" what we expect to receive.
2726
2727    /// Return a single value of this attributes name, or `None` if it is NOT present, or
2728    /// there are multiple values present (ambiguous).
2729    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    /// Return a single bool, if valid to transform this value into a boolean.
2739    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    /// Return a single uint32, if valid to transform this value.
2744    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    /// Return a single syntax type, if valid to transform this value.
2749    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    /// Return a single credential, if valid to transform this value.
2755    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    /// Get the set of passkeys on this account, if any are present.
2761    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    /// Get the set of devicekeys on this account, if any are present.
2769    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    /// Get the set of uihints on this account, if any are present.
2778    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    /// Return a single secret value, if valid to transform this value.
2783    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    /// Return a single datetime, if valid to transform this value.
2788    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    /// Return a single `&str`, if valid to transform this value.
2794    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    /// Return a single `&str`, if valid to transform this value.
2799    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    /// Return a single `&str`, if valid to transform this value.
2804    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    /// Return a single `&Url`, if valid to transform this value.
2809    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    /// Return a single protocol filter, if valid to transform this value.
2834    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    /// Return a single security principle name, if valid to transform this value.
2885    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    /// Assert if an attribute of this name is present on this entry.
2891    pub fn attribute_pres<A: AsRef<Attribute>>(&self, attr: A) -> bool {
2892        self.attrs.contains_key(attr.as_ref())
2893    }
2894
2895    /// Assert if an attribute of this name is present, and one of its values contains
2896    /// an exact match of this partial value.
2897    pub fn attribute_equality<A: AsRef<Attribute>>(&self, attr: A, value: &PartialValue) -> bool {
2898        // we assume based on schema normalisation on the way in
2899        // that the equality here of the raw values MUST be correct.
2900        // We also normalise filters, to ensure that their values are
2901        // syntax valid and will correctly match here with our indexes.
2902        match self.attrs.get(attr.as_ref()) {
2903            Some(v_list) => v_list.contains(value),
2904            None => false,
2905        }
2906    }
2907
2908    /// Assert if an attribute of this name is present, and one of it's values contains
2909    /// the following substring, if possible to perform the substring comparison.
2910    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    /// Assert if an attribute of this name is present, and one of its values startswith
2921    /// the following string, if possible to perform the comparison.
2922    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    /// Assert if an attribute of this name is present, and one of its values startswith
2933    /// the following string, if possible to perform the comparison.
2934    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    /// Assert if an attribute of this name is present, and one of its values is less than
2945    /// the following partial value
2946    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    // Since EntryValid/Invalid is just about class adherence, not Value correctness, we
2957    // can now apply filters to invalid entries - why? Because even if they aren't class
2958    // valid, we still have strict typing checks between the filter -> entry to guarantee
2959    // they should be functional. We'll never match something that isn't syntactically valid.
2960    #[inline(always)]
2961    #[instrument(level = "trace", name = "entry::entry_match_no_index", skip(self))]
2962    /// Test if the following filter applies to and matches this entry.
2963    pub fn entry_match_no_index(&self, filter: &Filter<FilterValidResolved>) -> bool {
2964        self.entry_match_no_index_inner(filter.to_inner())
2965    }
2966
2967    // This is private, but exists on all types, so that valid and normal can then
2968    // expose the simpler wrapper for entry_match_no_index only.
2969    // Assert if this filter matches the entry (no index)
2970    fn entry_match_no_index_inner(&self, filter: &FilterResolved) -> bool {
2971        // Go through the filter components and check them in the entry.
2972        // This is recursive!!!!
2973        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            // Check with ftweedal about or filter zero len correctness.
2981            FilterResolved::Or(l, _) => l.iter().any(|f| self.entry_match_no_index_inner(f)),
2982            // Check with ftweedal about and filter zero len correctness.
2983            FilterResolved::And(l, _) => l.iter().all(|f| self.entry_match_no_index_inner(f)),
2984            FilterResolved::Inclusion(_, _) => {
2985                // An inclusion doesn't make sense on an entry in isolation!
2986                // Inclusions are part of exists queries, on search they mean
2987                // nothing!
2988                false
2989            }
2990            FilterResolved::AndNot(f, _) => !self.entry_match_no_index_inner(f),
2991            FilterResolved::Invalid(_) => false,
2992        }
2993    }
2994
2995    /// Given this entry, generate a filter containing the requested attributes strings as
2996    /// equality components.
2997    pub fn filter_from_attrs(&self, attrs: &[Attribute]) -> Option<Filter<FilterInvalid>> {
2998        // Because we are a valid entry, a filter we create still may not
2999        // be valid because the internal server entry templates are still
3000        // created by humans! Plus double checking something already valid
3001        // is not bad ...
3002        //
3003        // Generate a filter from the attributes requested and defined.
3004        // Basically, this is a series of nested and's (which will be
3005        // optimised down later: but if someone wants to solve flatten() ...)
3006
3007        // Take name: (a, b), name: (c, d) -> (name, a), (name, b), (name, c), (name, d)
3008
3009        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    /// Given this entry, generate a modification list that would "assert"
3028    /// another entry is in the same/identical attribute state.
3029    pub fn gen_modlist_assert(
3030        &self,
3031        schema: &dyn SchemaTransaction,
3032    ) -> Result<ModifyList<ModifyInvalid>, SchemaError> {
3033        // Create a modlist from this entry. We make this assuming we want the entry
3034        // to have this one as a subset of values. This means if we have single
3035        // values, we'll replace, if they are multivalue, we present them.
3036        let mut mods = ModifyList::new();
3037
3038        for (k, vs) in self.attrs.iter() {
3039            // WHY?! We skip uuid here because it is INVALID for a UUID
3040            // to be in a modlist, and the base.rs plugin will fail if it
3041            // is there. This actually doesn't matter, because to apply the
3042            // modlist in these situations we already know the entry MUST
3043            // exist with that UUID, we only need to conform it's other
3044            // attributes into the same state.
3045            //
3046            // In the future, if we make uuid a real entry type, then this
3047            // check can "go away" because uuid will never exist as an ava.
3048            //
3049            // NOTE: Remove this check when uuid becomes a real attribute.
3050            // UUID is now a real attribute, but it also has an ava for db_entry
3051            // conversion - so what do? If we remove it here, we could have CSN issue with
3052            // repl on uuid conflict, but it probably shouldn't be an ava either ...
3053            // as a result, I think we need to keep this continue line to not cause issues.
3054            if *k == Attribute::Uuid {
3055                continue;
3056            }
3057            // Get the schema attribute type out.
3058            match schema.is_multivalue(k) {
3059                Ok(r) => {
3060                    // As this is single value, purge then present to maintain this
3061                    // invariant.
3062                    if !r ||
3063                        // we need to be able to express REMOVAL of attributes, so we
3064                        // purge here for migrations of certain system attributes.
3065                        *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                // A schema error happened, fail the whole operation.
3078                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    /// Determine if this entry is recycled or a tombstone, and map that to "None". This allows
3089    /// filter_map to effectively remove entries that should not be considered as "alive".
3090    pub fn mask_recycled_ts(&self) -> Option<&Self> {
3091        // Only when cls has ts/rc then None, else always Some(self).
3092        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    /// Determine if this entry is recycled, and map that to "None". This allows
3107    /// filter_map to effectively remove entries that are recycled in some cases.
3108    pub fn mask_recycled(&self) -> Option<&Self> {
3109        // Only when cls has ts/rc then None, else lways Some(self).
3110        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    /// Determine if this entry is a tombstone, and map that to "None". This allows
3123    /// filter_map to effectively remove entries that are tombstones in some cases.
3124    pub fn mask_tombstone(&self) -> Option<&Self> {
3125        // Only when cls has ts/rc then None, else lways Some(self).
3126        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    // This should always work? It's only on validate that we'll build
3144    // a list of syntax violations ...
3145    // If this already exists, we silently drop the event. This is because
3146    // we need this to be *state* based where we assert presence.
3147    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        // This returns true if the value WAS changed! See add_ava_int.
3155        if self.add_ava_int(attr_ref.clone(), value) {
3156            // In this case, we ONLY update the changestate if the value was already present!
3157            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    /// Remove an attribute-value pair from this entry. If the ava doesn't exist, we
3178    /// don't do anything else since we are asserting the absence of a value.
3179    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    /// Remove all values of this attribute from the entry. If it doesn't exist, this
3216    /// asserts that no content of that attribute exist.
3217    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        // self.valid.eclog.purge_ava(&self.valid.cid, attr);
3221
3222        let can_remove = self
3223            .attrs
3224            .get_mut(attr_ref)
3225            .map(|vs| vs.purge(&self.valid.cid))
3226            // Default to false since a missing attr can't be removed!
3227            .unwrap_or_default();
3228        if can_remove {
3229            self.attrs.remove(attr_ref);
3230        }
3231    }
3232
3233    /// Remove this value set from the entry, returning the value set at the time of removal.
3234    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            // Can return as is.
3241            Some(vs)
3242        } else {
3243            // This type may need special handling. Clone and reinsert.
3244            let r_vs = vs.clone();
3245            self.attrs.insert(attr_ref.clone(), vs);
3246            Some(r_vs)
3247        }
3248    }
3249
3250    /// Unlike pop or purge, this does NOT respect the attributes purge settings, meaning
3251    /// that this can break replication by force clearing the state of an attribute. It's
3252    /// useful for things like "session" to test the grace window by removing the revoked
3253    /// sessions from the value set that you otherwise, could not.
3254    #[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    /// Replace the content of this attribute with the values from this
3263    /// iterator. If the iterator is empty, the attribute is purged.
3264    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    /// Replace the content of this attribute with a new value set. Effectively this is
3273    /// a a "purge and set".
3274    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    /// Merge the content from the new ValueSet into the existing ValueSet. If no existing
3284    /// ValueSet is present, then these data are inserted.
3285    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    /// Apply the content of this modlist to this entry, enforcing the expressed state.
3296    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        // This may look naive - but it is correct. This is because
3326        // all items that end up in an item MUST have passed through
3327        // schema validation and normalisation so we can assume that
3328        // all rules were applied correctly. Thus we can just simply
3329        // do a char-compare like this.
3330        //
3331        // Of course, this is only true on the "Valid" types ... the others
3332        // are not guaranteed to support this ... but more likely that will
3333        // just end in eager false-results. We'll never say something is true
3334        // that should NOT be.
3335        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        // Schema doesn't matter here because we are duplicating a value
3361        // it should fail!
3362
3363        // We still probably need schema here anyway to validate what we
3364        // are adding ... Or do we validate after the changes are made in
3365        // total?
3366        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        // Should only be one value!
3372        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        // Also test non-matching attr syntax
3394        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        // Test application of changes to an entry.
3453        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 the changes are there
3465        assert!(e.attribute_equality(Attribute::UserId, &PartialValue::new_utf8s("william")));
3466        assert!(e.attribute_equality(Attribute::Attr, &PartialValue::new_iutf8("value")));
3467
3468        // Assert present for multivalue
3469        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        // Assert purge on single/multi/empty value
3480        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        // Assert removed on value that exists and doesn't exist
3497        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        // When we do None, None, we get nothing back.
3552        let r1 = Entry::idx_diff(&idxmeta, None, None);
3553        eprintln!("{r1:?}");
3554        assert_eq!(r1, Vec::with_capacity(0));
3555
3556        // Check generating a delete diff
3557        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        // Check generating an add diff
3571        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        // Check the mod cases now
3585
3586        // Check no changes
3587        let no_r = Entry::idx_diff(&idxmeta, Some(&e1), Some(&e1));
3588        assert!(no_r.is_empty());
3589
3590        // Check "adding" an attribute.
3591        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        // Check "removing" an attribute.
3595        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        // Change an attribute.
3599        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        // none, none,
3643        let r = Entry::idx_name2uuid_diff(None, None);
3644        assert_eq!(r, (None, None));
3645
3646        // none, some - test adding an entry gives back add sets
3647        {
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            // Note the uuid isn't present!
3671            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            // some, none,
3683            // Check delete, swap the order of args
3684            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            // some, some (same), should be empty changes.
3697            assert!(
3698                Entry::idx_name2uuid_diff(Some(&e), Some(&e))
3699                    == (Some(Set::new()), Some(Set::new()))
3700            );
3701        }
3702        // some, some (diff)
3703
3704        {
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            // One attr added
3723            assert!(
3724                Entry::idx_name2uuid_diff(Some(&e1), Some(&e2))
3725                    == (Some(btreeset!["testperson".to_string()]), Some(Set::new()))
3726            );
3727
3728            // One removed
3729            assert!(
3730                Entry::idx_name2uuid_diff(Some(&e2), Some(&e1))
3731                    == (Some(Set::new()), Some(btreeset!["testperson".to_string()]))
3732            );
3733        }
3734
3735        // Value changed, remove old, add new.
3736        {
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}