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    pub fn new_impersonate_identity(
378        ident: Identity,
379        entries: Vec<Entry<EntryInit, EntryNew>>,
380    ) -> Self {
381        CreateEvent {
382            ident,
383            entries,
384            return_created_uuids: false,
385        }
386    }
387
388    pub fn new_internal(entries: Vec<Entry<EntryInit, EntryNew>>) -> Self {
389        CreateEvent {
390            ident: Identity::from_internal(),
391            entries,
392            return_created_uuids: false,
393        }
394    }
395}
396
397#[derive(Debug)]
398pub struct ExistsEvent {
399    pub ident: Identity,
400    // This is the filter, as it will be processed.
401    pub filter: Filter<FilterValid>,
402    // This is the original filter, for the purpose of ACI checking.
403    pub filter_orig: Filter<FilterValid>,
404}
405
406impl ExistsEvent {
407    pub fn new_internal(filter: Filter<FilterValid>) -> Self {
408        ExistsEvent {
409            ident: Identity::from_internal(),
410            filter: filter.clone(),
411            filter_orig: filter,
412        }
413    }
414
415    /// ⚠️  - Bypass the schema state machine and force the filter to be considered valid.
416    /// This is a TEST ONLY method and will never be exposed in production.
417    #[cfg(test)]
418    pub fn new_internal_invalid(filter: Filter<FilterInvalid>) -> Self {
419        ExistsEvent {
420            ident: Identity::from_internal(),
421            filter: filter.clone().into_valid(),
422            filter_orig: filter.into_valid(),
423        }
424    }
425}
426
427#[derive(Debug)]
428pub struct DeleteEvent {
429    pub ident: Identity,
430    // This is the filter, as it will be processed.
431    pub filter: Filter<FilterValid>,
432    // This is the original filter, for the purpose of ACI checking.
433    pub filter_orig: Filter<FilterValid>,
434}
435
436impl DeleteEvent {
437    pub fn from_message(
438        ident: Identity,
439        req: &DeleteRequest,
440        qs: &mut QueryServerWriteTransaction,
441    ) -> Result<Self, OperationError> {
442        let f = Filter::from_rw(&ident, &req.filter, qs)?;
443        let filter_orig = f
444            .validate(qs.get_schema())
445            .map_err(OperationError::SchemaViolation)?;
446        let filter = filter_orig.clone().into_ignore_hidden();
447        Ok(DeleteEvent {
448            ident,
449            filter,
450            filter_orig,
451        })
452    }
453
454    pub fn from_parts(
455        ident: Identity,
456        f: &Filter<FilterInvalid>,
457        qs: &mut QueryServerWriteTransaction,
458    ) -> Result<Self, OperationError> {
459        let filter_orig = f
460            .validate(qs.get_schema())
461            .map_err(OperationError::SchemaViolation)?;
462        let filter = filter_orig.clone().into_ignore_hidden();
463        Ok(DeleteEvent {
464            ident,
465            filter,
466            filter_orig,
467        })
468    }
469
470    /// ⚠️  - Bypass the schema state machine and force the filter to be considered valid.
471    /// This is a TEST ONLY method and will never be exposed in production.
472    #[cfg(test)]
473    pub fn new_impersonate_entry(
474        e: Arc<Entry<EntrySealed, EntryCommitted>>,
475        filter: Filter<FilterInvalid>,
476    ) -> Self {
477        DeleteEvent {
478            ident: Identity::from_impersonate_entry_readwrite(e),
479            filter: filter.clone().into_valid(),
480            filter_orig: filter.into_valid(),
481        }
482    }
483
484    /// ⚠️  - Bypass the schema state machine, allowing an invalid filter to be used in an impersonate request.
485    /// This is a TEST ONLY method and will never be exposed in production.
486    #[cfg(test)]
487    pub fn new_impersonate_identity(ident: Identity, filter: Filter<FilterInvalid>) -> Self {
488        DeleteEvent {
489            ident,
490            filter: filter.clone().into_valid(),
491            filter_orig: filter.into_valid(),
492        }
493    }
494
495    /// ⚠️  - Bypass the schema state machine and force the filter to be considered valid.
496    /// This is a TEST ONLY method and will never be exposed in production.
497    #[cfg(test)]
498    pub fn new_internal_invalid(filter: Filter<FilterInvalid>) -> Self {
499        DeleteEvent {
500            ident: Identity::from_internal(),
501            filter: filter.clone().into_valid(),
502            filter_orig: filter.into_valid(),
503        }
504    }
505
506    pub fn new_internal(filter: Filter<FilterValid>) -> Self {
507        DeleteEvent {
508            ident: Identity::from_internal(),
509            filter: filter.clone(),
510            filter_orig: filter,
511        }
512    }
513}
514
515#[derive(Debug)]
516pub struct ModifyEvent {
517    pub ident: Identity,
518    // This is the filter, as it will be processed.
519    pub filter: Filter<FilterValid>,
520    // This is the original filter, for the purpose of ACI checking.
521    pub filter_orig: Filter<FilterValid>,
522    pub modlist: ModifyList<ModifyValid>,
523}
524
525impl ModifyEvent {
526    pub fn from_message(
527        ident: Identity,
528        req: &ModifyRequest,
529        qs: &mut QueryServerWriteTransaction,
530    ) -> Result<Self, OperationError> {
531        let f = Filter::from_rw(&ident, &req.filter, qs)?;
532        let m = ModifyList::from(&req.modlist, qs)?;
533        let filter_orig = f
534            .validate(qs.get_schema())
535            .map_err(OperationError::SchemaViolation)?;
536        let filter = filter_orig.clone().into_ignore_hidden();
537        let modlist = m
538            .validate(qs.get_schema())
539            .map_err(OperationError::SchemaViolation)?;
540        Ok(ModifyEvent {
541            ident,
542            filter,
543            filter_orig,
544            modlist,
545        })
546    }
547
548    pub fn from_parts(
549        ident: Identity,
550        target_uuid: Uuid,
551        proto_ml: &ProtoModifyList,
552        filter: Filter<FilterInvalid>,
553        qs: &mut QueryServerWriteTransaction,
554    ) -> Result<Self, OperationError> {
555        let f_uuid = filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(target_uuid)));
556        // Add any supplemental conditions we have.
557        let f = Filter::join_parts_and(f_uuid, filter);
558
559        let m = ModifyList::from(proto_ml, qs)?;
560        let filter_orig = f
561            .validate(qs.get_schema())
562            .map_err(OperationError::SchemaViolation)?;
563        let filter = filter_orig.clone().into_ignore_hidden();
564        let modlist = m
565            .validate(qs.get_schema())
566            .map_err(OperationError::SchemaViolation)?;
567
568        Ok(ModifyEvent {
569            ident,
570            filter,
571            filter_orig,
572            modlist,
573        })
574    }
575
576    pub fn from_internal_parts(
577        ident: Identity,
578        ml: &ModifyList<ModifyInvalid>,
579        filter: &Filter<FilterInvalid>,
580        qs: &QueryServerWriteTransaction,
581    ) -> Result<Self, OperationError> {
582        let filter_orig = filter
583            .validate(qs.get_schema())
584            .map_err(OperationError::SchemaViolation)?;
585        let filter = filter_orig.clone().into_ignore_hidden();
586        let modlist = ml
587            .validate(qs.get_schema())
588            .map_err(OperationError::SchemaViolation)?;
589
590        Ok(ModifyEvent {
591            ident,
592            filter,
593            filter_orig,
594            modlist,
595        })
596    }
597
598    pub fn from_target_uuid_attr_purge(
599        ident: Identity,
600        target_uuid: Uuid,
601        attr: Attribute,
602        filter: Filter<FilterInvalid>,
603        qs: &QueryServerWriteTransaction,
604    ) -> Result<Self, OperationError> {
605        let ml = ModifyList::new_purge(attr);
606        let f_uuid = filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(target_uuid)));
607        // Add any supplemental conditions we have.
608        let f = Filter::join_parts_and(f_uuid, filter);
609
610        let filter_orig = f
611            .validate(qs.get_schema())
612            .map_err(OperationError::SchemaViolation)?;
613        let filter = filter_orig.clone().into_ignore_hidden();
614        let modlist = ml
615            .validate(qs.get_schema())
616            .map_err(OperationError::SchemaViolation)?;
617        Ok(ModifyEvent {
618            ident,
619            filter,
620            filter_orig,
621            modlist,
622        })
623    }
624
625    pub fn new_internal(filter: Filter<FilterValid>, modlist: ModifyList<ModifyValid>) -> Self {
626        ModifyEvent {
627            ident: Identity::from_internal(),
628            filter: filter.clone(),
629            filter_orig: filter,
630            modlist,
631        }
632    }
633
634    /// ⚠️  - Bypass the schema state machine and force the filter to be considered valid.
635    /// This is a TEST ONLY method and will never be exposed in production.
636    #[cfg(test)]
637    pub fn new_internal_invalid(
638        filter: Filter<FilterInvalid>,
639        modlist: ModifyList<ModifyInvalid>,
640    ) -> Self {
641        ModifyEvent {
642            ident: Identity::from_internal(),
643            filter: filter.clone().into_valid(),
644            filter_orig: filter.into_valid(),
645            modlist: modlist.into_valid(),
646        }
647    }
648
649    /// ⚠️  - Bypass the schema state machine and force the filter to be considered valid.
650    /// This is a TEST ONLY method and will never be exposed in production.
651    #[cfg(test)]
652    pub fn new_impersonate_identity(
653        ident: Identity,
654        filter: Filter<FilterInvalid>,
655        modlist: ModifyList<ModifyInvalid>,
656    ) -> Self {
657        ModifyEvent {
658            ident,
659            filter: filter.clone().into_valid(),
660            filter_orig: filter.into_valid(),
661            modlist: modlist.into_valid(),
662        }
663    }
664
665    /// ⚠️  - Bypass the schema state machine and force the filter to be considered valid.
666    /// This is a TEST ONLY method and will never be exposed in production.
667    #[cfg(test)]
668    pub fn new_impersonate_entry(
669        e: Arc<Entry<EntrySealed, EntryCommitted>>,
670        filter: Filter<FilterInvalid>,
671        modlist: ModifyList<ModifyInvalid>,
672    ) -> Self {
673        ModifyEvent {
674            ident: Identity::from_impersonate_entry_readwrite(e),
675            filter: filter.clone().into_valid(),
676            filter_orig: filter.into_valid(),
677            modlist: modlist.into_valid(),
678        }
679    }
680
681    pub fn new_impersonate(
682        ident: &Identity,
683        filter: Filter<FilterValid>,
684        filter_orig: Filter<FilterValid>,
685        modlist: ModifyList<ModifyValid>,
686    ) -> Self {
687        ModifyEvent {
688            ident: Identity::from_impersonate(ident),
689            filter,
690            filter_orig,
691            modlist,
692        }
693    }
694}
695
696pub struct WhoamiResult {
697    youare: ProtoEntry,
698}
699
700impl WhoamiResult {
701    pub fn new(
702        qs: &mut QueryServerReadTransaction,
703        e: &Entry<EntryReduced, EntryCommitted>,
704    ) -> Result<Self, OperationError> {
705        Ok(WhoamiResult {
706            youare: e.to_pe(qs)?,
707        })
708    }
709
710    pub fn response(self) -> WhoamiResponse {
711        WhoamiResponse {
712            youare: self.youare,
713        }
714    }
715}
716
717#[derive(Debug)]
718pub struct PurgeTombstoneEvent {
719    pub ident: Identity,
720    pub eventid: Uuid,
721}
722
723impl Default for PurgeTombstoneEvent {
724    fn default() -> Self {
725        Self::new()
726    }
727}
728
729impl PurgeTombstoneEvent {
730    pub fn new() -> Self {
731        PurgeTombstoneEvent {
732            ident: Identity::from_internal(),
733            eventid: Uuid::new_v4(),
734        }
735    }
736}
737
738#[derive(Debug)]
739pub struct PurgeRecycledEvent {
740    pub ident: Identity,
741    pub eventid: Uuid,
742}
743
744impl Default for PurgeRecycledEvent {
745    fn default() -> Self {
746        Self::new()
747    }
748}
749
750impl PurgeRecycledEvent {
751    pub fn new() -> Self {
752        PurgeRecycledEvent {
753            ident: Identity::from_internal(),
754            eventid: Uuid::new_v4(),
755        }
756    }
757}
758
759#[derive(Debug)]
760pub struct PurgeDeleteAfterEvent {
761    pub ident: Identity,
762    pub eventid: Uuid,
763}
764
765impl Default for PurgeDeleteAfterEvent {
766    fn default() -> Self {
767        Self::new()
768    }
769}
770
771impl PurgeDeleteAfterEvent {
772    pub fn new() -> Self {
773        PurgeDeleteAfterEvent {
774            ident: Identity::from_internal(),
775            eventid: Uuid::new_v4(),
776        }
777    }
778}
779
780#[derive(Debug)]
781pub struct OnlineBackupEvent {
782    pub ident: Identity,
783    pub eventid: Uuid,
784}
785
786impl Default for OnlineBackupEvent {
787    fn default() -> Self {
788        Self::new()
789    }
790}
791
792impl OnlineBackupEvent {
793    pub fn new() -> Self {
794        OnlineBackupEvent {
795            ident: Identity::from_internal(),
796            eventid: Uuid::new_v4(),
797        }
798    }
799}
800
801#[derive(Debug)]
802pub struct ReviveRecycledEvent {
803    pub ident: Identity,
804    // This is the filter, as it will be processed.
805    pub filter: Filter<FilterValid>,
806    // Unlike the others, because of how this works, we don't need the orig filter
807    // to be retained, because the filter is the orig filter for this check.
808    //
809    // It will be duplicated into the modify ident as it exists.
810}
811
812impl ReviveRecycledEvent {
813    pub fn from_parts(
814        ident: Identity,
815        filter: &Filter<FilterInvalid>,
816        qs: &QueryServerWriteTransaction,
817    ) -> Result<Self, OperationError> {
818        let filter = filter
819            .validate(qs.get_schema())
820            .map(|f| f.into_recycled())
821            .map_err(OperationError::SchemaViolation)?;
822        Ok(ReviveRecycledEvent { ident, filter })
823    }
824
825    /// ⚠️  - Bypass the schema state machine and force the filter to be considered valid.
826    /// This is a TEST ONLY method and will never be exposed in production.
827    #[cfg(test)]
828    pub fn new_impersonate_entry(
829        e: Arc<Entry<EntrySealed, EntryCommitted>>,
830        filter: Filter<FilterInvalid>,
831    ) -> Self {
832        ReviveRecycledEvent {
833            ident: Identity::from_impersonate_entry_readwrite(e),
834            filter: filter.into_valid(),
835        }
836    }
837
838    #[cfg(test)]
839    pub(crate) fn new_internal(filter: Filter<FilterValid>) -> Self {
840        ReviveRecycledEvent {
841            ident: Identity::from_internal(),
842            filter,
843        }
844    }
845}