kanidmd_lib/plugins/
default_values.rs

1/// Set and maintain default values on entries that require them. This is separate to
2/// migrations that enforce entry existence and state on startup, this enforces
3/// default values for specific entry uuids over every transaction.
4use std::iter::once;
5use std::sync::Arc;
6
7// use crate::event::{CreateEvent, ModifyEvent};
8use crate::plugins::Plugin;
9use crate::prelude::*;
10
11pub struct DefaultValues {}
12
13impl Plugin for DefaultValues {
14    fn id() -> &'static str {
15        "plugin_default_values"
16    }
17
18    #[instrument(
19        level = "debug",
20        name = "default_values::pre_create_transform",
21        skip_all
22    )]
23    fn pre_create_transform(
24        qs: &mut QueryServerWriteTransaction,
25        cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
26        _ce: &CreateEvent,
27    ) -> Result<(), OperationError> {
28        Self::modify_inner(qs, cand)
29    }
30
31    #[instrument(level = "debug", name = "default_values::pre_modify", skip_all)]
32    fn pre_modify(
33        qs: &mut QueryServerWriteTransaction,
34        _pre_cand: &[Arc<EntrySealedCommitted>],
35        cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
36        _me: &ModifyEvent,
37    ) -> Result<(), OperationError> {
38        Self::modify_inner(qs, cand)
39    }
40
41    #[instrument(level = "debug", name = "default_values::pre_batch_modify", skip_all)]
42    fn pre_batch_modify(
43        qs: &mut QueryServerWriteTransaction,
44        _pre_cand: &[Arc<EntrySealedCommitted>],
45        cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
46        _me: &BatchModifyEvent,
47    ) -> Result<(), OperationError> {
48        Self::modify_inner(qs, cand)
49    }
50}
51
52impl DefaultValues {
53    fn modify_inner<T: Clone + std::fmt::Debug>(
54        _qs: &mut QueryServerWriteTransaction,
55        cand: &mut [Entry<EntryInvalid, T>],
56    ) -> Result<(), OperationError> {
57        cand.iter_mut().try_for_each(|e| {
58            // We have to do this rather than get_uuid here because at this stage we haven't
59            // scheme validated the entry so it's uuid could be missing in theory.
60
61            let Some(e_uuid) = e.get_ava_single_uuid(Attribute::Uuid) else {
62                trace!("entry does not contain a uuid");
63                return Ok(());
64            };
65
66            if e_uuid == UUID_IDM_ALL_ACCOUNTS {
67                // Set default account policy values if none exist.
68                e.add_ava(Attribute::Class, EntryClass::AccountPolicy.to_value());
69
70                if !e.attribute_pres(Attribute::AuthSessionExpiry) {
71                    e.set_ava(&Attribute::AuthSessionExpiry, once(
72                        Value::Uint32(DEFAULT_AUTH_SESSION_EXPIRY),
73                    ));
74                    debug!("default_values: idm_all_accounts - restore default auth_session_expiry");
75                }
76
77                // Setup the minimum functional level if one is not set already.
78                if !e.attribute_pres(Attribute::PrivilegeExpiry) {
79                    e.set_ava(&Attribute::PrivilegeExpiry, once(
80                        Value::Uint32(DEFAULT_AUTH_PRIVILEGE_EXPIRY),
81                    ));
82                    debug!("default_values: idm_all_accounts - restore default privilege_session_expiry");
83                }
84
85                trace!(?e);
86                Ok(())
87            } else {
88                Ok(())
89            }
90        })
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use crate::prelude::*;
97
98    // test we can create and generate the id
99    #[qs_test]
100    async fn test_default_values_idm_all_accounts(server: &QueryServer) {
101        let mut server_txn = server.write(duration_from_epoch_now()).await.unwrap();
102        let e_all_accounts = server_txn
103            .internal_search_uuid(UUID_IDM_ALL_ACCOUNTS)
104            .expect("must not fail");
105
106        assert!(e_all_accounts.attribute_equality(
107            Attribute::AuthSessionExpiry,
108            &PartialValue::Uint32(DEFAULT_AUTH_SESSION_EXPIRY)
109        ));
110        assert!(e_all_accounts.attribute_equality(
111            Attribute::PrivilegeExpiry,
112            &PartialValue::Uint32(DEFAULT_AUTH_PRIVILEGE_EXPIRY)
113        ));
114
115        // delete the values.
116        server_txn
117            .internal_modify_uuid(
118                UUID_IDM_ALL_ACCOUNTS,
119                &ModifyList::new_list(vec![
120                    Modify::Purged(Attribute::AuthSessionExpiry),
121                    Modify::Purged(Attribute::PrivilegeExpiry),
122                ]),
123            )
124            .expect("failed to modify account");
125
126        // They are re-populated.
127        let e_all_accounts = server_txn
128            .internal_search_uuid(UUID_IDM_ALL_ACCOUNTS)
129            .expect("must not fail");
130
131        assert!(e_all_accounts.attribute_equality(
132            Attribute::AuthSessionExpiry,
133            &PartialValue::Uint32(DEFAULT_AUTH_SESSION_EXPIRY)
134        ));
135        assert!(e_all_accounts.attribute_equality(
136            Attribute::PrivilegeExpiry,
137            &PartialValue::Uint32(DEFAULT_AUTH_PRIVILEGE_EXPIRY)
138        ));
139    }
140}