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