scim_server/resource/value_objects/
factory.rs1use super::extension::ExtensionAttributeValue;
16use super::value_object_trait::{ValueObject, ValueObjectConstructor, ValueObjectRegistry};
17use super::{EmailAddress, ExternalId, Name, ResourceId, SchemaUri, UserName};
18use crate::error::{ValidationError, ValidationResult};
19use crate::schema::types::{AttributeDefinition, AttributeType};
20use serde_json::Value;
21use std::collections::HashMap;
22
23pub struct ValueObjectFactory {
28 registry: ValueObjectRegistry,
29 type_mappings: HashMap<String, AttributeType>,
30}
31
32impl ValueObjectFactory {
33 pub fn new() -> Self {
35 let mut factory = Self {
36 registry: ValueObjectRegistry::new(),
37 type_mappings: HashMap::new(),
38 };
39
40 factory.register_default_constructors();
41 factory.setup_type_mappings();
42 factory
43 }
44
45 pub fn create_value_object(
47 &self,
48 definition: &AttributeDefinition,
49 value: &Value,
50 ) -> ValidationResult<Box<dyn ValueObject>> {
51 if matches!(value, Value::Null) && !definition.required {
53 return Err(ValidationError::NullValueForOptionalAttribute(
54 definition.name.clone(),
55 ));
56 }
57
58 if definition.multi_valued {
60 return self.create_multi_valued_object(definition, value);
61 }
62
63 match self.registry.create_value_object(definition, value) {
65 Ok(obj) => Ok(obj),
66 Err(_) => {
67 self.create_extension_attribute(definition, value)
69 }
70 }
71 }
72
73 fn create_multi_valued_object(
75 &self,
76 definition: &AttributeDefinition,
77 value: &Value,
78 ) -> ValidationResult<Box<dyn ValueObject>> {
79 let array = value
80 .as_array()
81 .ok_or_else(|| ValidationError::ExpectedArray(definition.name.clone()))?;
82
83 let mut objects = Vec::new();
85 for item_value in array {
86 let item_definition = AttributeDefinition {
88 multi_valued: false,
89 ..definition.clone()
90 };
91
92 let obj = self.create_value_object(&item_definition, item_value)?;
93 objects.push(obj);
94 }
95
96 Ok(Box::new(GenericMultiValuedAttribute::new(
100 definition.name.clone(),
101 objects,
102 )))
103 }
104
105 fn create_extension_attribute(
107 &self,
108 definition: &AttributeDefinition,
109 value: &Value,
110 ) -> ValidationResult<Box<dyn ValueObject>> {
111 let schema_uri = SchemaUri::new(format!(
113 "urn:ietf:params:scim:schemas:extension:unknown:{}",
114 definition.name
115 ))?;
116
117 let ext_attr = ExtensionAttributeValue::new(
118 schema_uri,
119 definition.name.clone(),
120 value.clone(),
121 Some(definition.clone()),
122 )?;
123
124 Ok(Box::new(ext_attr))
125 }
126
127 fn register_default_constructors(&mut self) {
129 self.registry
131 .register_constructor(Box::new(ResourceIdConstructor::new()));
132 self.registry
133 .register_constructor(Box::new(UserNameConstructor::new()));
134 self.registry
135 .register_constructor(Box::new(ExternalIdConstructor::new()));
136 self.registry
137 .register_constructor(Box::new(EmailAddressConstructor::new()));
138 self.registry
139 .register_constructor(Box::new(SchemaUriConstructor::new()));
140 self.registry
141 .register_constructor(Box::new(NameConstructor::new()));
142 }
144
145 fn setup_type_mappings(&mut self) {
147 self.type_mappings
148 .insert("id".to_string(), AttributeType::String);
149 self.type_mappings
150 .insert("userName".to_string(), AttributeType::String);
151 self.type_mappings
152 .insert("externalId".to_string(), AttributeType::String);
153 self.type_mappings
154 .insert("schemas".to_string(), AttributeType::Reference);
155 self.type_mappings
156 .insert("name".to_string(), AttributeType::Complex);
157 self.type_mappings
158 .insert("emails".to_string(), AttributeType::Complex);
159 self.type_mappings
160 .insert("phoneNumbers".to_string(), AttributeType::Complex);
161 self.type_mappings
162 .insert("addresses".to_string(), AttributeType::Complex);
163 self.type_mappings
164 .insert("meta".to_string(), AttributeType::Complex);
165 }
166
167 pub fn get_expected_type(&self, attribute_name: &str) -> Option<AttributeType> {
169 self.type_mappings.get(attribute_name).cloned()
170 }
171
172 pub fn register_constructor(&mut self, constructor: Box<dyn ValueObjectConstructor>) {
174 self.registry.register_constructor(constructor);
175 }
176
177 pub fn validate_composite_rules(
179 &self,
180 objects: &[Box<dyn ValueObject>],
181 ) -> ValidationResult<()> {
182 self.registry.validate_composite_rules(objects)
183 }
184
185 pub fn has_constructors(&self) -> bool {
187 self.registry.has_constructors()
188 }
189
190 pub fn create_value_objects_from_json(
192 &self,
193 definitions: &[AttributeDefinition],
194 json_obj: &serde_json::Map<String, Value>,
195 ) -> ValidationResult<Vec<Box<dyn ValueObject>>> {
196 let mut objects = Vec::new();
197
198 for definition in definitions {
199 if let Some(value) = json_obj.get(&definition.name) {
200 let obj = self.create_value_object(definition, value)?;
201 objects.push(obj);
202 } else if definition.required {
203 return Err(ValidationError::RequiredAttributeMissing(
204 definition.name.clone(),
205 ));
206 }
207 }
208
209 Ok(objects)
210 }
211}
212
213impl Default for ValueObjectFactory {
214 fn default() -> Self {
215 Self::new()
216 }
217}
218
219#[derive(Debug)]
221pub struct GenericMultiValuedAttribute {
222 attribute_name: String,
223 values: Vec<Box<dyn ValueObject>>,
224 primary_index: Option<usize>,
225}
226
227impl GenericMultiValuedAttribute {
228 pub fn new(attribute_name: String, values: Vec<Box<dyn ValueObject>>) -> Self {
229 Self {
230 attribute_name,
231 values,
232 primary_index: None,
233 }
234 }
235
236 pub fn values(&self) -> &[Box<dyn ValueObject>] {
237 &self.values
238 }
239
240 pub fn primary(&self) -> Option<&Box<dyn ValueObject>> {
241 self.primary_index.map(|idx| &self.values[idx])
242 }
243
244 pub fn set_primary(&mut self, index: usize) -> ValidationResult<()> {
245 if index >= self.values.len() {
246 return Err(ValidationError::InvalidPrimaryIndex {
247 attribute: self.attribute_name.clone(),
248 index,
249 length: self.values.len(),
250 });
251 }
252 self.primary_index = Some(index);
253 Ok(())
254 }
255}
256
257impl ValueObject for GenericMultiValuedAttribute {
258 fn attribute_type(&self) -> AttributeType {
259 AttributeType::Complex }
261
262 fn attribute_name(&self) -> &str {
263 &self.attribute_name
264 }
265
266 fn to_json(&self) -> ValidationResult<Value> {
267 let mut array = Vec::new();
268 for value_obj in &self.values {
269 array.push(value_obj.to_json()?);
270 }
271 Ok(Value::Array(array))
272 }
273
274 fn validate_against_schema(&self, definition: &AttributeDefinition) -> ValidationResult<()> {
275 if !definition.multi_valued {
276 return Err(ValidationError::NotMultiValued(definition.name.clone()));
277 }
278
279 let single_def = AttributeDefinition {
281 multi_valued: false,
282 ..definition.clone()
283 };
284
285 for value_obj in &self.values {
286 value_obj.validate_against_schema(&single_def)?;
287 }
288
289 Ok(())
290 }
291
292 fn as_json_value(&self) -> Value {
293 self.to_json().unwrap_or(Value::Null)
294 }
295
296 fn supports_definition(&self, definition: &AttributeDefinition) -> bool {
297 definition.multi_valued && definition.name == self.attribute_name
298 }
299
300 fn clone_boxed(&self) -> Box<dyn ValueObject> {
301 Box::new(GenericMultiValuedAttribute {
302 attribute_name: self.attribute_name.clone(),
303 values: self.values.iter().map(|v| v.clone_boxed()).collect(),
304 primary_index: self.primary_index,
305 })
306 }
307
308 fn as_any(&self) -> &dyn std::any::Any {
309 self
310 }
311}
312
313pub struct ResourceIdConstructor;
316
317impl ResourceIdConstructor {
318 pub fn new() -> Self {
319 Self
320 }
321}
322
323impl ValueObjectConstructor for ResourceIdConstructor {
324 fn try_construct(
325 &self,
326 definition: &AttributeDefinition,
327 value: &Value,
328 ) -> Option<ValidationResult<Box<dyn ValueObject>>> {
329 if definition.name == "id" && definition.data_type == AttributeType::String {
330 if let Some(id_str) = value.as_str() {
331 Some(
332 ResourceId::new(id_str.to_string())
333 .map(|id| Box::new(id) as Box<dyn ValueObject>),
334 )
335 } else {
336 Some(Err(ValidationError::InvalidAttributeType {
337 attribute: definition.name.clone(),
338 expected: "string".to_string(),
339 actual: "non-string".to_string(),
340 }))
341 }
342 } else {
343 None
344 }
345 }
346
347 fn priority(&self) -> u8 {
348 100 }
350
351 fn description(&self) -> &str {
352 "ResourceId constructor for 'id' attributes"
353 }
354}
355
356pub struct UserNameConstructor;
357
358impl UserNameConstructor {
359 pub fn new() -> Self {
360 Self
361 }
362}
363
364impl ValueObjectConstructor for UserNameConstructor {
365 fn try_construct(
366 &self,
367 definition: &AttributeDefinition,
368 value: &Value,
369 ) -> Option<ValidationResult<Box<dyn ValueObject>>> {
370 if definition.name == "userName" && definition.data_type == AttributeType::String {
371 if let Some(username_str) = value.as_str() {
372 Some(
373 UserName::new(username_str.to_string())
374 .map(|username| Box::new(username) as Box<dyn ValueObject>),
375 )
376 } else {
377 Some(Err(ValidationError::InvalidAttributeType {
378 attribute: definition.name.clone(),
379 expected: "string".to_string(),
380 actual: "non-string".to_string(),
381 }))
382 }
383 } else {
384 None
385 }
386 }
387
388 fn priority(&self) -> u8 {
389 100
390 }
391
392 fn description(&self) -> &str {
393 "UserName constructor for 'userName' attributes"
394 }
395}
396
397pub struct ExternalIdConstructor;
398
399impl ExternalIdConstructor {
400 pub fn new() -> Self {
401 Self
402 }
403}
404
405impl ValueObjectConstructor for ExternalIdConstructor {
406 fn try_construct(
407 &self,
408 definition: &AttributeDefinition,
409 value: &Value,
410 ) -> Option<ValidationResult<Box<dyn ValueObject>>> {
411 if definition.name == "externalId" && definition.data_type == AttributeType::String {
412 if let Some(ext_id_str) = value.as_str() {
413 Some(
414 ExternalId::new(ext_id_str.to_string())
415 .map(|ext_id| Box::new(ext_id) as Box<dyn ValueObject>),
416 )
417 } else {
418 Some(Err(ValidationError::InvalidAttributeType {
419 attribute: definition.name.clone(),
420 expected: "string".to_string(),
421 actual: "non-string".to_string(),
422 }))
423 }
424 } else {
425 None
426 }
427 }
428
429 fn priority(&self) -> u8 {
430 100
431 }
432
433 fn description(&self) -> &str {
434 "ExternalId constructor for 'externalId' attributes"
435 }
436}
437
438pub struct EmailAddressConstructor;
439
440impl EmailAddressConstructor {
441 pub fn new() -> Self {
442 Self
443 }
444}
445
446impl ValueObjectConstructor for EmailAddressConstructor {
447 fn try_construct(
448 &self,
449 definition: &AttributeDefinition,
450 value: &Value,
451 ) -> Option<ValidationResult<Box<dyn ValueObject>>> {
452 if (definition.name == "value" || definition.name.contains("email"))
453 && definition.data_type == AttributeType::String
454 {
455 if let Some(email_str) = value.as_str() {
456 Some(
457 EmailAddress::new(email_str.to_string(), None, None, None)
458 .map(|email| Box::new(email) as Box<dyn ValueObject>),
459 )
460 } else {
461 Some(Err(ValidationError::InvalidAttributeType {
462 attribute: definition.name.clone(),
463 expected: "string".to_string(),
464 actual: "non-string".to_string(),
465 }))
466 }
467 } else {
468 None
469 }
470 }
471
472 fn priority(&self) -> u8 {
473 80 }
475
476 fn description(&self) -> &str {
477 "EmailAddress constructor for email-related attributes"
478 }
479}
480
481pub struct SchemaUriConstructor;
482
483impl SchemaUriConstructor {
484 pub fn new() -> Self {
485 Self
486 }
487}
488
489impl ValueObjectConstructor for SchemaUriConstructor {
490 fn try_construct(
491 &self,
492 _definition: &AttributeDefinition,
493 _value: &Value,
494 ) -> Option<ValidationResult<Box<dyn ValueObject>>> {
495 None
497 }
498
499 fn priority(&self) -> u8 {
500 100
501 }
502
503 fn description(&self) -> &str {
504 "SchemaUri constructor for 'schemas' attributes"
505 }
506}
507
508pub struct NameConstructor;
509
510impl NameConstructor {
511 pub fn new() -> Self {
512 Self
513 }
514}
515
516impl ValueObjectConstructor for NameConstructor {
517 fn try_construct(
518 &self,
519 definition: &AttributeDefinition,
520 value: &Value,
521 ) -> Option<ValidationResult<Box<dyn ValueObject>>> {
522 if definition.name == "name" && definition.data_type == AttributeType::Complex {
523 Some(Name::from_json(value).map(|name| Box::new(name) as Box<dyn ValueObject>))
524 } else {
525 None
526 }
527 }
528
529 fn priority(&self) -> u8 {
530 100
531 }
532
533 fn description(&self) -> &str {
534 "Name constructor for 'name' complex attributes"
535 }
536}
537
538#[cfg(test)]
541mod tests {
542 use super::*;
543 use crate::schema::types::{Mutability, Uniqueness};
544
545 fn create_string_definition(name: &str) -> AttributeDefinition {
546 AttributeDefinition {
547 name: name.to_string(),
548 data_type: AttributeType::String,
549 multi_valued: false,
550 required: false,
551 case_exact: false,
552 mutability: Mutability::ReadWrite,
553 uniqueness: Uniqueness::None,
554 canonical_values: vec![],
555 sub_attributes: vec![],
556 returned: None,
557 }
558 }
559
560 #[test]
561 fn test_factory_creation() {
562 let factory = ValueObjectFactory::new();
563 assert!(factory.has_constructors());
564 }
565
566 #[test]
567 fn test_resource_id_construction() {
568 let factory = ValueObjectFactory::new();
569 let definition = create_string_definition("id");
570 let value = Value::String("test-id".to_string());
571
572 let result = factory.create_value_object(&definition, &value);
573 assert!(result.is_ok());
574
575 let obj = result.unwrap();
576 assert_eq!(obj.attribute_name(), "id");
577 assert_eq!(obj.attribute_type(), AttributeType::String);
578 }
579
580 #[test]
581 fn test_username_construction() {
582 let factory = ValueObjectFactory::new();
583 let definition = create_string_definition("userName");
584 let value = Value::String("testuser".to_string());
585
586 let result = factory.create_value_object(&definition, &value);
587 assert!(result.is_ok());
588
589 let obj = result.unwrap();
590 assert_eq!(obj.attribute_name(), "userName");
591 }
592}