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