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