1use std::slice;
6
7use kanidm_proto::internal::{
8 Modify as ProtoModify, ModifyList as ProtoModifyList, OperationError, SchemaError,
9};
10use kanidm_proto::v1::Entry as ProtoEntry;
11use serde::{Deserialize, Serialize};
13use std::collections::BTreeMap;
14
15use crate::prelude::*;
16use crate::schema::SchemaTransaction;
17use crate::value::{PartialValue, Value};
18
19#[derive(Serialize, Deserialize, Debug, Clone)]
20pub struct ModifyValid;
21#[derive(Serialize, Deserialize, Debug, Clone)]
22pub struct ModifyInvalid;
23
24#[derive(Debug, Clone)]
25#[allow(clippy::large_enum_variant)]
26pub enum Modify {
27 Present(Attribute, Value),
29 Removed(Attribute, PartialValue),
31 Purged(Attribute),
33 Assert(Attribute, PartialValue),
35 Set(Attribute, ValueSet),
38}
39
40pub fn m_pres(attr: Attribute, v: &Value) -> Modify {
41 Modify::Present(attr, v.clone())
42}
43
44pub fn m_remove(attr: Attribute, v: &PartialValue) -> Modify {
45 Modify::Removed(attr, v.clone())
46}
47
48pub fn m_purge(attr: Attribute) -> Modify {
49 Modify::Purged(attr)
50}
51
52pub fn m_assert(attr: Attribute, v: &PartialValue) -> Modify {
53 Modify::Assert(attr, v.clone())
54}
55
56impl Modify {
57 pub fn from(
58 m: &ProtoModify,
59 qs: &mut QueryServerWriteTransaction,
60 ) -> Result<Self, OperationError> {
61 Ok(match m {
62 ProtoModify::Present(a, v) => {
63 let a = Attribute::from(a.as_str());
64 let v = qs.clone_value(&a, v)?;
65 Modify::Present(a, v)
66 }
67 ProtoModify::Removed(a, v) => {
68 let a = Attribute::from(a.as_str());
69 let v = qs.clone_partialvalue(&a, v)?;
70 Modify::Removed(a, v)
71 }
72 ProtoModify::Purged(a) => Modify::Purged(Attribute::from(a.as_str())),
73 })
74 }
75}
76
77#[derive(Clone, Debug, Default)]
78pub struct ModifyList<VALID> {
79 #[allow(dead_code)]
81 valid: VALID,
82 mods: Vec<Modify>,
84}
85
86impl<'a> IntoIterator for &'a ModifyList<ModifyValid> {
87 type IntoIter = slice::Iter<'a, Modify>;
88 type Item = &'a Modify;
89
90 fn into_iter(self) -> Self::IntoIter {
91 self.mods.iter()
92 }
93}
94
95impl ModifyList<ModifyInvalid> {
96 pub fn new() -> Self {
97 ModifyList {
98 valid: ModifyInvalid,
99 mods: Vec::with_capacity(0),
100 }
101 }
102
103 pub fn new_list(mods: Vec<Modify>) -> Self {
104 ModifyList {
105 valid: ModifyInvalid,
106 mods,
107 }
108 }
109
110 pub fn new_purge_and_set(attr: Attribute, v: Value) -> Self {
111 Self::new_list(vec![m_purge(attr.clone()), Modify::Present(attr, v)])
112 }
113
114 pub fn new_append(attr: Attribute, v: Value) -> Self {
115 Self::new_list(vec![Modify::Present(attr, v)])
116 }
117
118 pub fn new_remove(attr: Attribute, pv: PartialValue) -> Self {
119 Self::new_list(vec![Modify::Removed(attr, pv)])
120 }
121
122 pub fn new_purge(attr: Attribute) -> Self {
123 Self::new_list(vec![m_purge(attr)])
124 }
125
126 pub fn new_set(attr: Attribute, vs: ValueSet) -> Self {
127 Self::new_list(vec![Modify::Set(attr, vs)])
128 }
129
130 pub fn push_mod(&mut self, modify: Modify) {
131 self.mods.push(modify)
132 }
133
134 pub fn from(
135 ml: &ProtoModifyList,
136 qs: &mut QueryServerWriteTransaction,
137 ) -> Result<Self, OperationError> {
138 let inner: Result<Vec<_>, _> = ml.mods.iter().map(|pm| Modify::from(pm, qs)).collect();
140 match inner {
141 Ok(m) => Ok(ModifyList {
142 valid: ModifyInvalid,
143 mods: m,
144 }),
145 Err(e) => Err(e),
146 }
147 }
148
149 pub fn from_patch(
150 pe: &ProtoEntry,
151 qs: &mut QueryServerWriteTransaction,
152 ) -> Result<Self, OperationError> {
153 let mut mods = Vec::with_capacity(0);
154
155 pe.attrs.iter().try_for_each(|(attr, vals)| {
156 let attr: Attribute = attr.as_str().into();
158 mods.push(m_purge(attr.clone()));
159 vals.iter().try_for_each(|val| {
162 qs.clone_value(&attr, val).map(|resolved_v| {
163 mods.push(Modify::Present(attr.clone(), resolved_v));
164 })
165 })
166 })?;
167 Ok(ModifyList {
168 valid: ModifyInvalid,
169 mods,
170 })
171 }
172
173 pub fn validate(
174 &self,
175 schema: &dyn SchemaTransaction,
176 ) -> Result<ModifyList<ModifyValid>, SchemaError> {
177 let schema_attributes = schema.get_attributes();
178 let res: Result<Vec<Modify>, _> = self
185 .mods
186 .iter()
187 .map(|m| match m {
188 Modify::Present(attr, value) => match schema_attributes.get(attr) {
189 Some(schema_a) => schema_a
190 .validate_value(attr, value)
191 .map(|_| Modify::Present(attr.clone(), value.clone())),
192 None => Err(SchemaError::InvalidAttribute(attr.to_string())),
193 },
194 Modify::Removed(attr, value) => match schema_attributes.get(attr) {
195 Some(schema_a) => schema_a
196 .validate_partialvalue(attr, value)
197 .map(|_| Modify::Removed(attr.clone(), value.clone())),
198 None => Err(SchemaError::InvalidAttribute(attr.to_string())),
199 },
200 Modify::Assert(attr, value) => match schema_attributes.get(attr) {
201 Some(schema_a) => schema_a
202 .validate_partialvalue(attr, value)
203 .map(|_| Modify::Assert(attr.clone(), value.clone())),
204 None => Err(SchemaError::InvalidAttribute(attr.to_string())),
205 },
206 Modify::Purged(attr) => match schema_attributes.get(attr) {
207 Some(_attr_name) => Ok(Modify::Purged(attr.clone())),
208 None => Err(SchemaError::InvalidAttribute(attr.to_string())),
209 },
210 Modify::Set(attr, valueset) => match schema_attributes.get(attr) {
211 Some(_attr_name) => Ok(Modify::Set(attr.clone(), valueset.clone())),
212 None => Err(SchemaError::InvalidAttribute(attr.to_string())),
213 },
214 })
215 .collect();
216
217 let valid_mods = res?;
218
219 Ok(ModifyList {
221 valid: ModifyValid,
222 mods: valid_mods,
223 })
224 }
225
226 #[cfg(test)]
229 pub(crate) fn into_valid(self) -> ModifyList<ModifyValid> {
230 ModifyList {
231 valid: ModifyValid,
232 mods: self.mods,
233 }
234 }
235}
236
237impl From<BTreeMap<Attribute, Option<ValueSet>>> for ModifyList<ModifyInvalid> {
238 fn from(attrs: BTreeMap<Attribute, Option<ValueSet>>) -> Self {
239 let mods = attrs
240 .into_iter()
241 .map(|(attr, maybe_valueset)| {
242 if let Some(valueset) = maybe_valueset {
243 Modify::Set(attr, valueset)
244 } else {
245 Modify::Purged(attr)
246 }
247 })
248 .collect();
249
250 ModifyList {
251 valid: ModifyInvalid,
252 mods,
253 }
254 }
255}
256
257impl ModifyList<ModifyValid> {
258 #[cfg(test)]
261 pub fn new_valid_list(mods: Vec<Modify>) -> Self {
262 ModifyList {
263 valid: ModifyValid,
264 mods,
265 }
266 }
267
268 pub fn iter(&self) -> slice::Iter<Modify> {
269 self.mods.iter()
270 }
271}
272
273impl<VALID> ModifyList<VALID> {
274 pub fn len(&self) -> usize {
275 self.mods.len()
276 }
277
278 pub fn is_empty(&self) -> bool {
279 self.len() == 0
280 }
281}