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 crate::be::dbentry::{DbEntry, DbEntryVers};
28use crate::be::dbvalue::DbValueSetV2;
29use crate::be::{IdxKey, IdxSlope};
30use crate::credential::apppwd::ApplicationPassword;
31use crate::credential::Credential;
32use crate::filter::{Filter, FilterInvalid, FilterResolved, FilterValidResolved};
33use crate::idm::ldap::ldap_vattr_map;
34use crate::modify::{Modify, ModifyInvalid, ModifyList, ModifyValid};
35use crate::prelude::*;
36use crate::repl::cid::Cid;
37use crate::repl::entry::EntryChangeState;
38use crate::repl::proto::{ReplEntryV1, ReplIncrementalEntryV1};
39use crate::schema::{SchemaAttribute, SchemaClass, SchemaTransaction};
40use crate::server::access::AccessEffectivePermission;
41use crate::value::{
42    ApiToken, CredentialType, IndexType, IntentTokenState, Oauth2Session, PartialValue, Session,
43    SyntaxType, Value,
44};
45use crate::valueset::{self, ScimResolveStatus, ValueSet, ValueSetSpn};
46use compact_jwt::JwsEs256Signer;
47use crypto_glue::s256::Sha256Output;
48use hashbrown::{HashMap, HashSet};
49use kanidm_proto::internal::ImageValue;
50use kanidm_proto::internal::{
51    ConsistencyError, Filter as ProtoFilter, OperationError, SchemaError, UiHint,
52};
53use kanidm_proto::scim_v1::server::ScimEffectiveAccess;
54use kanidm_proto::v1::Entry as ProtoEntry;
55use ldap3_proto::simple::{LdapPartialAttribute, LdapSearchResultEntry};
56use openssl::ec::EcKey;
57use openssl::pkey::{Private, Public};
58use std::cmp::Ordering;
59pub use std::collections::BTreeSet as Set;
60use std::collections::{BTreeMap as Map, BTreeMap, BTreeSet};
61use std::sync::Arc;
62use time::OffsetDateTime;
63use tracing::trace;
64use uuid::Uuid;
65use webauthn_rs::prelude::{
66    AttestationCaList, AttestedPasskey as AttestedPasskeyV4, Passkey as PasskeyV4,
67};
68
69pub type EntryInitNew = Entry<EntryInit, EntryNew>;
70pub type EntryInvalidNew = Entry<EntryInvalid, EntryNew>;
71pub type EntryRefreshNew = Entry<EntryRefresh, EntryNew>;
72pub type EntrySealedNew = Entry<EntrySealed, EntryNew>;
73pub type EntryValidCommitted = Entry<EntryValid, EntryCommitted>;
74pub type EntrySealedCommitted = Entry<EntrySealed, EntryCommitted>;
75pub type EntryInvalidCommitted = Entry<EntryInvalid, EntryCommitted>;
76pub type EntryReducedCommitted = Entry<EntryReduced, EntryCommitted>;
77pub type EntryTuple = (Arc<EntrySealedCommitted>, EntryInvalidCommitted);
78
79pub type EntryIncrementalNew = Entry<EntryIncremental, EntryNew>;
80pub type EntryIncrementalCommitted = Entry<EntryIncremental, EntryCommitted>;
81
82// Entry should have a lifecycle of types. This is Raw (modifiable) and Entry (verified).
83// This way, we can move between them, but only certain actions are possible on either
84// This means modifications happen on Raw, but to move to Entry, you schema normalise.
85// Vice versa, you can for free, move to Raw, but you lose the validation.
86
87// Because this is type system it's "free" in the end, and means we force validation
88// at the correct and required points of the entries life.
89
90// This is specifically important for the commit to the backend, as we only want to
91// commit validated types.
92
93// Has never been in the DB, so doesn't have an ID.
94#[derive(Clone, Debug)]
95pub struct EntryNew; // new
96
97// It's been in the DB, so it has an id
98#[derive(Clone, Debug)]
99pub struct EntryCommitted {
100    id: u64,
101}
102
103#[derive(Clone, Debug)]
104pub struct EntryInit;
105
106/*  |
107 *  | Init comes from a proto entry, it's new.
108 *  | We add the current Cid before we allow mods.
109 *  V
110 */
111
112#[derive(Clone, Debug)]
113pub struct EntryInvalid {
114    cid: Cid,
115    ecstate: EntryChangeState,
116}
117
118// Alternate path - this entry came from a full refresh, and already has an entry change state.
119#[derive(Clone, Debug)]
120pub struct EntryRefresh {
121    ecstate: EntryChangeState,
122}
123
124// Alternate path - this entry came from an incremental replication.
125#[derive(Clone, Debug)]
126pub struct EntryIncremental {
127    // Must have a uuid, else we can't proceed at all.
128    uuid: Uuid,
129    ecstate: EntryChangeState,
130}
131
132/*  |
133 *  | The changes made within this entry are validated by the schema.
134 *  V
135 */
136
137#[derive(Clone, Debug)]
138pub struct EntryValid {
139    // Asserted with schema, so we know it has a UUID now ...
140    uuid: Uuid,
141    ecstate: EntryChangeState,
142}
143
144/*  |
145 *  | The changes are extracted into the changelog as needed, creating a
146 *  | stable database entry.
147 *  V
148 */
149
150#[derive(Clone, Debug)]
151pub struct EntrySealed {
152    uuid: Uuid,
153    ecstate: EntryChangeState,
154}
155
156/*  |
157 *  | The entry has access controls applied to reduce what is yielded to a client
158 *  V
159 */
160
161#[derive(Clone, Debug)]
162pub struct EntryReduced {
163    uuid: Uuid,
164    effective_access: Option<Box<AccessEffectivePermission>>,
165}
166
167// One day this is going to be Map<Attribute, ValueSet> - @yaleman
168// Today is that day - @firstyear
169pub type Eattrs = Map<Attribute, ValueSet>;
170
171pub trait GetUuid {
172    fn get_uuid(&self) -> Uuid;
173}
174
175pub trait Committed {}
176
177impl Committed for EntrySealed {}
178impl Committed for EntryReduced {}
179
180pub(crate) fn compare_attrs(left: &Eattrs, right: &Eattrs) -> bool {
181    // We can't shortcut based on len because cid mod may not be present.
182    // Build the set of all keys between both.
183    let allkeys: Set<&Attribute> = left
184        .keys()
185        .chain(right.keys())
186        .filter(|k| *k != &Attribute::LastModifiedCid && *k != &Attribute::CreatedAtCid)
187        .collect();
188
189    allkeys.into_iter().all(|k| {
190        // Both must be Some, and both must have the same interiors.
191        let left_vs = left.get(k);
192        let right_vs = right.get(k);
193        let r = match (left_vs, right_vs) {
194            (Some(l), Some(r)) => l.eq(r),
195            _ => false,
196        };
197        if !r {
198            trace!(?k, ?left_vs, ?right_vs, "compare_attrs_allkeys");
199        }
200        r
201    })
202}
203
204/// Entry is the core data storage type of the server. Almost every aspect of the server is
205/// designed to read, handle and manipulate entries.
206///
207/// Entries store attribute value assertions, or AVA. These are sets of key-values.
208///
209/// Entries have a lifecycle within a single operation, and as part of replication.
210/// The lifecycle for operations is defined through state and valid types. Each entry has a pair
211/// Of these types at anytime. The first is the AVA [`schema`] and [`access`] control assertion
212/// state. This is represented by the type `VALID` as one of `EntryValid`, `EntryInvalid` or
213/// `EntryReduced`. Every entry starts as `EntryInvalid`, and when checked by the schema for
214/// correctness, transitions to `EntryValid`. While an entry is `EntryValid` it can not be
215/// altered - you must invalidate it to `EntryInvalid`, then modify, then check again.
216/// An entry that has had access controls applied moves from `EntryValid` to `EntryReduced`,
217/// to show that the AVAs have reduced to the valid read set of the current [`event`] user.
218///
219/// The second type of `STATE` represents the database commit state and internal db ID's. A
220/// new entry that has never been committed is `EntryNew`, but an entry that has been retrieved
221/// from the database is `EntryCommitted`. This affects the operations you can apply IE modify
222/// or delete.
223///
224/// These types exist to prevent at compile time, mishandling of Entries, to ensure they are always
225/// handled with the correct lifecycles and processes.
226///
227/// [`schema`]: ../schema/index.html
228/// [`access`]: ../access/index.html
229/// [`event`]: ../event/index.html
230pub struct Entry<VALID, STATE> {
231    valid: VALID,
232    state: STATE,
233    // We may need to change this to Set to allow borrow of Value -> PartialValue for lookups.
234    attrs: Eattrs,
235}
236
237impl<VALID, STATE> std::fmt::Debug for Entry<VALID, STATE>
238where
239    STATE: std::fmt::Debug,
240    VALID: std::fmt::Debug,
241{
242    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
243        f.debug_struct("Entry<EntrySealed, _>")
244            .field("state", &self.state)
245            .field("valid", &self.valid)
246            .field("attrs", &self.attrs)
247            .finish()
248    }
249}
250
251impl<STATE> std::fmt::Display for Entry<EntrySealed, STATE>
252where
253    STATE: Clone,
254{
255    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
256        write!(f, "{}", self.get_uuid())
257    }
258}
259
260impl<STATE> std::fmt::Display for Entry<EntryInit, STATE>
261where
262    STATE: Clone,
263{
264    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
265        write!(f, "Entry in initial state")
266    }
267}
268
269impl<STATE> Entry<EntryInit, STATE>
270where
271    STATE: Clone,
272{
273    /// Get the uuid of this entry.
274    pub fn get_uuid(&self) -> Option<Uuid> {
275        self.attrs
276            .get(&Attribute::Uuid)
277            .and_then(|vs| vs.to_uuid_single())
278    }
279}
280
281impl Default for Entry<EntryInit, EntryNew> {
282    fn default() -> Self {
283        Self::new()
284    }
285}
286
287impl FromIterator<(Attribute, ValueSet)> for EntryInitNew {
288    fn from_iter<I: IntoIterator<Item = (Attribute, ValueSet)>>(iter: I) -> Self {
289        let attrs = Eattrs::from_iter(iter);
290
291        Entry {
292            valid: EntryInit,
293            state: EntryNew,
294            attrs,
295        }
296    }
297}
298
299impl Entry<EntryInit, EntryNew> {
300    pub fn new() -> Self {
301        Entry {
302            // This means NEVER COMMITTED
303            valid: EntryInit,
304            state: EntryNew,
305            attrs: Map::new(),
306        }
307    }
308
309    /// Consume a Protocol Entry from JSON, and validate and process the data into an internal
310    /// [`Entry`] type.
311    pub fn from_proto_entry(
312        e: &ProtoEntry,
313        qs: &mut QueryServerWriteTransaction,
314    ) -> Result<Self, OperationError> {
315        trace!("from_proto_entry");
316        // Why not the trait? In the future we may want to extend
317        // this with server aware functions for changes of the
318        // incoming data.
319
320        // Somehow we need to take the tree of e attrs, and convert
321        // all ref types to our types ...
322        let map2: Result<Eattrs, OperationError> = e
323            .attrs
324            .iter()
325            .filter(|(_, v)| !v.is_empty())
326            .map(|(k, v)| {
327                trace!(?k, ?v, "attribute");
328                let attr_nk = Attribute::from(k.as_str());
329                let nv = valueset::from_result_value_iter(
330                    v.iter().map(|vr| qs.clone_value(&attr_nk, vr)),
331                );
332                trace!(?nv, "new valueset transform");
333                match nv {
334                    Ok(nvi) => Ok((attr_nk, nvi)),
335                    Err(e) => Err(e),
336                }
337            })
338            .collect();
339
340        let x = map2?;
341
342        Ok(Entry {
343            state: EntryNew,
344            valid: EntryInit,
345            attrs: x,
346        })
347    }
348
349    /// Given a proto entry in JSON formed as a serialised string, processed that string
350    /// into an Entry.
351    #[instrument(level = "debug", skip_all)]
352    pub fn from_proto_entry_str(
353        es: &str,
354        qs: &mut QueryServerWriteTransaction,
355    ) -> Result<Self, OperationError> {
356        if cfg!(test) {
357            if es.len() > 256 {
358                let (dsp_es, _) = es.split_at(255);
359                trace!("Parsing -> {}...", dsp_es);
360            } else {
361                trace!("Parsing -> {}", es);
362            }
363        }
364        // str -> Proto entry
365        let pe: ProtoEntry = serde_json::from_str(es).map_err(|e| {
366            // We probably shouldn't print ES here because that would allow users
367            // to inject content into our logs :)
368            admin_error!(?e, "SerdeJson Failure");
369            OperationError::SerdeJsonError
370        })?;
371        // now call from_proto_entry
372        Self::from_proto_entry(&pe, qs)
373    }
374
375    /// Assign the Change Identifier to this Entry, allowing it to be modified and then
376    /// written to the `Backend`
377    pub fn assign_cid(
378        mut self,
379        cid: Cid,
380        schema: &dyn SchemaTransaction,
381    ) -> Entry<EntryInvalid, EntryNew> {
382        /*
383         * Create the change log. This must be the last thing BEFORE we return!
384         * This is because we need to capture the set_last_changed attribute in
385         * the create transition.
386         */
387        let ecstate = EntryChangeState::new(&cid, &self.attrs, schema);
388
389        // Since the entry is now created, and modified here, we set the initial CID
390        // values.
391        let cv = vs_cid![cid.clone()];
392        let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
393        let cv = vs_cid![cid.clone()];
394        let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
395
396        Entry {
397            valid: EntryInvalid { cid, ecstate },
398            state: EntryNew,
399            attrs: self.attrs,
400        }
401    }
402
403    /// Compare this entry to another.
404    pub fn compare(&self, rhs: &Entry<EntrySealed, EntryCommitted>) -> bool {
405        compare_attrs(&self.attrs, &rhs.attrs)
406    }
407
408    /// ⚠️  This function bypasses the db commit and creates invalid replication metadata.
409    /// The entry it creates can never be replicated.
410    /// This is a TEST ONLY method and will never be exposed in production.
411    #[cfg(test)]
412    pub fn into_invalid_new(mut self) -> Entry<EntryInvalid, EntryNew> {
413        let cid = Cid::new_zero();
414        self.set_last_changed(cid.clone());
415
416        let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
417
418        Entry {
419            valid: EntryInvalid { cid, ecstate },
420            state: EntryNew,
421            attrs: self.attrs,
422        }
423    }
424
425    /// ⚠️  This function bypasses the db commit and creates invalid replication metadata.
426    /// The entry it creates can never be replicated.
427    /// This is a TEST ONLY method and will never be exposed in production.
428    #[cfg(test)]
429    pub fn into_valid_new(mut self) -> Entry<EntryValid, EntryNew> {
430        let cid = Cid::new_zero();
431        self.set_last_changed(cid.clone());
432        let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
433
434        Entry {
435            valid: EntryValid {
436                ecstate,
437                uuid: self.get_uuid().expect("Invalid uuid"),
438            },
439            state: EntryNew,
440            attrs: self.attrs,
441        }
442    }
443
444    /// ⚠️  This function bypasses the db commit, assigns fake db ids, and invalid replication metadata.
445    /// The entry it creates can never be committed safely or replicated.
446    /// This is a TEST ONLY method and will never be exposed in production.
447    #[cfg(test)]
448    pub fn into_sealed_committed(mut self) -> Entry<EntrySealed, EntryCommitted> {
449        let cid = Cid::new_zero();
450        self.set_last_changed(cid.clone());
451        let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
452        let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
453        Entry {
454            valid: EntrySealed { uuid, ecstate },
455            state: EntryCommitted { id: 0 },
456            attrs: self.attrs,
457        }
458    }
459
460    /// ⚠️  This function bypasses the db commit and creates invalid replication metadata.
461    /// The entry it creates can never be replicated.
462    /// This is a TEST ONLY method and will never be exposed in production.
463    #[cfg(test)]
464    pub fn into_sealed_new(mut self) -> Entry<EntrySealed, EntryNew> {
465        let cid = Cid::new_zero();
466        self.set_last_changed(cid.clone());
467        let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
468
469        Entry {
470            valid: EntrySealed {
471                uuid: self.get_uuid().expect("Invalid uuid"),
472                ecstate,
473            },
474            state: EntryNew,
475            attrs: self.attrs,
476        }
477    }
478
479    //              ⚠️   replication safety  ⚠️
480    // These functions are SAFE because they occur in the EntryInit
481    // state, which precedes the generation of the initial Create
482    // event for the attribute.
483    /// Add an attribute-value-assertion to this Entry.
484    pub fn add_ava(&mut self, attr: Attribute, value: Value) {
485        self.add_ava_int(attr, value);
486    }
487
488    pub fn remove_ava(&mut self, attr: &Attribute) {
489        self.attrs.remove(attr);
490    }
491
492    /// Set the content of this ava with this valueset, ignoring the previous data.
493    pub fn set_ava_set(&mut self, attr: &Attribute, vs: ValueSet) {
494        self.attrs.insert(attr.clone(), vs);
495    }
496
497    /// Replace the existing content of an attribute set of this Entry, with a new set of Values.
498    pub fn set_ava<T>(&mut self, attr: Attribute, iter: T)
499    where
500        T: IntoIterator<Item = Value>,
501    {
502        self.set_ava_iter_int(attr, iter);
503    }
504
505    pub fn get_ava_mut<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<&mut ValueSet> {
506        self.attrs.get_mut(attr.as_ref())
507    }
508}
509
510impl From<SchemaAttribute> for EntryInitNew {
511    fn from(value: SchemaAttribute) -> Self {
512        EntryInitNew::from(&value)
513    }
514}
515
516impl From<&SchemaAttribute> for EntryInitNew {
517    fn from(s: &SchemaAttribute) -> Self {
518        // Build the Map of the attributes
519        let mut attrs = Eattrs::new();
520        attrs.insert(Attribute::AttributeName, vs_iutf8![s.name.as_str()]);
521        attrs.insert(Attribute::Description, vs_utf8![s.description.to_owned()]);
522        attrs.insert(Attribute::Uuid, vs_uuid![s.uuid]);
523        attrs.insert(Attribute::MultiValue, vs_bool![s.multivalue]);
524        attrs.insert(Attribute::Phantom, vs_bool![s.phantom]);
525        attrs.insert(Attribute::SyncAllowed, vs_bool![s.sync_allowed]);
526        attrs.insert(Attribute::Replicated, vs_bool![s.replicated.into()]);
527        attrs.insert(Attribute::Unique, vs_bool![s.unique]);
528        attrs.insert(Attribute::Indexed, vs_bool![s.indexed]);
529        attrs.insert(Attribute::Syntax, vs_syntax![s.syntax]);
530        attrs.insert(
531            Attribute::Class,
532            vs_iutf8![
533                EntryClass::Object.into(),
534                EntryClass::System.into(),
535                EntryClass::AttributeType.into()
536            ],
537        );
538
539        // Insert stuff.
540
541        Entry {
542            valid: EntryInit,
543            state: EntryNew,
544            attrs,
545        }
546    }
547}
548
549impl From<SchemaClass> for EntryInitNew {
550    fn from(value: SchemaClass) -> Self {
551        EntryInitNew::from(&value)
552    }
553}
554
555impl From<&SchemaClass> for EntryInitNew {
556    fn from(s: &SchemaClass) -> Self {
557        let mut attrs = Eattrs::new();
558        attrs.insert(Attribute::ClassName, vs_iutf8![s.name.as_str()]);
559        attrs.insert(Attribute::Description, vs_utf8![s.description.to_owned()]);
560        attrs.insert(Attribute::SyncAllowed, vs_bool![s.sync_allowed]);
561        attrs.insert(Attribute::Uuid, vs_uuid![s.uuid]);
562        attrs.insert(
563            Attribute::Class,
564            vs_iutf8![
565                EntryClass::Object.into(),
566                EntryClass::System.into(),
567                EntryClass::ClassType.into()
568            ],
569        );
570
571        let vs_systemmay = ValueSetIutf8::from_iter(s.systemmay.iter().map(|sm| sm.as_str()));
572        if let Some(vs) = vs_systemmay {
573            attrs.insert(Attribute::SystemMay, vs);
574        }
575
576        let vs_systemmust = ValueSetIutf8::from_iter(s.systemmust.iter().map(|sm| sm.as_str()));
577        if let Some(vs) = vs_systemmust {
578            attrs.insert(Attribute::SystemMust, vs);
579        }
580
581        let vs_systemexcludes =
582            ValueSetIutf8::from_iter(s.systemexcludes.iter().map(|sm| sm.as_str()));
583        if let Some(vs) = vs_systemexcludes {
584            attrs.insert(Attribute::SystemExcludes, vs);
585        }
586
587        let vs_systemsupplements =
588            ValueSetIutf8::from_iter(s.systemsupplements.iter().map(|sm| sm.as_str()));
589        if let Some(vs) = vs_systemsupplements {
590            attrs.insert(Attribute::SystemSupplements, vs);
591        }
592
593        Entry {
594            valid: EntryInit,
595            state: EntryNew,
596            attrs,
597        }
598    }
599}
600
601impl Entry<EntryRefresh, EntryNew> {
602    pub fn from_repl_entry_v1(repl_entry: ReplEntryV1) -> Result<Self, OperationError> {
603        // From the entry, we have to rebuild the ecstate and the attrs.
604        let (ecstate, mut attrs) = repl_entry.rehydrate()?;
605
606        // During seal, these values will be re-written, but we need them present for
607        // schema validation.
608        let last_mod_cid = ecstate.get_max_cid();
609        let cv = vs_cid![last_mod_cid.clone()];
610        let _ = attrs.insert(Attribute::LastModifiedCid, cv);
611
612        let create_at_cid = ecstate.at();
613        let cv = vs_cid![create_at_cid.clone()];
614        let _ = attrs.insert(Attribute::CreatedAtCid, cv);
615
616        Ok(Entry {
617            valid: EntryRefresh { ecstate },
618            state: EntryNew,
619            attrs,
620        })
621    }
622}
623
624impl<STATE> Entry<EntryRefresh, STATE> {
625    pub fn validate(
626        self,
627        schema: &dyn SchemaTransaction,
628    ) -> Result<Entry<EntryValid, STATE>, SchemaError> {
629        let uuid: Uuid = self
630            .attrs
631            .get(&Attribute::Uuid)
632            .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
633            .and_then(|vs| {
634                vs.to_uuid_single()
635                    .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
636            })?;
637
638        // Build the new valid entry ...
639        let ne = Entry {
640            valid: EntryValid {
641                uuid,
642                ecstate: self.valid.ecstate,
643            },
644            state: self.state,
645            attrs: self.attrs,
646        };
647
648        ne.validate(schema).map(|()| ne)
649    }
650}
651
652impl<STATE> Entry<EntryIncremental, STATE> {
653    pub fn get_uuid(&self) -> Uuid {
654        self.valid.uuid
655    }
656}
657
658impl Entry<EntryIncremental, EntryNew> {
659    fn stub_ecstate(&self) -> EntryChangeState {
660        self.valid.ecstate.stub()
661    }
662
663    pub fn rehydrate(repl_inc_entry: ReplIncrementalEntryV1) -> Result<Self, OperationError> {
664        let (uuid, ecstate, attrs) = repl_inc_entry.rehydrate()?;
665
666        Ok(Entry {
667            valid: EntryIncremental { uuid, ecstate },
668            state: EntryNew,
669            attrs,
670        })
671    }
672
673    pub(crate) fn is_add_conflict(&self, db_entry: &EntrySealedCommitted) -> bool {
674        use crate::repl::entry::State;
675        debug_assert_eq!(self.valid.uuid, db_entry.valid.uuid);
676        // This is a conflict if the state 'at' is not identical
677        let self_cs = &self.valid.ecstate;
678        let db_cs = db_entry.get_changestate();
679
680        // Can only add conflict on live entries.
681        match (self_cs.current(), db_cs.current()) {
682            (State::Live { at: at_left, .. }, State::Live { at: at_right, .. }) => {
683                at_left != at_right
684            }
685            // Tombstone will always overwrite.
686            _ => false,
687        }
688    }
689
690    pub(crate) fn resolve_add_conflict(
691        &self,
692        cid: &Cid,
693        db_ent: &EntrySealedCommitted,
694    ) -> (Option<EntrySealedNew>, EntryIncrementalCommitted) {
695        use crate::repl::entry::State;
696        debug_assert_eq!(self.valid.uuid, db_ent.valid.uuid);
697        let self_cs = &self.valid.ecstate;
698        let db_cs = db_ent.get_changestate();
699
700        match (self_cs.current(), db_cs.current()) {
701            (
702                State::Live {
703                    at: at_left,
704                    changes: _changes_left,
705                },
706                State::Live {
707                    at: at_right,
708                    changes: _changes_right,
709                },
710            ) => {
711                debug_assert!(at_left != at_right);
712                // Determine which of the entries must become the conflict
713                // and which will now persist. There are three possible cases.
714                //
715                // 1. The incoming ReplIncremental is after DBentry. This means RI is the
716                //    conflicting node. We take no action and just return the db_ent
717                //    as the valid state.
718                //
719                //    Since we are returning the existing database entry, we already have
720                //    locally applies the needed LastModifiedCid and CreatedAtCid. We
721                //    can proceed with no other changes.
722                if at_left > at_right {
723                    trace!("RI > DE, return DE");
724                    (
725                        None,
726                        Entry {
727                            valid: EntryIncremental {
728                                uuid: db_ent.valid.uuid,
729                                ecstate: db_cs.clone(),
730                            },
731                            state: EntryCommitted {
732                                id: db_ent.state.id,
733                            },
734                            attrs: db_ent.attrs.clone(),
735                        },
736                    )
737                }
738                //
739                // 2. The incoming ReplIncremental is before DBentry. This means our
740                //    DE is the conflicting note. There are now two choices:
741                //    a.  We are the origin of the DE, and thus must create the conflict
742                //        entry for replication (to guarantee single create)
743                //    b.  We are not the origin of the DE and so do not create a conflict
744                //        entry.
745                //    In both cases we update the DE with the state of RI after we have
746                //    followed the above logic.
747                else {
748                    trace!("RI < DE, return RI");
749                    // Are we the origin?
750                    let conflict = if at_right.s_uuid == cid.s_uuid {
751                        trace!("Origin process conflict entry");
752                        // We are making a new entry!
753
754                        let mut cnf_ent = Entry {
755                            valid: EntryInvalid {
756                                cid: cid.clone(),
757                                ecstate: db_cs.clone(),
758                            },
759                            state: EntryNew,
760                            attrs: db_ent.attrs.clone(),
761                        };
762
763                        // Move the current uuid to source_uuid
764                        cnf_ent.add_ava(Attribute::SourceUuid, Value::Uuid(db_ent.valid.uuid));
765
766                        // We need to make a random uuid in the conflict gen process.
767                        let new_uuid = Uuid::new_v4();
768                        cnf_ent.purge_ava(Attribute::Uuid);
769                        cnf_ent.add_ava(Attribute::Uuid, Value::Uuid(new_uuid));
770                        cnf_ent.add_ava(Attribute::Class, EntryClass::Recycled.into());
771                        cnf_ent.add_ava(Attribute::Class, EntryClass::Conflict.into());
772
773                        // Bypass add_ava here so that we don't update the ecstate with the
774                        // metadata of these attrs.
775                        // Setup the last changed to now.
776                        let cv = vs_cid![cid.clone()];
777                        let _ = cnf_ent.attrs.insert(Attribute::LastModifiedCid, cv);
778                        // Set the created_at to now, since we are creating a new conflict entry here.
779                        let cv = vs_cid![cid.clone()];
780                        let _ = cnf_ent.attrs.insert(Attribute::CreatedAtCid, cv);
781
782                        // Now we have to internally bypass some states.
783                        // This is okay because conflict entries aren't subject
784                        // to schema anyway.
785                        let Entry {
786                            valid: EntryInvalid { cid: _, ecstate },
787                            state,
788                            attrs,
789                        } = cnf_ent;
790
791                        let cnf_ent = Entry {
792                            valid: EntrySealed {
793                                uuid: new_uuid,
794                                ecstate,
795                            },
796                            state,
797                            attrs,
798                        };
799
800                        Some(cnf_ent)
801                    } else {
802                        None
803                    };
804
805                    // Since we are going to make the incoming node, we need to now
806                    // populate it's last-mod and created attributes.
807
808                    let mut attrs = self.attrs.clone();
809                    let ecstate = self_cs.clone();
810
811                    let last_mod_cid = ecstate.get_max_cid();
812                    let cv = vs_cid![last_mod_cid.clone()];
813                    let _ = attrs.insert(Attribute::LastModifiedCid, cv);
814
815                    let create_at_cid = ecstate.at();
816                    let cv = vs_cid![create_at_cid.clone()];
817                    let _ = attrs.insert(Attribute::CreatedAtCid, cv);
818
819                    (
820                        conflict,
821                        Entry {
822                            valid: EntryIncremental {
823                                uuid: self.valid.uuid,
824                                ecstate,
825                            },
826                            state: EntryCommitted {
827                                id: db_ent.state.id,
828                            },
829                            attrs,
830                        },
831                    )
832                }
833            }
834            // Can never get here due to is_add_conflict above. This is because
835            // a tombstone state on either branch will trigger is_add_conflict
836            // causing this to be impossible to reach.
837            _ => unreachable!(),
838        }
839    }
840
841    pub(crate) fn merge_state(
842        &self,
843        db_ent: &EntrySealedCommitted,
844        schema: &dyn SchemaTransaction,
845        trim_cid: &Cid,
846    ) -> EntryIncrementalCommitted {
847        use crate::repl::entry::State;
848
849        // Paranoid check.
850        debug_assert_eq!(self.valid.uuid, db_ent.valid.uuid);
851
852        // First, determine if either side is a tombstone. This is needed so that only
853        // when both sides are live
854        let self_cs = &self.valid.ecstate;
855        let db_cs = db_ent.get_changestate();
856
857        match (self_cs.current(), db_cs.current()) {
858            (
859                State::Live {
860                    at: at_left,
861                    changes: changes_left,
862                },
863                State::Live {
864                    at: at_right,
865                    changes: changes_right,
866                },
867            ) => {
868                debug_assert_eq!(at_left, at_right);
869                // Given the current db entry, compare and merge our attributes to
870                // form a resultant entry attr and ecstate
871                //
872                // To shortcut this we dedup the attr set and then iterate.
873                let mut attr_set: Vec<_> =
874                    changes_left.keys().chain(changes_right.keys()).collect();
875                attr_set.shrink_to_fit();
876                attr_set.sort_unstable();
877                attr_set.dedup();
878
879                // Make a new ecstate and attrs set.
880                let mut changes = BTreeMap::default();
881                let mut eattrs = Eattrs::default();
882
883                // Now we have the set of attrs from both sides. Lets see what state they are in!
884                for attr_name in attr_set.into_iter() {
885                    match (changes_left.get(attr_name), changes_right.get(attr_name)) {
886                        (Some(cid_left), Some(cid_right)) => {
887                            // This is the normal / usual and most "fun" case. Here we need to determine
888                            // which side is latest and then do a valueset merge. This is also
889                            // needing schema awareness depending on the attribute!
890                            //
891                            // The behaviour is very dependent on the state of the attributes and
892                            // if they exist.
893                            let take_left = cid_left > cid_right;
894
895                            match (self.attrs.get(attr_name), db_ent.attrs.get(attr_name)) {
896                                (Some(vs_left), Some(vs_right)) if take_left => {
897                                    changes.insert(attr_name.clone(), cid_left.clone());
898                                    #[allow(clippy::todo)]
899                                    if let Some(merged_attr_state) =
900                                        vs_left.repl_merge_valueset(vs_right, trim_cid)
901                                    {
902                                        // NOTE: This is for special attr types that need to merge
903                                        // rather than choose content.
904                                        eattrs.insert(attr_name.clone(), merged_attr_state);
905                                    } else {
906                                        eattrs.insert(attr_name.clone(), vs_left.clone());
907                                    }
908                                }
909                                (Some(vs_left), Some(vs_right)) => {
910                                    changes.insert(attr_name.clone(), cid_right.clone());
911                                    #[allow(clippy::todo)]
912                                    if let Some(merged_attr_state) =
913                                        vs_right.repl_merge_valueset(vs_left, trim_cid)
914                                    {
915                                        // NOTE: This is for special attr types that need to merge
916                                        // rather than choose content.
917                                        eattrs.insert(attr_name.clone(), merged_attr_state);
918                                    } else {
919                                        eattrs.insert(attr_name.clone(), vs_right.clone());
920                                    }
921                                }
922                                (Some(vs_left), None) if take_left => {
923                                    changes.insert(attr_name.clone(), cid_left.clone());
924                                    eattrs.insert(attr_name.clone(), vs_left.clone());
925                                }
926                                (Some(_vs_left), None) => {
927                                    changes.insert(attr_name.clone(), cid_right.clone());
928                                    // Taking right, nothing to do due to no attr.
929                                }
930                                (None, Some(_vs_right)) if take_left => {
931                                    changes.insert(attr_name.clone(), cid_left.clone());
932                                    // Taking left, nothing to do due to no attr.
933                                }
934                                (None, Some(vs_right)) => {
935                                    changes.insert(attr_name.clone(), cid_right.clone());
936                                    eattrs.insert(attr_name.clone(), vs_right.clone());
937                                }
938                                (None, None) if take_left => {
939                                    changes.insert(attr_name.clone(), cid_left.clone());
940                                    // Taking left, nothing to do due to no attr.
941                                }
942                                (None, None) => {
943                                    changes.insert(attr_name.clone(), cid_right.clone());
944                                    // Taking right, nothing to do due to no attr.
945                                }
946                            }
947                            // End attr merging
948                        }
949                        (Some(cid_left), None) => {
950                            // Keep the value on the left.
951                            changes.insert(attr_name.clone(), cid_left.clone());
952                            if let Some(valueset) = self.attrs.get(attr_name) {
953                                eattrs.insert(attr_name.clone(), valueset.clone());
954                            }
955                        }
956                        (None, Some(cid_right)) => {
957                            // Keep the value on the right.
958                            changes.insert(attr_name.clone(), cid_right.clone());
959                            if let Some(valueset) = db_ent.attrs.get(attr_name) {
960                                eattrs.insert(attr_name.clone(), valueset.clone());
961                            }
962                        }
963                        (None, None) => {
964                            // Should be impossible! At least one side or the other must have a change.
965                            debug_assert!(false);
966                        }
967                    }
968                }
969
970                let mut ecstate = EntryChangeState::build(State::Live {
971                    at: at_left.clone(),
972                    changes,
973                });
974
975                // Similar to the process of "seal", remove anything that isn't
976                // replicated from the ecstate (should be a no-op), and then update
977                // the created/mod cid's.
978                ecstate.retain(|k, _| schema.is_replicated(k));
979
980                let cv = vs_cid![ecstate.get_max_cid().clone()];
981                let _ = eattrs.insert(Attribute::LastModifiedCid, cv);
982
983                let cv = vs_cid![ecstate.at().clone()];
984                let _ = eattrs.insert(Attribute::CreatedAtCid, cv);
985
986                Entry {
987                    valid: EntryIncremental {
988                        uuid: self.valid.uuid,
989                        ecstate,
990                    },
991                    state: EntryCommitted {
992                        id: db_ent.state.id,
993                    },
994                    attrs: eattrs,
995                }
996            }
997            (State::Tombstone { at: left_at }, State::Live { .. }) => {
998                // We have to generate the attrs here, since on replication
999                // we just send the tombstone ecstate rather than attrs. Our
1000                // db stub also lacks these attributes too.
1001                let mut attrs_new: Eattrs = Map::new();
1002                let class_ava = vs_iutf8![EntryClass::Object.into(), EntryClass::Tombstone.into()];
1003                let last_mod_ava = vs_cid![left_at.clone()];
1004                let created_ava = vs_cid![left_at.clone()];
1005
1006                attrs_new.insert(Attribute::Uuid, vs_uuid![self.valid.uuid]);
1007                attrs_new.insert(Attribute::Class, class_ava);
1008                attrs_new.insert(Attribute::LastModifiedCid, last_mod_ava);
1009                attrs_new.insert(Attribute::CreatedAtCid, created_ava);
1010
1011                Entry {
1012                    valid: EntryIncremental {
1013                        uuid: self.valid.uuid,
1014                        ecstate: self.valid.ecstate.clone(),
1015                    },
1016                    state: EntryCommitted {
1017                        id: db_ent.state.id,
1018                    },
1019                    attrs: attrs_new,
1020                }
1021            }
1022            (State::Live { .. }, State::Tombstone { .. }) => {
1023                // Our current DB entry is a tombstone - ignore the incoming live
1024                // entry and just retain our DB tombstone.
1025                //
1026                // Note we don't need to gen the attrs here since if a stub was made then
1027                // we'd be live:live. To be in live:ts, then our db entry MUST exist and
1028                // must be a ts.
1029
1030                Entry {
1031                    valid: EntryIncremental {
1032                        uuid: db_ent.valid.uuid,
1033                        ecstate: db_ent.valid.ecstate.clone(),
1034                    },
1035                    state: EntryCommitted {
1036                        id: db_ent.state.id,
1037                    },
1038                    attrs: db_ent.attrs.clone(),
1039                }
1040            }
1041            (State::Tombstone { at: left_at }, State::Tombstone { at: right_at }) => {
1042                // WARNING - this differs from the other tombstone check cases
1043                // lower of the two AT values. This way replicas always have the
1044                // earliest TS value. It's a rare case but needs handling.
1045
1046                let (at, ecstate) = if left_at < right_at {
1047                    (left_at, self.valid.ecstate.clone())
1048                } else {
1049                    (right_at, db_ent.valid.ecstate.clone())
1050                };
1051
1052                let mut attrs_new: Eattrs = Map::new();
1053                let class_ava = vs_iutf8![EntryClass::Object.into(), EntryClass::Tombstone.into()];
1054                let last_mod_ava = vs_cid![at.clone()];
1055                let created_ava = vs_cid![at.clone()];
1056
1057                attrs_new.insert(Attribute::Uuid, vs_uuid![db_ent.valid.uuid]);
1058                attrs_new.insert(Attribute::Class, class_ava);
1059                attrs_new.insert(Attribute::LastModifiedCid, last_mod_ava);
1060                attrs_new.insert(Attribute::CreatedAtCid, created_ava);
1061
1062                Entry {
1063                    valid: EntryIncremental {
1064                        uuid: db_ent.valid.uuid,
1065                        ecstate,
1066                    },
1067                    state: EntryCommitted {
1068                        id: db_ent.state.id,
1069                    },
1070                    attrs: attrs_new,
1071                }
1072            }
1073        }
1074    }
1075}
1076
1077impl Entry<EntryIncremental, EntryCommitted> {
1078    pub(crate) fn validate_repl(self, schema: &dyn SchemaTransaction) -> EntryValidCommitted {
1079        // Unlike the other method of schema validation, we can't return an error
1080        // here when schema fails - we need to in-place move the entry to a
1081        // conflict state so that the replication can proceed.
1082
1083        let mut ne = Entry {
1084            valid: EntryValid {
1085                uuid: self.valid.uuid,
1086                ecstate: self.valid.ecstate,
1087            },
1088            state: self.state,
1089            attrs: self.attrs,
1090        };
1091
1092        if let Err(e) = ne.validate(schema) {
1093            warn!(uuid = ?self.valid.uuid, err = ?e, "Entry failed schema check, moving to a conflict state");
1094            ne.add_ava_int(Attribute::Class, EntryClass::Recycled.into());
1095            ne.add_ava_int(Attribute::Class, EntryClass::Conflict.into());
1096            ne.add_ava_int(Attribute::SourceUuid, Value::Uuid(self.valid.uuid));
1097        }
1098        ne
1099    }
1100}
1101
1102impl<STATE> Entry<EntryInvalid, STATE> {
1103    pub(crate) fn get_uuid(&self) -> Option<Uuid> {
1104        self.attrs
1105            .get(&Attribute::Uuid)
1106            .and_then(|vs| vs.to_uuid_single())
1107    }
1108
1109    /// Validate that this entry and its attribute-value sets are conformant to the system's'
1110    /// schema and the relevant syntaxes.
1111    pub fn validate(
1112        self,
1113        schema: &dyn SchemaTransaction,
1114    ) -> Result<Entry<EntryValid, STATE>, SchemaError> {
1115        let uuid: Uuid = self
1116            .attrs
1117            .get(&Attribute::Uuid)
1118            .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
1119            .and_then(|vs| {
1120                vs.to_uuid_single()
1121                    .ok_or_else(|| SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
1122            })?;
1123
1124        // Build the new valid entry ...
1125        let ne = Entry {
1126            valid: EntryValid {
1127                uuid,
1128                ecstate: self.valid.ecstate,
1129            },
1130            state: self.state,
1131            attrs: self.attrs,
1132        };
1133
1134        ne.validate(schema).map(|()| ne)
1135    }
1136
1137    /// Access a reference set in a directly mutable form. This is "safe" because
1138    /// referential integrity will check the values added are valid, and because
1139    /// this is strongly typed it can't violate syntax.
1140    pub(crate) fn get_ava_refer_mut<A: AsRef<Attribute>>(
1141        &mut self,
1142        attr: A,
1143    ) -> Option<&mut BTreeSet<Uuid>> {
1144        self.get_ava_mut(attr).and_then(|vs| vs.as_refer_set_mut())
1145    }
1146
1147    pub(crate) fn get_ava_mut<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<&mut ValueSet> {
1148        let attr_ref = attr.as_ref();
1149        self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
1150        self.attrs.get_mut(attr.as_ref())
1151    }
1152}
1153
1154impl<VALID, STATE> Clone for Entry<VALID, STATE>
1155where
1156    VALID: Clone,
1157    STATE: Clone,
1158{
1159    // Dirty modifiable state. Works on any other state to dirty them.
1160    fn clone(&self) -> Entry<VALID, STATE> {
1161        Entry {
1162            valid: self.valid.clone(),
1163            state: self.state.clone(),
1164            attrs: self.attrs.clone(),
1165        }
1166    }
1167}
1168
1169impl Entry<EntryInvalid, EntryCommitted> {
1170    /// ⚠️  This function bypasses the schema validation and can panic if uuid is not found.
1171    /// The entry it creates can never be committed safely or replicated.
1172    /// This is a TEST ONLY method and will never be exposed in production.
1173    #[cfg(test)]
1174    pub fn into_valid_new(self) -> Entry<EntryValid, EntryNew> {
1175        let uuid = self.get_uuid().expect("Invalid uuid");
1176        Entry {
1177            valid: EntryValid {
1178                uuid,
1179                ecstate: self.valid.ecstate,
1180            },
1181            state: EntryNew,
1182            attrs: self.attrs,
1183        }
1184    }
1185
1186    /// Convert this entry into a recycled entry, that is "in the recycle bin".
1187    pub fn to_recycled(mut self) -> Self {
1188        // This will put the modify ahead of the recycle transition.
1189        self.add_ava(Attribute::Class, EntryClass::Recycled.into());
1190
1191        // Change state repl doesn't need this flag
1192        // self.valid.ecstate.recycled(&self.valid.cid);
1193
1194        Entry {
1195            valid: self.valid,
1196            state: self.state,
1197            attrs: self.attrs,
1198        }
1199    }
1200
1201    /// Convert this entry into a conflict, declaring what entries it conflicted against.
1202    pub fn to_conflict<T>(&mut self, iter: T)
1203    where
1204        T: IntoIterator<Item = Uuid>,
1205    {
1206        self.add_ava(Attribute::Class, EntryClass::Recycled.into());
1207        self.add_ava(Attribute::Class, EntryClass::Conflict.into());
1208        // Add all the source uuids we conflicted against.
1209        for source_uuid in iter {
1210            self.add_ava(Attribute::SourceUuid, Value::Uuid(source_uuid));
1211        }
1212    }
1213
1214    /// Extract this entry from the recycle bin into a live state.
1215    pub fn to_revived(mut self) -> Self {
1216        // This will put the modify ahead of the revive transition.
1217        self.remove_ava(Attribute::Class, &EntryClass::Recycled.into());
1218        self.remove_ava(Attribute::Class, &EntryClass::Conflict.into());
1219
1220        self.purge_ava(Attribute::CascadeDeleted);
1221        self.purge_ava(Attribute::SourceUuid);
1222        self.purge_ava(Attribute::RecycledDirectMemberOf);
1223
1224        // Change state repl doesn't need this flag
1225        // self.valid.ecstate.revive(&self.valid.cid);
1226
1227        Entry {
1228            valid: self.valid,
1229            state: self.state,
1230            attrs: self.attrs,
1231        }
1232    }
1233}
1234// Both invalid states can be reached from "entry -> invalidate"
1235
1236impl Entry<EntryInvalid, EntryNew> {
1237    /// This function steps back from EntryInvalid to EntryInit.
1238    /// This is a TEST ONLY method and will never be exposed in production.
1239    #[cfg(test)]
1240    pub fn into_init_new(self) -> Entry<EntryInit, EntryNew> {
1241        Entry {
1242            valid: EntryInit,
1243            state: EntryNew,
1244            attrs: self.attrs,
1245        }
1246    }
1247
1248    /// ⚠️  This function bypasses the schema validation and can panic if uuid is not found.
1249    /// The entry it creates can never be committed safely or replicated.
1250    /// This is a TEST ONLY method and will never be exposed in production.
1251    #[cfg(test)]
1252    pub fn into_valid_new(self) -> Entry<EntryValid, EntryNew> {
1253        let uuid = self.get_uuid().expect("Invalid uuid");
1254        Entry {
1255            valid: EntryValid {
1256                uuid,
1257                ecstate: self.valid.ecstate,
1258            },
1259            state: EntryNew,
1260            attrs: self.attrs,
1261        }
1262    }
1263
1264    /// ⚠️  This function bypasses the db commit, assigns fake db ids, and assigns an invalid uuid.
1265    /// The entry it creates can never be committed safely or replicated.
1266    /// This is a TEST ONLY method and will never be exposed in production.
1267    #[cfg(test)]
1268    pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1269        let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
1270        Entry {
1271            valid: EntrySealed {
1272                uuid,
1273                ecstate: self.valid.ecstate,
1274            },
1275            state: EntryCommitted { id: 0 },
1276            attrs: self.attrs,
1277        }
1278    }
1279
1280    /// ⚠️  This function bypasses the schema validation and assigns a fake uuid.
1281    /// The entry it creates can never be committed safely or replicated.
1282    /// This is a TEST ONLY method and will never be exposed in production.
1283    #[cfg(test)]
1284    pub fn into_valid_committed(self) -> Entry<EntryValid, EntryCommitted> {
1285        let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
1286        Entry {
1287            valid: EntryValid {
1288                uuid,
1289                ecstate: self.valid.ecstate,
1290            },
1291            state: EntryCommitted { id: 0 },
1292            attrs: self.attrs,
1293        }
1294    }
1295}
1296
1297impl Entry<EntryInvalid, EntryCommitted> {
1298    /// ⚠️  This function bypasses the schema validation and assigns a fake uuid.
1299    /// The entry it creates can never be committed safely or replicated.
1300    /// This is a TEST ONLY method and will never be exposed in production.
1301    #[cfg(test)]
1302    pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1303        let uuid = self.get_uuid().unwrap_or_else(Uuid::new_v4);
1304        Entry {
1305            valid: EntrySealed {
1306                uuid,
1307                ecstate: self.valid.ecstate,
1308            },
1309            state: self.state,
1310            attrs: self.attrs,
1311        }
1312    }
1313}
1314
1315impl Entry<EntrySealed, EntryNew> {
1316    /// ⚠️  This function bypasses schema validation and assigns an invalid uuid.
1317    /// The entry it creates can never be committed safely or replicated.
1318    /// This is a TEST ONLY method and will never be exposed in production.
1319    #[cfg(test)]
1320    pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1321        Entry {
1322            valid: self.valid,
1323            state: EntryCommitted { id: 0 },
1324            attrs: self.attrs,
1325        }
1326    }
1327
1328    /// Given this validated and sealed entry, process it with a `Backend` ID number so that it
1329    /// can be then serialised to the database.
1330    pub fn into_sealed_committed_id(self, id: u64) -> Entry<EntrySealed, EntryCommitted> {
1331        Entry {
1332            valid: self.valid,
1333            state: EntryCommitted { id },
1334            attrs: self.attrs,
1335        }
1336    }
1337
1338    pub fn compare(&self, rhs: &Entry<EntrySealed, EntryNew>) -> bool {
1339        compare_attrs(&self.attrs, &rhs.attrs)
1340    }
1341}
1342
1343type IdxDiff<'a> =
1344    Vec<Result<(&'a Attribute, IndexType, String), (&'a Attribute, IndexType, String)>>;
1345
1346impl<VALID> Entry<VALID, EntryCommitted> {
1347    /// If this entry has ever been committed to disk, retrieve its database id number.
1348    pub fn get_id(&self) -> u64 {
1349        self.state.id
1350    }
1351}
1352
1353impl<STATE> Entry<EntrySealed, STATE> {
1354    pub fn into_init(self) -> Entry<EntryInit, STATE> {
1355        Entry {
1356            valid: EntryInit,
1357            state: self.state,
1358            attrs: self.attrs,
1359        }
1360    }
1361}
1362
1363impl Entry<EntrySealed, EntryCommitted> {
1364    #[cfg(test)]
1365    pub(crate) fn get_last_changed(&self) -> Cid {
1366        self.valid.ecstate.get_max_cid().clone()
1367    }
1368
1369    /// State transititon to allow self to self for certain test macros.
1370    #[cfg(test)]
1371    pub fn into_sealed_committed(self) -> Entry<EntrySealed, EntryCommitted> {
1372        // NO-OP to satisfy macros.
1373        self
1374    }
1375
1376    pub(crate) fn stub_sealed_committed_id(
1377        id: u64,
1378        ctx_ent: &EntryIncrementalNew,
1379    ) -> EntrySealedCommitted {
1380        let uuid = ctx_ent.get_uuid();
1381        let ecstate = ctx_ent.stub_ecstate();
1382
1383        Entry {
1384            valid: EntrySealed { uuid, ecstate },
1385            state: EntryCommitted { id },
1386            attrs: Default::default(),
1387        }
1388    }
1389
1390    /// Insert a claim to this entry. This claim can NOT be persisted to disk, this is only
1391    /// used during a single Event session.
1392    pub fn insert_claim(&mut self, value: &str) {
1393        self.add_ava_int(Attribute::Claim, Value::new_iutf8(value));
1394    }
1395
1396    pub fn compare(&self, rhs: &Entry<EntrySealed, EntryCommitted>) -> bool {
1397        compare_attrs(&self.attrs, &rhs.attrs)
1398    }
1399
1400    /// Serialise this entry to its Database format ready for storage.
1401    pub fn to_dbentry(&self) -> DbEntry {
1402        // In the future this will do extra work to process uuid
1403        // into "attributes" suitable for dbentry storage.
1404        DbEntry {
1405            ent: DbEntryVers::V3 {
1406                changestate: self.valid.ecstate.to_db_changestate(),
1407                attrs: self
1408                    .attrs
1409                    .iter()
1410                    .map(|(k, vs)| {
1411                        let dbvs: DbValueSetV2 = vs.to_db_valueset_v2();
1412                        (k.clone(), dbvs)
1413                    })
1414                    .collect(),
1415            },
1416        }
1417    }
1418
1419    #[inline]
1420    /// Given this entry, extract the set of strings that can uniquely identify this for authentication
1421    /// purposes. These strings are then indexed.
1422    fn get_name2uuid_cands(&self) -> Set<String> {
1423        // The cands are:
1424        // * spn
1425        // * name
1426        // * gidnumber
1427
1428        let cands = [Attribute::Spn, Attribute::Name, Attribute::GidNumber];
1429        cands
1430            .iter()
1431            .filter_map(|cand| {
1432                self.attrs
1433                    .get(cand)
1434                    .map(|vs| vs.to_proto_string_clone_iter())
1435            })
1436            .flatten()
1437            .collect()
1438    }
1439
1440    #[inline]
1441    /// Given this entry, extract the set of strings that can externally identify this
1442    /// entry for sync purposes. These strings are then indexed.
1443    fn get_externalid2uuid(&self) -> Option<String> {
1444        self.attrs
1445            .get(&Attribute::SyncExternalId)
1446            .and_then(|vs| vs.to_proto_string_single())
1447    }
1448
1449    #[inline]
1450    /// Given this entry, extract its primary security principal name, or if not present
1451    /// extract its name, and if that's not present, extract its uuid.
1452    pub(crate) fn get_uuid2spn(&self) -> Value {
1453        self.attrs
1454            .get(&Attribute::Spn)
1455            .and_then(|vs| vs.to_value_single())
1456            .or_else(|| {
1457                self.attrs
1458                    .get(&Attribute::Name)
1459                    .and_then(|vs| vs.to_value_single())
1460            })
1461            .unwrap_or_else(|| Value::Uuid(self.get_uuid()))
1462    }
1463
1464    #[inline]
1465    /// Given this entry, determine its relative distinguished named for LDAP compatibility.
1466    ///
1467    /// See also - `get_display_id`
1468    pub(crate) fn get_uuid2rdn(&self) -> String {
1469        self.attrs
1470            .get(&Attribute::Spn)
1471            .and_then(|vs| vs.to_proto_string_single().map(|v| format!("spn={v}")))
1472            .or_else(|| {
1473                self.attrs
1474                    .get(&Attribute::Name)
1475                    .and_then(|vs| vs.to_proto_string_single().map(|v| format!("name={v}")))
1476            })
1477            .unwrap_or_else(|| format!("uuid={}", self.get_uuid().as_hyphenated()))
1478    }
1479
1480    /// Generate the required values for a name2uuid index. IE this is
1481    /// ALL possible names this entry COULD be known uniquely by!
1482    pub(crate) fn idx_name2uuid_diff(
1483        pre: Option<&Self>,
1484        post: Option<&Self>,
1485    ) -> (
1486        // Add
1487        Option<Set<String>>,
1488        // Remove
1489        Option<Set<String>>,
1490    ) {
1491        // needs to return gid for posix conversion
1492        match (pre, post) {
1493            (None, None) => {
1494                // No action required
1495                (None, None)
1496            }
1497            (None, Some(b)) => {
1498                // We are adding this entry (or restoring it),
1499                // so we need to add the values.
1500                (Some(b.get_name2uuid_cands()), None)
1501            }
1502            (Some(a), None) => {
1503                // Removing the entry, remove all values.
1504                (None, Some(a.get_name2uuid_cands()))
1505            }
1506            (Some(a), Some(b)) => {
1507                let pre_set = a.get_name2uuid_cands();
1508                let post_set = b.get_name2uuid_cands();
1509
1510                // what is in post, but not pre (added)
1511                let add_set: Set<_> = post_set.difference(&pre_set).cloned().collect();
1512                // what is in pre, but not post (removed)
1513                let rem_set: Set<_> = pre_set.difference(&post_set).cloned().collect();
1514                (Some(add_set), Some(rem_set))
1515            }
1516        }
1517    }
1518
1519    /// Generate the required values for externalid2uuid.
1520    pub(crate) fn idx_externalid2uuid_diff(
1521        pre: Option<&Self>,
1522        post: Option<&Self>,
1523    ) -> (Option<String>, Option<String>) {
1524        match (pre, post) {
1525            (None, None) => {
1526                // no action
1527                (None, None)
1528            }
1529            (None, Some(b)) => {
1530                // add
1531                (b.get_externalid2uuid(), None)
1532            }
1533            (Some(a), None) => {
1534                // remove
1535                (None, a.get_externalid2uuid())
1536            }
1537            (Some(a), Some(b)) => {
1538                let ia = a.get_externalid2uuid();
1539                let ib = b.get_externalid2uuid();
1540                if ia != ib {
1541                    // Note, we swap these since ib is the new post state
1542                    // we want to add, and ia is what we remove.
1543                    (ib, ia)
1544                } else {
1545                    // no action
1546                    (None, None)
1547                }
1548            }
1549        }
1550    }
1551
1552    /// Generate a differential between a previous and current entry state, and what changes this
1553    /// means for the current set of spn's for this uuid.
1554    pub(crate) fn idx_uuid2spn_diff(
1555        pre: Option<&Self>,
1556        post: Option<&Self>,
1557    ) -> Option<Result<Value, ()>> {
1558        match (pre, post) {
1559            (None, None) => {
1560                // no action
1561                None
1562            }
1563            (None, Some(b)) => {
1564                // add
1565                Some(Ok(b.get_uuid2spn()))
1566            }
1567            (Some(_a), None) => {
1568                // remove
1569                Some(Err(()))
1570            }
1571            (Some(a), Some(b)) => {
1572                let ia = a.get_uuid2spn();
1573                let ib = b.get_uuid2spn();
1574                if ia != ib {
1575                    // Add (acts as replace)
1576                    Some(Ok(ib))
1577                } else {
1578                    // no action
1579                    None
1580                }
1581            }
1582        }
1583    }
1584
1585    /// Generate a differential between a previous and current entry state, and what changes this
1586    /// means for the current set of LDAP relative distinguished names.
1587    pub(crate) fn idx_uuid2rdn_diff(
1588        pre: Option<&Self>,
1589        post: Option<&Self>,
1590    ) -> Option<Result<String, ()>> {
1591        match (pre, post) {
1592            (None, None) => {
1593                // no action
1594                None
1595            }
1596            (None, Some(b)) => {
1597                // add
1598                Some(Ok(b.get_uuid2rdn()))
1599            }
1600            (Some(_a), None) => {
1601                // remove
1602                Some(Err(()))
1603            }
1604            (Some(a), Some(b)) => {
1605                let ia = a.get_uuid2rdn();
1606                let ib = b.get_uuid2rdn();
1607                if ia != ib {
1608                    // Add (acts as replace)
1609                    Some(Ok(ib))
1610                } else {
1611                    // no action
1612                    None
1613                }
1614            }
1615        }
1616    }
1617
1618    /// Given the previous and current state of this entry, determine the indexing differential
1619    /// that needs to be applied. i.e. what indexes must be created, modified and removed.
1620    pub(crate) fn idx_diff<'a>(
1621        idxmeta: &'a HashMap<IdxKey, IdxSlope>,
1622        pre: Option<&Self>,
1623        post: Option<&Self>,
1624    ) -> IdxDiff<'a> {
1625        // We yield a list of Result, where Ok() means "add",
1626        // and Err() means "remove".
1627        // the value inside the result, is a tuple of attr, itype, idx_key
1628
1629        match (pre, post) {
1630            (None, None) => {
1631                // if both are none, yield empty list.
1632                Vec::with_capacity(0)
1633            }
1634            (Some(pre_e), None) => {
1635                // If we are none (?), yield our pre-state as removals.
1636                idxmeta
1637                    .keys()
1638                    .flat_map(|ikey| {
1639                        match pre_e.get_ava_set(&ikey.attr) {
1640                            None => Vec::with_capacity(0),
1641                            Some(vs) => {
1642                                let changes: Vec<Result<_, _>> = match ikey.itype {
1643                                    IndexType::Equality => {
1644                                        // We generate these keys out of the valueset now.
1645                                        vs.generate_idx_eq_keys()
1646                                            .into_iter()
1647                                            .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1648                                            .collect()
1649                                    }
1650                                    IndexType::Presence => {
1651                                        vec![Err((&ikey.attr, ikey.itype, "_".to_string()))]
1652                                    }
1653                                    IndexType::SubString => vs
1654                                        .generate_idx_sub_keys()
1655                                        .into_iter()
1656                                        .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1657                                        .collect(),
1658                                    IndexType::Ordering => vs
1659                                        .generate_idx_ord_keys()
1660                                        .into_iter()
1661                                        .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1662                                        .collect(),
1663                                };
1664                                changes
1665                            }
1666                        }
1667                    })
1668                    .collect()
1669            }
1670            (None, Some(post_e)) => {
1671                // If the pre-state is none, yield our additions.
1672                idxmeta
1673                    .keys()
1674                    .flat_map(|ikey| {
1675                        match post_e.get_ava_set(&ikey.attr) {
1676                            None => Vec::with_capacity(0),
1677                            Some(vs) => {
1678                                let changes: Vec<Result<_, _>> = match ikey.itype {
1679                                    IndexType::Equality => vs
1680                                        .generate_idx_eq_keys()
1681                                        .into_iter()
1682                                        .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1683                                        .collect(),
1684                                    IndexType::Presence => {
1685                                        vec![Ok((&ikey.attr, ikey.itype, "_".to_string()))]
1686                                    }
1687                                    IndexType::SubString => vs
1688                                        .generate_idx_sub_keys()
1689                                        .into_iter()
1690                                        .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1691                                        .collect(),
1692                                    IndexType::Ordering => vs
1693                                        .generate_idx_ord_keys()
1694                                        .into_iter()
1695                                        .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1696                                        .collect(),
1697                                };
1698                                // For each value
1699                                //
1700                                changes
1701                            }
1702                        }
1703                    })
1704                    .collect()
1705            }
1706            (Some(pre_e), Some(post_e)) => {
1707                assert_eq!(pre_e.state.id, post_e.state.id);
1708                idxmeta
1709                    .keys()
1710                    .flat_map(|ikey| {
1711                        match (
1712                            pre_e.get_ava_set(&ikey.attr),
1713                            post_e.get_ava_set(&ikey.attr),
1714                        ) {
1715                            (None, None) => {
1716                                // Neither have it, do nothing.
1717                                Vec::with_capacity(0)
1718                            }
1719                            (Some(pre_vs), None) => {
1720                                // It existed before, but not anymore
1721                                let changes: Vec<Result<_, _>> = match ikey.itype {
1722                                    IndexType::Equality => {
1723                                        // Turn each idx_key to the tuple of
1724                                        // changes.
1725                                        pre_vs
1726                                            .generate_idx_eq_keys()
1727                                            .into_iter()
1728                                            .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1729                                            .collect()
1730                                    }
1731                                    IndexType::Presence => {
1732                                        vec![Err((&ikey.attr, ikey.itype, "_".to_string()))]
1733                                    }
1734                                    IndexType::SubString => pre_vs
1735                                        .generate_idx_sub_keys()
1736                                        .into_iter()
1737                                        .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1738                                        .collect(),
1739                                    IndexType::Ordering => pre_vs
1740                                        .generate_idx_ord_keys()
1741                                        .into_iter()
1742                                        .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1743                                        .collect(),
1744                                };
1745                                changes
1746                            }
1747                            (None, Some(post_vs)) => {
1748                                // It was added now.
1749                                let changes: Vec<Result<_, _>> = match ikey.itype {
1750                                    IndexType::Equality => {
1751                                        // Turn each idx_key to the tuple of
1752                                        // changes.
1753                                        post_vs
1754                                            .generate_idx_eq_keys()
1755                                            .into_iter()
1756                                            .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1757                                            .collect()
1758                                    }
1759                                    IndexType::Presence => {
1760                                        vec![Ok((&ikey.attr, ikey.itype, "_".to_string()))]
1761                                    }
1762                                    IndexType::SubString => post_vs
1763                                        .generate_idx_sub_keys()
1764                                        .into_iter()
1765                                        .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1766                                        .collect(),
1767                                    IndexType::Ordering => post_vs
1768                                        .generate_idx_ord_keys()
1769                                        .into_iter()
1770                                        .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1771                                        .collect(),
1772                                };
1773                                changes
1774                            }
1775                            (Some(pre_vs), Some(post_vs)) => {
1776                                // it exists in both, we need to work out the difference within the attr.
1777                                let (mut pre_idx_keys, mut post_idx_keys) = match ikey.itype {
1778                                    IndexType::Equality => (
1779                                        pre_vs.generate_idx_eq_keys(),
1780                                        post_vs.generate_idx_eq_keys(),
1781                                    ),
1782                                    IndexType::Presence => {
1783                                        // No action - we still are "present", so nothing to do!
1784                                        (Vec::with_capacity(0), Vec::with_capacity(0))
1785                                    }
1786                                    IndexType::SubString => (
1787                                        pre_vs.generate_idx_sub_keys(),
1788                                        post_vs.generate_idx_sub_keys(),
1789                                    ),
1790                                    IndexType::Ordering => (
1791                                        pre_vs.generate_idx_ord_keys(),
1792                                        post_vs.generate_idx_ord_keys(),
1793                                    ),
1794                                };
1795
1796                                let sz = if pre_idx_keys.len() > post_idx_keys.len() {
1797                                    pre_idx_keys.len()
1798                                } else {
1799                                    post_idx_keys.len()
1800                                };
1801
1802                                let mut added_vs = Vec::with_capacity(sz);
1803                                let mut removed_vs = Vec::with_capacity(sz);
1804
1805                                if sz > 0 {
1806                                    pre_idx_keys.sort_unstable();
1807                                    post_idx_keys.sort_unstable();
1808
1809                                    let mut pre_iter = pre_idx_keys.iter();
1810                                    let mut post_iter = post_idx_keys.iter();
1811
1812                                    let mut pre = pre_iter.next();
1813                                    let mut post = post_iter.next();
1814
1815                                    loop {
1816                                        match (pre, post) {
1817                                            (Some(a), Some(b)) => {
1818                                                match a.cmp(b) {
1819                                                    Ordering::Less => {
1820                                                        removed_vs.push(a.clone());
1821                                                        pre = pre_iter.next();
1822                                                    }
1823                                                    Ordering::Equal => {
1824                                                        // In both - no action needed.
1825                                                        pre = pre_iter.next();
1826                                                        post = post_iter.next();
1827                                                    }
1828                                                    Ordering::Greater => {
1829                                                        added_vs.push(b.clone());
1830                                                        post = post_iter.next();
1831                                                    }
1832                                                }
1833                                            }
1834                                            (Some(a), None) => {
1835                                                removed_vs.push(a.clone());
1836                                                pre = pre_iter.next();
1837                                            }
1838                                            (None, Some(b)) => {
1839                                                added_vs.push(b.clone());
1840                                                post = post_iter.next();
1841                                            }
1842                                            (None, None) => {
1843                                                break;
1844                                            }
1845                                        }
1846                                    }
1847                                } // end sz > 0
1848
1849                                let mut diff =
1850                                    Vec::with_capacity(removed_vs.len() + added_vs.len());
1851
1852                                match ikey.itype {
1853                                    IndexType::SubString
1854                                    | IndexType::Ordering
1855                                    | IndexType::Equality => {
1856                                        removed_vs
1857                                            .into_iter()
1858                                            .map(|idx_key| Err((&ikey.attr, ikey.itype, idx_key)))
1859                                            .for_each(|v| diff.push(v));
1860                                        added_vs
1861                                            .into_iter()
1862                                            .map(|idx_key| Ok((&ikey.attr, ikey.itype, idx_key)))
1863                                            .for_each(|v| diff.push(v));
1864                                    }
1865                                    IndexType::Presence => {
1866                                        // No action - we still are "present", so nothing to do!
1867                                    }
1868                                };
1869                                // Return the diff
1870                                diff
1871                            }
1872                        }
1873                    })
1874                    .collect()
1875                // End diff of the entries
1876            }
1877        }
1878    }
1879
1880    pub fn from_dbentry(db_e: DbEntry, id: u64) -> Option<Self> {
1881        // Convert attrs from db format to value
1882
1883        let (attrs, ecstate) = match db_e.ent {
1884            DbEntryVers::V3 { changestate, attrs } => {
1885                let ecstate = EntryChangeState::from_db_changestate(changestate);
1886
1887                let r_attrs = attrs
1888                    .into_iter()
1889                    // Skip anything empty as new VS can't deal with it.
1890                    .filter(|(_k, vs)| !vs.is_empty())
1891                    .map(|(k, dbvs)| {
1892                        valueset::from_db_valueset_v2(dbvs)
1893                            .map(|vs: ValueSet| (k, vs))
1894                            .map_err(|e| {
1895                                error!(?e, "from_dbentry failed");
1896                            })
1897                    })
1898                    .collect::<Result<Eattrs, ()>>()
1899                    .ok()?;
1900
1901                (r_attrs, ecstate)
1902            }
1903        };
1904
1905        let uuid = attrs
1906            .get(&Attribute::Uuid)
1907            .and_then(|vs| vs.to_uuid_single())?;
1908
1909        Some(Entry {
1910            valid: EntrySealed { uuid, ecstate },
1911            state: EntryCommitted { id },
1912            attrs,
1913        })
1914    }
1915
1916    /// ⚠️  This function bypasses the access control validation logic and should NOT
1917    /// be used without special care and attention to ensure that no private data
1918    /// is leaked incorrectly to clients. Generally this is ONLY used inside of
1919    /// the access control processing functions which correctly applies the reduction
1920    /// steps.
1921    ///
1922    /// This is a TEST ONLY method and will never be exposed in production.
1923    #[cfg(test)]
1924    pub(crate) fn into_reduced(self) -> Entry<EntryReduced, EntryCommitted> {
1925        Entry {
1926            valid: EntryReduced {
1927                uuid: self.valid.uuid,
1928                effective_access: None,
1929            },
1930            state: self.state,
1931            attrs: self.attrs,
1932        }
1933    }
1934
1935    /// Given a set of attributes that are allowed to be seen on this entry, process and remove
1936    /// all other values that are NOT allowed in this query.
1937    pub fn reduce_attributes(
1938        &self,
1939        allowed_attrs: &BTreeSet<Attribute>,
1940        effective_access: Option<Box<AccessEffectivePermission>>,
1941    ) -> Entry<EntryReduced, EntryCommitted> {
1942        // Remove all attrs from our tree that are NOT in the allowed set.
1943        let f_attrs: Map<_, _> = self
1944            .attrs
1945            .iter()
1946            .filter_map(|(k, v)| {
1947                if allowed_attrs.contains(k) {
1948                    Some((k.clone(), v.clone()))
1949                } else {
1950                    None
1951                }
1952            })
1953            .collect();
1954
1955        let valid = EntryReduced {
1956            uuid: self.valid.uuid,
1957            effective_access,
1958        };
1959        let state = self.state.clone();
1960
1961        Entry {
1962            valid,
1963            state,
1964            attrs: f_attrs,
1965        }
1966    }
1967
1968    /// Convert this recycled entry, into a tombstone ready for reaping.
1969    pub fn to_tombstone(&self, cid: Cid) -> Entry<EntryInvalid, EntryCommitted> {
1970        let mut ecstate = self.valid.ecstate.clone();
1971        // Duplicate this to a tombstone entry
1972        let mut attrs_new: Eattrs = Map::new();
1973
1974        let class_ava = vs_iutf8![EntryClass::Object.into(), EntryClass::Tombstone.into()];
1975        let last_mod_ava = vs_cid![cid.clone()];
1976        let created_ava = vs_cid![cid.clone()];
1977
1978        attrs_new.insert(Attribute::Uuid, vs_uuid![self.get_uuid()]);
1979        attrs_new.insert(Attribute::Class, class_ava);
1980        attrs_new.insert(Attribute::LastModifiedCid, last_mod_ava);
1981        attrs_new.insert(Attribute::CreatedAtCid, created_ava);
1982
1983        // ⚠️  No return from this point!
1984        ecstate.tombstone(&cid);
1985
1986        Entry {
1987            valid: EntryInvalid { cid, ecstate },
1988            state: self.state.clone(),
1989            attrs: attrs_new,
1990        }
1991    }
1992
1993    /// Given a current transaction change identifier, mark this entry as valid and committed.
1994    pub fn into_valid(self, ecstate: EntryChangeState) -> Entry<EntryValid, EntryCommitted> {
1995        Entry {
1996            valid: EntryValid {
1997                uuid: self.valid.uuid,
1998                ecstate,
1999            },
2000            state: self.state,
2001            attrs: self.attrs,
2002        }
2003    }
2004
2005    pub fn verify(
2006        &self,
2007        schema: &dyn SchemaTransaction,
2008        results: &mut Vec<Result<(), ConsistencyError>>,
2009    ) {
2010        self.valid
2011            .ecstate
2012            .verify(schema, &self.attrs, self.state.id, results);
2013    }
2014}
2015
2016impl<STATE> Entry<EntryValid, STATE> {
2017    fn validate(&self, schema: &dyn SchemaTransaction) -> Result<(), SchemaError> {
2018        let schema_classes = schema.get_classes();
2019        let schema_attributes = schema.get_attributes();
2020
2021        // Now validate it!
2022        trace!(?self.attrs, "Entry::validate -> target");
2023
2024        // First, check we have class on the object ....
2025        if !self.attribute_pres(Attribute::Class) {
2026            // lrequest_error!("Missing attribute class");
2027            return Err(SchemaError::NoClassFound);
2028        }
2029
2030        if self.attribute_equality(Attribute::Class, &EntryClass::Conflict.into()) {
2031            // Conflict entries are exempt from schema enforcement. Return true.
2032            trace!("Skipping schema validation on conflict entry");
2033            return Ok(());
2034        };
2035
2036        // Are we in the recycle bin? We soften some checks if we are.
2037        let recycled = self.attribute_equality(Attribute::Class, &EntryClass::Recycled.into());
2038
2039        // Do we have extensible? We still validate syntax of attrs but don't
2040        // check for valid object structures.
2041        let extensible =
2042            self.attribute_equality(Attribute::Class, &EntryClass::ExtensibleObject.into());
2043
2044        let entry_classes = self.get_ava_set(Attribute::Class).ok_or_else(|| {
2045            admin_debug!("Attribute '{}' missing from entry", Attribute::Class);
2046            SchemaError::NoClassFound
2047        })?;
2048        let mut invalid_classes = Vec::with_capacity(0);
2049
2050        let mut classes: Vec<&SchemaClass> = Vec::with_capacity(entry_classes.len());
2051
2052        // We need to keep the btreeset of entry classes here so we can check the
2053        // requires and excludes.
2054        let entry_classes = if let Some(ec) = entry_classes.as_iutf8_set() {
2055            ec.iter()
2056                .for_each(|s| match schema_classes.get(s.as_str()) {
2057                    Some(x) => classes.push(x),
2058                    None => {
2059                        admin_debug!("invalid class: {:?}", s);
2060                        invalid_classes.push(s.to_string())
2061                    }
2062                });
2063            ec
2064        } else {
2065            admin_debug!("corrupt class attribute");
2066            return Err(SchemaError::NoClassFound);
2067        };
2068
2069        if !invalid_classes.is_empty() {
2070            return Err(SchemaError::InvalidClass(invalid_classes));
2071        };
2072
2073        // Now determine the set of excludes and requires we have, and then
2074        // assert we don't violate them.
2075
2076        let supplements_classes: Vec<_> = classes
2077            .iter()
2078            .flat_map(|cls| cls.systemsupplements.iter().chain(cls.supplements.iter()))
2079            .collect();
2080
2081        // So long as one supplement is present we can continue.
2082        let valid_supplements = if supplements_classes.is_empty() {
2083            // No need to check.
2084            true
2085        } else {
2086            supplements_classes
2087                .iter()
2088                .any(|class| entry_classes.contains(class.as_str()))
2089        };
2090
2091        if !valid_supplements {
2092            warn!(
2093                "Validation error, the following possible supplement classes are missing - {:?}",
2094                supplements_classes
2095            );
2096            let supplements_classes = supplements_classes.iter().map(|s| s.to_string()).collect();
2097            return Err(SchemaError::SupplementsNotSatisfied(supplements_classes));
2098        }
2099
2100        let excludes_classes: Vec<_> = classes
2101            .iter()
2102            .flat_map(|cls| cls.systemexcludes.iter().chain(cls.excludes.iter()))
2103            .collect();
2104
2105        let mut invalid_excludes = Vec::with_capacity(0);
2106
2107        excludes_classes.iter().for_each(|class| {
2108            if entry_classes.contains(class.as_str()) {
2109                invalid_excludes.push(class.to_string())
2110            }
2111        });
2112
2113        if !invalid_excludes.is_empty() {
2114            admin_warn!(
2115                "Validation error, the following excluded classes are present - {:?}",
2116                invalid_excludes
2117            );
2118            return Err(SchemaError::ExcludesNotSatisfied(invalid_excludes));
2119        }
2120
2121        // What this is really doing is taking a set of classes, and building an
2122        // "overall" class that describes this exact object for checking. IE we
2123        // build a super must/may set from the small class must/may sets.
2124
2125        //   for each class
2126        //      add systemmust/must and systemmay/may to their lists
2127        //      add anything from must also into may
2128
2129        // Now from the set of valid classes make a list of must/may
2130        //
2131        // NOTE: We still need this on extensible, because we still need to satisfy
2132        // our other must conditions as well!
2133        let must: Result<Vec<&SchemaAttribute>, _> = classes
2134            .iter()
2135            // Join our class systemmmust + must into one iter
2136            .flat_map(|cls| cls.systemmust.iter().chain(cls.must.iter()))
2137            .map(|s| {
2138                // This should NOT fail - if it does, it means our schema is
2139                // in an invalid state!
2140                schema_attributes.get(s).ok_or(SchemaError::Corrupted)
2141            })
2142            .collect();
2143
2144        let must = must?;
2145
2146        // Check that all must are inplace
2147        //   for each attr in must, check it's present on our ent
2148        let mut missing_must = Vec::with_capacity(0);
2149        for attr in must.iter() {
2150            let avas = self.get_ava_set(&attr.name);
2151            if avas.is_none() {
2152                missing_must.push(attr.name.clone());
2153            }
2154        }
2155
2156        if !missing_must.is_empty() {
2157            admin_warn!(
2158                "Validation error, the following required ({}) (must) attributes are missing - {:?}",
2159                self.get_display_id(), missing_must
2160            );
2161            // We if are in the recycle bin, we don't hard error here. This can occur when
2162            // a migration occurs and we delete an acp, and then the related group. Because
2163            // this would trigger refint which purges the acp_receiver_group, then this
2164            // must value becomes unsatisfiable. So here we soften the check for recycled
2165            // entries because they are in a "nebulous" state anyway.
2166            if !recycled {
2167                return Err(SchemaError::MissingMustAttribute(missing_must));
2168            }
2169        }
2170
2171        if extensible {
2172            self.attrs.iter().try_for_each(|(attr_name, avas)| {
2173                    match schema_attributes.get(attr_name) {
2174                        Some(a_schema) => {
2175                            // Now, for each type we do a *full* check of the syntax
2176                            // and validity of the ava.
2177                            if a_schema.phantom {
2178                                admin_warn!(
2179                                    "Rejecting attempt to add phantom attribute to extensible object: {}",
2180                                    attr_name
2181                                );
2182                                Err(SchemaError::PhantomAttribute(attr_name.to_string()))
2183                            } else {
2184                                a_schema.validate_ava(attr_name, avas)
2185                                // .map_err(|e| lrequest_error!("Failed to validate: {}", attr_name);)
2186                            }
2187                        }
2188                        None => {
2189                            admin_error!(
2190                                "Invalid Attribute {}, undefined in schema_attributes",
2191                                attr_name.to_string()
2192                            );
2193                            Err(SchemaError::InvalidAttribute(
2194                                attr_name.to_string()
2195                            ))
2196                        }
2197                    }
2198                })?;
2199        } else {
2200            // Note - we do NOT need to check phantom attributes here because they are
2201            // not allowed to exist in the class, which means a phantom attribute can't
2202            // be in the may/must set, and would FAIL our normal checks anyway.
2203
2204            // The set of "may" is a combination of may and must, since we have already
2205            // asserted that all must requirements are fulfilled. This allows us to
2206            // perform extended attribute checking in a single pass.
2207            let may: Result<Map<&Attribute, &SchemaAttribute>, _> = classes
2208                .iter()
2209                // Join our class systemmmust + must + systemmay + may into one.
2210                .flat_map(|cls| {
2211                    trace!(?cls);
2212                    cls.systemmust
2213                        .iter()
2214                        .chain(cls.must.iter())
2215                        .chain(cls.systemmay.iter())
2216                        .chain(cls.may.iter())
2217                })
2218                .map(|s| {
2219                    // This should NOT fail - if it does, it means our schema is
2220                    // in an invalid state!
2221                    Ok((s, schema_attributes.get(s).ok_or(SchemaError::Corrupted)?))
2222                })
2223                .collect();
2224
2225            let may = may?;
2226
2227            // TODO #70: Error needs to say what is missing
2228            // We need to return *all* missing attributes, not just the first error
2229            // we find. This will probably take a rewrite of the function definition
2230            // to return a result<_, vec<schemaerror>> and for the schema errors to take
2231            // information about what is invalid. It's pretty nontrivial.
2232
2233            // Check that any other attributes are in may
2234            //   for each attr on the object, check it's in the may+must set
2235            self.attrs.iter().try_for_each(|(attr_name, avas)| {
2236                    match may.get(attr_name) {
2237                        Some(a_schema) => {
2238                            // Now, for each type we do a *full* check of the syntax
2239                            // and validity of the ava.
2240                            a_schema.validate_ava(attr_name, avas)
2241                            // .map_err(|e| lrequest_error!("Failed to validate: {}", attr_name);
2242                        }
2243                        None => {
2244                            admin_error!(
2245                                "{} {} - not found in the list of valid attributes for this set of classes {:?} - valid attributes are {:?}",
2246
2247                                attr_name.as_str(),
2248                                self.get_display_id(),
2249                                entry_classes.iter().collect::<Vec<_>>(),
2250                                may.keys().collect::<Vec<_>>()
2251                            );
2252                            Err(SchemaError::AttributeNotValidForClass(
2253                                attr_name.to_string()
2254                            ))
2255                        }
2256                    }
2257                })?;
2258        }
2259
2260        // Well, we got here, so okay!
2261        Ok(())
2262    }
2263
2264    pub fn seal(mut self, schema: &dyn SchemaTransaction) -> Entry<EntrySealed, STATE> {
2265        let EntryValid { uuid, mut ecstate } = self.valid;
2266
2267        // Remove anything from the ecstate that is not a replicated attribute in the schema.
2268        // This is to allow ecstate equality to work, but also to just prevent ruv updates and
2269        // replicating things that only touched or changed phantom attrs.
2270        ecstate.retain(|k, _| schema.is_replicated(k));
2271
2272        // Update the last changed time.
2273        let last_mod_cid = ecstate.get_max_cid();
2274        let cv = vs_cid![last_mod_cid.clone()];
2275        let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
2276
2277        // Update created-at time. This is needed for migrations currently. It could
2278        // be alternately in the entry create path, but it makes more sense here as
2279        // we get the create_at time from the replication metadata
2280        let create_at_cid = ecstate.at();
2281        let cv = vs_cid![create_at_cid.clone()];
2282        let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
2283
2284        Entry {
2285            valid: EntrySealed { uuid, ecstate },
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
2296impl<STATE> GetUuid for Entry<EntrySealed, STATE>
2297where
2298    STATE: Clone,
2299{
2300    fn get_uuid(&self) -> Uuid {
2301        self.valid.uuid
2302    }
2303}
2304
2305impl<STATE> Entry<EntrySealed, STATE>
2306where
2307    STATE: Clone,
2308{
2309    pub fn invalidate(mut self, cid: Cid, trim_cid: &Cid) -> Entry<EntryInvalid, STATE> {
2310        // Trim attributes that require it. For most this is a no-op.
2311        for vs in self.attrs.values_mut() {
2312            vs.trim(trim_cid);
2313        }
2314
2315        // During migration to the new modified/created cid system, we need to account
2316        // for entries that don't have this yet. Normally we would apply this in seal()
2317        // to the current CID. At this point we enter in the expected value from the
2318        // entry. Note, we don't set last mod to cid yet, we leave that to seal() so that
2319        // if this entry is excluded later in the change, we haven't tainted anything, or
2320        // so that if the change only applies to non-replicated attrs we haven't mucked
2321        // up the value.
2322        let last_mod_cid = self.valid.ecstate.get_max_cid();
2323        let cv = vs_cid![last_mod_cid.clone()];
2324        let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
2325
2326        let create_at_cid = self.valid.ecstate.at();
2327        let cv = vs_cid![create_at_cid.clone()];
2328        let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
2329
2330        Entry {
2331            valid: EntryInvalid {
2332                cid,
2333                ecstate: self.valid.ecstate,
2334            },
2335            state: self.state,
2336            attrs: self.attrs,
2337        }
2338    }
2339
2340    pub fn get_uuid(&self) -> Uuid {
2341        self.valid.uuid
2342    }
2343
2344    pub fn get_changestate(&self) -> &EntryChangeState {
2345        &self.valid.ecstate
2346    }
2347
2348    /// Determine if any attribute of this entry changed excluding the attribute named.
2349    /// This allows for detection of entry changes unless the change was to a specific
2350    /// attribute.
2351    pub(crate) fn entry_changed_excluding_attribute<A: AsRef<Attribute>>(
2352        &self,
2353        attr: A,
2354        cid: &Cid,
2355    ) -> bool {
2356        let attr_ref = attr.as_ref();
2357
2358        use crate::repl::entry::State;
2359
2360        match self.get_changestate().current() {
2361            State::Live { at: _, changes } => {
2362                changes.iter().any(|(change_attr, change_id)| {
2363                    change_id >= cid &&
2364                    *change_attr != *attr_ref &&
2365                    // This always changes, and could throw off other detections.
2366                    *change_attr != Attribute::LastModifiedCid
2367                })
2368            }
2369            State::Tombstone { at } => at == cid,
2370        }
2371    }
2372
2373    /// ⚠️  - Invalidate an entry by resetting it's change state to time-zero. This entry
2374    /// can never be replicated after this.
2375    /// This is a TEST ONLY method and will never be exposed in production.
2376    #[cfg(test)]
2377    pub(crate) fn into_invalid(mut self) -> Entry<EntryInvalid, STATE> {
2378        let cid = Cid::new_zero();
2379        self.set_last_changed(cid.clone());
2380
2381        let ecstate = EntryChangeState::new_without_schema(&cid, &self.attrs);
2382
2383        Entry {
2384            valid: EntryInvalid { cid, ecstate },
2385            state: self.state,
2386            attrs: self.attrs,
2387        }
2388    }
2389}
2390
2391impl GetUuid for Entry<EntryReduced, EntryCommitted> {
2392    fn get_uuid(&self) -> Uuid {
2393        self.valid.uuid
2394    }
2395}
2396
2397impl Entry<EntryReduced, EntryCommitted> {
2398    pub fn get_uuid(&self) -> Uuid {
2399        self.valid.uuid
2400    }
2401
2402    /// Transform this reduced entry into a JSON protocol form that can be sent to clients.
2403    pub fn to_pe(&self, qs: &mut QueryServerReadTransaction) -> Result<ProtoEntry, OperationError> {
2404        // Turn values -> Strings.
2405        let attrs: Result<_, _> = self
2406            .attrs
2407            .iter()
2408            .map(|(k, vs)| qs.resolve_valueset(vs).map(|pvs| (k.to_string(), pvs)))
2409            .collect();
2410        Ok(ProtoEntry { attrs: attrs? })
2411    }
2412
2413    pub fn to_scim_kanidm<'a, TXN>(
2414        &self,
2415        read_txn: &mut TXN,
2416    ) -> Result<ScimEntryKanidm, OperationError>
2417    where
2418        TXN: QueryServerTransaction<'a>,
2419    {
2420        let result: Result<BTreeMap<Attribute, ScimValueKanidm>, OperationError> = self
2421            .attrs
2422            .iter()
2423            // We want to skip some attributes as they are already in the header.
2424            .filter(|(k, _vs)| **k != Attribute::Uuid)
2425            .filter_map(|(k, vs)| {
2426                let opt_resolve_status = vs.to_scim_value();
2427                let res_opt_scim_value = match opt_resolve_status {
2428                    None => Ok(None),
2429                    Some(ScimResolveStatus::Resolved(scim_value_kani)) => Ok(Some(scim_value_kani)),
2430                    Some(ScimResolveStatus::NeedsResolution(scim_value_interim)) => {
2431                        read_txn.resolve_scim_interim(scim_value_interim)
2432                    }
2433                };
2434                res_opt_scim_value
2435                    .transpose()
2436                    .map(|scim_res| scim_res.map(|scim_value| (k.clone(), scim_value)))
2437            })
2438            .collect();
2439
2440        let attrs = result?;
2441
2442        let ext_access_check = self.valid.effective_access.as_ref().map(|eff_acc| {
2443            let ident = eff_acc.ident;
2444            let delete = eff_acc.delete;
2445            let search = (&eff_acc.search).into();
2446            let modify_present = (&eff_acc.modify_pres).into();
2447            let modify_remove = (&eff_acc.modify_rem).into();
2448
2449            ScimEffectiveAccess {
2450                ident,
2451                delete,
2452                search,
2453                modify_present,
2454                modify_remove,
2455            }
2456        });
2457
2458        let id = self.get_uuid();
2459
2460        // Not sure how I want to handle this yet, I think we need some schema changes
2461        // to achieve this.
2462        let schemas = Vec::with_capacity(0);
2463
2464        Ok(ScimEntryKanidm {
2465            header: ScimEntryHeader {
2466                schemas,
2467                id,
2468                // TODO: Should be spn / name or uuid.
2469                external_id: None,
2470                // TODO - this one will be useful in future, but we need to change
2471                // entry to store some extra metadata.
2472                meta: None,
2473            },
2474            ext_access_check,
2475            attrs,
2476        })
2477    }
2478
2479    /// Transform this reduced entry into an LDAP form that can be sent to clients.
2480    pub fn to_ldap(
2481        &self,
2482        qs: &mut QueryServerReadTransaction,
2483        basedn: &str,
2484        // Did the client request all attributes?
2485        all_attrs: bool,
2486        // Did the ldap client request any sperific attribute names? If so,
2487        // we need to remap everything to match.
2488        l_attrs: &[String],
2489    ) -> Result<LdapSearchResultEntry, OperationError> {
2490        let rdn = qs.uuid_to_rdn(self.get_uuid())?;
2491
2492        let dn = format!("{rdn},{basedn}");
2493
2494        // Everything in our attrs set is "what was requested". So we can transform that now
2495        // so they are all in "ldap forms" which makes our next stage a bit easier.
2496
2497        // Stage 1 - transform our results to a map of kani attr -> ldap value.
2498        let attr_map: Result<Map<&str, Vec<Vec<u8>>>, _> = self
2499            .attrs
2500            .iter()
2501            .map(|(k, vs)| {
2502                qs.resolve_valueset_ldap(vs, basedn)
2503                    .map(|pvs| (k.as_str(), pvs))
2504            })
2505            .collect();
2506        let attr_map = attr_map?;
2507
2508        // Stage 2 - transform and get all our attr - names out that we need to return.
2509        //                  ldap a, kani a
2510        let attr_names: Vec<(&str, &str)> = if all_attrs {
2511            // Join the set of attr keys, and our requested attrs.
2512            self.attrs
2513                .keys()
2514                .map(|k| (k.as_str(), k.as_str()))
2515                .chain(
2516                    l_attrs
2517                        .iter()
2518                        .map(|k| (k.as_str(), ldap_vattr_map(k.as_str()).unwrap_or(k.as_str()))),
2519                )
2520                .collect()
2521        } else {
2522            // Just get the requested ones.
2523            l_attrs
2524                .iter()
2525                .map(|k| (k.as_str(), ldap_vattr_map(k.as_str()).unwrap_or(k.as_str())))
2526                .collect()
2527        };
2528
2529        // Stage 3 - given our map, generate the final result.
2530        let attributes: Vec<_> = attr_names
2531            .into_iter()
2532            .filter_map(|(ldap_a, kani_a)| {
2533                // In some special cases, we may need to transform or rewrite the values.
2534                match ldap_a {
2535                    LDAP_ATTR_DN => Some(LdapPartialAttribute {
2536                        atype: LDAP_ATTR_DN.to_string(),
2537                        vals: vec![dn.as_bytes().to_vec()],
2538                    }),
2539                    LDAP_ATTR_ENTRYDN => Some(LdapPartialAttribute {
2540                        atype: LDAP_ATTR_ENTRYDN.to_string(),
2541                        vals: vec![dn.as_bytes().to_vec()],
2542                    }),
2543                    LDAP_ATTR_MAIL_PRIMARY | LDAP_ATTR_EMAIL_PRIMARY => {
2544                        attr_map.get(kani_a).map(|pvs| LdapPartialAttribute {
2545                            atype: ldap_a.to_string(),
2546                            vals: pvs
2547                                .first()
2548                                .map(|first| vec![first.clone()])
2549                                .unwrap_or_default(),
2550                        })
2551                    }
2552                    LDAP_ATTR_MAIL_ALTERNATIVE | LDAP_ATTR_EMAIL_ALTERNATIVE => {
2553                        attr_map.get(kani_a).map(|pvs| LdapPartialAttribute {
2554                            atype: ldap_a.to_string(),
2555                            vals: pvs
2556                                .split_first()
2557                                .map(|(_, rest)| rest.to_vec())
2558                                .unwrap_or_default(),
2559                        })
2560                    }
2561                    ATTR_HOME_DIRECTORY => Some(LdapPartialAttribute {
2562                        atype: ATTR_HOME_DIRECTORY.to_string(),
2563                        vals: vec![format!("/home/{}", self.get_uuid()).into_bytes()],
2564                    }),
2565                    _ => attr_map.get(kani_a).map(|pvs| LdapPartialAttribute {
2566                        atype: ldap_a.to_string(),
2567                        vals: pvs.clone(),
2568                    }),
2569                }
2570            })
2571            .collect();
2572
2573        Ok(LdapSearchResultEntry { dn, attributes })
2574    }
2575}
2576
2577// impl<STATE> Entry<EntryValid, STATE> {
2578impl<VALID, STATE> Entry<VALID, STATE> {
2579    /// This internally adds an AVA to the entry. If the entry was newly added, then true is returned.
2580    /// If the value already existed, or was unable to be added, false is returned. Alternately,
2581    /// you can think of this boolean as "if a write occurred to the structure", true indicating that
2582    /// a change occurred.
2583    fn add_ava_int(&mut self, attr: Attribute, value: Value) -> bool {
2584        if let Some(vs) = self.attrs.get_mut(&attr) {
2585            let r = vs.insert_checked(value);
2586            debug_assert!(r.is_ok());
2587            // Default to the value not being present if wrong typed.
2588            r.unwrap_or(false)
2589        } else {
2590            #[allow(clippy::expect_used)]
2591            let vs = valueset::from_value_iter(std::iter::once(value))
2592                .expect("Unable to fail - non-zero iter, and single value type!");
2593            self.attrs.insert(attr, vs);
2594            // The attribute did not exist before.
2595            true
2596        }
2597        // Doesn't matter if it already exists, equality will replace.
2598    }
2599
2600    /// Overwrite the current set of values for an attribute, with this new set.
2601    fn set_ava_iter_int<T>(&mut self, attr: Attribute, iter: T)
2602    where
2603        T: IntoIterator<Item = Value>,
2604    {
2605        let Ok(vs) = valueset::from_value_iter(iter.into_iter()) else {
2606            trace!("set_ava_iter_int - empty from_value_iter, skipping");
2607            return;
2608        };
2609
2610        if let Some(existing_vs) = self.attrs.get_mut(&attr) {
2611            // This is the suboptimal path. This can only exist in rare cases.
2612            let _ = existing_vs.merge(&vs);
2613        } else {
2614            // Normally this is what's taken.
2615            self.attrs.insert(attr, vs);
2616        }
2617    }
2618
2619    /// Update the last_changed flag of this entry to the given change identifier.
2620    #[cfg(test)]
2621    fn set_last_changed(&mut self, cid: Cid) {
2622        let cv = vs_cid![cid.clone()];
2623        let _ = self.attrs.insert(Attribute::LastModifiedCid, cv);
2624        let cv = vs_cid![cid];
2625        let _ = self.attrs.insert(Attribute::CreatedAtCid, cv);
2626    }
2627
2628    pub(crate) fn get_display_id(&self) -> String {
2629        self.attrs
2630            .get(&Attribute::Spn)
2631            .and_then(|vs| vs.to_value_single())
2632            .or_else(|| {
2633                self.attrs
2634                    .get(&Attribute::Name)
2635                    .and_then(|vs| vs.to_value_single())
2636            })
2637            .or_else(|| {
2638                self.attrs
2639                    .get(&Attribute::Uuid)
2640                    .and_then(|vs| vs.to_value_single())
2641            })
2642            .map(|value| value.to_proto_string_clone())
2643            .unwrap_or_else(|| "no entry id available".to_string())
2644    }
2645
2646    pub fn has_class(&self, class: &EntryClass) -> bool {
2647        self.get_ava_set(Attribute::Class)
2648            .and_then(|vs| vs.as_iutf8_set())
2649            .map(|set| {
2650                let class_name: &str = class.into();
2651                set.contains(class_name)
2652            })
2653            .unwrap_or_default()
2654    }
2655
2656    /// Get an iterator over the current set of attribute names that this entry contains.
2657    pub fn get_ava_names(&self) -> impl Iterator<Item = &str> {
2658        // Get the set of all attribute names in the entry
2659        self.attrs.keys().map(|a| a.as_str())
2660    }
2661
2662    /// Get an iterator over the current set of values for an attribute name.
2663    pub fn get_ava(&self) -> &Eattrs {
2664        &self.attrs
2665    }
2666
2667    pub fn get_ava_iter(&self) -> impl Iterator<Item = (&Attribute, &ValueSet)> {
2668        self.attrs.iter()
2669    }
2670
2671    /// Return a reference to the current set of values that are associated to this attribute.
2672    pub fn get_ava_set<A: AsRef<Attribute>>(&self, attr: A) -> Option<&ValueSet> {
2673        self.attrs.get(attr.as_ref())
2674    }
2675
2676    pub fn get_ava_refer<A: AsRef<Attribute>>(&self, attr: A) -> Option<&BTreeSet<Uuid>> {
2677        self.get_ava_set(attr).and_then(|vs| vs.as_refer_set())
2678    }
2679
2680    pub fn get_ava_as_iutf8_iter<A: AsRef<Attribute>>(
2681        &self,
2682        attr: A,
2683    ) -> Option<impl Iterator<Item = &str>> {
2684        self.get_ava_set(attr).and_then(|vs| vs.as_iutf8_iter())
2685    }
2686
2687    pub fn get_ava_as_iutf8<A: AsRef<Attribute>>(&self, attr: A) -> Option<&BTreeSet<String>> {
2688        self.get_ava_set(attr).and_then(|vs| vs.as_iutf8_set())
2689    }
2690
2691    pub fn get_ava_as_image<A: AsRef<Attribute>>(&self, attr: A) -> Option<&HashSet<ImageValue>> {
2692        self.get_ava_set(attr).and_then(|vs| vs.as_imageset())
2693    }
2694
2695    pub fn get_ava_single_image<A: AsRef<Attribute>>(&self, attr: A) -> Option<ImageValue> {
2696        let images = self.get_ava_set(attr).and_then(|vs| vs.as_imageset())?;
2697        images.iter().next().cloned()
2698    }
2699
2700    pub fn get_ava_single_credential_type<A: AsRef<Attribute>>(
2701        &self,
2702        attr: A,
2703    ) -> Option<CredentialType> {
2704        self.get_ava_set(attr)
2705            .and_then(|vs| vs.to_credentialtype_single())
2706    }
2707
2708    pub fn get_ava_as_oauthscopes<A: AsRef<Attribute>>(
2709        &self,
2710        attr: A,
2711    ) -> Option<impl Iterator<Item = &str>> {
2712        self.get_ava_set(attr)
2713            .and_then(|vs| vs.as_oauthscope_iter())
2714    }
2715
2716    pub fn get_ava_as_oauthscopemaps<A: AsRef<Attribute>>(
2717        &self,
2718        attr: A,
2719    ) -> Option<&std::collections::BTreeMap<Uuid, std::collections::BTreeSet<String>>> {
2720        self.get_ava_set(attr).and_then(|vs| vs.as_oauthscopemap())
2721    }
2722
2723    pub fn get_ava_as_intenttokens<A: AsRef<Attribute>>(
2724        &self,
2725        attr: A,
2726    ) -> Option<&std::collections::BTreeMap<String, IntentTokenState>> {
2727        self.get_ava_set(attr)
2728            .and_then(|vs| vs.as_intenttoken_map())
2729    }
2730
2731    pub fn get_ava_as_session_map<A: AsRef<Attribute>>(
2732        &self,
2733        attr: A,
2734    ) -> Option<&std::collections::BTreeMap<Uuid, Session>> {
2735        self.get_ava_set(attr).and_then(|vs| vs.as_session_map())
2736    }
2737
2738    pub fn get_ava_as_apitoken_map<A: AsRef<Attribute>>(
2739        &self,
2740        attr: A,
2741    ) -> Option<&std::collections::BTreeMap<Uuid, ApiToken>> {
2742        self.get_ava_set(attr).and_then(|vs| vs.as_apitoken_map())
2743    }
2744
2745    pub fn get_ava_as_oauth2session_map<A: AsRef<Attribute>>(
2746        &self,
2747        attr: A,
2748    ) -> Option<&std::collections::BTreeMap<Uuid, Oauth2Session>> {
2749        self.get_ava_set(attr)
2750            .and_then(|vs| vs.as_oauth2session_map())
2751    }
2752
2753    pub fn get_ava_as_s256_set<A: AsRef<Attribute>>(
2754        &self,
2755        attr: A,
2756    ) -> Option<&std::collections::BTreeSet<Sha256Output>> {
2757        self.get_ava_set(attr).and_then(|vs| vs.as_s256_set())
2758    }
2759
2760    /// If possible, return an iterator over the set of values transformed into a `&str`.
2761    pub fn get_ava_iter_iname<A: AsRef<Attribute>>(
2762        &self,
2763        attr: A,
2764    ) -> Option<impl Iterator<Item = &str>> {
2765        self.get_ava_set(attr).and_then(|vs| vs.as_iname_iter())
2766    }
2767
2768    /// If possible, return an iterator over the set of values transformed into a `&str`.
2769    pub fn get_ava_iter_iutf8<A: AsRef<Attribute>>(
2770        &self,
2771        attr: A,
2772    ) -> Option<impl Iterator<Item = &str>> {
2773        self.get_ava_set(attr).and_then(|vs| vs.as_iutf8_iter())
2774    }
2775
2776    /// If possible, return an iterator over the set of values transformed into a `Uuid`.
2777    pub fn get_ava_as_refuuid<A: AsRef<Attribute>>(
2778        &self,
2779        attr: A,
2780    ) -> Option<Box<dyn Iterator<Item = Uuid> + '_>> {
2781        // If any value is NOT a reference, it's filtered out.
2782        self.get_ava_set(attr).and_then(|vs| vs.as_ref_uuid_iter())
2783    }
2784
2785    /// If possible, return an iterator over the set of ssh key values transformed into a `&str`.
2786    pub fn get_ava_iter_sshpubkeys<A: AsRef<Attribute>>(
2787        &self,
2788        attr: A,
2789    ) -> Option<impl Iterator<Item = String> + '_> {
2790        self.get_ava_set(attr)
2791            .and_then(|vs| vs.as_sshpubkey_string_iter())
2792    }
2793
2794    // These are special types to allow returning typed values from
2795    // an entry, if we "know" what we expect to receive.
2796
2797    /// Return a single value of this attributes name, or `None` if it is NOT present, or
2798    /// there are multiple values present (ambiguous).
2799    pub fn get_ava_single<A: AsRef<Attribute>>(&self, attr: A) -> Option<Value> {
2800        self.get_ava_set(attr).and_then(|vs| vs.to_value_single())
2801    }
2802
2803    pub fn get_ava_single_proto_string<A: AsRef<Attribute>>(&self, attr: A) -> Option<String> {
2804        self.get_ava_set(attr)
2805            .and_then(|vs| vs.to_proto_string_single())
2806    }
2807
2808    /// Return a single bool, if valid to transform this value into a boolean.
2809    pub fn get_ava_single_bool<A: AsRef<Attribute>>(&self, attr: A) -> Option<bool> {
2810        self.get_ava_set(attr).and_then(|vs| vs.to_bool_single())
2811    }
2812
2813    /// Return a single uint32, if valid to transform this value.
2814    pub fn get_ava_single_uint32<A: AsRef<Attribute>>(&self, attr: A) -> Option<u32> {
2815        self.get_ava_set(attr).and_then(|vs| vs.to_uint32_single())
2816    }
2817
2818    /// Return a single syntax type, if valid to transform this value.
2819    pub fn get_ava_single_syntax<A: AsRef<Attribute>>(&self, attr: A) -> Option<SyntaxType> {
2820        self.get_ava_set(attr)
2821            .and_then(|vs| vs.to_syntaxtype_single())
2822    }
2823
2824    /// Return a single credential, if valid to transform this value.
2825    pub fn get_ava_single_credential<A: AsRef<Attribute>>(&self, attr: A) -> Option<&Credential> {
2826        self.get_ava_set(attr)
2827            .and_then(|vs| vs.to_credential_single())
2828    }
2829
2830    /// Get the set of passkeys on this account, if any are present.
2831    pub fn get_ava_passkeys<A: AsRef<Attribute>>(
2832        &self,
2833        attr: A,
2834    ) -> Option<&BTreeMap<Uuid, (String, PasskeyV4)>> {
2835        self.get_ava_set(attr).and_then(|vs| vs.as_passkey_map())
2836    }
2837
2838    /// Get the set of devicekeys on this account, if any are present.
2839    pub fn get_ava_attestedpasskeys<A: AsRef<Attribute>>(
2840        &self,
2841        attr: A,
2842    ) -> Option<&BTreeMap<Uuid, (String, AttestedPasskeyV4)>> {
2843        self.get_ava_set(attr)
2844            .and_then(|vs| vs.as_attestedpasskey_map())
2845    }
2846
2847    /// Get the set of uihints on this account, if any are present.
2848    pub fn get_ava_uihint<A: AsRef<Attribute>>(&self, attr: A) -> Option<&BTreeSet<UiHint>> {
2849        self.get_ava_set(attr).and_then(|vs| vs.as_uihint_set())
2850    }
2851
2852    /// Return a single secret value, if valid to transform this value.
2853    pub fn get_ava_single_secret<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2854        self.get_ava_set(attr).and_then(|vs| vs.to_secret_single())
2855    }
2856
2857    /// Return a single datetime, if valid to transform this value.
2858    pub fn get_ava_single_datetime<A: AsRef<Attribute>>(&self, attr: A) -> Option<OffsetDateTime> {
2859        self.get_ava_set(attr)
2860            .and_then(|vs| vs.to_datetime_single())
2861    }
2862
2863    /// Return a single `&str`, if valid to transform this value.
2864    pub(crate) fn get_ava_single_utf8<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2865        self.get_ava_set(attr).and_then(|vs| vs.to_utf8_single())
2866    }
2867
2868    /// Return a single `&str`, if valid to transform this value.
2869    pub(crate) fn get_ava_single_iutf8<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2870        self.get_ava_set(attr).and_then(|vs| vs.to_iutf8_single())
2871    }
2872
2873    /// Return a single `&str`, if valid to transform this value.
2874    pub(crate) fn get_ava_single_iname<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2875        self.get_ava_set(attr).and_then(|vs| vs.to_iname_single())
2876    }
2877
2878    /// Return a single `&Url`, if valid to transform this value.
2879    pub fn get_ava_single_url<A: AsRef<Attribute>>(&self, attr: A) -> Option<&Url> {
2880        self.get_ava_set(attr).and_then(|vs| vs.to_url_single())
2881    }
2882
2883    pub fn get_ava_single_uuid<A: AsRef<Attribute>>(&self, attr: A) -> Option<Uuid> {
2884        self.get_ava_set(attr).and_then(|vs| vs.to_uuid_single())
2885    }
2886
2887    pub fn get_ava_single_refer<A: AsRef<Attribute>>(&self, attr: A) -> Option<Uuid> {
2888        self.get_ava_set(attr).and_then(|vs| vs.to_refer_single())
2889    }
2890
2891    pub fn get_ava_mail_primary<A: AsRef<Attribute>>(&self, attr: A) -> Option<&str> {
2892        self.get_ava_set(attr)
2893            .and_then(|vs| vs.to_email_address_primary_str())
2894    }
2895
2896    pub fn get_ava_iter_mail<A: AsRef<Attribute>>(
2897        &self,
2898        attr: A,
2899    ) -> Option<impl Iterator<Item = &str>> {
2900        self.get_ava_set(attr).and_then(|vs| vs.as_email_str_iter())
2901    }
2902
2903    /// Return a single protocol filter, if valid to transform this value.
2904    pub fn get_ava_single_protofilter<A: AsRef<Attribute>>(&self, attr: A) -> Option<&ProtoFilter> {
2905        self.get_ava_set(attr)
2906            .and_then(|vs| vs.to_json_filter_single())
2907    }
2908
2909    pub fn get_ava_single_private_binary<A: AsRef<Attribute>>(&self, attr: A) -> Option<&[u8]> {
2910        self.get_ava_set(attr)
2911            .and_then(|vs| vs.to_private_binary_single())
2912    }
2913
2914    pub fn get_ava_single_jws_key_es256<A: AsRef<Attribute>>(
2915        &self,
2916        attr: A,
2917    ) -> Option<&JwsEs256Signer> {
2918        self.get_ava_set(attr)
2919            .and_then(|vs| vs.to_jws_key_es256_single())
2920    }
2921
2922    pub fn get_ava_single_eckey_private<A: AsRef<Attribute>>(
2923        &self,
2924        attr: A,
2925    ) -> Option<&EcKey<Private>> {
2926        self.get_ava_set(attr)
2927            .and_then(|vs| vs.to_eckey_private_single())
2928    }
2929
2930    pub fn get_ava_single_eckey_public<A: AsRef<Attribute>>(
2931        &self,
2932        attr: A,
2933    ) -> Option<&EcKey<Public>> {
2934        self.get_ava_set(attr)
2935            .and_then(|vs| vs.to_eckey_public_single())
2936    }
2937
2938    pub fn get_ava_webauthn_attestation_ca_list<A: AsRef<Attribute>>(
2939        &self,
2940        attr: A,
2941    ) -> Option<&AttestationCaList> {
2942        self.get_ava_set(attr)
2943            .and_then(|vs| vs.as_webauthn_attestation_ca_list())
2944    }
2945
2946    pub fn get_ava_application_password<A: AsRef<Attribute>>(
2947        &self,
2948        attr: A,
2949    ) -> Option<&BTreeMap<Uuid, Vec<ApplicationPassword>>> {
2950        self.get_ava_set(attr)
2951            .and_then(|vs| vs.as_application_password_map())
2952    }
2953
2954    /// Return a single security principle name, if valid to transform this value.
2955    pub(crate) fn generate_spn(&self, domain_name: &str) -> Option<ValueSet> {
2956        if let Some(name) = self.get_ava_single_iname(Attribute::Name) {
2957            // There is a name attribute, lets return it.
2958            return Some(ValueSetSpn::new((name.into(), domain_name.into())));
2959        }
2960
2961        // Some entries may not have a name. There are now three paths.
2962        let spn_set = self.get_ava_set(Attribute::Spn)?;
2963
2964        if spn_set.syntax() == SyntaxType::SecurityPrincipalName {
2965            // - we already have a valid spn
2966            Some(spn_set.clone())
2967        } else if let Some(name) = spn_set.to_iname_single() {
2968            // - we have an iname stashed into the spn attr for generation.
2969            Some(ValueSetSpn::new((name.into(), domain_name.into())))
2970        } else {
2971            // - We have no way to proceed
2972            None
2973        }
2974    }
2975
2976    /// Assert if an attribute of this name is present on this entry.
2977    pub fn attribute_pres<A: AsRef<Attribute>>(&self, attr: A) -> bool {
2978        self.attrs.contains_key(attr.as_ref())
2979    }
2980
2981    /// Assert if an attribute of this name is present, and one of its values contains
2982    /// an exact match of this partial value.
2983    pub fn attribute_equality<A: AsRef<Attribute>>(&self, attr: A, value: &PartialValue) -> bool {
2984        // we assume based on schema normalisation on the way in
2985        // that the equality here of the raw values MUST be correct.
2986        // We also normalise filters, to ensure that their values are
2987        // syntax valid and will correctly match here with our indexes.
2988        match self.attrs.get(attr.as_ref()) {
2989            Some(v_list) => v_list.contains(value),
2990            None => false,
2991        }
2992    }
2993
2994    /// Assert if an attribute of this name is present, and one of it's values contains
2995    /// the following substring, if possible to perform the substring comparison.
2996    pub fn attribute_substring<A: AsRef<Attribute>>(
2997        &self,
2998        attr: A,
2999        subvalue: &PartialValue,
3000    ) -> bool {
3001        self.get_ava_set(attr)
3002            .map(|vset| vset.substring(subvalue))
3003            .unwrap_or(false)
3004    }
3005
3006    /// Assert if an attribute of this name is present, and one of its values startswith
3007    /// the following string, if possible to perform the comparison.
3008    pub fn attribute_startswith<A: AsRef<Attribute>>(
3009        &self,
3010        attr: A,
3011        subvalue: &PartialValue,
3012    ) -> bool {
3013        self.get_ava_set(attr)
3014            .map(|vset| vset.startswith(subvalue))
3015            .unwrap_or(false)
3016    }
3017
3018    /// Assert if an attribute of this name is present, and one of its values startswith
3019    /// the following string, if possible to perform the comparison.
3020    pub fn attribute_endswith<A: AsRef<Attribute>>(
3021        &self,
3022        attr: A,
3023        subvalue: &PartialValue,
3024    ) -> bool {
3025        self.get_ava_set(attr)
3026            .map(|vset| vset.endswith(subvalue))
3027            .unwrap_or(false)
3028    }
3029
3030    /// Assert if an attribute of this name is present, and one of its values is less than
3031    /// the following partial value
3032    pub fn attribute_lessthan<A: AsRef<Attribute>>(
3033        &self,
3034        attr: A,
3035        subvalue: &PartialValue,
3036    ) -> bool {
3037        self.get_ava_set(attr)
3038            .map(|vset| vset.lessthan(subvalue))
3039            .unwrap_or(false)
3040    }
3041
3042    // Since EntryValid/Invalid is just about class adherence, not Value correctness, we
3043    // can now apply filters to invalid entries - why? Because even if they aren't class
3044    // valid, we still have strict typing checks between the filter -> entry to guarantee
3045    // they should be functional. We'll never match something that isn't syntactically valid.
3046    #[inline(always)]
3047    #[instrument(level = "trace", name = "entry::entry_match_no_index", skip(self))]
3048    /// Test if the following filter applies to and matches this entry.
3049    pub fn entry_match_no_index(&self, filter: &Filter<FilterValidResolved>) -> bool {
3050        self.entry_match_no_index_inner(filter.to_inner())
3051    }
3052
3053    // This is private, but exists on all types, so that valid and normal can then
3054    // expose the simpler wrapper for entry_match_no_index only.
3055    // Assert if this filter matches the entry (no index)
3056    fn entry_match_no_index_inner(&self, filter: &FilterResolved) -> bool {
3057        // Go through the filter components and check them in the entry.
3058        // This is recursive!!!!
3059        match filter {
3060            FilterResolved::Eq(attr, value, _) => self.attribute_equality(attr, value),
3061            FilterResolved::Cnt(attr, subvalue, _) => self.attribute_substring(attr, subvalue),
3062            FilterResolved::Stw(attr, subvalue, _) => self.attribute_startswith(attr, subvalue),
3063            FilterResolved::Enw(attr, subvalue, _) => self.attribute_endswith(attr, subvalue),
3064            FilterResolved::Pres(attr, _) => self.attribute_pres(attr),
3065            FilterResolved::LessThan(attr, subvalue, _) => self.attribute_lessthan(attr, subvalue),
3066            // Check with ftweedal about or filter zero len correctness.
3067            FilterResolved::Or(l, _) => l.iter().any(|f| self.entry_match_no_index_inner(f)),
3068            // Check with ftweedal about and filter zero len correctness.
3069            FilterResolved::And(l, _) => l.iter().all(|f| self.entry_match_no_index_inner(f)),
3070            FilterResolved::Inclusion(_, _) => {
3071                // An inclusion doesn't make sense on an entry in isolation!
3072                // Inclusions are part of exists queries, on search they mean
3073                // nothing!
3074                false
3075            }
3076            FilterResolved::AndNot(f, _) => !self.entry_match_no_index_inner(f),
3077            FilterResolved::Invalid(_) => false,
3078        }
3079    }
3080
3081    /// Given this entry, generate a filter containing the requested attributes strings as
3082    /// equality components.
3083    pub fn filter_from_attrs(&self, attrs: &[Attribute]) -> Option<Filter<FilterInvalid>> {
3084        // Because we are a valid entry, a filter we create still may not
3085        // be valid because the internal server entry templates are still
3086        // created by humans! Plus double checking something already valid
3087        // is not bad ...
3088        //
3089        // Generate a filter from the attributes requested and defined.
3090        // Basically, this is a series of nested and's (which will be
3091        // optimised down later: but if someone wants to solve flatten() ...)
3092
3093        // Take name: (a, b), name: (c, d) -> (name, a), (name, b), (name, c), (name, d)
3094
3095        let mut pairs: Vec<(Attribute, PartialValue)> = Vec::with_capacity(0);
3096
3097        for attr in attrs {
3098            match self.attrs.get(attr) {
3099                Some(values) => values
3100                    .to_partialvalue_iter()
3101                    .for_each(|pv| pairs.push((attr.clone(), pv))),
3102                None => return None,
3103            }
3104        }
3105
3106        let res: Vec<FC> = pairs
3107            .into_iter()
3108            .map(|(attr, pv)| FC::Eq(attr, pv))
3109            .collect();
3110        Some(filter_all!(f_and(res)))
3111    }
3112
3113    /// Given this entry, generate a modification list that would "assert"
3114    /// another entry is in the same/identical attribute state.
3115    pub fn gen_modlist_assert(
3116        &self,
3117        schema: &dyn SchemaTransaction,
3118    ) -> Result<ModifyList<ModifyInvalid>, SchemaError> {
3119        // Create a modlist from this entry. We make this assuming we want the entry
3120        // to have this one as a subset of values. This means if we have single
3121        // values, we'll replace, if they are multivalue, we present them.
3122        let mut mods = ModifyList::new();
3123
3124        for (k, vs) in self.attrs.iter() {
3125            // WHY?! We skip uuid here because it is INVALID for a UUID
3126            // to be in a modlist, and the base.rs plugin will fail if it
3127            // is there. This actually doesn't matter, because to apply the
3128            // modlist in these situations we already know the entry MUST
3129            // exist with that UUID, we only need to conform it's other
3130            // attributes into the same state.
3131            //
3132            // In the future, if we make uuid a real entry type, then this
3133            // check can "go away" because uuid will never exist as an ava.
3134            //
3135            // NOTE: Remove this check when uuid becomes a real attribute.
3136            // UUID is now a real attribute, but it also has an ava for db_entry
3137            // conversion - so what do? If we remove it here, we could have CSN issue with
3138            // repl on uuid conflict, but it probably shouldn't be an ava either ...
3139            // as a result, I think we need to keep this continue line to not cause issues.
3140            if *k == Attribute::Uuid {
3141                continue;
3142            }
3143            // Get the schema attribute type out.
3144            match schema.is_multivalue(k) {
3145                Ok(r) => {
3146                    // As this is single value, purge then present to maintain this
3147                    // invariant.
3148                    if !r ||
3149                        // we need to be able to express REMOVAL of attributes, so we
3150                        // purge here for migrations of certain system attributes.
3151                        *k == Attribute::AcpReceiverGroup ||
3152                        *k == Attribute::AcpCreateAttr ||
3153                        *k == Attribute::AcpCreateClass ||
3154                        *k == Attribute::AcpModifyPresentAttr ||
3155                        *k == Attribute::AcpModifyRemovedAttr ||
3156                        *k == Attribute::AcpModifyClass ||
3157                        *k == Attribute::SystemMust ||
3158                        *k == Attribute::SystemMay
3159                    {
3160                        mods.push_mod(Modify::Purged(k.clone()));
3161                    }
3162                }
3163                // A schema error happened, fail the whole operation.
3164                Err(e) => return Err(e),
3165            }
3166            for v in vs.to_value_iter() {
3167                mods.push_mod(Modify::Present(k.clone(), v.clone()));
3168            }
3169        }
3170
3171        Ok(mods)
3172    }
3173
3174    /// Determine if this entry is recycled or a tombstone, and map that to "None". This allows
3175    /// filter_map to effectively remove entries that should not be considered as "alive".
3176    pub fn mask_recycled_ts(&self) -> Option<&Self> {
3177        // Only when cls has ts/rc then None, else always Some(self).
3178        match self.attrs.get(&Attribute::Class) {
3179            Some(cls) => {
3180                if cls.contains(&EntryClass::Tombstone.to_partialvalue())
3181                    || cls.contains(&EntryClass::Recycled.to_partialvalue())
3182                {
3183                    None
3184                } else {
3185                    Some(self)
3186                }
3187            }
3188            None => Some(self),
3189        }
3190    }
3191
3192    /// Determine if this entry is recycled, and map that to "None". This allows
3193    /// filter_map to effectively remove entries that are recycled in some cases.
3194    pub fn mask_recycled(&self) -> Option<&Self> {
3195        // Only when cls has ts/rc then None, else lways Some(self).
3196        match self.attrs.get(&Attribute::Class) {
3197            Some(cls) => {
3198                if cls.contains(&EntryClass::Recycled.to_partialvalue()) {
3199                    None
3200                } else {
3201                    Some(self)
3202                }
3203            }
3204            None => Some(self),
3205        }
3206    }
3207
3208    /// Determine if this entry is a tombstone, and map that to "None". This allows
3209    /// filter_map to effectively remove entries that are tombstones in some cases.
3210    pub fn mask_tombstone(&self) -> Option<&Self> {
3211        // Only when cls has ts/rc then None, else lways Some(self).
3212        match self.attrs.get(&Attribute::Class) {
3213            Some(cls) => {
3214                if cls.contains(&EntryClass::Tombstone.to_partialvalue()) {
3215                    None
3216                } else {
3217                    Some(self)
3218                }
3219            }
3220            None => Some(self),
3221        }
3222    }
3223}
3224
3225impl<STATE> Entry<EntryInvalid, STATE>
3226where
3227    STATE: Clone,
3228{
3229    // This should always work? It's only on validate that we'll build
3230    // a list of syntax violations ...
3231    // If this already exists, we silently drop the event. This is because
3232    // we need this to be *state* based where we assert presence.
3233    pub fn add_ava(&mut self, attr: Attribute, value: Value) {
3234        self.valid.ecstate.change_ava(&self.valid.cid, &attr);
3235        self.add_ava_int(attr, value);
3236    }
3237
3238    pub fn add_ava_if_not_exist<A: AsRef<Attribute>>(&mut self, attr: A, value: Value) {
3239        let attr_ref = attr.as_ref();
3240        // This returns true if the value WAS changed! See add_ava_int.
3241        if self.add_ava_int(attr_ref.clone(), value) {
3242            // In this case, we ONLY update the changestate if the value was already present!
3243            self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3244        }
3245    }
3246
3247    fn assert_ava<A: AsRef<Attribute>>(
3248        &mut self,
3249        attr: A,
3250        value: &PartialValue,
3251    ) -> Result<(), OperationError> {
3252        self.valid
3253            .ecstate
3254            .change_ava(&self.valid.cid, attr.as_ref());
3255
3256        if self.attribute_equality(attr, value) {
3257            Ok(())
3258        } else {
3259            Err(OperationError::ModifyAssertionFailed)
3260        }
3261    }
3262
3263    /// Remove an attribute-value pair from this entry. If the ava doesn't exist, we
3264    /// don't do anything else since we are asserting the absence of a value.
3265    pub(crate) fn remove_ava<A: AsRef<Attribute>>(&mut self, attr: A, value: &PartialValue) {
3266        let attr_ref = attr.as_ref();
3267        self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3268
3269        let rm = if let Some(vs) = self.attrs.get_mut(attr_ref) {
3270            vs.remove(value, &self.valid.cid);
3271            vs.is_empty()
3272        } else {
3273            false
3274        };
3275        if rm {
3276            self.attrs.remove(attr_ref);
3277        };
3278    }
3279
3280    pub(crate) fn remove_avas<A: AsRef<Attribute>>(
3281        &mut self,
3282        attr: A,
3283        values: &BTreeSet<PartialValue>,
3284    ) {
3285        let attr_ref = attr.as_ref();
3286        self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3287
3288        let rm = if let Some(vs) = self.attrs.get_mut(attr_ref) {
3289            values.iter().for_each(|k| {
3290                vs.remove(k, &self.valid.cid);
3291            });
3292            vs.is_empty()
3293        } else {
3294            false
3295        };
3296        if rm {
3297            self.attrs.remove(attr_ref);
3298        };
3299    }
3300
3301    /// Remove all values of this attribute from the entry. If it doesn't exist, this
3302    /// asserts that no content of that attribute exist.
3303    pub(crate) fn purge_ava<A: AsRef<Attribute>>(&mut self, attr: A) {
3304        let attr_ref = attr.as_ref();
3305        self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3306        // self.valid.eclog.purge_ava(&self.valid.cid, attr);
3307
3308        let can_remove = self
3309            .attrs
3310            .get_mut(attr_ref)
3311            .map(|vs| vs.purge(&self.valid.cid))
3312            // Default to false since a missing attr can't be removed!
3313            .unwrap_or_default();
3314        if can_remove {
3315            self.attrs.remove(attr_ref);
3316        }
3317    }
3318
3319    /// Remove this value set from the entry, returning the value set at the time of removal.
3320    pub fn pop_ava<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<ValueSet> {
3321        let attr_ref = attr.as_ref();
3322        self.valid.ecstate.change_ava(&self.valid.cid, attr_ref);
3323
3324        let mut vs = self.attrs.remove(attr_ref)?;
3325        if vs.purge(&self.valid.cid) {
3326            // Can return as is.
3327            Some(vs)
3328        } else {
3329            // This type may need special handling. Clone and reinsert.
3330            let r_vs = vs.clone();
3331            self.attrs.insert(attr_ref.clone(), vs);
3332            Some(r_vs)
3333        }
3334    }
3335
3336    /// Unlike pop or purge, this does NOT respect the attributes purge settings, meaning
3337    /// that this can break replication by force clearing the state of an attribute. It's
3338    /// useful for things like "session" to test the grace window by removing the revoked
3339    /// sessions from the value set that you otherwise, could not.
3340    #[cfg(test)]
3341    pub(crate) fn force_trim_ava<A: AsRef<Attribute>>(&mut self, attr: A) -> Option<ValueSet> {
3342        self.valid
3343            .ecstate
3344            .change_ava(&self.valid.cid, attr.as_ref());
3345        self.attrs.remove(attr.as_ref())
3346    }
3347
3348    /// Replace the content of this attribute with the values from this
3349    /// iterator. If the iterator is empty, the attribute is purged.
3350    pub fn set_ava<T>(&mut self, attr: &Attribute, iter: T)
3351    where
3352        T: Clone + IntoIterator<Item = Value>,
3353    {
3354        self.purge_ava(attr);
3355        self.set_ava_iter_int(attr.clone(), iter)
3356    }
3357
3358    /// Replace the content of this attribute with a new value set. Effectively this is
3359    /// a a "purge and set".
3360    pub fn set_ava_set(&mut self, attr: &Attribute, vs: ValueSet) {
3361        self.purge_ava(attr);
3362        if let Some(existing_vs) = self.attrs.get_mut(attr) {
3363            let _ = existing_vs.merge(&vs);
3364        } else {
3365            self.attrs.insert(attr.clone(), vs);
3366        }
3367    }
3368
3369    /// Merge the content from the new ValueSet into the existing ValueSet. If no existing
3370    /// ValueSet is present, then these data are inserted.
3371    pub fn merge_ava_set(&mut self, attr: &Attribute, vs: ValueSet) -> Result<(), OperationError> {
3372        self.valid.ecstate.change_ava(&self.valid.cid, attr);
3373        if let Some(existing_vs) = self.attrs.get_mut(attr) {
3374            existing_vs.merge(&vs)
3375        } else {
3376            self.attrs.insert(attr.clone(), vs);
3377            Ok(())
3378        }
3379    }
3380
3381    /// Apply the content of this modlist to this entry, enforcing the expressed state.
3382    pub fn apply_modlist(
3383        &mut self,
3384        modlist: &ModifyList<ModifyValid>,
3385    ) -> Result<(), OperationError> {
3386        for modify in modlist {
3387            match modify {
3388                Modify::Present(attr, value) => {
3389                    self.add_ava(attr.clone(), value.clone());
3390                }
3391                Modify::Removed(attr, value) => {
3392                    self.remove_ava(attr, value);
3393                }
3394                Modify::Purged(attr) => {
3395                    self.purge_ava(attr);
3396                }
3397                Modify::Assert(attr, value) => {
3398                    self.assert_ava(attr, value).inspect_err(|_e| {
3399                        error!("Modification assertion was not met. {} {:?}", attr, value);
3400                    })?;
3401                }
3402                Modify::Set(attr, valueset) => self.set_ava_set(attr, valueset.clone()),
3403            }
3404        }
3405        Ok(())
3406    }
3407}
3408
3409impl<VALID, STATE> PartialEq for Entry<VALID, STATE> {
3410    fn eq(&self, rhs: &Entry<VALID, STATE>) -> bool {
3411        // This may look naive - but it is correct. This is because
3412        // all items that end up in an item MUST have passed through
3413        // schema validation and normalisation so we can assume that
3414        // all rules were applied correctly. Thus we can just simply
3415        // do a char-compare like this.
3416        //
3417        // Of course, this is only true on the "Valid" types ... the others
3418        // are not guaranteed to support this ... but more likely that will
3419        // just end in eager false-results. We'll never say something is true
3420        // that should NOT be.
3421        compare_attrs(&self.attrs, &rhs.attrs)
3422    }
3423}
3424
3425#[cfg(test)]
3426mod tests {
3427    use crate::prelude::*;
3428    use std::collections::BTreeSet as Set;
3429
3430    use hashbrown::HashMap;
3431
3432    use crate::be::{IdxKey, IdxSlope};
3433    use crate::entry::{Entry, EntryInit, EntryInvalid, EntryNew};
3434    use crate::modify::{Modify, ModifyList};
3435    use crate::value::{IndexType, PartialValue, Value};
3436
3437    #[test]
3438    fn test_entry_basic() {
3439        let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3440
3441        e.add_ava(Attribute::UserId, Value::from("william"));
3442    }
3443
3444    #[test]
3445    fn test_entry_dup_value() {
3446        // Schema doesn't matter here because we are duplicating a value
3447        // it should fail!
3448
3449        // We still probably need schema here anyway to validate what we
3450        // are adding ... Or do we validate after the changes are made in
3451        // total?
3452        let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3453        e.add_ava(Attribute::UserId, Value::from("william"));
3454        e.add_ava(Attribute::UserId, Value::from("william"));
3455
3456        let values = e.get_ava_set(Attribute::UserId).expect("Failed to get ava");
3457        // Should only be one value!
3458        assert_eq!(values.len(), 1)
3459    }
3460
3461    #[test]
3462    fn test_entry_pres() {
3463        let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3464        e.add_ava(Attribute::UserId, Value::from("william"));
3465
3466        assert!(e.attribute_pres(Attribute::UserId));
3467        assert!(!e.attribute_pres(Attribute::Name));
3468    }
3469
3470    #[test]
3471    fn test_entry_equality() {
3472        let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3473
3474        e.add_ava(Attribute::UserId, Value::from("william"));
3475
3476        assert!(e.attribute_equality(Attribute::UserId, &PartialValue::new_utf8s("william")));
3477        assert!(!e.attribute_equality(Attribute::UserId, &PartialValue::new_utf8s("test")));
3478        assert!(!e.attribute_equality(Attribute::NonExist, &PartialValue::new_utf8s("william")));
3479        // Also test non-matching attr syntax
3480        assert!(!e.attribute_equality(Attribute::UserId, &PartialValue::new_iutf8("william")));
3481    }
3482
3483    #[test]
3484    fn test_entry_substring() {
3485        let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3486
3487        e.add_ava(Attribute::UserId, Value::from("william"));
3488
3489        assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("william")));
3490        assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("will")));
3491        assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("liam")));
3492        assert!(e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("lli")));
3493        assert!(!e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("llim")));
3494        assert!(!e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("bob")));
3495        assert!(!e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("wl")));
3496
3497        assert!(e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("will")));
3498        assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("liam")));
3499        assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("lli")));
3500        assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("llim")));
3501        assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("bob")));
3502        assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("wl")));
3503
3504        assert!(e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("liam")));
3505        assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("will")));
3506        assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("lli")));
3507        assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("llim")));
3508        assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("bob")));
3509        assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("wl")));
3510    }
3511
3512    #[test]
3513    fn test_entry_lessthan() {
3514        let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3515
3516        let pv2 = PartialValue::new_uint32(2);
3517        let pv8 = PartialValue::new_uint32(8);
3518        let pv10 = PartialValue::new_uint32(10);
3519        let pv15 = PartialValue::new_uint32(15);
3520
3521        e1.add_ava(Attribute::TestAttr, Value::new_uint32(10));
3522
3523        assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv2));
3524        assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv8));
3525        assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv10));
3526        assert!(e1.attribute_lessthan(Attribute::TestAttr, &pv15));
3527
3528        e1.add_ava(Attribute::TestAttr, Value::new_uint32(8));
3529
3530        assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv2));
3531        assert!(!e1.attribute_lessthan(Attribute::TestAttr, &pv8));
3532        assert!(e1.attribute_lessthan(Attribute::TestAttr, &pv10));
3533        assert!(e1.attribute_lessthan(Attribute::TestAttr, &pv15));
3534    }
3535
3536    #[test]
3537    fn test_entry_apply_modlist() {
3538        // Test application of changes to an entry.
3539        let mut e: Entry<EntryInvalid, EntryNew> = Entry::new().into_invalid_new();
3540
3541        e.add_ava(Attribute::UserId, Value::from("william"));
3542
3543        let present_single_mods = ModifyList::new_valid_list(vec![Modify::Present(
3544            Attribute::Attr,
3545            Value::new_iutf8("value"),
3546        )]);
3547
3548        assert!(e.apply_modlist(&present_single_mods).is_ok());
3549
3550        // Assert the changes are there
3551        assert!(e.attribute_equality(Attribute::UserId, &PartialValue::new_utf8s("william")));
3552        assert!(e.attribute_equality(Attribute::Attr, &PartialValue::new_iutf8("value")));
3553
3554        // Assert present for multivalue
3555        let present_multivalue_mods = ModifyList::new_valid_list(vec![
3556            Modify::Present(Attribute::Class, Value::new_iutf8("test")),
3557            Modify::Present(Attribute::Class, Value::new_iutf8("multi_test")),
3558        ]);
3559
3560        assert!(e.apply_modlist(&present_multivalue_mods).is_ok());
3561
3562        assert!(e.attribute_equality(Attribute::Class, &PartialValue::new_iutf8("test")));
3563        assert!(e.attribute_equality(Attribute::Class, &PartialValue::new_iutf8("multi_test")));
3564
3565        // Assert purge on single/multi/empty value
3566        let purge_single_mods = ModifyList::new_valid_list(vec![Modify::Purged(Attribute::Attr)]);
3567
3568        assert!(e.apply_modlist(&purge_single_mods).is_ok());
3569
3570        assert!(!e.attribute_pres(Attribute::Attr));
3571
3572        let purge_multi_mods = ModifyList::new_valid_list(vec![Modify::Purged(Attribute::Class)]);
3573
3574        assert!(e.apply_modlist(&purge_multi_mods).is_ok());
3575
3576        assert!(!e.attribute_pres(Attribute::Class));
3577
3578        let purge_empty_mods = purge_single_mods;
3579
3580        assert!(e.apply_modlist(&purge_empty_mods).is_ok());
3581
3582        // Assert removed on value that exists and doesn't exist
3583        let remove_mods = ModifyList::new_valid_list(vec![Modify::Removed(
3584            Attribute::Attr,
3585            PartialValue::new_iutf8("value"),
3586        )]);
3587
3588        assert!(e.apply_modlist(&present_single_mods).is_ok());
3589        assert!(e.attribute_equality(Attribute::Attr, &PartialValue::new_iutf8("value")));
3590        assert!(e.apply_modlist(&remove_mods).is_ok());
3591        assert!(!e.attrs.contains_key(&Attribute::Attr));
3592
3593        let remove_empty_mods = remove_mods;
3594
3595        assert!(e.apply_modlist(&remove_empty_mods).is_ok());
3596
3597        assert!(!e.attrs.contains_key(&Attribute::Attr));
3598    }
3599
3600    #[test]
3601    fn test_entry_idx_diff() {
3602        let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3603        e1.add_ava(Attribute::UserId, Value::from("william"));
3604        let mut e1_mod = e1.clone();
3605        e1_mod.add_ava(Attribute::Extra, Value::from("test"));
3606
3607        let e1 = e1.into_sealed_committed();
3608        let e1_mod = e1_mod.into_sealed_committed();
3609
3610        let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3611        e2.add_ava(Attribute::UserId, Value::from("claire"));
3612        let e2 = e2.into_sealed_committed();
3613
3614        let mut idxmeta = HashMap::with_capacity(8);
3615        idxmeta.insert(
3616            IdxKey {
3617                attr: Attribute::UserId,
3618                itype: IndexType::Equality,
3619            },
3620            IdxSlope::MAX,
3621        );
3622        idxmeta.insert(
3623            IdxKey {
3624                attr: Attribute::UserId,
3625                itype: IndexType::Presence,
3626            },
3627            IdxSlope::MAX,
3628        );
3629        idxmeta.insert(
3630            IdxKey {
3631                attr: Attribute::Extra,
3632                itype: IndexType::Equality,
3633            },
3634            IdxSlope::MAX,
3635        );
3636
3637        // When we do None, None, we get nothing back.
3638        let r1 = Entry::idx_diff(&idxmeta, None, None);
3639        eprintln!("{r1:?}");
3640        assert_eq!(r1, Vec::with_capacity(0));
3641
3642        // Check generating a delete diff
3643        let mut del_r = Entry::idx_diff(&idxmeta, Some(&e1), None);
3644        del_r.sort_unstable();
3645        eprintln!("del_r {del_r:?}");
3646        assert!(
3647            del_r[0]
3648                == Err((
3649                    &Attribute::UserId,
3650                    IndexType::Equality,
3651                    "william".to_string()
3652                ))
3653        );
3654        assert!(del_r[1] == Err((&Attribute::UserId, IndexType::Presence, "_".to_string())));
3655
3656        // Check generating an add diff
3657        let mut add_r = Entry::idx_diff(&idxmeta, None, Some(&e1));
3658        add_r.sort_unstable();
3659        eprintln!("{add_r:?}");
3660        assert!(
3661            add_r[0]
3662                == Ok((
3663                    &Attribute::UserId,
3664                    IndexType::Equality,
3665                    "william".to_string()
3666                ))
3667        );
3668        assert!(add_r[1] == Ok((&Attribute::UserId, IndexType::Presence, "_".to_string())));
3669
3670        // Check the mod cases now
3671
3672        // Check no changes
3673        let no_r = Entry::idx_diff(&idxmeta, Some(&e1), Some(&e1));
3674        assert!(no_r.is_empty());
3675
3676        // Check "adding" an attribute.
3677        let add_a_r = Entry::idx_diff(&idxmeta, Some(&e1), Some(&e1_mod));
3678        assert!(add_a_r[0] == Ok((&Attribute::Extra, IndexType::Equality, "test".to_string())));
3679
3680        // Check "removing" an attribute.
3681        let del_a_r = Entry::idx_diff(&idxmeta, Some(&e1_mod), Some(&e1));
3682        assert!(del_a_r[0] == Err((&Attribute::Extra, IndexType::Equality, "test".to_string())));
3683
3684        // Change an attribute.
3685        let mut chg_r = Entry::idx_diff(&idxmeta, Some(&e1), Some(&e2));
3686        chg_r.sort_unstable();
3687        eprintln!("{chg_r:?}");
3688        assert!(
3689            chg_r[1]
3690                == Err((
3691                    &Attribute::UserId,
3692                    IndexType::Equality,
3693                    "william".to_string()
3694                ))
3695        );
3696
3697        assert!(
3698            chg_r[0]
3699                == Ok((
3700                    &Attribute::UserId,
3701                    IndexType::Equality,
3702                    "claire".to_string()
3703                ))
3704        );
3705    }
3706
3707    #[test]
3708    fn test_entry_mask_recycled_ts() {
3709        let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3710        e1.add_ava(Attribute::Class, EntryClass::Person.to_value());
3711        let e1 = e1.into_sealed_committed();
3712        assert!(e1.mask_recycled_ts().is_some());
3713
3714        let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3715        e2.add_ava(Attribute::Class, EntryClass::Person.to_value());
3716        e2.add_ava(Attribute::Class, EntryClass::Recycled.into());
3717        let e2 = e2.into_sealed_committed();
3718        assert!(e2.mask_recycled_ts().is_none());
3719
3720        let mut e3: Entry<EntryInit, EntryNew> = Entry::new();
3721        e3.add_ava(Attribute::Class, EntryClass::Tombstone.into());
3722        let e3 = e3.into_sealed_committed();
3723        assert!(e3.mask_recycled_ts().is_none());
3724    }
3725
3726    #[test]
3727    fn test_entry_idx_name2uuid_diff() {
3728        // none, none,
3729        let r = Entry::idx_name2uuid_diff(None, None);
3730        assert_eq!(r, (None, None));
3731
3732        // none, some - test adding an entry gives back add sets
3733        {
3734            let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3735            e.add_ava(Attribute::Class, EntryClass::Person.to_value());
3736            let e = e.into_sealed_committed();
3737
3738            assert!(Entry::idx_name2uuid_diff(None, Some(&e)) == (Some(Set::new()), None));
3739        }
3740
3741        {
3742            let mut e: Entry<EntryInit, EntryNew> = Entry::new();
3743            e.add_ava(Attribute::Class, EntryClass::Person.to_value());
3744            e.add_ava(Attribute::GidNumber, Value::new_uint32(1300));
3745            e.add_ava(Attribute::Name, Value::new_iname("testperson"));
3746            e.add_ava(
3747                Attribute::Spn,
3748                Value::new_spn_str("testperson", "example.com"),
3749            );
3750            e.add_ava(
3751                Attribute::Uuid,
3752                Value::Uuid(uuid!("9fec0398-c46c-4df4-9df5-b0016f7d563f")),
3753            );
3754            let e = e.into_sealed_committed();
3755
3756            // Note the uuid isn't present!
3757            assert!(
3758                Entry::idx_name2uuid_diff(None, Some(&e))
3759                    == (
3760                        Some(btreeset![
3761                            "1300".to_string(),
3762                            "testperson".to_string(),
3763                            "testperson@example.com".to_string()
3764                        ]),
3765                        None
3766                    )
3767            );
3768            // some, none,
3769            // Check delete, swap the order of args
3770            assert!(
3771                Entry::idx_name2uuid_diff(Some(&e), None)
3772                    == (
3773                        None,
3774                        Some(btreeset![
3775                            "1300".to_string(),
3776                            "testperson".to_string(),
3777                            "testperson@example.com".to_string()
3778                        ])
3779                    )
3780            );
3781
3782            // some, some (same), should be empty changes.
3783            assert!(
3784                Entry::idx_name2uuid_diff(Some(&e), Some(&e))
3785                    == (Some(Set::new()), Some(Set::new()))
3786            );
3787        }
3788        // some, some (diff)
3789
3790        {
3791            let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3792            e1.add_ava(Attribute::Class, EntryClass::Person.to_value());
3793            e1.add_ava(
3794                Attribute::Spn,
3795                Value::new_spn_str("testperson", "example.com"),
3796            );
3797            let e1 = e1.into_sealed_committed();
3798
3799            let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3800            e2.add_ava(Attribute::Class, EntryClass::Person.to_value());
3801            e2.add_ava(Attribute::Name, Value::new_iname("testperson"));
3802            e2.add_ava(
3803                Attribute::Spn,
3804                Value::new_spn_str("testperson", "example.com"),
3805            );
3806            let e2 = e2.into_sealed_committed();
3807
3808            // One attr added
3809            assert!(
3810                Entry::idx_name2uuid_diff(Some(&e1), Some(&e2))
3811                    == (Some(btreeset!["testperson".to_string()]), Some(Set::new()))
3812            );
3813
3814            // One removed
3815            assert!(
3816                Entry::idx_name2uuid_diff(Some(&e2), Some(&e1))
3817                    == (Some(Set::new()), Some(btreeset!["testperson".to_string()]))
3818            );
3819        }
3820
3821        // Value changed, remove old, add new.
3822        {
3823            let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3824            e1.add_ava(Attribute::Class, EntryClass::Person.to_value());
3825            e1.add_ava(
3826                Attribute::Spn,
3827                Value::new_spn_str("testperson", "example.com"),
3828            );
3829            let e1 = e1.into_sealed_committed();
3830
3831            let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3832            e2.add_ava(Attribute::Class, EntryClass::Person.to_value());
3833            e2.add_ava(
3834                Attribute::Spn,
3835                Value::new_spn_str("renameperson", "example.com"),
3836            );
3837            let e2 = e2.into_sealed_committed();
3838
3839            assert!(
3840                Entry::idx_name2uuid_diff(Some(&e1), Some(&e2))
3841                    == (
3842                        Some(btreeset!["renameperson@example.com".to_string()]),
3843                        Some(btreeset!["testperson@example.com".to_string()])
3844                    )
3845            );
3846        }
3847    }
3848
3849    #[test]
3850    fn test_entry_idx_uuid2spn_diff() {
3851        assert!(Entry::idx_uuid2spn_diff(None, None).is_none());
3852
3853        let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3854        e1.add_ava(
3855            Attribute::Spn,
3856            Value::new_spn_str("testperson", "example.com"),
3857        );
3858        let e1 = e1.into_sealed_committed();
3859
3860        let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3861        e2.add_ava(
3862            Attribute::Spn,
3863            Value::new_spn_str("renameperson", "example.com"),
3864        );
3865        let e2 = e2.into_sealed_committed();
3866
3867        assert!(
3868            Entry::idx_uuid2spn_diff(None, Some(&e1))
3869                == Some(Ok(Value::new_spn_str("testperson", "example.com")))
3870        );
3871        assert!(Entry::idx_uuid2spn_diff(Some(&e1), None) == Some(Err(())));
3872        assert!(Entry::idx_uuid2spn_diff(Some(&e1), Some(&e1)).is_none());
3873        assert!(
3874            Entry::idx_uuid2spn_diff(Some(&e1), Some(&e2))
3875                == Some(Ok(Value::new_spn_str("renameperson", "example.com")))
3876        );
3877    }
3878
3879    #[test]
3880    fn test_entry_idx_uuid2rdn_diff() {
3881        assert!(Entry::idx_uuid2rdn_diff(None, None).is_none());
3882
3883        let mut e1: Entry<EntryInit, EntryNew> = Entry::new();
3884        e1.add_ava(
3885            Attribute::Spn,
3886            Value::new_spn_str("testperson", "example.com"),
3887        );
3888        let e1 = e1.into_sealed_committed();
3889
3890        let mut e2: Entry<EntryInit, EntryNew> = Entry::new();
3891        e2.add_ava(
3892            Attribute::Spn,
3893            Value::new_spn_str("renameperson", "example.com"),
3894        );
3895        let e2 = e2.into_sealed_committed();
3896
3897        assert!(
3898            Entry::idx_uuid2rdn_diff(None, Some(&e1))
3899                == Some(Ok("spn=testperson@example.com".to_string()))
3900        );
3901        assert!(Entry::idx_uuid2rdn_diff(Some(&e1), None) == Some(Err(())));
3902        assert!(Entry::idx_uuid2rdn_diff(Some(&e1), Some(&e1)).is_none());
3903        assert!(
3904            Entry::idx_uuid2rdn_diff(Some(&e1), Some(&e2))
3905                == Some(Ok("spn=renameperson@example.com".to_string()))
3906        );
3907    }
3908}