kanidmd_lib/plugins/
mod.rs

1//! Plugins allow an `Event` to be inspected and transformed during the write
2//! paths of the server. This allows richer expression of some concepts and
3//! helps to ensure that data is always in specific known states within the
4//! `QueryServer`
5
6use crate::entry::{Entry, EntryCommitted, EntryInvalid, EntryNew, EntrySealed};
7use crate::event::{CreateEvent, DeleteEvent, ModifyEvent, ReviveRecycledEvent};
8use crate::prelude::*;
9use std::collections::{BTreeMap, BTreeSet};
10use std::sync::Arc;
11
12mod attrunique;
13mod base;
14mod cred_import;
15mod default_values;
16mod domain;
17pub(crate) mod dyngroup;
18mod eckeygen;
19pub(crate) mod gidnumber;
20pub(crate) mod hmac_name_unique;
21mod keyobject;
22mod memberof;
23mod namehistory;
24mod oauth2;
25mod refint;
26mod session;
27mod spn;
28mod valuedeny;
29
30trait Plugin {
31    fn id() -> &'static str;
32
33    fn pre_create_transform(
34        _qs: &mut QueryServerWriteTransaction,
35        _cand: &mut Vec<EntryInvalidNew>,
36        _ce: &CreateEvent,
37    ) -> Result<(), OperationError> {
38        error!(
39            "plugin {} has an unimplemented pre_create_transform!",
40            Self::id()
41        );
42        debug_assert!(false);
43        Err(OperationError::InvalidState)
44    }
45
46    #[allow(dead_code)]
47    fn pre_create(
48        _qs: &mut QueryServerWriteTransaction,
49        // List of what we will commit that is valid?
50        _cand: &[EntrySealedNew],
51        _ce: &CreateEvent,
52    ) -> Result<(), OperationError> {
53        error!("plugin {} has an unimplemented pre_create!", Self::id());
54        debug_assert!(false);
55        Err(OperationError::InvalidState)
56    }
57
58    fn post_create(
59        _qs: &mut QueryServerWriteTransaction,
60        // List of what we committed that was valid?
61        _cand: &[EntrySealedCommitted],
62        _ce: &CreateEvent,
63    ) -> Result<(), OperationError> {
64        error!("plugin {} has an unimplemented post_create!", Self::id());
65        debug_assert!(false);
66        Err(OperationError::InvalidState)
67    }
68
69    fn teardown_memorials(
70        _qs: &mut QueryServerWriteTransaction,
71        _memorial_pairs: &mut [(&EntrySealedCommitted, &mut EntryInvalidCommitted)],
72        _re: &ReviveRecycledEvent,
73    ) -> Result<(), OperationError> {
74        error!("plugin {} has an unimplemented pre_modify!", Self::id());
75        debug_assert!(false);
76        Err(OperationError::InvalidState)
77    }
78
79    fn pre_modify(
80        _qs: &mut QueryServerWriteTransaction,
81        _pre_cand: &[Arc<EntrySealedCommitted>],
82        _cand: &mut Vec<EntryInvalidCommitted>,
83        _me: &ModifyEvent,
84    ) -> Result<(), OperationError> {
85        error!("plugin {} has an unimplemented pre_modify!", Self::id());
86        debug_assert!(false);
87        Err(OperationError::InvalidState)
88    }
89
90    fn post_modify(
91        _qs: &mut QueryServerWriteTransaction,
92        // List of what we modified that was valid?
93        _pre_cand: &[Arc<EntrySealedCommitted>],
94        _cand: &[EntrySealedCommitted],
95        _ce: &ModifyEvent,
96    ) -> Result<(), OperationError> {
97        error!("plugin {} has an unimplemented post_modify!", Self::id());
98        debug_assert!(false);
99        Err(OperationError::InvalidState)
100    }
101
102    fn pre_batch_modify(
103        _qs: &mut QueryServerWriteTransaction,
104        _pre_cand: &[Arc<EntrySealedCommitted>],
105        _cand: &mut Vec<EntryInvalidCommitted>,
106        _me: &BatchModifyEvent,
107    ) -> Result<(), OperationError> {
108        error!(
109            "plugin {} has an unimplemented pre_batch_modify!",
110            Self::id()
111        );
112        debug_assert!(false);
113        Err(OperationError::InvalidState)
114    }
115
116    fn post_batch_modify(
117        _qs: &mut QueryServerWriteTransaction,
118        // List of what we modified that was valid?
119        _pre_cand: &[Arc<EntrySealedCommitted>],
120        _cand: &[EntrySealedCommitted],
121        _me: &BatchModifyEvent,
122    ) -> Result<(), OperationError> {
123        error!(
124            "plugin {} has an unimplemented post_batch_modify!",
125            Self::id()
126        );
127        debug_assert!(false);
128        Err(OperationError::InvalidState)
129    }
130
131    fn build_memorials(
132        _qs: &mut QueryServerWriteTransaction,
133        _cand: &[Arc<EntrySealedCommitted>],
134        _memorials: &mut BTreeMap<Uuid, EntryInitNew>,
135        _de: &DeleteEvent,
136    ) -> Result<(), OperationError> {
137        error!(
138            "plugin {} has an unimplemented run_build_memorials!",
139            Self::id()
140        );
141        debug_assert!(false);
142        Err(OperationError::InvalidState)
143    }
144
145    fn pre_delete(
146        _qs: &mut QueryServerWriteTransaction,
147        _cand: &mut Vec<EntryInvalidCommitted>,
148        _de: &DeleteEvent,
149    ) -> Result<(), OperationError> {
150        error!("plugin {} has an unimplemented pre_delete!", Self::id());
151        debug_assert!(false);
152        Err(OperationError::InvalidState)
153    }
154
155    fn post_delete(
156        _qs: &mut QueryServerWriteTransaction,
157        // List of what we delete that was valid?
158        _cand: &[EntrySealedCommitted],
159        _ce: &DeleteEvent,
160    ) -> Result<(), OperationError> {
161        error!("plugin {} has an unimplemented post_delete!", Self::id());
162        debug_assert!(false);
163        Err(OperationError::InvalidState)
164    }
165
166    fn pre_repl_refresh(
167        _qs: &mut QueryServerWriteTransaction,
168        _cand: &[EntryRefreshNew],
169    ) -> Result<(), OperationError> {
170        error!(
171            "plugin {} has an unimplemented pre_repl_refresh!",
172            Self::id()
173        );
174        debug_assert!(false);
175        Err(OperationError::InvalidState)
176    }
177
178    fn post_repl_refresh(
179        _qs: &mut QueryServerWriteTransaction,
180        _cand: &[EntrySealedCommitted],
181    ) -> Result<(), OperationError> {
182        error!(
183            "plugin {} has an unimplemented post_repl_refresh!",
184            Self::id()
185        );
186        debug_assert!(false);
187        Err(OperationError::InvalidState)
188    }
189
190    // fn pre_repl_incremental(
191    //     _qs: &mut QueryServerWriteTransaction,
192    //     _cand: &mut [(EntryIncrementalCommitted, Arc<EntrySealedCommitted>)],
193    // ) -> Result<(), OperationError> {
194    //     error!(
195    //         "plugin {} has an unimplemented pre_repl_incremental!",
196    //         Self::id()
197    //     );
198    //     debug_assert!(false);
199    //     Err(OperationError::InvalidState)
200    // }
201
202    fn post_repl_incremental_conflict(
203        _qs: &mut QueryServerWriteTransaction,
204        _cand: &[(EntrySealedCommitted, Arc<EntrySealedCommitted>)],
205        _conflict_uuids: &mut BTreeSet<Uuid>,
206    ) -> Result<(), OperationError> {
207        error!(
208            "plugin {} has an unimplemented post_repl_incremental_conflict!",
209            Self::id()
210        );
211        debug_assert!(false);
212        Err(OperationError::InvalidState)
213    }
214
215    fn post_repl_incremental(
216        _qs: &mut QueryServerWriteTransaction,
217        _pre_cand: &[Arc<EntrySealedCommitted>],
218        _cand: &[EntrySealedCommitted],
219        _conflict_uuids: &BTreeSet<Uuid>,
220    ) -> Result<(), OperationError> {
221        error!(
222            "plugin {} has an unimplemented post_repl_incremental!",
223            Self::id()
224        );
225        debug_assert!(false);
226        Err(OperationError::InvalidState)
227    }
228
229    fn verify(_qs: &mut QueryServerReadTransaction) -> Vec<Result<(), ConsistencyError>> {
230        error!("plugin {} has an unimplemented verify!", Self::id());
231        vec![Err(ConsistencyError::Unknown)]
232    }
233}
234
235pub struct Plugins {}
236
237macro_rules! run_verify_plugin {
238    (
239        $qs:ident,
240        $results:expr,
241        $target_plugin:ty
242    ) => {{
243        let mut r = <$target_plugin>::verify($qs);
244        $results.append(&mut r);
245    }};
246}
247
248impl Plugins {
249    #[instrument(level = "debug", name = "plugins::run_pre_create_transform", skip_all)]
250    pub fn run_pre_create_transform(
251        qs: &mut QueryServerWriteTransaction,
252        cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
253        ce: &CreateEvent,
254    ) -> Result<(), OperationError> {
255        base::Base::pre_create_transform(qs, cand, ce)?;
256        valuedeny::ValueDeny::pre_create_transform(qs, cand, ce)?;
257
258        oauth2::OAuth2::pre_create_transform(qs, cand, ce)?;
259        eckeygen::EcdhKeyGen::pre_create_transform(qs, cand, ce)?;
260        keyobject::KeyObjectManagement::pre_create_transform(qs, cand, ce)?;
261        cred_import::CredImport::pre_create_transform(qs, cand, ce)?;
262
263        gidnumber::GidNumber::pre_create_transform(qs, cand, ce)?;
264        domain::Domain::pre_create_transform(qs, cand, ce)?;
265        spn::Spn::pre_create_transform(qs, cand, ce)?;
266        default_values::DefaultValues::pre_create_transform(qs, cand, ce)?;
267        namehistory::NameHistory::pre_create_transform(qs, cand, ce)?;
268        hmac_name_unique::HmacNameUnique::pre_create_transform(qs, cand, ce)?;
269        // Should always be last
270        attrunique::AttrUnique::pre_create_transform(qs, cand, ce)
271    }
272
273    #[instrument(level = "trace", name = "plugins::run_pre_create", skip_all)]
274    pub fn run_pre_create(
275        _qs: &mut QueryServerWriteTransaction,
276        _cand: &[Entry<EntrySealed, EntryNew>],
277        _ce: &CreateEvent,
278    ) -> Result<(), OperationError> {
279        Ok(())
280    }
281
282    #[instrument(level = "debug", name = "plugins::run_post_create", skip_all)]
283    pub fn run_post_create(
284        qs: &mut QueryServerWriteTransaction,
285        cand: &[Entry<EntrySealed, EntryCommitted>],
286        ce: &CreateEvent,
287    ) -> Result<(), OperationError> {
288        refint::ReferentialIntegrity::post_create(qs, cand, ce)?;
289        memberof::MemberOf::post_create(qs, cand, ce)
290    }
291
292    #[instrument(level = "debug", name = "plugins::run_teardown_memorials", skip_all)]
293    pub fn run_teardown_memorials(
294        qs: &mut QueryServerWriteTransaction,
295        memorial_pairs: &mut [(&EntrySealedCommitted, &mut EntryInvalidCommitted)],
296        re: &ReviveRecycledEvent,
297    ) -> Result<(), OperationError> {
298        hmac_name_unique::HmacNameUnique::teardown_memorials(qs, memorial_pairs, re)
299    }
300
301    #[instrument(level = "debug", name = "plugins::run_pre_modify", skip_all)]
302    pub fn run_pre_modify(
303        qs: &mut QueryServerWriteTransaction,
304        pre_cand: &[Arc<EntrySealedCommitted>],
305        cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
306        me: &ModifyEvent,
307    ) -> Result<(), OperationError> {
308        base::Base::pre_modify(qs, pre_cand, cand, me)?;
309        valuedeny::ValueDeny::pre_modify(qs, pre_cand, cand, me)?;
310
311        oauth2::OAuth2::pre_modify(qs, pre_cand, cand, me)?;
312        eckeygen::EcdhKeyGen::pre_modify(qs, pre_cand, cand, me)?;
313        keyobject::KeyObjectManagement::pre_modify(qs, pre_cand, cand, me)?;
314        cred_import::CredImport::pre_modify(qs, pre_cand, cand, me)?;
315
316        gidnumber::GidNumber::pre_modify(qs, pre_cand, cand, me)?;
317        domain::Domain::pre_modify(qs, pre_cand, cand, me)?;
318        spn::Spn::pre_modify(qs, pre_cand, cand, me)?;
319        session::SessionConsistency::pre_modify(qs, pre_cand, cand, me)?;
320        default_values::DefaultValues::pre_modify(qs, pre_cand, cand, me)?;
321        namehistory::NameHistory::pre_modify(qs, pre_cand, cand, me)?;
322        hmac_name_unique::HmacNameUnique::pre_modify(qs, pre_cand, cand, me)?;
323        // attr unique should always be last
324        attrunique::AttrUnique::pre_modify(qs, pre_cand, cand, me)
325    }
326
327    #[instrument(level = "debug", name = "plugins::run_post_modify", skip_all)]
328    pub fn run_post_modify(
329        qs: &mut QueryServerWriteTransaction,
330        pre_cand: &[Arc<Entry<EntrySealed, EntryCommitted>>],
331        cand: &[Entry<EntrySealed, EntryCommitted>],
332        me: &ModifyEvent,
333    ) -> Result<(), OperationError> {
334        refint::ReferentialIntegrity::post_modify(qs, pre_cand, cand, me)?;
335        spn::Spn::post_modify(qs, pre_cand, cand, me)?;
336        memberof::MemberOf::post_modify(qs, pre_cand, cand, me)
337    }
338
339    #[instrument(level = "debug", name = "plugins::run_pre_batch_modify", skip_all)]
340    pub fn run_pre_batch_modify(
341        qs: &mut QueryServerWriteTransaction,
342        pre_cand: &[Arc<EntrySealedCommitted>],
343        cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
344        me: &BatchModifyEvent,
345    ) -> Result<(), OperationError> {
346        base::Base::pre_batch_modify(qs, pre_cand, cand, me)?;
347        valuedeny::ValueDeny::pre_batch_modify(qs, pre_cand, cand, me)?;
348
349        oauth2::OAuth2::pre_batch_modify(qs, pre_cand, cand, me)?;
350        eckeygen::EcdhKeyGen::pre_batch_modify(qs, pre_cand, cand, me)?;
351        keyobject::KeyObjectManagement::pre_batch_modify(qs, pre_cand, cand, me)?;
352        cred_import::CredImport::pre_batch_modify(qs, pre_cand, cand, me)?;
353
354        gidnumber::GidNumber::pre_batch_modify(qs, pre_cand, cand, me)?;
355        domain::Domain::pre_batch_modify(qs, pre_cand, cand, me)?;
356        spn::Spn::pre_batch_modify(qs, pre_cand, cand, me)?;
357        session::SessionConsistency::pre_batch_modify(qs, pre_cand, cand, me)?;
358        default_values::DefaultValues::pre_batch_modify(qs, pre_cand, cand, me)?;
359        namehistory::NameHistory::pre_batch_modify(qs, pre_cand, cand, me)?;
360        hmac_name_unique::HmacNameUnique::pre_batch_modify(qs, pre_cand, cand, me)?;
361        // attr unique should always be last
362        attrunique::AttrUnique::pre_batch_modify(qs, pre_cand, cand, me)
363    }
364
365    #[instrument(level = "debug", name = "plugins::run_post_batch_modify", skip_all)]
366    pub fn run_post_batch_modify(
367        qs: &mut QueryServerWriteTransaction,
368        pre_cand: &[Arc<Entry<EntrySealed, EntryCommitted>>],
369        cand: &[Entry<EntrySealed, EntryCommitted>],
370        me: &BatchModifyEvent,
371    ) -> Result<(), OperationError> {
372        refint::ReferentialIntegrity::post_batch_modify(qs, pre_cand, cand, me)?;
373        spn::Spn::post_batch_modify(qs, pre_cand, cand, me)?;
374        memberof::MemberOf::post_batch_modify(qs, pre_cand, cand, me)
375    }
376
377    #[instrument(level = "debug", name = "plugins::run_build_memorials", skip_all)]
378    pub fn run_build_memorials(
379        qs: &mut QueryServerWriteTransaction,
380        cand: &[Arc<EntrySealedCommitted>],
381        memorials: &mut BTreeMap<Uuid, EntryInitNew>,
382        de: &DeleteEvent,
383    ) -> Result<(), OperationError> {
384        hmac_name_unique::HmacNameUnique::build_memorials(qs, cand, memorials, de)
385    }
386
387    #[instrument(level = "debug", name = "plugins::run_pre_delete", skip_all)]
388    pub fn run_pre_delete(
389        qs: &mut QueryServerWriteTransaction,
390        cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
391        de: &DeleteEvent,
392    ) -> Result<(), OperationError> {
393        memberof::MemberOf::pre_delete(qs, cand, de)
394    }
395
396    #[instrument(level = "debug", name = "plugins::run_post_delete", skip_all)]
397    pub fn run_post_delete(
398        qs: &mut QueryServerWriteTransaction,
399        cand: &[Entry<EntrySealed, EntryCommitted>],
400        de: &DeleteEvent,
401    ) -> Result<(), OperationError> {
402        refint::ReferentialIntegrity::post_delete(qs, cand, de)?;
403        memberof::MemberOf::post_delete(qs, cand, de)
404    }
405
406    #[instrument(level = "debug", name = "plugins::run_pre_repl_refresh", skip_all)]
407    pub fn run_pre_repl_refresh(
408        qs: &mut QueryServerWriteTransaction,
409        cand: &[EntryRefreshNew],
410    ) -> Result<(), OperationError> {
411        attrunique::AttrUnique::pre_repl_refresh(qs, cand)
412    }
413
414    #[instrument(level = "debug", name = "plugins::run_post_repl_refresh", skip_all)]
415    pub fn run_post_repl_refresh(
416        qs: &mut QueryServerWriteTransaction,
417        cand: &[EntrySealedCommitted],
418    ) -> Result<(), OperationError> {
419        refint::ReferentialIntegrity::post_repl_refresh(qs, cand)?;
420        memberof::MemberOf::post_repl_refresh(qs, cand)
421    }
422
423    #[instrument(level = "debug", name = "plugins::run_pre_repl_incremental", skip_all)]
424    pub fn run_pre_repl_incremental(
425        _qs: &mut QueryServerWriteTransaction,
426        _cand: &mut [(EntryIncrementalCommitted, Arc<EntrySealedCommitted>)],
427    ) -> Result<(), OperationError> {
428        // Cleanup sessions on incoming replication? May not actually
429        // be needed since each node will be session checking and replicating
430        // those cleanups as needed.
431        // session::SessionConsistency::pre_repl_incremental(qs, cand)?;
432        Ok(())
433    }
434
435    #[instrument(
436        level = "debug",
437        name = "plugins::run_post_repl_incremental_conflict",
438        skip_all
439    )]
440    pub fn run_post_repl_incremental_conflict(
441        qs: &mut QueryServerWriteTransaction,
442        cand: &[(EntrySealedCommitted, Arc<EntrySealedCommitted>)],
443        conflict_uuids: &mut BTreeSet<Uuid>,
444    ) -> Result<(), OperationError> {
445        // Attr unique MUST BE FIRST.
446        attrunique::AttrUnique::post_repl_incremental_conflict(qs, cand, conflict_uuids)?;
447        // refint probably needs to be last to ensure all refers are correctly cleaned.
448        refint::ReferentialIntegrity::post_repl_incremental_conflict(qs, cand, conflict_uuids)
449    }
450
451    #[instrument(level = "debug", name = "plugins::run_post_repl_incremental", skip_all)]
452    pub fn run_post_repl_incremental(
453        qs: &mut QueryServerWriteTransaction,
454        pre_cand: &[Arc<EntrySealedCommitted>],
455        cand: &[EntrySealedCommitted],
456        conflict_uuids: &BTreeSet<Uuid>,
457    ) -> Result<(), OperationError> {
458        // Nothing to do yet.
459        // domain::Domain::post_repl_incremental(qs, pre_cand, cand, conflict_uuids)?;
460        spn::Spn::post_repl_incremental(qs, pre_cand, cand, conflict_uuids)?;
461        // refint MUST proceed memberof.
462        refint::ReferentialIntegrity::post_repl_incremental(qs, pre_cand, cand, conflict_uuids)?;
463        // Memberof MUST BE LAST.
464        memberof::MemberOf::post_repl_incremental(qs, pre_cand, cand, conflict_uuids)
465    }
466
467    #[instrument(level = "debug", name = "plugins::run_verify", skip_all)]
468    pub fn run_verify(
469        qs: &mut QueryServerReadTransaction,
470        results: &mut Vec<Result<(), ConsistencyError>>,
471    ) {
472        run_verify_plugin!(qs, results, base::Base);
473        run_verify_plugin!(qs, results, valuedeny::ValueDeny);
474        run_verify_plugin!(qs, results, attrunique::AttrUnique);
475        run_verify_plugin!(qs, results, refint::ReferentialIntegrity);
476        run_verify_plugin!(qs, results, keyobject::KeyObjectManagement);
477        run_verify_plugin!(qs, results, dyngroup::DynGroup);
478        run_verify_plugin!(qs, results, memberof::MemberOf);
479        run_verify_plugin!(qs, results, spn::Spn);
480    }
481}