kanidmd_lib/valueset/
json.rs

1use crate::prelude::*;
2use crate::schema::SchemaAttribute;
3use crate::valueset::ScimResolveStatus;
4use crate::valueset::{DbValueSetV2, ValueSet};
5use kanidm_proto::internal::Filter as ProtoFilter;
6use smolset::SmolSet;
7
8#[derive(Debug, Clone)]
9pub struct ValueSetJsonFilter {
10    set: SmolSet<[ProtoFilter; 1]>,
11}
12
13impl ValueSetJsonFilter {
14    pub fn new(b: ProtoFilter) -> Box<Self> {
15        let mut set = SmolSet::new();
16        set.insert(b);
17        Box::new(ValueSetJsonFilter { set })
18    }
19
20    pub fn push(&mut self, b: ProtoFilter) -> bool {
21        self.set.insert(b)
22    }
23
24    pub fn from_dbvs2(data: &[String]) -> Result<ValueSet, OperationError> {
25        let set = data
26            .iter()
27            .map(|s| serde_json::from_str(s).map_err(|_| OperationError::SerdeJsonError))
28            .collect::<Result<_, _>>()?;
29        Ok(Box::new(ValueSetJsonFilter { set }))
30    }
31
32    // We need to allow this, because rust doesn't allow us to impl FromIterator on foreign
33    // types, and protofilter 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 = ProtoFilter>,
38    {
39        let set = iter.into_iter().collect();
40        Some(Box::new(ValueSetJsonFilter { set }))
41    }
42}
43
44impl ValueSetT for ValueSetJsonFilter {
45    fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
46        match value {
47            Value::JsonFilt(u) => Ok(self.set.insert(u)),
48            _ => {
49                debug_assert!(false);
50                Err(OperationError::InvalidValueState)
51            }
52        }
53    }
54
55    fn clear(&mut self) {
56        self.set.clear();
57    }
58
59    fn remove(&mut self, pv: &PartialValue, _cid: &Cid) -> bool {
60        match pv {
61            PartialValue::JsonFilt(u) => self.set.remove(u),
62            _ => {
63                debug_assert!(false);
64                true
65            }
66        }
67    }
68
69    fn contains(&self, pv: &PartialValue) -> bool {
70        match pv {
71            PartialValue::JsonFilt(u) => self.set.contains(u),
72            _ => false,
73        }
74    }
75
76    fn substring(&self, _pv: &PartialValue) -> bool {
77        false
78    }
79
80    fn startswith(&self, _pv: &PartialValue) -> bool {
81        false
82    }
83
84    fn endswith(&self, _pv: &PartialValue) -> bool {
85        false
86    }
87
88    fn lessthan(&self, _pv: &PartialValue) -> bool {
89        false
90    }
91
92    fn len(&self) -> usize {
93        self.set.len()
94    }
95
96    fn generate_idx_eq_keys(&self) -> Vec<String> {
97        self.set
98            .iter()
99            .map(|s| {
100                #[allow(clippy::expect_used)]
101                serde_json::to_string(s).expect("A json filter value was corrupted during run-time")
102            })
103            .collect()
104    }
105
106    fn syntax(&self) -> SyntaxType {
107        SyntaxType::JsonFilter
108    }
109
110    fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
111        true
112    }
113
114    fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
115        Box::new(self.set.iter().filter_map(|i| {
116            serde_json::to_string(i)
117                .inspect_err(|err| {
118                    error!(?err, "A json filter value was corrupted during run-time")
119                })
120                .ok()
121        }))
122    }
123
124    fn to_scim_value(&self) -> Option<ScimResolveStatus> {
125        Some(ScimResolveStatus::Resolved(ScimValueKanidm::from(
126            self.set
127                .iter()
128                .filter_map(|s| {
129                    serde_json::to_string(s)
130                        .inspect_err(|err| {
131                            error!(?err, "A json filter value was corrupted during run-time")
132                        })
133                        .ok()
134                })
135                .collect::<Vec<_>>(),
136        )))
137    }
138
139    fn to_db_valueset_v2(&self) -> DbValueSetV2 {
140        DbValueSetV2::JsonFilter(
141            self.set
142                .iter()
143                .filter_map(|s| {
144                    serde_json::to_string(s)
145                        .inspect_err(|err| {
146                            error!(?err, "A json filter value was corrupted during run-time")
147                        })
148                        .ok()
149                })
150                .collect(),
151        )
152    }
153
154    fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
155        Box::new(self.set.iter().cloned().map(PartialValue::JsonFilt))
156    }
157
158    fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
159        Box::new(self.set.iter().cloned().map(Value::JsonFilt))
160    }
161
162    fn equal(&self, other: &ValueSet) -> bool {
163        if let Some(other) = other.as_json_filter_set() {
164            &self.set == other
165        } else {
166            debug_assert!(false);
167            false
168        }
169    }
170
171    fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
172        if let Some(b) = other.as_json_filter_set() {
173            mergesets!(self.set, b)
174        } else {
175            debug_assert!(false);
176            Err(OperationError::InvalidValueState)
177        }
178    }
179
180    fn to_json_filter_single(&self) -> Option<&ProtoFilter> {
181        if self.set.len() == 1 {
182            self.set.iter().take(1).next()
183        } else {
184            None
185        }
186    }
187
188    fn as_json_filter_set(&self) -> Option<&SmolSet<[ProtoFilter; 1]>> {
189        Some(&self.set)
190    }
191}
192
193#[derive(Debug, Clone)]
194pub struct ValueSetJson {
195    object: JsonValue,
196}
197
198impl ValueSetJson {
199    pub fn new(object: JsonValue) -> Box<Self> {
200        Box::new(ValueSetJson { object })
201    }
202}
203
204impl ValueSetT for ValueSetJson {
205    fn insert_checked(&mut self, value: Value) -> Result<bool, OperationError> {
206        match value {
207            Value::Json(object) => {
208                self.object = object;
209                Ok(true)
210            }
211            _ => {
212                debug_assert!(false);
213                Err(OperationError::InvalidValueState)
214            }
215        }
216    }
217
218    fn clear(&mut self) {
219        self.object = JsonValue::Null;
220    }
221
222    fn remove(&mut self, pv: &PartialValue, _cid: &Cid) -> bool {
223        match pv {
224            PartialValue::Json => {
225                self.object = JsonValue::Null;
226                true
227            }
228            _ => {
229                debug_assert!(false);
230                true
231            }
232        }
233    }
234
235    fn contains(&self, pv: &PartialValue) -> bool {
236        matches!(pv, PartialValue::Json)
237    }
238
239    fn substring(&self, _pv: &PartialValue) -> bool {
240        false
241    }
242
243    fn startswith(&self, _pv: &PartialValue) -> bool {
244        false
245    }
246
247    fn endswith(&self, _pv: &PartialValue) -> bool {
248        false
249    }
250
251    fn lessthan(&self, _pv: &PartialValue) -> bool {
252        false
253    }
254
255    fn len(&self) -> usize {
256        if self.object == JsonValue::Null {
257            0
258        } else {
259            1
260        }
261    }
262
263    fn generate_idx_eq_keys(&self) -> Vec<String> {
264        Vec::with_capacity(0)
265    }
266
267    fn syntax(&self) -> SyntaxType {
268        SyntaxType::Json
269    }
270
271    fn validate(&self, _schema_attr: &SchemaAttribute) -> bool {
272        true
273    }
274
275    fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
276        Box::new(
277            serde_json::to_string(&self.object)
278                .inspect_err(|err| error!(?err, "A json object was corrupted during run-time"))
279                .ok()
280                .into_iter(),
281        )
282    }
283
284    fn to_scim_value(&self) -> Option<ScimResolveStatus> {
285        serde_json::to_string(&self.object)
286            .inspect_err(|err| error!(?err, "A json object was corrupted during run-time"))
287            .ok()
288            .map(|value| ScimResolveStatus::Resolved(ScimValueKanidm::from(value)))
289    }
290
291    fn to_db_valueset_v2(&self) -> DbValueSetV2 {
292        DbValueSetV2::Json(self.object.clone())
293    }
294
295    fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = PartialValue> + '_> {
296        Box::new(std::iter::once(PartialValue::Json))
297    }
298
299    fn to_value_iter(&self) -> Box<dyn Iterator<Item = Value> + '_> {
300        Box::new(std::iter::once(Value::Json(self.object.clone())))
301    }
302
303    fn equal(&self, other: &ValueSet) -> bool {
304        if let Some(other) = other.as_json_object() {
305            &self.object == other
306        } else {
307            debug_assert!(false);
308            false
309        }
310    }
311
312    fn merge(&mut self, other: &ValueSet) -> Result<(), OperationError> {
313        if let Some(b) = other.as_json_object() {
314            self.object = b.clone();
315            Ok(())
316        } else {
317            debug_assert!(false);
318            Err(OperationError::InvalidValueState)
319        }
320    }
321
322    fn as_json_object(&self) -> Option<&JsonValue> {
323        Some(&self.object)
324    }
325}
326
327#[cfg(test)]
328mod tests {
329    use super::{ProtoFilter, ValueSetJsonFilter};
330    use crate::prelude::{Attribute, ValueSet};
331
332    #[test]
333    fn test_scim_json_filter() {
334        let filter = ProtoFilter::Pres(Attribute::Class.to_string());
335        let vs: ValueSet = ValueSetJsonFilter::new(filter);
336
337        let data = r#"
338[
339  "{\"pres\":\"class\"}"
340]
341        "#;
342        crate::valueset::scim_json_reflexive(&vs, data);
343
344        // Test that we can parse json values into a valueset.
345        // crate::valueset::scim_json_put_reflexive::<ValueSetJsonFilter>(&vs, &[])
346    }
347}