kanidmd_lib/
entry.rs

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