Skip to main content

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