kanidmd_lib/
entry.rs

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