scim_server/schema/
registry.rs1use super::types::{AttributeDefinition, AttributeType, Schema};
7
8use chrono::{DateTime, FixedOffset};
9use serde_json::Value;
10use std::collections::HashMap;
11use std::fs;
12use std::path::Path;
13
14#[derive(Debug, Clone)]
19pub struct SchemaRegistry {
20 core_user_schema: Schema,
21 core_group_schema: Schema,
22 schemas: HashMap<String, Schema>,
23}
24
25impl SchemaRegistry {
26 pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
28 Self::from_schema_dir("schemas")
29 }
30
31 pub fn from_schema_dir<P: AsRef<Path>>(
33 schema_dir: P,
34 ) -> Result<Self, Box<dyn std::error::Error>> {
35 let user_schema_path = schema_dir.as_ref().join("User.json");
36 let core_user_schema = Self::load_schema_from_file(&user_schema_path)?;
37
38 let group_schema_path = schema_dir.as_ref().join("Group.json");
39 let core_group_schema = Self::load_schema_from_file(&group_schema_path)?;
40
41 let mut schemas = HashMap::new();
42 schemas.insert(core_user_schema.id.clone(), core_user_schema.clone());
43 schemas.insert(core_group_schema.id.clone(), core_group_schema.clone());
44
45 Ok(Self {
46 core_user_schema,
47 core_group_schema,
48 schemas,
49 })
50 }
51
52 fn load_schema_from_file<P: AsRef<Path>>(
54 path: P,
55 ) -> Result<Schema, Box<dyn std::error::Error>> {
56 let content = fs::read_to_string(&path)?;
57 let mut schema: Schema = serde_json::from_str(&content)?;
58
59 Self::convert_json_schema(&mut schema);
61
62 Ok(schema)
63 }
64
65 fn convert_json_schema(schema: &mut Schema) {
67 for attr in &mut schema.attributes {
68 Self::convert_attribute_definition(attr);
69 }
70 }
71
72 fn convert_attribute_definition(attr: &mut AttributeDefinition) {
74 for sub_attr in &mut attr.sub_attributes {
79 Self::convert_attribute_definition(sub_attr);
80 }
81 }
82
83 pub fn get_schemas(&self) -> Vec<&Schema> {
85 self.schemas.values().collect()
86 }
87
88 pub fn get_schema(&self, id: &str) -> Option<&Schema> {
90 self.schemas.get(id)
91 }
92
93 pub fn get_user_schema(&self) -> &Schema {
95 &self.core_user_schema
96 }
97
98 pub fn get_group_schema(&self) -> &Schema {
99 &self.core_group_schema
100 }
101
102 pub fn add_schema(&mut self, schema: Schema) -> Result<(), Box<dyn std::error::Error>> {
104 self.schemas.insert(schema.id.clone(), schema);
105 Ok(())
106 }
107
108 pub fn get_schema_by_id(&self, schema_id: &str) -> Option<&Schema> {
110 self.schemas.get(schema_id)
111 }
112
113 pub(super) fn is_valid_datetime_format(&self, value: &str) -> bool {
124 if value.is_empty() {
125 return false;
126 }
127
128 DateTime::<FixedOffset>::parse_from_rfc3339(value).is_ok()
130 }
131
132 pub(super) fn is_valid_base64(&self, value: &str) -> bool {
138 if value.is_empty() {
139 return false;
140 }
141
142 let base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
145 value.chars().all(|c| base64_chars.contains(c))
146 }
147
148 pub(super) fn is_valid_uri_format(&self, value: &str) -> bool {
153 if value.is_empty() {
154 return false;
155 }
156
157 value.contains("://") || value.starts_with("urn:")
160 }
161
162 pub(super) fn get_value_type(value: &Value) -> &'static str {
164 match value {
165 Value::Null => "null",
166 Value::Bool(_) => "boolean",
167 Value::Number(n) if n.is_i64() => "integer",
168 Value::Number(_) => "decimal",
169 Value::String(_) => "string",
170 Value::Array(_) => "array",
171 Value::Object(_) => "object",
172 }
173 }
174
175 pub(super) fn get_complex_attribute_definition(
177 &self,
178 attr_name: &str,
179 ) -> Option<&AttributeDefinition> {
180 self.core_user_schema
182 .attributes
183 .iter()
184 .find(|attr| attr.name == attr_name && matches!(attr.data_type, AttributeType::Complex))
185 }
186}
187
188impl Default for SchemaRegistry {
189 fn default() -> Self {
190 Self::new().expect("Failed to load default schemas")
191 }
192}