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 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 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 let mut map = BTreeMap::new();
116
117 for ClientScimCertificate { der } in der_values {
118 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 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 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 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 crate::valueset::scim_json_put_reflexive::<ValueSetCertificate>(&vs, &[])
356 }
357}