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