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::from(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.to_string();
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
264            .keys()
265            .map(KeyId::to_string)
266            // .map(hex::encode)
267            .collect()
268    }
269
270    fn syntax(&self) -> SyntaxType {
271        SyntaxType::KeyInternal
272    }
273
274    fn validate(&self, _schema_attr: &crate::schema::SchemaAttribute) -> bool {
275        // Validate that every key id is a valid iname.
276        self.map.keys().map(KeyId::as_str).all(|s| {
277            // We validate these two first to prevent injection attacks.
278            Value::validate_str_escapes(s)
279                && Value::validate_singleline(s)
280                && Value::validate_hexstr(s)
281        })
282    }
283
284    fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
285        Box::new(self.map.iter().map(|(kid, key_object)| {
286            format!(
287                "{}: {} {} {}",
288                kid, key_object.status, key_object.usage, key_object.valid_from
289            )
290        }))
291    }
292
293    fn to_scim_value(&self) -> Option<ScimResolveStatus> {
294        Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
295            self.map
296                .iter()
297                .map(|(kid, key_object)| {
298                    let odt: OffsetDateTime =
299                        OffsetDateTime::UNIX_EPOCH + Duration::from_secs(key_object.valid_from);
300
301                    ScimKeyInternal {
302                        key_id: kid.to_string(),
303                        status: key_object.status.to_string(),
304                        usage: key_object.usage.to_string(),
305                        valid_from: odt,
306                    }
307                })
308                .collect::<Vec<_>>(),
309        )))
310    }
311
312    fn to_db_valueset_v2(&self) -> DbValueSetV2 {
313        let keys = self.to_vec_dbvs();
314        DbValueSetV2::KeyInternal(keys)
315    }
316
317    fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = crate::value::PartialValue> + '_> {
318        Box::new(
319            self.map
320                .keys()
321                .map(KeyId::to_string)
322                .map(PartialValue::HexString),
323        )
324    }
325
326    fn to_value_iter(&self) -> Box<dyn Iterator<Item = crate::value::Value> + '_> {
327        debug_assert!(false);
328        Box::new(self.map.iter().map(
329            |(
330                id,
331                KeyInternalData {
332                    usage,
333                    status,
334                    status_cid,
335                    der,
336                    valid_from,
337                },
338            )| {
339                Value::KeyInternal {
340                    id: id.clone(),
341                    usage: *usage,
342                    status: *status,
343                    status_cid: status_cid.clone(),
344                    der: der.clone(),
345                    valid_from: *valid_from,
346                }
347            },
348        ))
349    }
350
351    fn equal(&self, other: &super::ValueSet) -> bool {
352        if let Some(other) = other.as_key_internal_map() {
353            &self.map == other
354        } else {
355            debug_assert!(false);
356            false
357        }
358    }
359
360    fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
361        let Some(b) = other.as_key_internal_map() else {
362            debug_assert!(false);
363            return Err(OperationError::InvalidValueState);
364        };
365
366        for (k_other, v_other) in b.iter() {
367            if let Some(v_self) = self.map.get_mut(k_other) {
368                // Revoked is always a greater status than retained or valid.
369                if v_other.status > v_self.status {
370                    *v_self = v_other.clone();
371                }
372            } else {
373                // Not present, just insert.
374                self.map.insert(k_other.clone(), v_other.clone());
375            }
376        }
377
378        Ok(())
379    }
380
381    fn as_key_internal_map(&self) -> Option<&BTreeMap<KeyId, KeyInternalData>> {
382        Some(&self.map)
383    }
384
385    fn repl_merge_valueset(&self, older: &ValueSet, trim_cid: &Cid) -> Option<ValueSet> {
386        let b = older.as_key_internal_map()?;
387
388        let mut map = self.map.clone();
389
390        for (k_other, v_other) in b.iter() {
391            if let Some(v_self) = map.get_mut(k_other) {
392                // Revoked is always a greater status than retained or valid.
393                if v_other.status > v_self.status {
394                    *v_self = v_other.clone();
395                }
396            } else {
397                // Not present, just insert.
398                map.insert(k_other.clone(), v_other.clone());
399            }
400        }
401
402        let mut vs = Box::new(ValueSetKeyInternal { map });
403
404        vs.trim(trim_cid);
405
406        Some(vs)
407    }
408}
409
410#[cfg(test)]
411mod tests {
412    use super::{KeyInternalData, ValueSetKeyInternal};
413    use crate::prelude::*;
414    use crate::server::keys::KeyId;
415    use crate::value::*;
416    use crypto_glue::traits::Zeroizing;
417
418    #[test]
419    fn test_valueset_key_internal_purge_trim() {
420        let kid = KeyId::from("test".to_string());
421        let usage = KeyUsage::JwsEs256;
422        let valid_from = 0;
423        let status = KeyStatus::Valid;
424        let status_cid = Cid::new_zero();
425        let der = Zeroizing::new(Vec::with_capacity(0));
426
427        let mut vs_a: ValueSet =
428            ValueSetKeyInternal::new(kid.clone(), usage, valid_from, status, status_cid, der);
429
430        let one_cid = Cid::new_count(1);
431
432        // Simulate session revocation.
433        vs_a.purge(&one_cid);
434
435        assert_eq!(vs_a.len(), 1);
436
437        let key_internal = vs_a
438            .as_key_internal_map()
439            .and_then(|map| map.get(&kid))
440            .expect("Unable to locate session");
441
442        assert_eq!(key_internal.status, KeyStatus::Revoked);
443        assert_eq!(key_internal.status_cid, one_cid);
444
445        // Now trim
446        let two_cid = Cid::new_count(2);
447
448        vs_a.trim(&two_cid);
449
450        assert!(vs_a.is_empty());
451    }
452
453    #[test]
454    fn test_valueset_key_internal_merge_left() {
455        let kid = KeyId::from("test".to_string());
456        let usage = KeyUsage::JwsEs256;
457        let valid_from = 0;
458        let status = KeyStatus::Valid;
459        let status_cid = Cid::new_zero();
460        let der = Zeroizing::new(Vec::with_capacity(0));
461
462        let mut vs_a: ValueSet = ValueSetKeyInternal::new(
463            kid.clone(),
464            usage,
465            valid_from,
466            status,
467            status_cid.clone(),
468            der.clone(),
469        );
470
471        let status = KeyStatus::Revoked;
472
473        let vs_b: ValueSet =
474            ValueSetKeyInternal::new(kid.clone(), usage, valid_from, status, status_cid, der);
475
476        vs_a.merge(&vs_b).expect("Failed to merge");
477
478        assert_eq!(vs_a.len(), 1);
479        let key_internal = vs_a
480            .as_key_internal_map()
481            .and_then(|map| map.get(&kid))
482            .expect("Unable to locate session");
483
484        assert_eq!(key_internal.status, KeyStatus::Revoked);
485    }
486
487    #[test]
488    fn test_valueset_key_internal_merge_right() {
489        let kid = KeyId::from("test".to_string());
490        let usage = KeyUsage::JwsEs256;
491        let valid_from = 0;
492        let status = KeyStatus::Valid;
493        let status_cid = Cid::new_zero();
494        let der = Zeroizing::new(Vec::with_capacity(0));
495
496        let vs_a: ValueSet = ValueSetKeyInternal::new(
497            kid.clone(),
498            usage,
499            valid_from,
500            status,
501            status_cid.clone(),
502            der.clone(),
503        );
504
505        let status = KeyStatus::Revoked;
506
507        let mut vs_b: ValueSet =
508            ValueSetKeyInternal::new(kid.clone(), usage, valid_from, status, status_cid, der);
509
510        vs_b.merge(&vs_a).expect("Failed to merge");
511
512        assert_eq!(vs_b.len(), 1);
513
514        let key_internal = vs_b
515            .as_key_internal_map()
516            .and_then(|map| map.get(&kid))
517            .expect("Unable to locate session");
518
519        assert_eq!(key_internal.status, KeyStatus::Revoked);
520    }
521
522    #[test]
523    fn test_valueset_key_internal_repl_merge_left() {
524        let kid = KeyId::from("test".to_string());
525        let usage = KeyUsage::JwsEs256;
526        let valid_from = 0;
527        let status = KeyStatus::Valid;
528        let zero_cid = Cid::new_zero();
529        let one_cid = Cid::new_count(1);
530        let two_cid = Cid::new_count(2);
531        let der = Zeroizing::new(Vec::with_capacity(0));
532
533        let kid_2 = KeyId::from("key_2".to_string());
534
535        let vs_a: ValueSet = ValueSetKeyInternal::from_key_iter(
536            [
537                (
538                    kid.clone(),
539                    KeyInternalData {
540                        usage,
541                        valid_from,
542                        status,
543                        status_cid: two_cid.clone(),
544                        der: der.clone(),
545                    },
546                ),
547                (
548                    kid_2.clone(),
549                    KeyInternalData {
550                        usage,
551                        valid_from,
552                        status: KeyStatus::Revoked,
553                        status_cid: zero_cid.clone(),
554                        der: der.clone(),
555                    },
556                ),
557            ]
558            .into_iter(),
559        )
560        .expect("Failed to build valueset");
561
562        let status = KeyStatus::Revoked;
563
564        let vs_b: ValueSet =
565            ValueSetKeyInternal::new(kid.clone(), usage, valid_from, status, two_cid, der);
566
567        let vs_r = vs_a
568            .repl_merge_valueset(&vs_b, &one_cid)
569            .expect("Failed to merge");
570
571        let key_internal_map = vs_r.as_key_internal_map().expect("Unable to access map");
572
573        eprintln!("{key_internal_map:?}");
574
575        assert_eq!(vs_r.len(), 1);
576
577        let key_internal = key_internal_map.get(&kid).expect("Unable to access key");
578
579        assert_eq!(key_internal.status, KeyStatus::Revoked);
580
581        // Assert the item was trimmed
582        assert!(!key_internal_map.contains_key(&kid_2));
583    }
584
585    #[test]
586    fn test_valueset_key_internal_repl_merge_right() {
587        let kid = KeyId::from("test".to_string());
588        let usage = KeyUsage::JwsEs256;
589        let valid_from = 0;
590        let status = KeyStatus::Valid;
591        let zero_cid = Cid::new_zero();
592        let one_cid = Cid::new_count(1);
593        let two_cid = Cid::new_count(2);
594        let der = Zeroizing::new(Vec::with_capacity(0));
595
596        let kid_2 = KeyId::from("key_2".to_string());
597
598        let vs_a: ValueSet = ValueSetKeyInternal::from_key_iter(
599            [
600                (
601                    kid.clone(),
602                    KeyInternalData {
603                        usage,
604                        valid_from,
605                        status,
606                        status_cid: two_cid.clone(),
607                        der: der.clone(),
608                    },
609                ),
610                (
611                    kid_2.clone(),
612                    KeyInternalData {
613                        usage,
614                        valid_from,
615                        status: KeyStatus::Revoked,
616                        status_cid: zero_cid.clone(),
617                        der: der.clone(),
618                    },
619                ),
620            ]
621            .into_iter(),
622        )
623        .expect("Failed to build valueset");
624
625        let status = KeyStatus::Revoked;
626
627        let vs_b: ValueSet =
628            ValueSetKeyInternal::new(kid.clone(), usage, valid_from, status, two_cid, der);
629
630        let vs_r = vs_b
631            .repl_merge_valueset(&vs_a, &one_cid)
632            .expect("Failed to merge");
633
634        let key_internal_map = vs_r.as_key_internal_map().expect("Unable to access map");
635
636        eprintln!("{key_internal_map:?}");
637
638        assert_eq!(vs_r.len(), 1);
639
640        let key_internal = key_internal_map.get(&kid).expect("Unable to access key");
641
642        assert_eq!(key_internal.status, KeyStatus::Revoked);
643
644        // Assert the item was trimmed
645        assert!(!key_internal_map.contains_key(&kid_2));
646    }
647
648    #[test]
649    fn test_scim_key_internal() {
650        let kid = KeyId::from("test".to_string());
651        let usage = KeyUsage::JwsEs256;
652        let valid_from = 0;
653        let status = KeyStatus::Valid;
654        let status_cid = Cid::new_zero();
655        let der = Zeroizing::new(Vec::with_capacity(0));
656
657        let vs: ValueSet =
658            ValueSetKeyInternal::new(kid.clone(), usage, valid_from, status, status_cid, der);
659
660        let data = r#"
661[
662  {
663    "keyId": "test",
664    "status": "valid",
665    "usage": "jws_es256",
666    "validFrom": "1970-01-01T00:00:00Z"
667  }
668]
669        "#;
670        crate::valueset::scim_json_reflexive(&vs, data);
671    }
672}