1use crate::plugins::Plugin;
2use crate::prelude::*;
3use std::sync::Arc;
45pub struct KeyObjectManagement {}
67impl Plugin for KeyObjectManagement {
8fn id() -> &'static str {
9"plugin_keyobject_management"
10}
1112#[instrument(
13 level = "debug",
14 name = "keyobject_management::pre_create_transform",
15 skip_all
16 )]
17fn pre_create_transform(
18 qs: &mut QueryServerWriteTransaction,
19 cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
20 _ce: &CreateEvent,
21 ) -> Result<(), OperationError> {
22Self::apply_keyobject_inner(qs, cand)
23 }
2425#[instrument(level = "debug", name = "keyobject_management::pre_modify", skip_all)]
26fn pre_modify(
27 qs: &mut QueryServerWriteTransaction,
28 _pre_cand: &[Arc<EntrySealedCommitted>],
29 cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
30 _me: &ModifyEvent,
31 ) -> Result<(), OperationError> {
32Self::apply_keyobject_inner(qs, cand)
33 }
3435#[instrument(
36 level = "debug",
37 name = "keyobject_management::pre_batch_modify",
38 skip_all
39 )]
40fn 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> {
46Self::apply_keyobject_inner(qs, cand)
47 }
4849/*
50 #[instrument(level = "debug", name = "keyobject_management::pre_delete", skip_all)]
51 fn pre_delete(
52 _qs: &mut QueryServerWriteTransaction,
53 // Should these be EntrySealed
54 _cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
55 _de: &DeleteEvent,
56 ) -> Result<(), OperationError> {
57 Ok(())
58 }
59 */
6061#[instrument(level = "debug", name = "keyobject_management::verify", skip_all)]
62fn verify(qs: &mut QueryServerReadTransaction) -> Vec<Result<(), ConsistencyError>> {
63let filt_in = filter!(f_eq(Attribute::Class, EntryClass::KeyProvider.into()));
6465let key_providers = match qs
66 .internal_search(filt_in)
67 .map_err(|_| Err(ConsistencyError::QueryServerSearchFailure))
68 {
69Ok(all_cand) => all_cand,
70Err(e) => return vec![e],
71 };
7273// Put the providers into a map by uuid.
74let key_providers: hashbrown::HashSet<_> = key_providers
75 .into_iter()
76 .map(|entry| entry.get_uuid())
77 .collect();
7879let filt_in = filter!(f_eq(Attribute::Class, EntryClass::KeyObject.into()));
8081let key_objects = match qs
82 .internal_search(filt_in)
83 .map_err(|_| Err(ConsistencyError::QueryServerSearchFailure))
84 {
85Ok(all_cand) => all_cand,
86Err(e) => return vec![e],
87 };
8889 key_objects
90 .into_iter()
91 .filter_map(|key_object_entry| {
92let object_uuid = key_object_entry.get_uuid();
9394// Each key objects must relate to a provider.
95let Some(provider_uuid) =
96 key_object_entry.get_ava_single_refer(Attribute::KeyProvider)
97else {
98error!(?object_uuid, "Invalid key object, no key provider uuid.");
99return Some(ConsistencyError::KeyProviderUuidMissing {
100 key_object: object_uuid,
101 });
102 };
103104if !key_providers.contains(&provider_uuid) {
105error!(
106?object_uuid,
107?provider_uuid,
108"Invalid key object, key provider referenced is not found."
109);
110return Some(ConsistencyError::KeyProviderNotFound {
111 key_object: object_uuid,
112 provider: provider_uuid,
113 });
114 }
115116// Every key object needs at least *one* key it stores.
117if !key_object_entry
118 .attribute_equality(Attribute::Class, &EntryClass::KeyObjectJwtEs256.into())
119 {
120error!(?object_uuid, "Invalid key object, contains no keys.");
121return Some(ConsistencyError::KeyProviderNoKeys {
122 key_object: object_uuid,
123 });
124 }
125126None
127})
128 .map(Err)
129 .collect::<Vec<_>>()
130 }
131}
132133impl KeyObjectManagement {
134fn apply_keyobject_inner<T: Clone>(
135 qs: &mut QueryServerWriteTransaction,
136 cand: &mut [Entry<EntryInvalid, T>],
137 ) -> Result<(), OperationError> {
138// New keys will be valid from right meow!
139let valid_from = qs.get_curtime();
140let txn_cid = qs.get_cid().clone();
141let key_providers = qs.get_key_providers_mut();
142// ====================================================================
143 // Transform any found KeyObjects and manage any related key operations
144 // for them
145146cand.iter_mut()
147 .filter(|entry| {
148 entry.attribute_equality(Attribute::Class, &EntryClass::KeyObject.into())
149 })
150 .try_for_each(|entry| {
151// The entry should not have set any type of KeyObject at this point.
152 // Should we force delete those attrs here just incase?
153entry.remove_ava(Attribute::Class, &EntryClass::KeyObjectInternal.into());
154155// Must be set by now.
156let key_object_uuid = entry
157 .get_uuid()
158 .ok_or(OperationError::KP0008KeyObjectMissingUuid)?;
159160trace!(?key_object_uuid, "Setting up key object");
161162// Get the default provider, and create a new ephemeral key object
163 // inside it. If the object existed already, we clone it so that we can stage
164 // our changes.
165let mut key_object = key_providers.get_or_create_in_default(key_object_uuid)?;
166167// Import any keys that we were asked to import. This is before revocation so that
168 // any keyId here might also be able to be revoked.
169let maybe_import = entry.pop_ava(Attribute::KeyActionImportJwsEs256);
170if 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 }
176177let maybe_import = entry.pop_ava(Attribute::KeyActionImportJwsRs256);
178if 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 }
184185// If revoke. This weird looking let dance is to ensure that the inner hexstring set
186 // lives long enough.
187let maybe_revoked = entry.pop_ava(Attribute::KeyActionRevoke);
188if 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 }
193194// Rotation is after revocation, but before assertion. This way if the user
195 // asked for rotation and revocation, we don't double rotate when we get to
196 // the assert phase. We also only get a rotation time if the time is in the
197 // future, to avoid rotating keys in the past.
198if let Some(rotation_time) = entry
199 .pop_ava(Attribute::KeyActionRotate)
200 .and_then(|vs| vs.to_datetime_single())
201 .and_then(|odt| {
202let secs = odt.unix_timestamp() as u64;
203if secs > valid_from.as_secs() {
204Some(Duration::from_secs(secs))
205 } else {
206None
207}
208 })
209 {
210 key_object.rotate_keys(rotation_time, &txn_cid)?;
211 }
212213if entry.attribute_equality(Attribute::Class, &EntryClass::KeyObjectJwtEs256.into())
214 {
215// Assert that this object has a valid es256 key present. Post revoke, it may NOT
216 // be present. This differs to rotate, in that the assert verifes we have at least
217 // *one* key that is valid in all conditions.
218key_object.jws_es256_assert(Duration::ZERO, &txn_cid)?;
219 }
220221if entry.attribute_equality(Attribute::Class, &EntryClass::KeyObjectJwtRs256.into())
222 {
223 key_object.jws_rs256_assert(Duration::ZERO, &txn_cid)?;
224 }
225226if entry
227 .attribute_equality(Attribute::Class, &EntryClass::KeyObjectJweA128GCM.into())
228 {
229 key_object.jwe_a128gcm_assert(Duration::ZERO, &txn_cid)?;
230 }
231232// Turn that object into it's entry template to create. I think we need to make this
233 // some kind of merge_vs?
234key_object
235 .as_valuesets()?
236.into_iter()
237 .try_for_each(|(attribute, valueset)| {
238 entry.merge_ava_set(&attribute, valueset)
239 })?;
240241Ok(())
242 })
243 }
244}
245246// Unlike other plugins, tests for this plugin will be located in server/lib/src/server/keys.
247//
248// The reason is because we can preconfigure different providers to test these paths in future.