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