kanidmd_lib/plugins/
keyobject.rs1use crate::plugins::Plugin;
2use crate::prelude::*;
3use std::sync::Arc;
4
5pub struct KeyObjectManagement {}
6
7impl Plugin for KeyObjectManagement {
8    fn id() -> &'static str {
9        "plugin_keyobject_management"
10    }
11
12    #[instrument(
13        level = "debug",
14        name = "keyobject_management::pre_create_transform",
15        skip_all
16    )]
17    fn pre_create_transform(
18        qs: &mut QueryServerWriteTransaction,
19        cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
20        _ce: &CreateEvent,
21    ) -> Result<(), OperationError> {
22        Self::apply_keyobject_inner(qs, cand)
23    }
24
25    #[instrument(level = "debug", name = "keyobject_management::pre_modify", skip_all)]
26    fn pre_modify(
27        qs: &mut QueryServerWriteTransaction,
28        _pre_cand: &[Arc<EntrySealedCommitted>],
29        cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
30        _me: &ModifyEvent,
31    ) -> Result<(), OperationError> {
32        Self::apply_keyobject_inner(qs, cand)
33    }
34
35    #[instrument(
36        level = "debug",
37        name = "keyobject_management::pre_batch_modify",
38        skip_all
39    )]
40    fn pre_batch_modify(
41        qs: &mut QueryServerWriteTransaction,
42        _pre_cand: &[Arc<EntrySealedCommitted>],
43        cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
44        _me: &BatchModifyEvent,
45    ) -> Result<(), OperationError> {
46        Self::apply_keyobject_inner(qs, cand)
47    }
48
49    #[instrument(level = "debug", name = "keyobject_management::verify", skip_all)]
62    fn verify(qs: &mut QueryServerReadTransaction) -> Vec<Result<(), ConsistencyError>> {
63        let filt_in = filter!(f_eq(Attribute::Class, EntryClass::KeyProvider.into()));
64
65        let key_providers = match qs
66            .internal_search(filt_in)
67            .map_err(|_| Err(ConsistencyError::QueryServerSearchFailure))
68        {
69            Ok(all_cand) => all_cand,
70            Err(e) => return vec![e],
71        };
72
73        let key_providers: hashbrown::HashSet<_> = key_providers
75            .into_iter()
76            .map(|entry| entry.get_uuid())
77            .collect();
78
79        let filt_in = filter!(f_eq(Attribute::Class, EntryClass::KeyObject.into()));
80
81        let key_objects = match qs
82            .internal_search(filt_in)
83            .map_err(|_| Err(ConsistencyError::QueryServerSearchFailure))
84        {
85            Ok(all_cand) => all_cand,
86            Err(e) => return vec![e],
87        };
88
89        key_objects
90            .into_iter()
91            .filter_map(|key_object_entry| {
92                let object_uuid = key_object_entry.get_uuid();
93
94                let Some(provider_uuid) =
96                    key_object_entry.get_ava_single_refer(Attribute::KeyProvider)
97                else {
98                    error!(?object_uuid, "Invalid key object, no key provider uuid.");
99                    return Some(ConsistencyError::KeyProviderUuidMissing {
100                        key_object: object_uuid,
101                    });
102                };
103
104                if !key_providers.contains(&provider_uuid) {
105                    error!(
106                        ?object_uuid,
107                        ?provider_uuid,
108                        "Invalid key object, key provider referenced is not found."
109                    );
110                    return Some(ConsistencyError::KeyProviderNotFound {
111                        key_object: object_uuid,
112                        provider: provider_uuid,
113                    });
114                }
115
116                if !key_object_entry
118                    .attribute_equality(Attribute::Class, &EntryClass::KeyObjectJwtEs256.into())
119                {
120                    error!(?object_uuid, "Invalid key object, contains no keys.");
121                    return Some(ConsistencyError::KeyProviderNoKeys {
122                        key_object: object_uuid,
123                    });
124                }
125
126                None
127            })
128            .map(Err)
129            .collect::<Vec<_>>()
130    }
131}
132
133impl KeyObjectManagement {
134    fn apply_keyobject_inner<T: Clone>(
135        qs: &mut QueryServerWriteTransaction,
136        cand: &mut [Entry<EntryInvalid, T>],
137    ) -> Result<(), OperationError> {
138        let valid_from = qs.get_curtime();
140        let txn_cid = qs.get_cid().clone();
141        let key_providers = qs.get_key_providers_mut();
142        cand.iter_mut()
147            .filter(|entry| {
148                entry.attribute_equality(Attribute::Class, &EntryClass::KeyObject.into())
149            })
150            .try_for_each(|entry| {
151                entry.remove_ava(Attribute::Class, &EntryClass::KeyObjectInternal.into());
154
155                let key_object_uuid = entry
157                    .get_uuid()
158                    .ok_or(OperationError::KP0008KeyObjectMissingUuid)?;
159
160                trace!(?key_object_uuid, "Setting up key object");
161
162                let mut key_object = key_providers.get_or_create_in_default(key_object_uuid)?;
166
167                let maybe_import = entry.pop_ava(Attribute::KeyActionImportJwsEs256);
170                if let Some(import_keys) = maybe_import
171                    .as_ref()
172                    .and_then(|vs| vs.as_private_binary_set())
173                {
174                    key_object.jws_es256_import(import_keys, valid_from, &txn_cid)?;
175                }
176
177                let maybe_import = entry.pop_ava(Attribute::KeyActionImportJwsRs256);
178                if let Some(import_keys) = maybe_import
179                    .as_ref()
180                    .and_then(|vs| vs.as_private_binary_set())
181                {
182                    key_object.jws_rs256_import(import_keys, valid_from, &txn_cid)?;
183                }
184
185                let maybe_revoked = entry.pop_ava(Attribute::KeyActionRevoke);
188                if let Some(revoke_keys) =
189                    maybe_revoked.as_ref().and_then(|vs| vs.as_hexstring_set())
190                {
191                    key_object.revoke_keys(revoke_keys, &txn_cid)?;
192                }
193
194                if let Some(rotation_time) = entry
199                    .pop_ava(Attribute::KeyActionRotate)
200                    .and_then(|vs| vs.to_datetime_single())
201                    .map(|odt| {
202                        let secs = odt.unix_timestamp() as u64;
203                        if secs > valid_from.as_secs() {
204                            Duration::from_secs(secs)
205                        } else {
206                            valid_from
207                        }
208                    })
209                {
210                    debug!(?rotation_time, "initiate key rotation");
211                    key_object.rotate_keys(rotation_time, &txn_cid)?;
212                }
213
214                if entry.attribute_equality(Attribute::Class, &EntryClass::KeyObjectJwtEs256.into())
215                {
216                    key_object.jws_es256_assert(Duration::ZERO, &txn_cid)?;
220                }
221
222                if entry.attribute_equality(Attribute::Class, &EntryClass::KeyObjectJwtRs256.into())
223                {
224                    key_object.jws_rs256_assert(Duration::ZERO, &txn_cid)?;
225                }
226
227                if entry
228                    .attribute_equality(Attribute::Class, &EntryClass::KeyObjectJweA128GCM.into())
229                {
230                    key_object.jwe_a128gcm_assert(Duration::ZERO, &txn_cid)?;
231                }
232
233                key_object
236                    .as_valuesets()?
237                    .into_iter()
238                    .try_for_each(|(attribute, valueset)| {
239                        entry.merge_ava_set(&attribute, valueset)
240                    })?;
241
242                Ok(())
243            })
244    }
245}
246
247