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