kanidmd_lib/valueset/
certificate.rs

1use crate::be::dbvalue::DbValueCertificate;
2use crate::prelude::*;
3use crate::schema::SchemaAttribute;
4use crate::valueset::ScimResolveStatus;
5use crate::valueset::{DbValueSetV2, ValueSet, ValueSetResolveStatus, ValueSetScimPut};
6use kanidm_proto::scim_v1::client::ScimCertificate as ClientScimCertificate;
7use kanidm_proto::scim_v1::server::ScimCertificate;
8use kanidm_proto::scim_v1::JsonValue;
9use std::collections::BTreeMap;
10
11use kanidm_lib_crypto::{
12    x509_cert::{
13        der::{Decode, Encode, EncodePem},
14        pem::LineEnding,
15        x509_public_key_s256, Certificate,
16    },
17    Sha256Digest,
18};
19
20#[derive(Debug, Clone)]
21pub struct ValueSetCertificate {
22    map: BTreeMap<Sha256Digest, Box<Certificate>>,
23}
24
25impl ValueSetCertificate {
26    pub fn new(certificate: Box<Certificate>) -> Result<Box<Self>, OperationError> {
27        let mut map = BTreeMap::new();
28
29        let pk_s256 = x509_public_key_s256(&certificate).ok_or_else(|| {
30            error!("Unable to digest public key");
31            OperationError::VS0002CertificatePublicKeyDigest
32        })?;
33        map.insert(pk_s256, certificate);
34
35        Ok(Box::new(ValueSetCertificate { map }))
36    }
37
38    pub fn from_dbvs2(data: Vec<DbValueCertificate>) -> Result<ValueSet, OperationError> {
39        Self::from_dbv_iter(data.into_iter())
40    }
41
42    fn from_dbv_iter(
43        certs: impl Iterator<Item = DbValueCertificate>,
44    ) -> Result<ValueSet, OperationError> {
45        let mut map = BTreeMap::new();
46
47        for db_cert in certs {
48            match db_cert {
49                DbValueCertificate::V1 { certificate_der } => {
50                    // Parse the DER
51                    let certificate = Certificate::from_der(&certificate_der)
52                        .map(Box::new)
53                        .map_err(|x509_err| {
54                            error!(?x509_err, "Unable to restore certificate from DER");
55                            OperationError::VS0003CertificateDerDecode
56                        })?;
57
58                    // sha256 the public key
59                    let pk_s256 = x509_public_key_s256(&certificate).ok_or_else(|| {
60                        error!("Unable to digest public key");
61                        OperationError::VS0004CertificatePublicKeyDigest
62                    })?;
63
64                    map.insert(pk_s256, certificate);
65                }
66            }
67        }
68
69        Ok(Box::new(ValueSetCertificate { map }))
70    }
71
72    fn to_vec_dbvs(&self) -> Vec<DbValueCertificate> {
73        self.map
74            .iter()
75            .filter_map(|(pk_s256, cert)| {
76                cert.to_der()
77                    .map_err(|der_err| {
78                        error!(
79                            ?pk_s256,
80                            ?der_err,
81                            "Failed to serialise certificate to der. This value will be dropped!"
82                        );
83                    })
84                    .ok()
85            })
86            .map(|certificate_der| DbValueCertificate::V1 { certificate_der })
87            .collect()
88    }
89
90    #[allow(clippy::should_implement_trait)]
91    pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
92    where
93        T: IntoIterator<Item = Box<Certificate>>,
94    {
95        let mut map = BTreeMap::new();
96
97        for certificate in iter {
98            let pk_s256 = x509_public_key_s256(&certificate)?;
99            map.insert(pk_s256, certificate);
100        }
101
102        Some(Box::new(ValueSetCertificate { map }))
103    }
104}
105
106impl ValueSetScimPut for ValueSetCertificate {
107    fn from_scim_json_put(value: JsonValue) -> Result<ValueSetResolveStatus, OperationError> {
108        let der_values: Vec<ClientScimCertificate> =
109            serde_json::from_value(value).map_err(|err| {
110                error!(?err, "SCIM Certificate syntax invalid");
111                OperationError::SC0012CertificateSyntaxInvalid
112            })?;
113
114        // For each one, check it's a real der certificate.
115        let mut map = BTreeMap::new();
116
117        for ClientScimCertificate { der } in der_values {
118            // Parse the DER
119            let certificate = Certificate::from_der(&der)
120                .map(Box::new)
121                .map_err(|x509_err| {
122                    error!(?x509_err, "Unable to restore certificate from DER");
123                    OperationError::SC0013CertificateInvalidDer
124                })?;
125
126            // sha256 the public key
127            let pk_s256 = x509_public_key_s256(&certificate).ok_or_else(|| {
128                error!("Unable to digest public key");
129                OperationError::SC0014CertificateInvalidDigest
130            })?;
131
132            map.insert(pk_s256, certificate);
133        }
134
135        Ok(ValueSetResolveStatus::Resolved(Box::new(
136            ValueSetCertificate { map },
137        )))
138    }
139}
140
141impl ValueSetT for ValueSetCertificate {
142    fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
143        match value {
144            Value::Certificate(certificate) => {
145                let pk_s256 = x509_public_key_s256(&certificate).ok_or_else(|| {
146                    error!("Unable to digest public key");
147                    OperationError::VS0005CertificatePublicKeyDigest
148                })?;
149
150                // bool -> true if the insert did not trigger a duplicate.
151                Ok(self.map.insert(pk_s256, certificate).is_none())
152            }
153            _ => {
154                debug_assert!(false);
155                Err(OperationError::InvalidValueState)
156            }
157        }
158    }
159
160    fn clear(&mut self) {
161        self.map.clear();
162    }
163
164    fn remove(&mut self, pv: &PartialValue, _cid: &Cid) -> bool {
165        match pv {
166            PartialValue::HexString(hs) => {
167                let mut buf = Sha256Digest::default();
168                if hex::decode_to_slice(hs, &mut buf).is_ok() {
169                    self.map.remove(&buf).is_some()
170                } else {
171                    false
172                }
173            }
174            _ => false,
175        }
176    }
177
178    fn contains(&self, pv: &PartialValue) -> bool {
179        match pv {
180            PartialValue::HexString(hs) => {
181                let mut buf = Sha256Digest::default();
182                if hex::decode_to_slice(hs, &mut buf).is_ok() {
183                    self.map.contains_key(&buf)
184                } else {
185                    false
186                }
187            }
188            _ => false,
189        }
190    }
191
192    fn substring(&self, _pv: &PartialValue) -> bool {
193        false
194    }
195
196    fn startswith(&self, _pv: &PartialValue) -> bool {
197        false
198    }
199
200    fn endswith(&self, _pv: &PartialValue) -> bool {
201        false
202    }
203
204    fn lessthan(&self, _pv: &PartialValue) -> bool {
205        false
206    }
207
208    fn len(&self) -> usize {
209        self.map.len()
210    }
211
212    fn generate_idx_eq_keys(&self) -> Vec<String> {
213        self.map.keys().map(hex::encode).collect()
214    }
215
216    fn syntax(&self) -> SyntaxType {
217        SyntaxType::Certificate
218    }
219
220    fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
221        true
222    }
223
224    fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
225        Box::new(self.map.iter().filter_map(|(pk_s256, cert)| {
226            cert.to_pem(LineEnding::LF)
227                .ok()
228                .map(|pem| format!("{}\n{}", hex::encode(pk_s256), pem))
229        }))
230    }
231
232    fn to_scim_value(&self) -> Option<ScimResolveStatus> {
233        let vals: Vec<ScimCertificate> = self
234            .map
235            .iter()
236            .filter_map(|(s256, cert)| {
237                cert.to_der()
238                    .map_err(|der_err| {
239                        error!(
240                            ?s256,
241                            ?der_err,
242                            "Failed to serialise certificate to der. This value will be dropped!"
243                        );
244                    })
245                    .ok()
246                    .map(|der| (s256, der))
247            })
248            .map(|(s256, cert_der)| ScimCertificate {
249                s256: s256.to_vec(),
250                der: cert_der,
251            })
252            .collect::<Vec<_>>();
253
254        if vals.is_empty() {
255            None
256        } else {
257            Some(ScimValueKanidm::from(vals).into())
258        }
259    }
260
261    fn to_db_valueset_v2(&self) -> DbValueSetV2 {
262        let data = self.to_vec_dbvs();
263        DbValueSetV2::Certificate(data)
264    }
265
266    fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
267        Box::new(
268            self.map
269                .keys()
270                .map(hex::encode)
271                .map(PartialValue::HexString),
272        )
273    }
274
275    fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
276        Box::new(self.map.values().cloned().map(Value::Certificate))
277    }
278
279    fn equal(&self, other: &ValueSet) -> bool {
280        if let Some(other) = other.as_certificate_set() {
281            &self.map == other
282        } else {
283            debug_assert!(false);
284            false
285        }
286    }
287
288    fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
289        if let Some(b) = other.as_certificate_set() {
290            mergemaps!(self.map, b)
291        } else {
292            debug_assert!(false);
293            Err(OperationError::InvalidValueState)
294        }
295    }
296
297    fn to_certificate_single(&self) -> Option<&Certificate> {
298        if self.map.len() == 1 {
299            self.map.values().take(1).map(|b| b.as_ref()).next()
300        } else {
301            None
302        }
303    }
304
305    fn as_certificate_set(&self) -> Option<&BTreeMap<Sha256Digest, Box<Certificate>>> {
306        Some(&self.map)
307    }
308}
309
310#[cfg(test)]
311mod tests {
312    use super::ValueSetCertificate;
313    use crate::prelude::{ScimValueKanidm, ValueSet};
314    use kanidm_lib_crypto::x509_cert::der::DecodePem;
315    use kanidm_lib_crypto::x509_cert::Certificate;
316
317    // Generated with:
318    //
319    // openssl ecparam -out ec_key.pem -name secp256r1 -genkey
320    // openssl req -new -key ec_key.pem -x509 -nodes -days 365 -out cert.pem
321    const PEM_DATA: &str = r#"-----BEGIN CERTIFICATE-----
322MIIB3zCCAYWgAwIBAgIUdJ6IWvI+8M6nwK7ykUK7/iBq7yQwCgYIKoZIzj0EAwIw
323RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu
324dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDA4MjEwNjQ2MzBaFw0yNTA4MjEw
325NjQ2MzBaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD
326VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjO
327PQMBBwNCAAS2Szn4NPmgxawC1+MRC41jqobemNkXkRZ9AgozK0zRDFc6k1IHUZ++
328wN0USpXDQYDnJfATqvlpKPebnHxTytt6o1MwUTAdBgNVHQ4EFgQU1oR1x2CnoPap
329JMKPCVVzqWf2ANYwHwYDVR0jBBgwFoAU1oR1x2CnoPapJMKPCVVzqWf2ANYwDwYD
330VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNIADBFAiBpy0o2CY97MIxeQ0HgG44Y
331raBy6edj7W0EIH+yQxkDEwIhAI0nVKaI6duHLAvtKW6CfEQFG6jKg7dyk37YYiRD
3322jS0
333-----END CERTIFICATE-----"#;
334
335    #[test]
336    fn test_scim_certificate() {
337        let cert = Certificate::from_pem(PEM_DATA).unwrap();
338
339        let vs: ValueSet = ValueSetCertificate::new(Box::new(cert)).unwrap();
340
341        let scim_value = vs.to_scim_value().unwrap().assume_resolved();
342
343        let cert = match scim_value {
344            ScimValueKanidm::ArrayCertificate(mut set) => set.pop().unwrap(),
345            _ => unreachable!(),
346        };
347
348        let expect_s256 =
349            hex::decode("8c98a09d0a50db92ccb6d05be846d9b9315015520d19a3e1739aeb8d84ebc28d")
350                .unwrap();
351
352        assert_eq!(cert.s256, expect_s256);
353
354        // Test that we can parse json values into a valueset.
355        crate::valueset::scim_json_put_reflexive::<ValueSetCertificate>(&vs, &[])
356    }
357}