kanidmd_lib/valueset/
uuid.rs

1use crate::prelude::*;
2use crate::schema::SchemaAttribute;
3use crate::valueset::{
4    uuid_to_proto_string, DbValueSetV2, ScimResolveStatus, ScimValueIntermediate, ValueSet,
5    ValueSetIntermediate, ValueSetResolveStatus, ValueSetScimPut,
6};
7use kanidm_proto::scim_v1::JsonValue;
8use smolset::SmolSet;
9use std::collections::BTreeSet;
10
11#[derive(Debug, Clone)]
12pub struct ValueSetUuid {
13    set: SmolSet<[Uuid; 1]>,
14}
15
16impl ValueSetUuid {
17    pub fn new(u: Uuid) -> Box<Self> {
18        let mut set = SmolSet::new();
19        set.insert(u);
20        Box::new(ValueSetUuid { set })
21    }
22
23    pub fn push(&mut self, u: Uuid) -> bool {
24        self.set.insert(u)
25    }
26
27    pub fn from_dbvs2(data: Vec<Uuid>) -> Result<ValueSet, OperationError> {
28        let set = data.into_iter().collect();
29        Ok(Box::new(ValueSetUuid { set }))
30    }
31
32    // We need to allow this, because rust doesn't allow us to impl FromIterator on foreign
33    // types, and uuid is foreign.
34    #[allow(clippy::should_implement_trait)]
35    pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
36    where
37        T: IntoIterator<Item = Uuid>,
38    {
39        let set = iter.into_iter().collect();
40        Some(Box::new(ValueSetUuid { set }))
41    }
42}
43
44impl ValueSetScimPut for ValueSetUuid {
45    fn from_scim_json_put(value: JsonValue) -> Result<ValueSetResolveStatus, OperationError> {
46        let uuid: Uuid = serde_json::from_value(value).map_err(|err| {
47            warn!(?err, "Invalid SCIM Uuid syntax");
48            OperationError::SC0004UuidSyntaxInvalid
49        })?;
50
51        let mut set = SmolSet::new();
52        set.insert(uuid);
53        Ok(ValueSetResolveStatus::Resolved(Box::new(ValueSetUuid {
54            set,
55        })))
56    }
57}
58
59impl ValueSetT for ValueSetUuid {
60    fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
61        match value {
62            Value::Uuid(u) => Ok(self.set.insert(u)),
63            _ => {
64                debug_assert!(false);
65                Err(OperationError::InvalidValueState)
66            }
67        }
68    }
69
70    fn clear(&mut self) {
71        self.set.clear();
72    }
73
74    fn remove(&mut self, pv: &PartialValue, _cid: &Cid) -> bool {
75        match pv {
76            PartialValue::Uuid(u) => self.set.remove(u),
77            _ => {
78                debug_assert!(false);
79                true
80            }
81        }
82    }
83
84    fn contains(&self, pv: &PartialValue) -> bool {
85        match pv {
86            PartialValue::Uuid(u) => self.set.contains(u),
87            _ => false,
88        }
89    }
90
91    fn substring(&self, _pv: &PartialValue) -> bool {
92        false
93    }
94
95    fn startswith(&self, _pv: &PartialValue) -> bool {
96        false
97    }
98
99    fn endswith(&self, _pv: &PartialValue) -> bool {
100        false
101    }
102
103    fn lessthan(&self, pv: &PartialValue) -> bool {
104        match pv {
105            PartialValue::Uuid(u) => self.set.iter().any(|v| v < u),
106            _ => false,
107        }
108    }
109
110    fn len(&self) -> usize {
111        self.set.len()
112    }
113
114    fn generate_idx_eq_keys(&self) -> Vec<String> {
115        self.set
116            .iter()
117            .map(|u| u.as_hyphenated().to_string())
118            .collect()
119    }
120
121    fn syntax(&self) -> SyntaxType {
122        SyntaxType::Uuid
123    }
124
125    fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
126        true
127    }
128
129    fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
130        Box::new(self.set.iter().copied().map(uuid_to_proto_string))
131    }
132
133    fn to_scim_value(&self) -> Option<ScimResolveStatus> {
134        self.set
135            .iter()
136            .next()
137            .copied()
138            .map(ScimValueKanidm::Uuid)
139            .map(ScimResolveStatus::Resolved)
140    }
141
142    fn to_db_valueset_v2(&self) -> DbValueSetV2 {
143        DbValueSetV2::Uuid(self.set.iter().cloned().collect())
144    }
145
146    fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
147        Box::new(self.set.iter().copied().map(PartialValue::Uuid))
148    }
149
150    fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
151        Box::new(self.set.iter().copied().map(Value::Uuid))
152    }
153
154    fn equal(&self, other: &ValueSet) -> bool {
155        if let Some(other) = other.as_uuid_set() {
156            &self.set == other
157        } else {
158            debug_assert!(false);
159            false
160        }
161    }
162
163    fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
164        if let Some(b) = other.as_uuid_set() {
165            mergesets!(self.set, b)
166        } else {
167            debug_assert!(false);
168            Err(OperationError::InvalidValueState)
169        }
170    }
171
172    fn to_uuid_single(&self) -> Option<Uuid> {
173        if self.set.len() == 1 {
174            self.set.iter().copied().take(1).next()
175        } else {
176            None
177        }
178    }
179
180    fn as_uuid_set(&self) -> Option<&SmolSet<[Uuid; 1]>> {
181        Some(&self.set)
182    }
183
184    /*
185    fn as_uuid_iter(&self) -> Option<Box<dyn Iterator<Item = Uuid> + '_>> {
186        Some(Box::new(self.set.iter().copied()))
187    }
188    */
189}
190
191#[derive(Debug, Clone)]
192pub struct ValueSetRefer {
193    set: BTreeSet<Uuid>,
194}
195
196impl ValueSetRefer {
197    pub fn new(u: Uuid) -> Box<Self> {
198        let mut set = BTreeSet::new();
199        set.insert(u);
200        Box::new(ValueSetRefer { set })
201    }
202
203    pub fn push(&mut self, u: Uuid) -> bool {
204        self.set.insert(u)
205    }
206
207    pub fn from_dbvs2(data: Vec<Uuid>) -> Result<ValueSet, OperationError> {
208        let set = data.into_iter().collect();
209        Ok(Box::new(ValueSetRefer { set }))
210    }
211
212    pub fn from_repl_v1(data: &[Uuid]) -> Result<ValueSet, OperationError> {
213        let set = data.iter().copied().collect();
214        Ok(Box::new(ValueSetRefer { set }))
215    }
216
217    // We need to allow this, because rust doesn't allow us to impl FromIterator on foreign
218    // types, and uuid is foreign.
219    #[allow(clippy::should_implement_trait)]
220    pub fn from_iter<T>(iter: T) -> Option<Box<Self>>
221    where
222        T: IntoIterator<Item = Uuid>,
223    {
224        let set: BTreeSet<_> = iter.into_iter().collect();
225        if set.is_empty() {
226            None
227        } else {
228            Some(Box::new(ValueSetRefer { set }))
229        }
230    }
231
232    pub(crate) fn from_set(set: BTreeSet<Uuid>) -> ValueSet {
233        Box::new(ValueSetRefer { set })
234    }
235}
236
237impl ValueSetScimPut for ValueSetRefer {
238    fn from_scim_json_put(value: JsonValue) -> Result<ValueSetResolveStatus, OperationError> {
239        use kanidm_proto::scim_v1::client::{ScimReference, ScimReferences};
240
241        let scim_refs: ScimReferences = serde_json::from_value(value).map_err(|err| {
242            warn!(?err, "Invalid SCIM reference set syntax");
243            OperationError::SC0002ReferenceSyntaxInvalid
244        })?;
245
246        let mut resolved = BTreeSet::default();
247        let mut unresolved = Vec::with_capacity(scim_refs.len());
248
249        for scim_ref in scim_refs.into_iter() {
250            match scim_ref {
251                ScimReference {
252                    uuid: None,
253                    value: None,
254                } => {
255                    warn!("Invalid SCIM reference set syntax, uuid and value are both unset.");
256                    return Err(OperationError::SC0002ReferenceSyntaxInvalid);
257                }
258                ScimReference {
259                    uuid: Some(uuid), ..
260                } => {
261                    resolved.insert(uuid);
262                }
263                ScimReference {
264                    value: Some(val), ..
265                } => {
266                    unresolved.push(val);
267                }
268            }
269        }
270
271        // We may not actually need to resolve anything, but to make tests easier we
272        // always return that we need resolution.
273        Ok(ValueSetResolveStatus::NeedsResolution(
274            ValueSetIntermediate::References {
275                resolved,
276                unresolved,
277            },
278        ))
279    }
280}
281
282impl ValueSetT for ValueSetRefer {
283    fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
284        match value {
285            Value::Refer(u) => Ok(self.set.insert(u)),
286            _ => {
287                debug_assert!(false);
288                Err(OperationError::InvalidValueState)
289            }
290        }
291    }
292
293    fn clear(&mut self) {
294        self.set.clear();
295    }
296
297    fn remove(&mut self, pv: &PartialValue, _cid: &Cid) -> bool {
298        match pv {
299            PartialValue::Refer(u) => self.set.remove(u),
300            _ => {
301                debug_assert!(false);
302                true
303            }
304        }
305    }
306
307    fn contains(&self, pv: &PartialValue) -> bool {
308        match pv {
309            PartialValue::Refer(u) => self.set.contains(u),
310            _ => false,
311        }
312    }
313
314    fn substring(&self, _pv: &PartialValue) -> bool {
315        false
316    }
317
318    fn startswith(&self, _pv: &PartialValue) -> bool {
319        false
320    }
321
322    fn endswith(&self, _pv: &PartialValue) -> bool {
323        false
324    }
325
326    fn lessthan(&self, pv: &PartialValue) -> bool {
327        match pv {
328            PartialValue::Refer(u) => self.set.iter().any(|v| v < u),
329            _ => false,
330        }
331    }
332
333    fn len(&self) -> usize {
334        self.set.len()
335    }
336
337    fn generate_idx_eq_keys(&self) -> Vec<String> {
338        self.set
339            .iter()
340            .map(|u| u.as_hyphenated().to_string())
341            .collect()
342    }
343
344    fn syntax(&self) -> SyntaxType {
345        SyntaxType::ReferenceUuid
346    }
347
348    fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
349        true
350    }
351
352    fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
353        Box::new(self.set.iter().copied().map(uuid_to_proto_string))
354    }
355
356    fn to_scim_value(&self) -> Option<ScimResolveStatus> {
357        let uuids = self.set.iter().copied().collect::<Vec<_>>();
358        Some(ScimResolveStatus::NeedsResolution(
359            ScimValueIntermediate::References(uuids),
360        ))
361    }
362
363    fn to_db_valueset_v2(&self) -> DbValueSetV2 {
364        DbValueSetV2::Reference(self.set.iter().cloned().collect())
365    }
366
367    fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
368        Box::new(self.set.iter().copied().map(PartialValue::Refer))
369    }
370
371    fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
372        Box::new(self.set.iter().copied().map(Value::Refer))
373    }
374
375    fn equal(&self, other: &ValueSet) -> bool {
376        if let Some(other) = other.as_refer_set() {
377            &self.set == other
378        } else {
379            debug_assert!(false);
380            false
381        }
382    }
383
384    fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
385        if let Some(b) = other.as_refer_set() {
386            mergesets!(self.set, b)
387        } else {
388            debug_assert!(false);
389            Err(OperationError::InvalidValueState)
390        }
391    }
392
393    fn to_refer_single(&self) -> Option<Uuid> {
394        if self.set.len() == 1 {
395            self.set.iter().copied().take(1).next()
396        } else {
397            None
398        }
399    }
400
401    fn as_refer_set(&self) -> Option<&BTreeSet<Uuid>> {
402        Some(&self.set)
403    }
404
405    fn as_refer_set_mut(&mut self) -> Option<&mut BTreeSet<Uuid>> {
406        Some(&mut self.set)
407    }
408
409    fn as_ref_uuid_iter(&self) -> Option<Box<dyn Iterator<Item = Uuid> + '_>> {
410        Some(Box::new(self.set.iter().copied()))
411    }
412}
413
414#[cfg(test)]
415mod tests {
416    use super::{ValueSetRefer, ValueSetUuid};
417    use crate::prelude::*;
418
419    #[test]
420    fn test_scim_uuid() {
421        let vs: ValueSet = ValueSetUuid::new(uuid::uuid!("4d21d04a-dc0e-42eb-b850-34dd180b107f"));
422
423        let data = r#""4d21d04a-dc0e-42eb-b850-34dd180b107f""#;
424
425        crate::valueset::scim_json_reflexive(&vs, data);
426
427        // Test that we can parse json values into a valueset.
428        crate::valueset::scim_json_put_reflexive::<ValueSetUuid>(&vs, &[])
429    }
430
431    #[qs_test]
432    async fn test_scim_refer(server: &QueryServer) {
433        let mut write_txn = server.write(duration_from_epoch_now()).await.unwrap();
434
435        let t_uuid = uuid::uuid!("4d21d04a-dc0e-42eb-b850-34dd180b107f");
436        assert!(write_txn
437            .internal_create(vec![entry_init!(
438                (Attribute::Class, EntryClass::Object.to_value()),
439                (Attribute::Class, EntryClass::Account.to_value()),
440                (Attribute::Class, EntryClass::Person.to_value()),
441                (Attribute::Name, Value::new_iname("testperson1")),
442                (Attribute::Uuid, Value::Uuid(t_uuid)),
443                (Attribute::Description, Value::new_utf8s("testperson1")),
444                (Attribute::DisplayName, Value::new_utf8s("testperson1"))
445            ),])
446            .is_ok());
447
448        let vs: ValueSet = ValueSetRefer::new(t_uuid);
449
450        let data = r#"[{"uuid": "4d21d04a-dc0e-42eb-b850-34dd180b107f", "value": "testperson1@example.com"}]"#;
451
452        crate::valueset::scim_json_reflexive_unresolved(&mut write_txn, &vs, data);
453
454        // Test that we can parse json values into a valueset.
455        crate::valueset::scim_json_put_reflexive_unresolved::<ValueSetRefer>(
456            &mut write_txn,
457            &vs,
458            &[],
459        );
460
461        assert!(write_txn.commit().is_ok());
462    }
463}