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 std::collections::BTreeSet;
7use std::sync::Arc;
8
9use crate::entry::{Entry, EntryCommitted, EntryInvalid, EntryNew, EntrySealed};
10use crate::event::{CreateEvent, DeleteEvent, ModifyEvent};
11use crate::prelude::*;
12
13mod attrunique;
14mod base;
15mod cred_import;
16mod default_values;
17mod domain;
18pub(crate) mod dyngroup;
19mod eckeygen;
20pub(crate) mod gidnumber;
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        admin_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        admin_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        admin_error!("plugin {} has an unimplemented post_create!", Self::id());
65        debug_assert!(false);
66        Err(OperationError::InvalidState)
67    }
68
69    fn pre_modify(
70        _qs: &mut QueryServerWriteTransaction,
71        _pre_cand: &[Arc<EntrySealedCommitted>],
72        _cand: &mut Vec<EntryInvalidCommitted>,
73        _me: &ModifyEvent,
74    ) -> Result<(), OperationError> {
75        admin_error!("plugin {} has an unimplemented pre_modify!", Self::id());
76        debug_assert!(false);
77        Err(OperationError::InvalidState)
78    }
79
80    fn post_modify(
81        _qs: &mut QueryServerWriteTransaction,
82        // List of what we modified that was valid?
83        _pre_cand: &[Arc<EntrySealedCommitted>],
84        _cand: &[EntrySealedCommitted],
85        _ce: &ModifyEvent,
86    ) -> Result<(), OperationError> {
87        admin_error!("plugin {} has an unimplemented post_modify!", Self::id());
88        debug_assert!(false);
89        Err(OperationError::InvalidState)
90    }
91
92    fn pre_batch_modify(
93        _qs: &mut QueryServerWriteTransaction,
94        _pre_cand: &[Arc<EntrySealedCommitted>],
95        _cand: &mut Vec<EntryInvalidCommitted>,
96        _me: &BatchModifyEvent,
97    ) -> Result<(), OperationError> {
98        admin_error!(
99            "plugin {} has an unimplemented pre_batch_modify!",
100            Self::id()
101        );
102        debug_assert!(false);
103        Err(OperationError::InvalidState)
104    }
105
106    fn post_batch_modify(
107        _qs: &mut QueryServerWriteTransaction,
108        // List of what we modified that was valid?
109        _pre_cand: &[Arc<EntrySealedCommitted>],
110        _cand: &[EntrySealedCommitted],
111        _me: &BatchModifyEvent,
112    ) -> Result<(), OperationError> {
113        admin_error!(
114            "plugin {} has an unimplemented post_batch_modify!",
115            Self::id()
116        );
117        debug_assert!(false);
118        Err(OperationError::InvalidState)
119    }
120
121    fn pre_delete(
122        _qs: &mut QueryServerWriteTransaction,
123        _cand: &mut Vec<EntryInvalidCommitted>,
124        _de: &DeleteEvent,
125    ) -> Result<(), OperationError> {
126        admin_error!("plugin {} has an unimplemented pre_delete!", Self::id());
127        debug_assert!(false);
128        Err(OperationError::InvalidState)
129    }
130
131    fn post_delete(
132        _qs: &mut QueryServerWriteTransaction,
133        // List of what we delete that was valid?
134        _cand: &[EntrySealedCommitted],
135        _ce: &DeleteEvent,
136    ) -> Result<(), OperationError> {
137        admin_error!("plugin {} has an unimplemented post_delete!", Self::id());
138        debug_assert!(false);
139        Err(OperationError::InvalidState)
140    }
141
142    fn pre_repl_refresh(
143        _qs: &mut QueryServerWriteTransaction,
144        _cand: &[EntryRefreshNew],
145    ) -> Result<(), OperationError> {
146        admin_error!(
147            "plugin {} has an unimplemented pre_repl_refresh!",
148            Self::id()
149        );
150        debug_assert!(false);
151        Err(OperationError::InvalidState)
152    }
153
154    fn post_repl_refresh(
155        _qs: &mut QueryServerWriteTransaction,
156        _cand: &[EntrySealedCommitted],
157    ) -> Result<(), OperationError> {
158        admin_error!(
159            "plugin {} has an unimplemented post_repl_refresh!",
160            Self::id()
161        );
162        debug_assert!(false);
163        Err(OperationError::InvalidState)
164    }
165
166    // fn pre_repl_incremental(
167    //     _qs: &mut QueryServerWriteTransaction,
168    //     _cand: &mut [(EntryIncrementalCommitted, Arc<EntrySealedCommitted>)],
169    // ) -> Result<(), OperationError> {
170    //     admin_error!(
171    //         "plugin {} has an unimplemented pre_repl_incremental!",
172    //         Self::id()
173    //     );
174    //     debug_assert!(false);
175    //     Err(OperationError::InvalidState)
176    // }
177
178    fn post_repl_incremental_conflict(
179        _qs: &mut QueryServerWriteTransaction,
180        _cand: &[(EntrySealedCommitted, Arc<EntrySealedCommitted>)],
181        _conflict_uuids: &mut BTreeSet<Uuid>,
182    ) -> Result<(), OperationError> {
183        admin_error!(
184            "plugin {} has an unimplemented post_repl_incremental_conflict!",
185            Self::id()
186        );
187        debug_assert!(false);
188        Err(OperationError::InvalidState)
189    }
190
191    fn post_repl_incremental(
192        _qs: &mut QueryServerWriteTransaction,
193        _pre_cand: &[Arc<EntrySealedCommitted>],
194        _cand: &[EntrySealedCommitted],
195        _conflict_uuids: &BTreeSet<Uuid>,
196    ) -> Result<(), OperationError> {
197        admin_error!(
198            "plugin {} has an unimplemented post_repl_incremental!",
199            Self::id()
200        );
201        debug_assert!(false);
202        Err(OperationError::InvalidState)
203    }
204
205    fn verify(_qs: &mut QueryServerReadTransaction) -> Vec<Result<(), ConsistencyError>> {
206        admin_error!("plugin {} has an unimplemented verify!", Self::id());
207        vec![Err(ConsistencyError::Unknown)]
208    }
209}
210
211pub struct Plugins {}
212
213macro_rules! run_verify_plugin {
214    (
215        $qs:ident,
216        $results:expr,
217        $target_plugin:ty
218    ) => {{
219        let mut r = <$target_plugin>::verify($qs);
220        $results.append(&mut r);
221    }};
222}
223
224impl Plugins {
225    #[instrument(level = "debug", name = "plugins::run_pre_create_transform", skip_all)]
226    pub fn run_pre_create_transform(
227        qs: &mut QueryServerWriteTransaction,
228        cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
229        ce: &CreateEvent,
230    ) -> Result<(), OperationError> {
231        base::Base::pre_create_transform(qs, cand, ce)?;
232        valuedeny::ValueDeny::pre_create_transform(qs, cand, ce)?;
233
234        oauth2::OAuth2::pre_create_transform(qs, cand, ce)?;
235        eckeygen::EcdhKeyGen::pre_create_transform(qs, cand, ce)?;
236        keyobject::KeyObjectManagement::pre_create_transform(qs, cand, ce)?;
237        cred_import::CredImport::pre_create_transform(qs, cand, ce)?;
238
239        gidnumber::GidNumber::pre_create_transform(qs, cand, ce)?;
240        domain::Domain::pre_create_transform(qs, cand, ce)?;
241        spn::Spn::pre_create_transform(qs, cand, ce)?;
242        default_values::DefaultValues::pre_create_transform(qs, cand, ce)?;
243        namehistory::NameHistory::pre_create_transform(qs, cand, ce)?;
244        // Should always be last
245        attrunique::AttrUnique::pre_create_transform(qs, cand, ce)
246    }
247
248    #[instrument(level = "trace", name = "plugins::run_pre_create", skip_all)]
249    pub fn run_pre_create(
250        _qs: &mut QueryServerWriteTransaction,
251        _cand: &[Entry<EntrySealed, EntryNew>],
252        _ce: &CreateEvent,
253    ) -> Result<(), OperationError> {
254        Ok(())
255    }
256
257    #[instrument(level = "debug", name = "plugins::run_post_create", skip_all)]
258    pub fn run_post_create(
259        qs: &mut QueryServerWriteTransaction,
260        cand: &[Entry<EntrySealed, EntryCommitted>],
261        ce: &CreateEvent,
262    ) -> Result<(), OperationError> {
263        refint::ReferentialIntegrity::post_create(qs, cand, ce)?;
264        memberof::MemberOf::post_create(qs, cand, ce)
265    }
266
267    #[instrument(level = "debug", name = "plugins::run_pre_modify", skip_all)]
268    pub fn run_pre_modify(
269        qs: &mut QueryServerWriteTransaction,
270        pre_cand: &[Arc<EntrySealedCommitted>],
271        cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
272        me: &ModifyEvent,
273    ) -> Result<(), OperationError> {
274        base::Base::pre_modify(qs, pre_cand, cand, me)?;
275        valuedeny::ValueDeny::pre_modify(qs, pre_cand, cand, me)?;
276
277        oauth2::OAuth2::pre_modify(qs, pre_cand, cand, me)?;
278        eckeygen::EcdhKeyGen::pre_modify(qs, pre_cand, cand, me)?;
279        keyobject::KeyObjectManagement::pre_modify(qs, pre_cand, cand, me)?;
280        cred_import::CredImport::pre_modify(qs, pre_cand, cand, me)?;
281
282        gidnumber::GidNumber::pre_modify(qs, pre_cand, cand, me)?;
283        domain::Domain::pre_modify(qs, pre_cand, cand, me)?;
284        spn::Spn::pre_modify(qs, pre_cand, cand, me)?;
285        session::SessionConsistency::pre_modify(qs, pre_cand, cand, me)?;
286        default_values::DefaultValues::pre_modify(qs, pre_cand, cand, me)?;
287        namehistory::NameHistory::pre_modify(qs, pre_cand, cand, me)?;
288        // attr unique should always be last
289        attrunique::AttrUnique::pre_modify(qs, pre_cand, cand, me)
290    }
291
292    #[instrument(level = "debug", name = "plugins::run_post_modify", skip_all)]
293    pub fn run_post_modify(
294        qs: &mut QueryServerWriteTransaction,
295        pre_cand: &[Arc<Entry<EntrySealed, EntryCommitted>>],
296        cand: &[Entry<EntrySealed, EntryCommitted>],
297        me: &ModifyEvent,
298    ) -> Result<(), OperationError> {
299        refint::ReferentialIntegrity::post_modify(qs, pre_cand, cand, me)?;
300        spn::Spn::post_modify(qs, pre_cand, cand, me)?;
301        memberof::MemberOf::post_modify(qs, pre_cand, cand, me)
302    }
303
304    #[instrument(level = "debug", name = "plugins::run_pre_batch_modify", skip_all)]
305    pub fn run_pre_batch_modify(
306        qs: &mut QueryServerWriteTransaction,
307        pre_cand: &[Arc<EntrySealedCommitted>],
308        cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
309        me: &BatchModifyEvent,
310    ) -> Result<(), OperationError> {
311        base::Base::pre_batch_modify(qs, pre_cand, cand, me)?;
312        valuedeny::ValueDeny::pre_batch_modify(qs, pre_cand, cand, me)?;
313
314        oauth2::OAuth2::pre_batch_modify(qs, pre_cand, cand, me)?;
315        eckeygen::EcdhKeyGen::pre_batch_modify(qs, pre_cand, cand, me)?;
316        keyobject::KeyObjectManagement::pre_batch_modify(qs, pre_cand, cand, me)?;
317        cred_import::CredImport::pre_batch_modify(qs, pre_cand, cand, me)?;
318
319        gidnumber::GidNumber::pre_batch_modify(qs, pre_cand, cand, me)?;
320        domain::Domain::pre_batch_modify(qs, pre_cand, cand, me)?;
321        spn::Spn::pre_batch_modify(qs, pre_cand, cand, me)?;
322        session::SessionConsistency::pre_batch_modify(qs, pre_cand, cand, me)?;
323        default_values::DefaultValues::pre_batch_modify(qs, pre_cand, cand, me)?;
324        namehistory::NameHistory::pre_batch_modify(qs, pre_cand, cand, me)?;
325        // attr unique should always be last
326        attrunique::AttrUnique::pre_batch_modify(qs, pre_cand, cand, me)
327    }
328
329    #[instrument(level = "debug", name = "plugins::run_post_batch_modify", skip_all)]
330    pub fn run_post_batch_modify(
331        qs: &mut QueryServerWriteTransaction,
332        pre_cand: &[Arc<Entry<EntrySealed, EntryCommitted>>],
333        cand: &[Entry<EntrySealed, EntryCommitted>],
334        me: &BatchModifyEvent,
335    ) -> Result<(), OperationError> {
336        refint::ReferentialIntegrity::post_batch_modify(qs, pre_cand, cand, me)?;
337        spn::Spn::post_batch_modify(qs, pre_cand, cand, me)?;
338        memberof::MemberOf::post_batch_modify(qs, pre_cand, cand, me)
339    }
340
341    #[instrument(level = "debug", name = "plugins::run_pre_delete", skip_all)]
342    pub fn run_pre_delete(
343        qs: &mut QueryServerWriteTransaction,
344        cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
345        de: &DeleteEvent,
346    ) -> Result<(), OperationError> {
347        memberof::MemberOf::pre_delete(qs, cand, de)
348    }
349
350    #[instrument(level = "debug", name = "plugins::run_post_delete", skip_all)]
351    pub fn run_post_delete(
352        qs: &mut QueryServerWriteTransaction,
353        cand: &[Entry<EntrySealed, EntryCommitted>],
354        de: &DeleteEvent,
355    ) -> Result<(), OperationError> {
356        refint::ReferentialIntegrity::post_delete(qs, cand, de)?;
357        memberof::MemberOf::post_delete(qs, cand, de)
358    }
359
360    #[instrument(level = "debug", name = "plugins::run_pre_repl_refresh", skip_all)]
361    pub fn run_pre_repl_refresh(
362        qs: &mut QueryServerWriteTransaction,
363        cand: &[EntryRefreshNew],
364    ) -> Result<(), OperationError> {
365        attrunique::AttrUnique::pre_repl_refresh(qs, cand)
366    }
367
368    #[instrument(level = "debug", name = "plugins::run_post_repl_refresh", skip_all)]
369    pub fn run_post_repl_refresh(
370        qs: &mut QueryServerWriteTransaction,
371        cand: &[EntrySealedCommitted],
372    ) -> Result<(), OperationError> {
373        refint::ReferentialIntegrity::post_repl_refresh(qs, cand)?;
374        memberof::MemberOf::post_repl_refresh(qs, cand)
375    }
376
377    #[instrument(level = "debug", name = "plugins::run_pre_repl_incremental", skip_all)]
378    pub fn run_pre_repl_incremental(
379        _qs: &mut QueryServerWriteTransaction,
380        _cand: &mut [(EntryIncrementalCommitted, Arc<EntrySealedCommitted>)],
381    ) -> Result<(), OperationError> {
382        // Cleanup sessions on incoming replication? May not actually
383        // be needed since each node will be session checking and replicating
384        // those cleanups as needed.
385        // session::SessionConsistency::pre_repl_incremental(qs, cand)?;
386        Ok(())
387    }
388
389    #[instrument(
390        level = "debug",
391        name = "plugins::run_post_repl_incremental_conflict",
392        skip_all
393    )]
394    pub fn run_post_repl_incremental_conflict(
395        qs: &mut QueryServerWriteTransaction,
396        cand: &[(EntrySealedCommitted, Arc<EntrySealedCommitted>)],
397        conflict_uuids: &mut BTreeSet<Uuid>,
398    ) -> Result<(), OperationError> {
399        // Attr unique MUST BE FIRST.
400        attrunique::AttrUnique::post_repl_incremental_conflict(qs, cand, conflict_uuids)
401    }
402
403    #[instrument(level = "debug", name = "plugins::run_post_repl_incremental", skip_all)]
404    pub fn run_post_repl_incremental(
405        qs: &mut QueryServerWriteTransaction,
406        pre_cand: &[Arc<EntrySealedCommitted>],
407        cand: &[EntrySealedCommitted],
408        conflict_uuids: &BTreeSet<Uuid>,
409    ) -> Result<(), OperationError> {
410        // Nothing to do yet.
411        // domain::Domain::post_repl_incremental(qs, pre_cand, cand, conflict_uuids)?;
412        spn::Spn::post_repl_incremental(qs, pre_cand, cand, conflict_uuids)?;
413        // refint MUST proceed memberof.
414        refint::ReferentialIntegrity::post_repl_incremental(qs, pre_cand, cand, conflict_uuids)?;
415        // Memberof MUST BE LAST.
416        memberof::MemberOf::post_repl_incremental(qs, pre_cand, cand, conflict_uuids)
417    }
418
419    #[instrument(level = "debug", name = "plugins::run_verify", skip_all)]
420    pub fn run_verify(
421        qs: &mut QueryServerReadTransaction,
422        results: &mut Vec<Result<(), ConsistencyError>>,
423    ) {
424        run_verify_plugin!(qs, results, base::Base);
425        run_verify_plugin!(qs, results, valuedeny::ValueDeny);
426        run_verify_plugin!(qs, results, attrunique::AttrUnique);
427        run_verify_plugin!(qs, results, refint::ReferentialIntegrity);
428        run_verify_plugin!(qs, results, keyobject::KeyObjectManagement);
429        run_verify_plugin!(qs, results, dyngroup::DynGroup);
430        run_verify_plugin!(qs, results, memberof::MemberOf);
431        run_verify_plugin!(qs, results, spn::Spn);
432    }
433}