kanidmd_lib/valueset/
key_internal.rs

1use crate::be::dbvalue::{DbValueKeyInternal, DbValueKeyStatus, DbValueKeyUsage};
2use crate::prelude::*;
3use crate::server::keys::KeyId;
4use crate::value::{KeyStatus, KeyUsage};
5use crate::valueset::ScimResolveStatus;
6use crate::valueset::{DbValueSetV2, ValueSet};
7use kanidm_proto::scim_v1::server::ScimKeyInternal;
8use std::collections::BTreeMap;
9use std::fmt;
10use time::OffsetDateTime;
11
12#[derive(Clone, PartialEq, Eq)]
13pub struct KeyInternalData {
14    pub usage: KeyUsage,
15    pub valid_from: u64,
16    pub status: KeyStatus,
17    pub status_cid: Cid,
18    pub der: Vec<u8>,
19}
20
21impl fmt::Debug for KeyInternalData {
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        f.debug_struct("KeyInternalData")
24            .field("usage", &self.usage)
25            .field("valid_from", &self.valid_from)
26            .field("status", &self.status)
27            .field("status_cid", &self.status_cid)
28            .finish()
29    }
30}
31
32#[derive(Debug, Clone)]
33pub struct ValueSetKeyInternal {
34    map: BTreeMap<KeyId, KeyInternalData>,
35}
36
37impl ValueSetKeyInternal {
38    pub fn new(
39        id: KeyId,
40        usage: KeyUsage,
41        valid_from: u64,
42        status: KeyStatus,
43        status_cid: Cid,
44        der: Vec<u8>,
45    ) -> Box<Self> {
46        let map = BTreeMap::from([(
47            id,
48            KeyInternalData {
49                usage,
50                valid_from,
51                status,
52                status_cid,
53                der,
54            },
55        )]);
56
57        Box::new(ValueSetKeyInternal { map })
58    }
59
60    pub fn from_key_iter(
61        keys: impl Iterator<Item = (KeyId, KeyInternalData)>,
62    ) -> Result<ValueSet, OperationError> {
63        let map = keys.collect();
64
65        Ok(Box::new(ValueSetKeyInternal { map }))
66    }
67
68    fn from_dbv_iter(
69        keys: impl Iterator<Item = DbValueKeyInternal>,
70    ) -> Result<ValueSet, OperationError> {
71        let map = keys
72            .map(|dbv_key| {
73                match dbv_key {
74                    DbValueKeyInternal::V1 {
75                        id,
76                        usage,
77                        valid_from,
78                        status,
79                        status_cid,
80                        der,
81                    } => {
82                        // Type cast, for now, these are both Vec<u8>
83                        let id: KeyId = id;
84                        let usage = match usage {
85                            DbValueKeyUsage::JwsEs256 => KeyUsage::JwsEs256,
86                            DbValueKeyUsage::JwsRs256 => KeyUsage::JwsRs256,
87                            DbValueKeyUsage::JweA128GCM => KeyUsage::JweA128GCM,
88                        };
89                        let status_cid = status_cid.into();
90                        let status = match status {
91                            DbValueKeyStatus::Valid => KeyStatus::Valid,
92                            DbValueKeyStatus::Retained => KeyStatus::Retained,
93                            DbValueKeyStatus::Revoked => KeyStatus::Revoked,
94                        };
95
96                        Ok((
97                            id,
98                            KeyInternalData {
99                                usage,
100                                valid_from,
101                                status,
102                                status_cid,
103                                der,
104                            },
105                        ))
106                    }
107                }
108            })
109            .collect::<Result<BTreeMap<_, _>, _>>()?;
110
111        Ok(Box::new(ValueSetKeyInternal { map }))
112    }
113
114    pub fn from_dbvs2(keys: Vec<DbValueKeyInternal>) -> Result<ValueSet, OperationError> {
115        Self::from_dbv_iter(keys.into_iter())
116    }
117
118    fn to_vec_dbvs(&self) -> Vec<DbValueKeyInternal> {
119        self.map
120            .iter()
121            .map(
122                |(
123                    id,
124                    KeyInternalData {
125                        usage,
126                        status,
127                        status_cid,
128                        valid_from,
129                        der,
130                    },
131                )| {
132                    let id: String = id.clone();
133                    let usage = match usage {
134                        KeyUsage::JwsEs256 => DbValueKeyUsage::JwsEs256,
135                        KeyUsage::JwsRs256 => DbValueKeyUsage::JwsRs256,
136                        KeyUsage::JweA128GCM => DbValueKeyUsage::JweA128GCM,
137                    };
138                    let status_cid = status_cid.into();
139                    let status = match status {
140                        KeyStatus::Valid => DbValueKeyStatus::Valid,
141                        KeyStatus::Retained => DbValueKeyStatus::Retained,
142                        KeyStatus::Revoked => DbValueKeyStatus::Revoked,
143                    };
144
145                    DbValueKeyInternal::V1 {
146                        id,
147                        usage,
148                        status,
149                        status_cid,
150                        der: der.clone(),
151                        valid_from: *valid_from,
152                    }
153                },
154            )
155            .collect()
156    }
157}
158
159impl ValueSetT for ValueSetKeyInternal {
160    fn insert_checked(&mut self, _value: crate::value::Value) -> Result<bool, OperationError> {
161        // match value {
162        // I'm not sure we ever need to actually push this?
163        /*
164        Value::KeyInternal {
165            id,
166            usage,
167            valid_from,
168            status,
169            der,
170        } => {
171            todo!();
172        }
173        */
174        // _ => {
175        debug_assert!(false);
176        Err(OperationError::InvalidValueState)
177        // }
178        // }
179    }
180
181    fn clear(&mut self) {
182        // When is this called?
183        debug_assert!(false);
184        self.map.clear();
185    }
186
187    fn remove(&mut self, pv: &crate::value::PartialValue, _cid: &Cid) -> bool {
188        match pv {
189            PartialValue::HexString(kid) => {
190                if let Some(key_object) = self.map.get_mut(kid) {
191                    if !matches!(key_object.status, KeyStatus::Revoked) {
192                        // Do we need to track the Cid like sessions?
193                        key_object.status = KeyStatus::Revoked;
194                        true
195                    } else {
196                        false
197                    }
198                } else {
199                    false
200                }
201            }
202            _ => false,
203        }
204    }
205
206    fn purge(&mut self, cid: &Cid) -> bool {
207        for key_object in self.map.values_mut() {
208            if !matches!(key_object.status, KeyStatus::Revoked) {
209                key_object.status_cid = cid.clone();
210                key_object.status = KeyStatus::Revoked;
211            }
212        }
213        false
214    }
215
216    fn trim(&mut self, trim_cid: &Cid) {
217        self.map.retain(|_, key_internal| {
218            match &key_internal.status {
219                KeyStatus::Revoked if &key_internal.status_cid < trim_cid => {
220                    // This value is past the replication trim window and can now safely
221                    // be removed
222                    false
223                }
224                // Retain all else
225                _ => true,
226            }
227        });
228    }
229
230    fn contains(&self, pv: &crate::value::PartialValue) -> bool {
231        match pv {
232            PartialValue::HexString(kid) => self.map.contains_key(kid),
233            _ => false,
234        }
235    }
236
237    fn substring(&self, _pv: &crate::value::PartialValue) -> bool {
238        false
239    }
240
241    fn startswith(&self, _pv: &PartialValue) -> bool {
242        false
243    }
244
245    fn endswith(&self, _pv: &PartialValue) -> bool {
246        false
247    }
248
249    fn lessthan(&self, _pv: &crate::value::PartialValue) -> bool {
250        false
251    }
252
253    fn len(&self) -> usize {
254        self.map.len()
255    }
256
257    fn generate_idx_eq_keys(&self) -> Vec<String> {
258        self.map.keys().map(hex::encode).collect()
259    }
260
261    fn syntax(&self) -> SyntaxType {
262        SyntaxType::KeyInternal
263    }
264
265    fn validate(&self, _schema_attr: &crate::schema::SchemaAttribute) -> bool {
266        // Validate that every key id is a valid iname.
267        self.map.keys().all(|s| {
268            // We validate these two first to prevent injection attacks.
269            Value::validate_str_escapes(s)
270                && Value::validate_singleline(s)
271                && Value::validate_hexstr(s.as_str())
272        })
273    }
274
275    fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
276        Box::new(self.map.iter().map(|(kid, key_object)| {
277            format!(
278                "{}: {} {} {}",
279                kid, key_object.status, key_object.usage, key_object.valid_from
280            )
281        }))
282    }
283
284    fn to_scim_value(&self) -> Option<ScimResolveStatus> {
285        Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
286            self.map
287                .iter()
288                .map(|(kid, key_object)| {
289                    let odt: OffsetDateTime =
290                        OffsetDateTime::UNIX_EPOCH + Duration::from_secs(key_object.valid_from);
291
292                    ScimKeyInternal {
293                        key_id: kid.clone(),
294                        status: key_object.status.to_string(),
295                        usage: key_object.usage.to_string(),
296                        valid_from: odt,
297                    }
298                })
299                .collect::<Vec<_>>(),
300        )))
301    }
302
303    fn to_db_valueset_v2(&self) -> DbValueSetV2 {
304        let keys = self.to_vec_dbvs();
305        DbValueSetV2::KeyInternal(keys)
306    }
307
308    fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = crate::value::PartialValue> + '_> {
309        Box::new(self.map.keys().cloned().map(PartialValue::HexString))
310    }
311
312    fn to_value_iter(&self) -> Box<dyn Iterator<Item = crate::value::Value> + '_> {
313        debug_assert!(false);
314        Box::new(self.map.iter().map(
315            |(
316                id,
317                KeyInternalData {
318                    usage,
319                    status,
320                    status_cid,
321                    der,
322                    valid_from,
323                },
324            )| {
325                Value::KeyInternal {
326                    id: id.clone(),
327                    usage: *usage,
328                    status: *status,
329                    status_cid: status_cid.clone(),
330                    der: der.clone(),
331                    valid_from: *valid_from,
332                }
333            },
334        ))
335    }
336
337    fn equal(&self, other: &super::ValueSet) -> bool {
338        if let Some(other) = other.as_key_internal_map() {
339            &self.map == other
340        } else {
341            debug_assert!(false);
342            false
343        }
344    }
345
346    fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
347        let Some(b) = other.as_key_internal_map() else {
348            debug_assert!(false);
349            return Err(OperationError::InvalidValueState);
350        };
351
352        for (k_other, v_other) in b.iter() {
353            if let Some(v_self) = self.map.get_mut(k_other) {
354                // Revoked is always a greater status than retained or valid.
355                if v_other.status > v_self.status {
356                    *v_self = v_other.clone();
357                }
358            } else {
359                // Not present, just insert.
360                self.map.insert(k_other.clone(), v_other.clone());
361            }
362        }
363
364        Ok(())
365    }
366
367    fn as_key_internal_map(&self) -> Option<&BTreeMap<KeyId, KeyInternalData>> {
368        Some(&self.map)
369    }
370
371    fn repl_merge_valueset(&self, older: &ValueSet, trim_cid: &Cid) -> Option<ValueSet> {
372        let b = older.as_key_internal_map()?;
373
374        let mut map = self.map.clone();
375
376        for (k_other, v_other) in b.iter() {
377            if let Some(v_self) = map.get_mut(k_other) {
378                // Revoked is always a greater status than retained or valid.
379                if v_other.status > v_self.status {
380                    *v_self = v_other.clone();
381                }
382            } else {
383                // Not present, just insert.
384                map.insert(k_other.clone(), v_other.clone());
385            }
386        }
387
388        let mut vs = Box::new(ValueSetKeyInternal { map });
389
390        vs.trim(trim_cid);
391
392        Some(vs)
393    }
394}
395
396#[cfg(test)]
397mod tests {
398    use super::{KeyInternalData, ValueSetKeyInternal};
399    use crate::prelude::*;
400    use crate::value::*;
401
402    #[test]
403    fn test_valueset_key_internal_purge_trim() {
404        let kid = "test".to_string();
405        let usage = KeyUsage::JwsEs256;
406        let valid_from = 0;
407        let status = KeyStatus::Valid;
408        let status_cid = Cid::new_zero();
409        let der = Vec::with_capacity(0);
410
411        let mut vs_a: ValueSet =
412            ValueSetKeyInternal::new(kid.clone(), usage, valid_from, status, status_cid, der);
413
414        let one_cid = Cid::new_count(1);
415
416        // Simulate session revocation.
417        vs_a.purge(&one_cid);
418
419        assert_eq!(vs_a.len(), 1);
420
421        let key_internal = vs_a
422            .as_key_internal_map()
423            .and_then(|map| map.get(&kid))
424            .expect("Unable to locate session");
425
426        assert_eq!(key_internal.status, KeyStatus::Revoked);
427        assert_eq!(key_internal.status_cid, one_cid);
428
429        // Now trim
430        let two_cid = Cid::new_count(2);
431
432        vs_a.trim(&two_cid);
433
434        assert!(vs_a.is_empty());
435    }
436
437    #[test]
438    fn test_valueset_key_internal_merge_left() {
439        let kid = "test".to_string();
440        let usage = KeyUsage::JwsEs256;
441        let valid_from = 0;
442        let status = KeyStatus::Valid;
443        let status_cid = Cid::new_zero();
444        let der = Vec::with_capacity(0);
445
446        let mut vs_a: ValueSet = ValueSetKeyInternal::new(
447            kid.clone(),
448            usage,
449            valid_from,
450            status,
451            status_cid.clone(),
452            der.clone(),
453        );
454
455        let status = KeyStatus::Revoked;
456
457        let vs_b: ValueSet =
458            ValueSetKeyInternal::new(kid.clone(), usage, valid_from, status, status_cid, der);
459
460        vs_a.merge(&vs_b).expect("Failed to merge");
461
462        assert_eq!(vs_a.len(), 1);
463        let key_internal = vs_a
464            .as_key_internal_map()
465            .and_then(|map| map.get(&kid))
466            .expect("Unable to locate session");
467
468        assert_eq!(key_internal.status, KeyStatus::Revoked);
469    }
470
471    #[test]
472    fn test_valueset_key_internal_merge_right() {
473        let kid = "test".to_string();
474        let usage = KeyUsage::JwsEs256;
475        let valid_from = 0;
476        let status = KeyStatus::Valid;
477        let status_cid = Cid::new_zero();
478        let der = Vec::with_capacity(0);
479
480        let vs_a: ValueSet = ValueSetKeyInternal::new(
481            kid.clone(),
482            usage,
483            valid_from,
484            status,
485            status_cid.clone(),
486            der.clone(),
487        );
488
489        let status = KeyStatus::Revoked;
490
491        let mut vs_b: ValueSet =
492            ValueSetKeyInternal::new(kid.clone(), usage, valid_from, status, status_cid, der);
493
494        vs_b.merge(&vs_a).expect("Failed to merge");
495
496        assert_eq!(vs_b.len(), 1);
497
498        let key_internal = vs_b
499            .as_key_internal_map()
500            .and_then(|map| map.get(&kid))
501            .expect("Unable to locate session");
502
503        assert_eq!(key_internal.status, KeyStatus::Revoked);
504    }
505
506    #[test]
507    fn test_valueset_key_internal_repl_merge_left() {
508        let kid = "test".to_string();
509        let usage = KeyUsage::JwsEs256;
510        let valid_from = 0;
511        let status = KeyStatus::Valid;
512        let zero_cid = Cid::new_zero();
513        let one_cid = Cid::new_count(1);
514        let two_cid = Cid::new_count(2);
515        let der = Vec::with_capacity(0);
516
517        let kid_2 = "key_2".to_string();
518
519        let vs_a: ValueSet = ValueSetKeyInternal::from_key_iter(
520            [
521                (
522                    kid.clone(),
523                    KeyInternalData {
524                        usage,
525                        valid_from,
526                        status,
527                        status_cid: two_cid.clone(),
528                        der: der.clone(),
529                    },
530                ),
531                (
532                    kid_2.clone(),
533                    KeyInternalData {
534                        usage,
535                        valid_from,
536                        status: KeyStatus::Revoked,
537                        status_cid: zero_cid.clone(),
538                        der: der.clone(),
539                    },
540                ),
541            ]
542            .into_iter(),
543        )
544        .expect("Failed to build valueset");
545
546        let status = KeyStatus::Revoked;
547
548        let vs_b: ValueSet =
549            ValueSetKeyInternal::new(kid.clone(), usage, valid_from, status, two_cid, der);
550
551        let vs_r = vs_a
552            .repl_merge_valueset(&vs_b, &one_cid)
553            .expect("Failed to merge");
554
555        let key_internal_map = vs_r.as_key_internal_map().expect("Unable to access map");
556
557        eprintln!("{:?}", key_internal_map);
558
559        assert_eq!(vs_r.len(), 1);
560
561        let key_internal = key_internal_map.get(&kid).expect("Unable to access key");
562
563        assert_eq!(key_internal.status, KeyStatus::Revoked);
564
565        // Assert the item was trimmed
566        assert!(!key_internal_map.contains_key(&kid_2));
567    }
568
569    #[test]
570    fn test_valueset_key_internal_repl_merge_right() {
571        let kid = "test".to_string();
572        let usage = KeyUsage::JwsEs256;
573        let valid_from = 0;
574        let status = KeyStatus::Valid;
575        let zero_cid = Cid::new_zero();
576        let one_cid = Cid::new_count(1);
577        let two_cid = Cid::new_count(2);
578        let der = Vec::with_capacity(0);
579
580        let kid_2 = "key_2".to_string();
581
582        let vs_a: ValueSet = ValueSetKeyInternal::from_key_iter(
583            [
584                (
585                    kid.clone(),
586                    KeyInternalData {
587                        usage,
588                        valid_from,
589                        status,
590                        status_cid: two_cid.clone(),
591                        der: der.clone(),
592                    },
593                ),
594                (
595                    kid_2.clone(),
596                    KeyInternalData {
597                        usage,
598                        valid_from,
599                        status: KeyStatus::Revoked,
600                        status_cid: zero_cid.clone(),
601                        der: der.clone(),
602                    },
603                ),
604            ]
605            .into_iter(),
606        )
607        .expect("Failed to build valueset");
608
609        let status = KeyStatus::Revoked;
610
611        let vs_b: ValueSet =
612            ValueSetKeyInternal::new(kid.clone(), usage, valid_from, status, two_cid, der);
613
614        let vs_r = vs_b
615            .repl_merge_valueset(&vs_a, &one_cid)
616            .expect("Failed to merge");
617
618        let key_internal_map = vs_r.as_key_internal_map().expect("Unable to access map");
619
620        eprintln!("{:?}", key_internal_map);
621
622        assert_eq!(vs_r.len(), 1);
623
624        let key_internal = key_internal_map.get(&kid).expect("Unable to access key");
625
626        assert_eq!(key_internal.status, KeyStatus::Revoked);
627
628        // Assert the item was trimmed
629        assert!(!key_internal_map.contains_key(&kid_2));
630    }
631
632    #[test]
633    fn test_scim_key_internal() {
634        let kid = "test".to_string();
635        let usage = KeyUsage::JwsEs256;
636        let valid_from = 0;
637        let status = KeyStatus::Valid;
638        let status_cid = Cid::new_zero();
639        let der = Vec::with_capacity(0);
640
641        let vs: ValueSet =
642            ValueSetKeyInternal::new(kid.clone(), usage, valid_from, status, status_cid, der);
643
644        let data = r#"
645[
646  {
647    "keyId": "test",
648    "status": "valid",
649    "usage": "jws_es256",
650    "validFrom": "1970-01-01T00:00:00Z"
651  }
652]
653        "#;
654        crate::valueset::scim_json_reflexive(&vs, data);
655    }
656}