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