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