kanidmd_lib/
event.rs

1//! An `event` is a self contained module of data, that contains all of the
2//! required information for any operation to proceed. While there are many
3//! types of potential events, they all eventually lower to one of:
4//!
5//! * AuthEvent
6//! * SearchEvent
7//! * ExistsEvent
8//! * ModifyEvent
9//! * CreateEvent
10//! * DeleteEvent
11//!
12//! An "event" is generally then passed to the `QueryServer` for processing.
13//! By making these fully self contained units, it means that we can assert
14//! at event creation time we have all the correct data required to proceed
15//! with the operation, and a clear path to know how to transform events between
16//! various types.
17
18use std::collections::BTreeSet;
19#[cfg(test)]
20use std::sync::Arc;
21
22use kanidm_proto::internal::{
23    CreateRequest, DeleteRequest, ModifyList as ProtoModifyList, ModifyRequest, OperationError,
24    SearchRequest, SearchResponse,
25};
26use kanidm_proto::v1::{Entry as ProtoEntry, WhoamiResponse};
27use ldap3_proto::simple::LdapFilter;
28use uuid::Uuid;
29
30use crate::entry::{Entry, EntryCommitted, EntryInit, EntryNew, EntryReduced};
31use crate::filter::{Filter, FilterInvalid, FilterValid};
32use crate::modify::{ModifyInvalid, ModifyList, ModifyValid};
33use crate::prelude::*;
34use crate::schema::SchemaTransaction;
35use crate::value::PartialValue;
36
37#[derive(Debug)]
38pub struct SearchResult {
39    entries: Vec<ProtoEntry>,
40}
41
42impl SearchResult {
43    pub fn new(
44        qs: &mut QueryServerReadTransaction,
45        entries: &[Entry<EntryReduced, EntryCommitted>],
46    ) -> Result<Self, OperationError> {
47        let entries: Result<_, _> = entries
48            .iter()
49            .map(|e| {
50                // All the needed transforms for this result are done
51                // in search_ext. This is just an entry -> protoentry
52                // step.
53                e.to_pe(qs)
54            })
55            .collect();
56        Ok(SearchResult { entries: entries? })
57    }
58
59    // Consume self into a search response
60    pub fn response(self) -> SearchResponse {
61        SearchResponse {
62            entries: self.entries,
63        }
64    }
65
66    // Consume into the array of entries, used in the json proto
67    pub fn into_proto_array(self) -> Vec<ProtoEntry> {
68        self.entries
69    }
70}
71
72#[derive(Debug)]
73pub struct SearchEvent {
74    pub ident: Identity,
75    // This is the filter as we apply and process it.
76    pub filter: Filter<FilterValid>,
77    // This is the original filter, for the purpose of ACI checking.
78    pub filter_orig: Filter<FilterValid>,
79    pub attrs: Option<BTreeSet<Attribute>>,
80    pub effective_access_check: bool,
81}
82
83impl SearchEvent {
84    pub fn from_message(
85        ident: Identity,
86        req: &SearchRequest,
87        qs: &mut QueryServerReadTransaction,
88    ) -> Result<Self, OperationError> {
89        let f = Filter::from_ro(&ident, &req.filter, qs)?;
90        // We do need to do this twice to account for the ignore_hidden
91        // changes.
92        let filter_orig = f
93            .validate(qs.get_schema())
94            .map_err(OperationError::SchemaViolation)?;
95        let filter = filter_orig.clone().into_ignore_hidden();
96        Ok(SearchEvent {
97            ident,
98            filter,
99            filter_orig,
100            // We can't get this from the SearchMessage because it's annoying with the
101            // current macro design.
102            attrs: None,
103            effective_access_check: false,
104        })
105    }
106
107    pub fn from_internal_message(
108        ident: Identity,
109        filter: &Filter<FilterInvalid>,
110        attrs: Option<&[String]>,
111        qs: &mut QueryServerReadTransaction,
112    ) -> Result<Self, OperationError> {
113        let r_attrs: Option<BTreeSet<Attribute>> = attrs.map(|vs| {
114            vs.iter()
115                .filter_map(|a| qs.get_schema().normalise_attr_if_exists(a.as_str()))
116                .collect()
117        });
118
119        if let Some(s) = &r_attrs {
120            if s.is_empty() {
121                request_error!("EmptyRequest for attributes");
122                return Err(OperationError::EmptyRequest);
123            }
124        }
125
126        let filter_orig = filter.validate(qs.get_schema()).map_err(|e| {
127            request_error!(?e, "filter schema violation");
128            OperationError::SchemaViolation(e)
129        })?;
130        let filter = filter_orig.clone().into_ignore_hidden();
131
132        Ok(SearchEvent {
133            ident,
134            filter,
135            filter_orig,
136            attrs: r_attrs,
137            effective_access_check: false,
138        })
139    }
140
141    pub fn from_internal_recycle_message(
142        ident: Identity,
143        filter: &Filter<FilterInvalid>,
144        attrs: Option<&[String]>,
145        qs: &QueryServerReadTransaction,
146    ) -> Result<Self, OperationError> {
147        let r_attrs: Option<BTreeSet<Attribute>> = attrs.map(|vs| {
148            vs.iter()
149                .filter_map(|a| {
150                    qs.get_schema()
151                        .normalise_attr_if_exists(a.as_str())
152                        .map(|a_str| Attribute::from(a_str.as_str()))
153                })
154                .collect()
155        });
156
157        if let Some(s) = &r_attrs {
158            if s.is_empty() {
159                return Err(OperationError::EmptyRequest);
160            }
161        }
162
163        let filter_orig = filter
164            .validate(qs.get_schema())
165            .map(|f| f.into_recycled())
166            .map_err(OperationError::SchemaViolation)?;
167        let filter = filter_orig.clone();
168
169        Ok(SearchEvent {
170            ident,
171            filter,
172            filter_orig,
173            attrs: r_attrs,
174            effective_access_check: false,
175        })
176    }
177
178    pub fn from_whoami_request(
179        ident: Identity,
180        qs: &QueryServerReadTransaction,
181    ) -> Result<Self, OperationError> {
182        let filter_orig = filter_all!(f_self())
183            .validate(qs.get_schema())
184            .map_err(OperationError::SchemaViolation)?;
185        let filter = filter_orig.clone().into_ignore_hidden();
186
187        Ok(SearchEvent {
188            ident,
189            filter,
190            filter_orig,
191            attrs: None,
192            effective_access_check: false,
193        })
194    }
195
196    pub fn from_target_uuid_request(
197        ident: Identity,
198        target_uuid: Uuid,
199        qs: &QueryServerReadTransaction,
200    ) -> Result<Self, OperationError> {
201        let filter_orig = filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(target_uuid)))
202            .validate(qs.get_schema())
203            .map_err(OperationError::SchemaViolation)?;
204        let filter = filter_orig.clone().into_ignore_hidden();
205        Ok(SearchEvent {
206            ident,
207            filter,
208            filter_orig,
209            attrs: None,
210            effective_access_check: false,
211        })
212    }
213
214    /// ⚠️  - Bypass the schema state machine and force the filter to be considered valid.
215    /// This is a TEST ONLY method and will never be exposed in production.
216    #[cfg(test)]
217    pub fn new_impersonate_entry(
218        e: Arc<Entry<EntrySealed, EntryCommitted>>,
219        filter: Filter<FilterInvalid>,
220    ) -> Self {
221        SearchEvent {
222            ident: Identity::from_impersonate_entry_readonly(e),
223            filter: filter.clone().into_valid(),
224            filter_orig: filter.into_valid(),
225            attrs: None,
226            effective_access_check: false,
227        }
228    }
229
230    /// ⚠️  - Bypass the schema state machine and force the filter to be considered valid.
231    /// This is a TEST ONLY method and will never be exposed in production.
232    #[cfg(test)]
233    pub fn new_impersonate_identity(ident: Identity, filter: Filter<FilterInvalid>) -> Self {
234        SearchEvent {
235            ident,
236            filter: filter.clone().into_valid(),
237            filter_orig: filter.into_valid(),
238            attrs: None,
239            effective_access_check: false,
240        }
241    }
242
243    pub fn new_impersonate(
244        ident: &Identity,
245        filter: Filter<FilterValid>,
246        filter_orig: Filter<FilterValid>,
247    ) -> Self {
248        SearchEvent {
249            ident: Identity::from_impersonate(ident),
250            filter,
251            filter_orig,
252            attrs: None,
253            effective_access_check: false,
254        }
255    }
256
257    /// ⚠️  - Bypass the schema state machine and force the filter to be considered valid.
258    /// This is a TEST ONLY method and will never be exposed in production.
259    #[cfg(test)]
260    pub fn new_rec_impersonate_entry(
261        e: Arc<Entry<EntrySealed, EntryCommitted>>,
262        filter: Filter<FilterInvalid>,
263    ) -> Self {
264        /* Impersonate a request for recycled objects */
265        let filter_orig = filter.into_valid();
266        let filter = filter_orig.clone().into_recycled();
267        SearchEvent {
268            ident: Identity::from_impersonate_entry_readonly(e),
269            filter,
270            filter_orig,
271            attrs: None,
272            effective_access_check: false,
273        }
274    }
275
276    /// ⚠️  - Bypass the schema state machine and force the filter to be considered valid.
277    /// This is a TEST ONLY method and will never be exposed in production.
278    #[cfg(test)]
279    pub fn new_ext_impersonate_entry(
280        e: Arc<Entry<EntrySealed, EntryCommitted>>,
281        filter: Filter<FilterInvalid>,
282    ) -> Self {
283        /* Impersonate an external request AKA filter ts + recycle */
284        SearchEvent {
285            ident: Identity::from_impersonate_entry_readonly(e),
286            filter: filter.clone().into_valid().into_ignore_hidden(),
287            filter_orig: filter.into_valid(),
288            attrs: None,
289            effective_access_check: false,
290        }
291    }
292
293    pub(crate) fn new_ext_impersonate_uuid(
294        qs: &mut QueryServerReadTransaction,
295        ident: Identity,
296        lf: &LdapFilter,
297        attrs: Option<BTreeSet<Attribute>>,
298    ) -> Result<Self, OperationError> {
299        // Kanidm Filter from LdapFilter
300        let f = Filter::from_ldap_ro(&ident, lf, qs)?;
301        let filter_orig = f
302            .validate(qs.get_schema())
303            .map_err(OperationError::SchemaViolation)?;
304        let filter = filter_orig.clone().into_ignore_hidden();
305        Ok(SearchEvent {
306            ident,
307            filter,
308            filter_orig,
309            attrs,
310            effective_access_check: false,
311        })
312    }
313
314    /// ⚠️  - Bypass the schema state machine and force the filter to be considered valid.
315    /// This is a TEST ONLY method and will never be exposed in production.
316    #[cfg(test)]
317    pub fn new_internal_invalid(filter: Filter<FilterInvalid>) -> Self {
318        SearchEvent {
319            ident: Identity::from_internal(),
320            filter: filter.clone().into_valid(),
321            filter_orig: filter.into_valid(),
322            attrs: None,
323            effective_access_check: false,
324        }
325    }
326
327    pub fn new_internal(filter: Filter<FilterValid>) -> Self {
328        SearchEvent {
329            ident: Identity::from_internal(),
330            filter: filter.clone(),
331            filter_orig: filter,
332            attrs: None,
333            effective_access_check: false,
334        }
335    }
336}
337
338// Represents the decoded entries from the protocol -> internal entry representation
339// including information about the identity performing the request, and if the
340// request is internal or not.
341#[derive(Debug)]
342pub struct CreateEvent {
343    pub ident: Identity,
344    // This may still actually change to handle the *raw* nature of the
345    // input that we plan to parse.
346    pub entries: Vec<Entry<EntryInit, EntryNew>>,
347    // Is the CreateEvent from an internal or external source?
348    // This may affect which plugins are run ...
349    /// If true, the list of created entry UUID's will be returned.
350    pub return_created_uuids: bool,
351}
352
353impl CreateEvent {
354    pub fn from_message(
355        ident: Identity,
356        req: &CreateRequest,
357        qs: &mut QueryServerWriteTransaction,
358    ) -> Result<Self, OperationError> {
359        let rentries: Result<Vec<_>, _> = req
360            .entries
361            .iter()
362            .map(|e| Entry::from_proto_entry(e, qs))
363            .collect();
364        // From ProtoEntry -> Entry
365        // What is the correct consuming iterator here? Can we
366        // even do that?
367        match rentries {
368            Ok(entries) => Ok(CreateEvent {
369                ident,
370                entries,
371                return_created_uuids: false,
372            }),
373            Err(e) => Err(e),
374        }
375    }
376
377    #[cfg(test)]
378    pub fn new_impersonate_identity(
379        ident: Identity,
380        entries: Vec<Entry<EntryInit, EntryNew>>,
381    ) -> Self {
382        CreateEvent {
383            ident,
384            entries,
385            return_created_uuids: false,
386        }
387    }
388
389    pub fn new_internal(entries: Vec<Entry<EntryInit, EntryNew>>) -> Self {
390        CreateEvent {
391            ident: Identity::from_internal(),
392            entries,
393            return_created_uuids: false,
394        }
395    }
396}
397
398#[derive(Debug)]
399pub struct ExistsEvent {
400    pub ident: Identity,
401    // This is the filter, as it will be processed.
402    pub filter: Filter<FilterValid>,
403    // This is the original filter, for the purpose of ACI checking.
404    pub filter_orig: Filter<FilterValid>,
405}
406
407impl ExistsEvent {
408    pub fn new_internal(filter: Filter<FilterValid>) -> Self {
409        ExistsEvent {
410            ident: Identity::from_internal(),
411            filter: filter.clone(),
412            filter_orig: filter,
413        }
414    }
415
416    /// ⚠️  - Bypass the schema state machine and force the filter to be considered valid.
417    /// This is a TEST ONLY method and will never be exposed in production.
418    #[cfg(test)]
419    pub fn new_internal_invalid(filter: Filter<FilterInvalid>) -> Self {
420        ExistsEvent {
421            ident: Identity::from_internal(),
422            filter: filter.clone().into_valid(),
423            filter_orig: filter.into_valid(),
424        }
425    }
426}
427
428#[derive(Debug)]
429pub struct DeleteEvent {
430    pub ident: Identity,
431    // This is the filter, as it will be processed.
432    pub filter: Filter<FilterValid>,
433    // This is the original filter, for the purpose of ACI checking.
434    pub filter_orig: Filter<FilterValid>,
435}
436
437impl DeleteEvent {
438    pub fn from_message(
439        ident: Identity,
440        req: &DeleteRequest,
441        qs: &mut QueryServerWriteTransaction,
442    ) -> Result<Self, OperationError> {
443        let f = Filter::from_rw(&ident, &req.filter, qs)?;
444        let filter_orig = f
445            .validate(qs.get_schema())
446            .map_err(OperationError::SchemaViolation)?;
447        let filter = filter_orig.clone().into_ignore_hidden();
448        Ok(DeleteEvent {
449            ident,
450            filter,
451            filter_orig,
452        })
453    }
454
455    pub fn from_parts(
456        ident: Identity,
457        f: &Filter<FilterInvalid>,
458        qs: &mut QueryServerWriteTransaction,
459    ) -> Result<Self, OperationError> {
460        let filter_orig = f
461            .validate(qs.get_schema())
462            .map_err(OperationError::SchemaViolation)?;
463        let filter = filter_orig.clone().into_ignore_hidden();
464        Ok(DeleteEvent {
465            ident,
466            filter,
467            filter_orig,
468        })
469    }
470
471    /// ⚠️  - Bypass the schema state machine and force the filter to be considered valid.
472    /// This is a TEST ONLY method and will never be exposed in production.
473    #[cfg(test)]
474    pub fn new_impersonate_entry(
475        e: Arc<Entry<EntrySealed, EntryCommitted>>,
476        filter: Filter<FilterInvalid>,
477    ) -> Self {
478        DeleteEvent {
479            ident: Identity::from_impersonate_entry_readwrite(e),
480            filter: filter.clone().into_valid(),
481            filter_orig: filter.into_valid(),
482        }
483    }
484
485    /// ⚠️  - Bypass the schema state machine, allowing an invalid filter to be used in an impersonate request.
486    /// This is a TEST ONLY method and will never be exposed in production.
487    #[cfg(test)]
488    pub fn new_impersonate_identity(ident: Identity, filter: Filter<FilterInvalid>) -> Self {
489        DeleteEvent {
490            ident,
491            filter: filter.clone().into_valid(),
492            filter_orig: filter.into_valid(),
493        }
494    }
495
496    /// ⚠️  - Bypass the schema state machine and force the filter to be considered valid.
497    /// This is a TEST ONLY method and will never be exposed in production.
498    #[cfg(test)]
499    pub fn new_internal_invalid(filter: Filter<FilterInvalid>) -> Self {
500        DeleteEvent {
501            ident: Identity::from_internal(),
502            filter: filter.clone().into_valid(),
503            filter_orig: filter.into_valid(),
504        }
505    }
506
507    pub fn new_internal(filter: Filter<FilterValid>) -> Self {
508        DeleteEvent {
509            ident: Identity::from_internal(),
510            filter: filter.clone(),
511            filter_orig: filter,
512        }
513    }
514}
515
516#[derive(Debug)]
517pub struct ModifyEvent {
518    pub ident: Identity,
519    // This is the filter, as it will be processed.
520    pub filter: Filter<FilterValid>,
521    // This is the original filter, for the purpose of ACI checking.
522    pub filter_orig: Filter<FilterValid>,
523    pub modlist: ModifyList<ModifyValid>,
524}
525
526impl ModifyEvent {
527    pub fn from_message(
528        ident: Identity,
529        req: &ModifyRequest,
530        qs: &mut QueryServerWriteTransaction,
531    ) -> Result<Self, OperationError> {
532        let f = Filter::from_rw(&ident, &req.filter, qs)?;
533        let m = ModifyList::from(&req.modlist, qs)?;
534        let filter_orig = f
535            .validate(qs.get_schema())
536            .map_err(OperationError::SchemaViolation)?;
537        let filter = filter_orig.clone().into_ignore_hidden();
538        let modlist = m
539            .validate(qs.get_schema())
540            .map_err(OperationError::SchemaViolation)?;
541        Ok(ModifyEvent {
542            ident,
543            filter,
544            filter_orig,
545            modlist,
546        })
547    }
548
549    pub fn from_parts(
550        ident: Identity,
551        target_uuid: Uuid,
552        proto_ml: &ProtoModifyList,
553        filter: Filter<FilterInvalid>,
554        qs: &mut QueryServerWriteTransaction,
555    ) -> Result<Self, OperationError> {
556        let f_uuid = filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(target_uuid)));
557        // Add any supplemental conditions we have.
558        let f = Filter::join_parts_and(f_uuid, filter);
559
560        let m = ModifyList::from(proto_ml, qs)?;
561        let filter_orig = f
562            .validate(qs.get_schema())
563            .map_err(OperationError::SchemaViolation)?;
564        let filter = filter_orig.clone().into_ignore_hidden();
565        let modlist = m
566            .validate(qs.get_schema())
567            .map_err(OperationError::SchemaViolation)?;
568
569        Ok(ModifyEvent {
570            ident,
571            filter,
572            filter_orig,
573            modlist,
574        })
575    }
576
577    pub fn from_internal_parts(
578        ident: Identity,
579        ml: &ModifyList<ModifyInvalid>,
580        filter: &Filter<FilterInvalid>,
581        qs: &QueryServerWriteTransaction,
582    ) -> Result<Self, OperationError> {
583        let filter_orig = filter
584            .validate(qs.get_schema())
585            .map_err(OperationError::SchemaViolation)?;
586        let filter = filter_orig.clone().into_ignore_hidden();
587        let modlist = ml
588            .validate(qs.get_schema())
589            .map_err(OperationError::SchemaViolation)?;
590
591        Ok(ModifyEvent {
592            ident,
593            filter,
594            filter_orig,
595            modlist,
596        })
597    }
598
599    pub fn from_target_uuid_attr_purge(
600        ident: Identity,
601        target_uuid: Uuid,
602        attr: Attribute,
603        filter: Filter<FilterInvalid>,
604        qs: &QueryServerWriteTransaction,
605    ) -> Result<Self, OperationError> {
606        let ml = ModifyList::new_purge(attr);
607        let f_uuid = filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(target_uuid)));
608        // Add any supplemental conditions we have.
609        let f = Filter::join_parts_and(f_uuid, filter);
610
611        let filter_orig = f
612            .validate(qs.get_schema())
613            .map_err(OperationError::SchemaViolation)?;
614        let filter = filter_orig.clone().into_ignore_hidden();
615        let modlist = ml
616            .validate(qs.get_schema())
617            .map_err(OperationError::SchemaViolation)?;
618        Ok(ModifyEvent {
619            ident,
620            filter,
621            filter_orig,
622            modlist,
623        })
624    }
625
626    pub fn new_internal(filter: Filter<FilterValid>, modlist: ModifyList<ModifyValid>) -> Self {
627        ModifyEvent {
628            ident: Identity::from_internal(),
629            filter: filter.clone(),
630            filter_orig: filter,
631            modlist,
632        }
633    }
634
635    /// ⚠️  - Bypass the schema state machine and force the filter to be considered valid.
636    /// This is a TEST ONLY method and will never be exposed in production.
637    #[cfg(test)]
638    pub fn new_internal_invalid(
639        filter: Filter<FilterInvalid>,
640        modlist: ModifyList<ModifyInvalid>,
641    ) -> Self {
642        ModifyEvent {
643            ident: Identity::from_internal(),
644            filter: filter.clone().into_valid(),
645            filter_orig: filter.into_valid(),
646            modlist: modlist.into_valid(),
647        }
648    }
649
650    /// ⚠️  - Bypass the schema state machine and force the filter to be considered valid.
651    /// This is a TEST ONLY method and will never be exposed in production.
652    #[cfg(test)]
653    pub fn new_impersonate_identity(
654        ident: Identity,
655        filter: Filter<FilterInvalid>,
656        modlist: ModifyList<ModifyInvalid>,
657    ) -> Self {
658        ModifyEvent {
659            ident,
660            filter: filter.clone().into_valid(),
661            filter_orig: filter.into_valid(),
662            modlist: modlist.into_valid(),
663        }
664    }
665
666    /// ⚠️  - Bypass the schema state machine and force the filter to be considered valid.
667    /// This is a TEST ONLY method and will never be exposed in production.
668    #[cfg(test)]
669    pub fn new_impersonate_entry(
670        e: Arc<Entry<EntrySealed, EntryCommitted>>,
671        filter: Filter<FilterInvalid>,
672        modlist: ModifyList<ModifyInvalid>,
673    ) -> Self {
674        ModifyEvent {
675            ident: Identity::from_impersonate_entry_readwrite(e),
676            filter: filter.clone().into_valid(),
677            filter_orig: filter.into_valid(),
678            modlist: modlist.into_valid(),
679        }
680    }
681
682    pub fn new_impersonate(
683        ident: &Identity,
684        filter: Filter<FilterValid>,
685        filter_orig: Filter<FilterValid>,
686        modlist: ModifyList<ModifyValid>,
687    ) -> Self {
688        ModifyEvent {
689            ident: Identity::from_impersonate(ident),
690            filter,
691            filter_orig,
692            modlist,
693        }
694    }
695}
696
697pub struct WhoamiResult {
698    youare: ProtoEntry,
699}
700
701impl WhoamiResult {
702    pub fn new(
703        qs: &mut QueryServerReadTransaction,
704        e: &Entry<EntryReduced, EntryCommitted>,
705    ) -> Result<Self, OperationError> {
706        Ok(WhoamiResult {
707            youare: e.to_pe(qs)?,
708        })
709    }
710
711    pub fn response(self) -> WhoamiResponse {
712        WhoamiResponse {
713            youare: self.youare,
714        }
715    }
716}
717
718#[derive(Debug)]
719pub struct PurgeTombstoneEvent {
720    pub ident: Identity,
721    pub eventid: Uuid,
722}
723
724impl Default for PurgeTombstoneEvent {
725    fn default() -> Self {
726        Self::new()
727    }
728}
729
730impl PurgeTombstoneEvent {
731    pub fn new() -> Self {
732        PurgeTombstoneEvent {
733            ident: Identity::from_internal(),
734            eventid: Uuid::new_v4(),
735        }
736    }
737}
738
739#[derive(Debug)]
740pub struct PurgeRecycledEvent {
741    pub ident: Identity,
742    pub eventid: Uuid,
743}
744
745impl Default for PurgeRecycledEvent {
746    fn default() -> Self {
747        Self::new()
748    }
749}
750
751impl PurgeRecycledEvent {
752    pub fn new() -> Self {
753        PurgeRecycledEvent {
754            ident: Identity::from_internal(),
755            eventid: Uuid::new_v4(),
756        }
757    }
758}
759
760#[derive(Debug)]
761pub struct OnlineBackupEvent {
762    pub ident: Identity,
763    pub eventid: Uuid,
764}
765
766impl Default for OnlineBackupEvent {
767    fn default() -> Self {
768        Self::new()
769    }
770}
771
772impl OnlineBackupEvent {
773    pub fn new() -> Self {
774        OnlineBackupEvent {
775            ident: Identity::from_internal(),
776            eventid: Uuid::new_v4(),
777        }
778    }
779}
780
781#[derive(Debug)]
782pub struct ReviveRecycledEvent {
783    pub ident: Identity,
784    // This is the filter, as it will be processed.
785    pub filter: Filter<FilterValid>,
786    // Unlike the others, because of how this works, we don't need the orig filter
787    // to be retained, because the filter is the orig filter for this check.
788    //
789    // It will be duplicated into the modify ident as it exists.
790}
791
792impl ReviveRecycledEvent {
793    pub fn from_parts(
794        ident: Identity,
795        filter: &Filter<FilterInvalid>,
796        qs: &QueryServerWriteTransaction,
797    ) -> Result<Self, OperationError> {
798        let filter = filter
799            .validate(qs.get_schema())
800            .map(|f| f.into_recycled())
801            .map_err(OperationError::SchemaViolation)?;
802        Ok(ReviveRecycledEvent { ident, filter })
803    }
804
805    /// ⚠️  - Bypass the schema state machine and force the filter to be considered valid.
806    /// This is a TEST ONLY method and will never be exposed in production.
807    #[cfg(test)]
808    pub fn new_impersonate_entry(
809        e: Arc<Entry<EntrySealed, EntryCommitted>>,
810        filter: Filter<FilterInvalid>,
811    ) -> Self {
812        ReviveRecycledEvent {
813            ident: Identity::from_impersonate_entry_readwrite(e),
814            filter: filter.into_valid(),
815        }
816    }
817
818    #[cfg(test)]
819    pub(crate) fn new_internal(filter: Filter<FilterValid>) -> Self {
820        ReviveRecycledEvent {
821            ident: Identity::from_internal(),
822            filter,
823        }
824    }
825}