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        // May be a single reference, lets wrap it in an array to proceed.
242        let value = if !value.is_array() && value.is_object() {
243            JsonValue::Array(vec![value])
244        } else {
245            value
246        };
247
248        let scim_refs: ScimReferences = serde_json::from_value(value).map_err(|err| {
249            warn!(?err, "Invalid SCIM reference set syntax");
250            OperationError::SC0002ReferenceSyntaxInvalid
251        })?;
252
253        let mut resolved = BTreeSet::default();
254        let mut unresolved = Vec::with_capacity(scim_refs.len());
255
256        for scim_ref in scim_refs.into_iter() {
257            match scim_ref {
258                ScimReference {
259                    uuid: None,
260                    value: None,
261                } => {
262                    warn!("Invalid SCIM reference set syntax, uuid and value are both unset.");
263                    return Err(OperationError::SC0002ReferenceSyntaxInvalid);
264                }
265                ScimReference {
266                    uuid: Some(uuid), ..
267                } => {
268                    resolved.insert(uuid);
269                }
270                ScimReference {
271                    value: Some(val), ..
272                } => {
273                    unresolved.push(val);
274                }
275            }
276        }
277
278        // We may not actually need to resolve anything, but to make tests easier we
279        // always return that we need resolution.
280        Ok(ValueSetResolveStatus::NeedsResolution(
281            ValueSetIntermediate::References {
282                resolved,
283                unresolved,
284            },
285        ))
286    }
287}
288
289impl ValueSetT for ValueSetRefer {
290    fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
291        match value {
292            Value::Refer(u) => Ok(self.set.insert(u)),
293            _ => {
294                debug_assert!(false);
295                Err(OperationError::InvalidValueState)
296            }
297        }
298    }
299
300    fn clear(&mut self) {
301        self.set.clear();
302    }
303
304    fn remove(&mut self, pv: &PartialValue, _cid: &Cid) -> bool {
305        match pv {
306            PartialValue::Refer(u) => self.set.remove(u),
307            _ => {
308                debug_assert!(false);
309                true
310            }
311        }
312    }
313
314    fn contains(&self, pv: &PartialValue) -> bool {
315        match pv {
316            PartialValue::Refer(u) => self.set.contains(u),
317            _ => false,
318        }
319    }
320
321    fn substring(&self, _pv: &PartialValue) -> bool {
322        false
323    }
324
325    fn startswith(&self, _pv: &PartialValue) -> bool {
326        false
327    }
328
329    fn endswith(&self, _pv: &PartialValue) -> bool {
330        false
331    }
332
333    fn lessthan(&self, pv: &PartialValue) -> bool {
334        match pv {
335            PartialValue::Refer(u) => self.set.iter().any(|v| v < u),
336            _ => false,
337        }
338    }
339
340    fn len(&self) -> usize {
341        self.set.len()
342    }
343
344    fn generate_idx_eq_keys(&self) -> Vec<String> {
345        self.set
346            .iter()
347            .map(|u| u.as_hyphenated().to_string())
348            .collect()
349    }
350
351    fn syntax(&self) -> SyntaxType {
352        SyntaxType::ReferenceUuid
353    }
354
355    fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
356        true
357    }
358
359    fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
360        Box::new(self.set.iter().copied().map(uuid_to_proto_string))
361    }
362
363    fn to_scim_value(&self) -> Option<ScimResolveStatus> {
364        let uuids = self.set.iter().copied().collect::<Vec<_>>();
365        Some(ScimResolveStatus::NeedsResolution(
366            ScimValueIntermediate::References(uuids),
367        ))
368    }
369
370    fn to_db_valueset_v2(&self) -> DbValueSetV2 {
371        DbValueSetV2::Reference(self.set.iter().cloned().collect())
372    }
373
374    fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
375        Box::new(self.set.iter().copied().map(PartialValue::Refer))
376    }
377
378    fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
379        Box::new(self.set.iter().copied().map(Value::Refer))
380    }
381
382    fn equal(&self, other: &ValueSet) -> bool {
383        if let Some(other) = other.as_refer_set() {
384            &self.set == other
385        } else {
386            debug_assert!(false);
387            false
388        }
389    }
390
391    fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
392        if let Some(b) = other.as_refer_set() {
393            mergesets!(self.set, b)
394        } else {
395            debug_assert!(false);
396            Err(OperationError::InvalidValueState)
397        }
398    }
399
400    fn to_refer_single(&self) -> Option<Uuid> {
401        if self.set.len() == 1 {
402            self.set.iter().copied().take(1).next()
403        } else {
404            None
405        }
406    }
407
408    fn as_refer_set(&self) -> Option<&BTreeSet<Uuid>> {
409        Some(&self.set)
410    }
411
412    fn as_refer_set_mut(&mut self) -> Option<&mut BTreeSet<Uuid>> {
413        Some(&mut self.set)
414    }
415
416    fn as_ref_uuid_iter(&self) -> Option<Box<dyn Iterator<Item = Uuid> + '_>> {
417        Some(Box::new(self.set.iter().copied()))
418    }
419}
420
421#[cfg(test)]
422mod tests {
423    use super::{ValueSetRefer, ValueSetUuid};
424    use crate::prelude::*;
425
426    #[test]
427    fn test_scim_uuid() {
428        let vs: ValueSet = ValueSetUuid::new(uuid::uuid!("4d21d04a-dc0e-42eb-b850-34dd180b107f"));
429
430        let data = r#""4d21d04a-dc0e-42eb-b850-34dd180b107f""#;
431
432        crate::valueset::scim_json_reflexive(&vs, data);
433
434        // Test that we can parse json values into a valueset.
435        crate::valueset::scim_json_put_reflexive::<ValueSetUuid>(&vs, &[])
436    }
437
438    #[qs_test]
439    async fn test_scim_refer(server: &QueryServer) {
440        let mut write_txn = server.write(duration_from_epoch_now()).await.unwrap();
441
442        let t_uuid = uuid::uuid!("4d21d04a-dc0e-42eb-b850-34dd180b107f");
443        assert!(write_txn
444            .internal_create(vec![entry_init!(
445                (Attribute::Class, EntryClass::Object.to_value()),
446                (Attribute::Class, EntryClass::Account.to_value()),
447                (Attribute::Class, EntryClass::Person.to_value()),
448                (Attribute::Name, Value::new_iname("testperson1")),
449                (Attribute::Uuid, Value::Uuid(t_uuid)),
450                (Attribute::Description, Value::new_utf8s("testperson1")),
451                (Attribute::DisplayName, Value::new_utf8s("testperson1"))
452            ),])
453            .is_ok());
454
455        let vs: ValueSet = ValueSetRefer::new(t_uuid);
456
457        let data = r#"[{"uuid": "4d21d04a-dc0e-42eb-b850-34dd180b107f", "value": "testperson1@example.com"}]"#;
458
459        crate::valueset::scim_json_reflexive_unresolved(&mut write_txn, &vs, data);
460
461        // Test that we can parse json values into a valueset.
462        crate::valueset::scim_json_put_reflexive_unresolved::<ValueSetRefer>(
463            &mut write_txn,
464            &vs,
465            &[],
466        );
467
468        assert!(write_txn.commit().is_ok());
469    }
470}