scim_server/
schema_discovery.rs1use crate::error::{BuildError, BuildResult, ScimResult};
9
10use crate::schema::{Schema, SchemaRegistry};
11use serde::{Deserialize, Serialize};
12
13use std::marker::PhantomData;
14
15#[derive(Debug)]
19pub struct Uninitialized;
20
21#[derive(Debug)]
25pub struct Ready;
26
27pub struct SchemaDiscovery<State = Ready> {
55 inner: Option<DiscoveryInner>,
56 _state: PhantomData<State>,
57}
58
59impl<State> std::fmt::Debug for SchemaDiscovery<State> {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 f.debug_struct("SchemaDiscovery")
62 .field("inner", &self.inner.is_some())
63 .field("state", &std::any::type_name::<State>())
64 .finish()
65 }
66}
67
68struct DiscoveryInner {
70 schema_registry: SchemaRegistry,
71 service_config: ServiceProviderConfig,
72}
73
74impl std::fmt::Debug for DiscoveryInner {
75 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76 f.debug_struct("DiscoveryInner")
77 .field("schema_registry", &"SchemaRegistry")
78 .field("service_config", &self.service_config)
79 .finish()
80 }
81}
82
83impl SchemaDiscovery<Uninitialized> {
84 pub fn new() -> BuildResult<SchemaDiscovery<Ready>> {
90 let schema_registry = SchemaRegistry::new().map_err(|_e| BuildError::SchemaLoadError {
91 schema_id: "Core".to_string(),
92 })?;
93
94 let service_config = ServiceProviderConfig::default();
95
96 let inner = DiscoveryInner {
97 schema_registry,
98 service_config,
99 };
100
101 Ok(SchemaDiscovery {
102 inner: Some(inner),
103 _state: PhantomData,
104 })
105 }
106}
107
108impl SchemaDiscovery<Ready> {
109 pub async fn get_schemas(&self) -> ScimResult<Vec<Schema>> {
116 let inner = self.inner.as_ref().expect("Server should be initialized");
117 Ok(inner
118 .schema_registry
119 .get_schemas()
120 .into_iter()
121 .cloned()
122 .collect())
123 }
124
125 pub async fn get_schema(&self, id: &str) -> ScimResult<Option<Schema>> {
134 let inner = self.inner.as_ref().expect("Server should be initialized");
135 Ok(inner.schema_registry.get_schema(id).cloned())
136 }
137
138 pub async fn get_service_provider_config(&self) -> ScimResult<ServiceProviderConfig> {
143 let inner = self.inner.as_ref().expect("Server should be initialized");
144 Ok(inner.service_config.clone())
145 }
146
147 pub fn schema_registry(&self) -> &SchemaRegistry {
152 let inner = self
153 .inner
154 .as_ref()
155 .expect("Discovery component should be initialized");
156 &inner.schema_registry
157 }
158
159 pub fn service_config(&self) -> &ServiceProviderConfig {
161 let inner = self
162 .inner
163 .as_ref()
164 .expect("Discovery component should be initialized");
165 &inner.service_config
166 }
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
174pub struct ServiceProviderConfig {
175 #[serde(rename = "patch")]
177 pub patch_supported: bool,
178
179 #[serde(rename = "bulk")]
181 pub bulk_supported: bool,
182
183 #[serde(rename = "filter")]
185 pub filter_supported: bool,
186
187 #[serde(rename = "changePassword")]
189 pub change_password_supported: bool,
190
191 #[serde(rename = "sort")]
193 pub sort_supported: bool,
194
195 #[serde(rename = "etag")]
197 pub etag_supported: bool,
198
199 #[serde(rename = "authenticationSchemes")]
201 pub authentication_schemes: Vec<AuthenticationScheme>,
202
203 #[serde(rename = "bulk.maxOperations")]
205 pub bulk_max_operations: Option<u32>,
206
207 #[serde(rename = "bulk.maxPayloadSize")]
209 pub bulk_max_payload_size: Option<u64>,
210
211 #[serde(rename = "filter.maxResults")]
213 pub filter_max_results: Option<u32>,
214}
215
216impl Default for ServiceProviderConfig {
217 fn default() -> Self {
218 Self {
219 patch_supported: false,
220 bulk_supported: false,
221 filter_supported: false,
222 change_password_supported: false,
223 sort_supported: false,
224 etag_supported: false,
225 authentication_schemes: vec![],
226 bulk_max_operations: None,
227 bulk_max_payload_size: None,
228 filter_max_results: Some(200),
229 }
230 }
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
235pub struct AuthenticationScheme {
236 pub name: String,
238 pub description: String,
240 #[serde(rename = "specUri")]
242 pub spec_uri: Option<String>,
243 #[serde(rename = "documentationUri")]
245 pub documentation_uri: Option<String>,
246 #[serde(rename = "type")]
248 pub auth_type: String,
249 pub primary: bool,
251}
252
253#[cfg(test)]
254mod tests {
255 use super::*;
256
257 #[tokio::test]
258 async fn test_discovery_creation() {
259 let discovery = SchemaDiscovery::new().expect("Failed to create discovery component");
260
261 let schemas = discovery
263 .get_schemas()
264 .await
265 .expect("Failed to get schemas");
266 assert!(!schemas.is_empty());
267 }
268
269 #[tokio::test]
270 async fn test_schema_access() {
271 let discovery = SchemaDiscovery::new().expect("Failed to create discovery component");
272
273 let user_schema = discovery
275 .get_schema("urn:ietf:params:scim:schemas:core:2.0:User")
276 .await
277 .expect("Failed to get schema");
278
279 assert!(user_schema.is_some());
280 if let Some(schema) = user_schema {
281 assert_eq!(schema.name, "User");
282 }
283 }
284
285 #[test]
286 fn test_service_provider_config() {
287 let config = ServiceProviderConfig::default();
288 assert!(!config.patch_supported);
289 assert!(!config.bulk_supported);
290 assert!(!config.filter_supported);
291 }
292}