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