scim_server/
lib.rs

1//! # SCIM Server Library for Rust
2//!
3//! A comprehensive System for Cross-domain Identity Management (SCIM) server library
4//! that enables developers to implement SCIM-compliant identity providers with minimal effort.
5//!
6//! [![Crates.io](https://img.shields.io/crates/v/scim-server.svg)](https://crates.io/crates/scim-server)
7//! [![Documentation](https://docs.rs/scim-server/badge.svg)](https://docs.rs/scim-server)
8//! [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/yourusername/scim-server/blob/main/LICENSE)
9//! [![Build Status](https://github.com/yourusername/scim-server/workflows/CI/badge.svg)](https://github.com/yourusername/scim-server/actions)
10//!
11//! ## Overview
12//!
13//! This library transforms SCIM from a complex enterprise integration challenge into a
14//! straightforward provider implementation task, allowing developers to focus on their
15//! core business logic while automatically gaining enterprise SSO and provisioning capabilities.
16//!
17//! ### Key Value Propositions
18//!
19//! - **For SaaS Developers**: Add enterprise-grade SCIM provisioning without protocol expertise
20//! - **For Enterprise Customers**: Seamless identity provisioning and deprovisioning
21//! - **For Integration Teams**: Standards-compliant SCIM 2.0 implementation out of the box
22//!
23//! ## Table of Contents
24//!
25//! - [Two Main Components](#two-main-components)
26//! - [Table of Contents](#two-main-components)
27//! - [Features](#features)
28//! - [Installation](#installation)
29//! - [MCP Integration](#mcp-model-context-protocol-integration)
30//! - [Architecture Overview](#architecture-overview)
31//! - [ETag Concurrency Control](#etag-concurrency-control)
32//! - [Logging Support](#logging-support)
33//! - [Multi-Tenant Support](#multi-tenant-support)
34//! - [Quick Start - Full SCIM Server](#quick-start---full-scim-server)
35//! - [Schema Discovery](#schema-discovery)
36//! - [Provider Implementation](#provider-implementation)
37//! - [Examples](#examples)
38//! - [Schema Validation Utility](#schema-validation-utility)
39//! - [Performance](#performance)
40//! - [SCIM 2.0 Compliance](#scim-20-compliance)
41//! - [Contributing](#contributing)
42//!
43//! ## Two Main Components
44//!
45//! This library provides two distinct components:
46//!
47//! - **`ScimServer`** - Full-featured dynamic server for production SCIM endpoints with runtime resource registration and CRUD operations
48//! - **`SchemaDiscovery`** - Lightweight component for schema discovery and service provider configuration
49//!
50//! ## Features
51//!
52//! - **RFC Compliance**: Full RFC 7643/7644 compliance for core User and Group schemas
53//! - **Type Safety**: Compile-time guarantees preventing invalid operations
54//! - **Multi-Tenant Ready**: Built-in tenant isolation and context management
55//! - **Provider Agnostic**: Works with any storage backend via trait abstraction
56//! - **Async-First**: Non-blocking operations with tokio integration
57//! - **Value Objects**: Type-safe SCIM attribute handling with validation
58//! - **Schema-Driven**: Dynamic resource types with runtime schema validation
59//! - **Comprehensive Logging**: Structured logging with request IDs and tenant context
60//! - **Flexible Backends**: Choose from env_logger, tracing, slog, or any log-compatible crate
61//! - **Auto-Discovery**: Automatic provider capability detection
62//! - **ETag Concurrency Control**: Built-in optimistic locking with conditional operations
63//! - **MCP Integration**: AI agent support via Model Context Protocol (optional feature)
64//!
65//! ## Installation
66//!
67//! Add this to your `Cargo.toml`:
68//!
69//! ```toml
70//! [dependencies]
71//! scim-server = "0.1.0"
72//!
73//! # For async runtime
74//! tokio = { version = "1.0", features = ["full"] }
75//!
76//! # For logging (choose one)
77//! env_logger = "0.10"  # Simple logging
78//! # OR
79//! tracing-subscriber = "0.3"  # Structured logging
80//! ```
81//!
82//! ### Optional Features
83//!
84//! ```toml
85//! [dependencies]
86//! scim-server = { version = "0.1.0", features = ["mcp"] }
87//! ```
88//!
89//! - **`mcp`**: Enables Model Context Protocol integration for AI agents
90//!
91//! ## MCP (Model Context Protocol) Integration
92//!
93//! The SCIM server provides optional MCP integration for AI agent interactions. When enabled,
94//! the server exposes SCIM operations as structured tools that AI agents can discover and use.
95//!
96//! ### Enabling MCP Support
97//!
98//! Add the MCP feature to your `Cargo.toml`:
99//!
100//! ```toml
101//! [dependencies]
102//! scim-server = { version = "0.1.0", features = ["mcp"] }
103//! ```
104//!
105//! ### Basic MCP Server Setup
106//!
107//! ```rust,no_run
108//! use scim_server::{ScimServer, providers::InMemoryProvider};
109//! // Note: MCP integration requires the "mcp" feature
110//! // use scim_server::mcp_integration::ScimMcpServer;
111//!
112//! #[tokio::main]
113//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
114//!     // Create SCIM server
115//!     let provider = InMemoryProvider::new();
116//!     let scim_server = ScimServer::new(provider)?;
117//!
118//!     // With MCP feature enabled:
119//!     // let mcp_server = ScimMcpServer::new(scim_server);
120//!     // let tools = mcp_server.get_tools();
121//!     // All operations automatically include ETag versioning
122//!     // mcp_server.run_stdio().await?;
123//!
124//!     println!("SCIM server created successfully");
125//!     Ok(())
126//! }
127//! ```
128//!
129//! ### Available MCP Tools with ETag Support
130//!
131//! The MCP integration provides these tools for AI agents with automatic ETag versioning:
132//!
133//! - **`scim_create_user`** - Create new users with SCIM schema validation (returns ETag)
134//! - **`scim_get_user`** - Retrieve user by ID with full attribute access (returns ETag)
135//! - **`scim_update_user`** - Update user attributes with optional ETag conflict detection
136//! - **`scim_delete_user`** - Remove users with optional ETag safety checks
137//! - **`scim_list_users`** - List all users with pagination support
138//! - **`scim_search_users`** - Search users by attributes with filtering
139//! - **`scim_user_exists`** - Check user existence for validation
140//! - **`scim_get_schemas`** - Retrieve all available schemas for AI understanding
141//! - **`scim_get_schema`** - Get specific schema details for validation
142//! - **`scim_server_info`** - Get server capabilities and supported operations
143//!
144//! ### AI Agent ETag Usage Pattern
145//!
146//! ```rust
147//! # #[cfg(feature = "mcp")]
148//! # {
149//! use serde_json::json;
150//! use scim_server::mcp_integration::ScimMcpServer;
151//! use scim_server::providers::InMemoryProvider;
152//! use scim_server::ScimServer;
153//!
154//! # async {
155//! // AI agent workflow with ETag versioning
156//! let provider = InMemoryProvider::new();
157//! let scim_server = ScimServer::new(provider).unwrap();
158//! let mcp_server = ScimMcpServer::new(scim_server);
159//!
160//! // 1. Create user - captures ETag for subsequent operations
161//! let create_result = mcp_server.execute_tool(
162//!     "scim_create_user",
163//!     json!({
164//!         "user_data": {
165//!             "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
166//!             "userName": "ai.agent@company.com",
167//!             "active": true
168//!         }
169//!     })
170//! ).await;
171//!
172//! let metadata = create_result.metadata.unwrap();
173//! let user_id = metadata["resource_id"].as_str().unwrap();
174//! let etag = metadata["etag"].as_str().unwrap();
175//!
176//! // 2. Conditional update using ETag - prevents lost updates
177//! let update_result = mcp_server.execute_tool(
178//!     "scim_update_user",
179//!     json!({
180//!         "user_id": user_id,
181//!         "user_data": {"userName": "ai.agent@company.com", "active": false},
182//!         "expected_version": etag  // ETag from create operation
183//!     })
184//! ).await;
185//!
186//! // 3. Handle version conflicts automatically
187//! if !update_result.success {
188//!     if update_result.content["is_version_conflict"].as_bool().unwrap_or(false) {
189//!         // AI agent should refresh and retry with current version
190//!         println!("Version conflict - resource modified by another client");
191//!     }
192//! }
193//! # };
194//! # }
195//! ```
196//!
197//! ### Multi-Tenant MCP Operations
198//!
199//! AI agents can work with multi-tenant environments:
200//!
201//! ```rust
202//! # #[cfg(feature = "mcp")]
203//! # {
204//! use serde_json::json;
205//! use scim_server::mcp_integration::ScimMcpServer;
206//! use scim_server::providers::InMemoryProvider;
207//! use scim_server::ScimServer;
208//!
209//! # async {
210//! // Set up MCP server with InMemoryProvider
211//! let provider = InMemoryProvider::new();
212//! let scim_server = ScimServer::new(provider).unwrap();
213//! let mcp_server = ScimMcpServer::new(scim_server);
214//!
215//! // Create user in specific tenant
216//! let result = mcp_server.execute_tool(
217//!     "scim_create_user",
218//!     json!({
219//!         "user_data": {
220//!             "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
221//!             "userName": "ai.agent@company.com",
222//!             "active": true
223//!         },
224//!         "tenant_id": "enterprise-corp"
225//!     })
226//! ).await;
227//! println!("MCP operation success: {}", result.success);
228//! # };
229//! # }
230//! ```
231//!
232//! ### AI Agent Integration Benefits
233//!
234//! - **Schema Discovery**: AI agents can introspect SCIM schemas for proper validation
235//! - **ETag Versioning**: Automatic optimistic locking prevents lost updates in concurrent scenarios
236//! - **Type Safety**: All operations include JSON schema validation for inputs
237//! - **Error Handling**: Structured error responses with actionable information
238//! - **Version Conflict Detection**: Clear indicators for AI agents to handle concurrent modifications
239//! - **Multi-Tenant**: Automatic tenant isolation for enterprise scenarios
240//! - **Comprehensive CRUD**: Full resource lifecycle management with version control
241//! - **Standards Compliance**: SCIM 2.0 compliant operations for enterprise integration
242//!
243//! ### Custom MCP Server Configuration
244//!
245//! ```rust,no_run
246//! # #[cfg(feature = "mcp")]
247//! use scim_server::mcp_integration::{ScimMcpServer, McpServerInfo};
248//! # #[cfg(feature = "mcp")]
249//! # use scim_server::{ScimServer, providers::InMemoryProvider};
250//!
251//! # #[cfg(feature = "mcp")]
252//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
253//! let provider = InMemoryProvider::new();
254//! let scim_server = ScimServer::new(provider)?;
255//!
256//! // Custom server information for AI agent discovery
257//! let server_info = McpServerInfo {
258//!     name: "Enterprise SCIM Server".to_string(),
259//!     version: "2.0.0".to_string(),
260//!     description: "Production SCIM server with AI agent support".to_string(),
261//!     supported_resource_types: vec!["User".to_string(), "Group".to_string()],
262//! };
263//!
264//! let mcp_server = ScimMcpServer::with_info(scim_server, server_info);
265//! # Ok(())
266//! # }
267//! ```
268//!
269//! See `examples/mcp_server_example.rs` for a complete MCP integration demonstration.
270//!
271//! ### When to Use MCP Integration
272//!
273//! **Use MCP integration when:**
274//! - Building AI agents that need identity management capabilities
275//! - Creating automated provisioning systems with AI decision making
276//! - Developing chatbots or virtual assistants with user management features
277//! - Building intelligent HR systems with automated user lifecycle management
278//! - Creating AI-powered compliance and audit systems
279//!
280//! **Use regular SCIM server when:**
281//! - Building traditional web applications with SCIM endpoints
282//! - Integrating with existing identity providers (Okta, Azure AD, etc.)
283//! - Creating standard enterprise SCIM provisioning bridges
284//! - Building REST APIs for human operators or traditional applications
285//!
286//! The MCP integration adds AI-specific tooling and structured schemas on top of
287//! the core SCIM functionality without changing the underlying SCIM compliance.
288//!
289//! ## Architecture Overview
290//!
291//! The SCIM server library follows a layered architecture:
292//!
293//! ```text
294//! ┌─────────────────────────────────────────────────┐
295//! │                HTTP Layer                       │  ← Your web framework
296//! │             (Axum, Actix, etc.)                │
297//! ├─────────────────────────────────────────────────┤
298//! │              SCIM Protocol Layer                │  ← This library
299//! │         (ScimServer, SchemaDiscovery)           │
300//! ├─────────────────────────────────────────────────┤
301//! │            Provider Abstraction                 │  ← ResourceProvider trait
302//! │        (InMemoryProvider, your impl)            │
303//! ├─────────────────────────────────────────────────┤
304//! │              Storage Layer                      │  ← Your database/storage
305//! │        (PostgreSQL, MongoDB, etc.)              │
306//! └─────────────────────────────────────────────────┘
307//! ```
308//!
309//! ## ETag Concurrency Control
310//!
311//! The library provides built-in optimistic concurrency control using ETags as specified
312//! in RFC 7232. This prevents accidental overwrites when multiple clients modify the same
313//! resource simultaneously.
314//!
315//! ### Automatic Version Management
316//!
317//! All resources automatically include version information computed from their content:
318//!
319//! ```rust
320//! use scim_server::resource::{conditional_provider::VersionedResource, version::ScimVersion};
321//! use scim_server::{ScimServer, providers::InMemoryProvider};
322//! use serde_json::json;
323//!
324//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
325//! let provider = InMemoryProvider::new();
326//! let server = ScimServer::new(provider)?;
327//!
328//! // All operations automatically include version information
329//! let context = scim_server::resource::RequestContext::with_generated_id();
330//!
331//! // Create returns a versioned resource
332//! let user_data = json!({
333//!     "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
334//!     "userName": "john.doe",
335//!     "active": true
336//! });
337//!
338//! let versioned = server.provider().create_versioned_resource("User", user_data, &context).await.unwrap();
339//! println!("Resource version: {}", versioned.version().to_http_header());
340//! # Ok(())
341//! # }
342//! ```
343//!
344//! ### Conditional Operations
345//!
346//! Perform safe updates and deletes using expected versions:
347//!
348//! ```rust,no_run
349//! use scim_server::resource::{
350//!     version::{ScimVersion, ConditionalResult},
351//!     conditional_provider::VersionedResource,
352//!     RequestContext,
353//! };
354//! use scim_server::{ScimServer, providers::InMemoryProvider};
355//! use serde_json::json;
356//!
357//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
358//! let provider = InMemoryProvider::new();
359//! let server = ScimServer::new(provider)?;
360//! let context = RequestContext::with_generated_id();
361//!
362//! // Get current resource with version
363//! let versioned = server.provider().get_versioned_resource("User", "123", &context).await.unwrap();
364//! if let Some(versioned) = versioned {
365//!     let current_version = versioned.version().clone();
366//!
367//!     // Conditional update - only succeeds if version matches
368//!     let update_data = json!({
369//!         "id": "123",
370//!         "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
371//!         "userName": "john.doe",
372//!         "active": false  // Changed field
373//!     });
374//!
375//!     match server.provider().conditional_update(
376//!         "User", "123", update_data, &current_version, &context
377//!     ).await? {
378//!         ConditionalResult::Success(updated_resource) => {
379//!             println!("Update successful: {}", updated_resource.version().to_http_header());
380//!         },
381//!         ConditionalResult::VersionMismatch(conflict) => {
382//!             println!("Version conflict: {}", conflict.message);
383//!         },
384//!         ConditionalResult::NotFound => {
385//!             println!("Resource not found");
386//!         }
387//!     }
388//! }
389//! # Ok(())
390//! # }
391//! ```
392//!
393//! ### HTTP Integration
394//!
395//! ETags work seamlessly with HTTP frameworks:
396//!
397//! ```rust,no_run
398//! use scim_server::operation_handler::{ScimOperationHandler, ScimOperationRequest};
399//! use scim_server::resource::version::ScimVersion;
400//! use scim_server::{ScimServer, providers::InMemoryProvider};
401//! use serde_json::json;
402//!
403//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
404//! let provider = InMemoryProvider::new();
405//! let server = ScimServer::new(provider)?;
406//! let handler = ScimOperationHandler::new(server);
407//!
408//! // Conditional update with ETag from HTTP If-Match header
409//! let expected_version = ScimVersion::parse_http_header("W/\"abc123\"")?;
410//! let update_request = ScimOperationRequest::update(
411//!     "User",
412//!     "123",
413//!     json!({"userName": "jane.doe", "active": true})
414//! ).with_expected_version(expected_version);
415//!
416//! let response = handler.handle_operation(update_request).await;
417//! if response.success {
418//! if let Some(version_str) = response.metadata.additional.get("etag") {
419//!         println!("New weak ETag: {}", version_str);
420//!     }
421//! }
422//! # Ok(())
423//! # }
424//! ```
425//!
426//! ### Version Computation
427//!
428//! Versions are computed deterministically from resource content using SHA-256 hashing:
429//!
430//! - **Content-Based**: Versions change when any resource field changes
431//! - **Deterministic**: Same content always produces the same version
432//! - **Collision-Resistant**: Cryptographic hashing prevents accidental conflicts
433//! - **Provider-Agnostic**: Works with any storage backend
434//!
435//! ### Benefits
436//!
437//! - **Prevent Lost Updates**: Automatic detection of concurrent modifications
438//! - **Data Integrity**: Ensure operations only proceed with expected resource state
439//! - **HTTP Compliance**: Full RFC 7232 weak ETag support for web applications
440//! - **Zero Configuration**: Works automatically with any ResourceProvider implementation
441//! - **Performance**: Efficient hash-based versioning with minimal overhead
442//!
443//! ## Logging Support
444//!
445//! The library uses the standard Rust `log` crate facade, allowing you to choose your preferred
446//! logging backend. All SCIM operations are logged with structured information including:
447//!
448//! - Request IDs for operation tracing
449//! - Tenant context for multi-tenant deployments
450//! - Resource lifecycle events (create, read, update, delete)
451//! - Error conditions with full context
452//! - Performance and debugging information
453//!
454//! ### Quick Logging Setup
455//!
456//! ```rust,no_run
457//! // Simple logging for development
458//! env_logger::init();
459//!
460//! // Or with custom configuration
461//! env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info"))
462//!     .format_timestamp_secs()
463//!     .init();
464//! ```
465//!
466//! See `examples/logging_example.rs` for comprehensive logging demonstrations.
467//!
468//! ## Multi-Tenant Support
469//!
470//! The library provides built-in multi-tenant capabilities:
471//!
472//! ```rust
473//! use scim_server::{TenantContext, RequestContext};
474//! use serde_json::json;
475//!
476//! // Create tenant context
477//! let tenant = TenantContext::new("customer-123".to_string(), "app-456".to_string());
478//! let context = RequestContext::with_tenant_generated_id(tenant);
479//!
480//! // Example user data
481//! let user_data = json!({"userName": "john.doe", "active": true});
482//! println!("Tenant ID: {}", context.tenant_context.unwrap().tenant_id);
483//! println!("User data: {}", user_data);
484//!
485//! // All operations are automatically scoped to this tenant
486//! // let user = server.create_resource("User", user_data, &context).await?;
487//! ```
488//!
489//! ## Quick Start - Full SCIM Server
490//!
491//! ```rust,no_run
492//! use scim_server::{ScimServer, ResourceProvider, Resource, RequestContext, ScimOperation, ListQuery, create_user_resource_handler};
493//! use std::collections::HashMap;
494//! use tokio::sync::RwLock;
495//! use std::sync::Arc;
496//! use serde_json::Value;
497//! use std::future::Future;
498//!
499//! struct MyResourceProvider {
500//!     resources: Arc<RwLock<HashMap<String, Resource>>>,
501//! }
502//!
503//! impl MyResourceProvider {
504//!     fn new() -> Self {
505//!         Self {
506//!             resources: Arc::new(RwLock::new(HashMap::new())),
507//!         }
508//!     }
509//! }
510//!
511//! #[derive(Debug, thiserror::Error)]
512//! #[error("Provider error")]
513//! struct MyError;
514//!
515//! impl ResourceProvider for MyResourceProvider {
516//!     type Error = MyError;
517//!
518//!     fn create_resource(&self, resource_type: &str, data: Value, _context: &RequestContext) -> impl Future<Output = Result<Resource, Self::Error>> + Send {
519//!         async move {
520//!             Resource::from_json(resource_type.to_string(), data)
521//!                 .map_err(|_| MyError)
522//!         }
523//!     }
524//!
525//!     fn get_resource(&self, _resource_type: &str, _id: &str, _context: &RequestContext) -> impl Future<Output = Result<Option<Resource>, Self::Error>> + Send {
526//!         async move { Ok(None) }
527//!     }
528//!
529//!     fn update_resource(&self, resource_type: &str, _id: &str, data: Value, _context: &RequestContext) -> impl Future<Output = Result<Resource, Self::Error>> + Send {
530//!         async move {
531//!             Resource::from_json(resource_type.to_string(), data)
532//!                 .map_err(|_| MyError)
533//!         }
534//!     }
535//!
536//!     fn delete_resource(&self, _resource_type: &str, _id: &str, _context: &RequestContext) -> impl Future<Output = Result<(), Self::Error>> + Send {
537//!         async move { Ok(()) }
538//!     }
539//!
540//!     fn list_resources(&self, _resource_type: &str, _query: Option<&ListQuery>, _context: &RequestContext) -> impl Future<Output = Result<Vec<Resource>, Self::Error>> + Send {
541//!         async move { Ok(vec![]) }
542//!     }
543//!
544//!     fn find_resource_by_attribute(&self, _resource_type: &str, _attribute: &str, _value: &Value, _context: &RequestContext) -> impl Future<Output = Result<Option<Resource>, Self::Error>> + Send {
545//!         async move { Ok(None) }
546//!     }
547//!
548//!     fn resource_exists(&self, _resource_type: &str, _id: &str, _context: &RequestContext) -> impl Future<Output = Result<bool, Self::Error>> + Send {
549//!         async move { Ok(false) }
550//!     }
551//!
552//!     // Conditional operations are automatically available with default implementations
553//!     // Override conditional_update and conditional_delete for more efficient provider-specific implementations
554//! }
555//!
556//! #[tokio::main]
557//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
558//!     // Implement your data access layer
559//!     let provider = MyResourceProvider::new();
560//!
561//!     // Create dynamic SCIM server
562//!     let mut server = ScimServer::new(provider)?;
563//!
564//!     // Register resource types with their operations
565//!     let user_schema = server.get_schema_by_id("urn:ietf:params:scim:schemas:core:2.0:User").unwrap().clone();
566//!     let user_handler = create_user_resource_handler(user_schema);
567//!     let _ = server.register_resource_type("User", user_handler, vec![ScimOperation::Create, ScimOperation::Read]);
568//!
569//!     // Use server for SCIM operations
570//!     let schemas = server.get_all_schemas();
571//!     println!("Available schemas: {}", schemas.len());
572//!
573//!     Ok(())
574//! }
575//! ```
576//!
577//! ## Schema Discovery
578//!
579//! For schema discovery and service provider configuration only:
580//!
581//! ```rust
582//! use scim_server::SchemaDiscovery;
583//!
584//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
585//! let discovery = SchemaDiscovery::new()?;
586//!
587//! // Get available schemas
588//! let schemas = discovery.get_schemas().await?;
589//! println!("Available schemas: {}", schemas.len());
590//!
591//! // Get service provider configuration
592//! let config = discovery.get_service_provider_config().await?;
593//! println!("Bulk operations supported: {}", config.bulk_supported);
594//! # Ok(())
595//! # }
596//! ```
597//!
598//! ## Provider Implementation
599//!
600//! Implement the `ResourceProvider` trait for your storage backend:
601//!
602//! ```rust,no_run
603//! use scim_server::{ResourceProvider, Resource, RequestContext, ListQuery};
604//! use serde_json::Value;
605//! use std::future::Future;
606//!
607//! struct MyDatabaseProvider {
608//!     // Your database connection, etc.
609//! }
610//!
611//! #[derive(Debug, thiserror::Error)]
612//! #[error("Database error")]
613//! struct MyError;
614//!
615//! impl ResourceProvider for MyDatabaseProvider {
616//!     type Error = MyError;
617//!
618//!     fn create_resource(&self, resource_type: &str, data: Value, context: &RequestContext) -> impl Future<Output = Result<Resource, Self::Error>> + Send {
619//!         async move {
620//!             // Implement your database creation logic here
621//!             Resource::from_json(resource_type.to_string(), data)
622//!                 .map_err(|_| MyError)
623//!         }
624//!     }
625//!
626//!     // Implement other required methods...
627//!     # fn get_resource(&self, _resource_type: &str, _id: &str, _context: &RequestContext) -> impl Future<Output = Result<Option<Resource>, Self::Error>> + Send {
628//!     #     async move { Ok(None) }
629//!     # }
630//!     # fn update_resource(&self, resource_type: &str, _id: &str, data: Value, _context: &RequestContext) -> impl Future<Output = Result<Resource, Self::Error>> + Send {
631//!     #     async move { Resource::from_json(resource_type.to_string(), data).map_err(|_| MyError) }
632//!     # }
633//!     # fn delete_resource(&self, _resource_type: &str, _id: &str, _context: &RequestContext) -> impl Future<Output = Result<(), Self::Error>> + Send {
634//!     #     async move { Ok(()) }
635//!     # }
636//!     # fn list_resources(&self, _resource_type: &str, _query: Option<&ListQuery>, _context: &RequestContext) -> impl Future<Output = Result<Vec<Resource>, Self::Error>> + Send {
637//!     #     async move { Ok(vec![]) }
638//!     # }
639//!     # fn find_resource_by_attribute(&self, _resource_type: &str, _attribute: &str, _value: &Value, _context: &RequestContext) -> impl Future<Output = Result<Option<Resource>, Self::Error>> + Send {
640//!     #     async move { Ok(None) }
641//!     # }
642//!     # fn resource_exists(&self, _resource_type: &str, _id: &str, _context: &RequestContext) -> impl Future<Output = Result<bool, Self::Error>> + Send {
643//!     #     async move { Ok(false) }
644//!     # }
645//! }
646//! ```
647//!
648//! ## Examples
649//!
650//! This crate includes comprehensive examples in the `examples/` directory:
651//!
652//! - **`basic_usage.rs`** - Simple SCIM server setup
653//! - **`etag_concurrency_example.rs`** - ETag concurrency control and conditional operations
654//! - **`mcp_etag_example.rs`** - AI agent ETag usage with Model Context Protocol
655//! - **`multi_tenant_demo.rs`** - Multi-tenant operations
656//! - **`logging_example.rs`** - Comprehensive logging configuration
657//! - **`operation_handler_example.rs`** - Framework-agnostic operation handling
658//! - **`provider_capabilities.rs`** - Automatic capability discovery
659//!
660//! Run examples with:
661//! ```bash
662//! cargo run --example basic_usage
663//! cargo run --example etag_concurrency_example
664//! cargo run --example mcp_etag_example --features mcp
665//! ```
666//!
667//! ## Schema Validation Utility
668//!
669//! This crate includes a command-line schema validation utility for testing and validating
670//! SCIM schema files. The `schema-validator` binary helps ensure your schemas conform to
671//! the expected format before using them in production.
672//!
673//! ### Usage
674//!
675//! #### During Development (with Cargo)
676//! ```bash
677//! # Validate a single schema file
678//! cargo run --bin schema-validator path/to/User.json
679//!
680//! # Validate all schemas in a directory
681//! cargo run --bin schema-validator ./path/to/schemas/
682//! ```
683//!
684//! #### Standalone Installation
685//! ```bash
686//! # Install the binary globally
687//! cargo install --path . --bin schema-validator
688//!
689//! # Then use directly
690//! schema-validator path/to/User.json
691//! schema-validator ./path/to/schemas/
692//! ```
693//!
694//! #### From Published Crate (when available)
695//! ```bash
696//! # Install from crates.io
697//! cargo install scim-server --bin schema-validator
698//!
699//! # Use anywhere
700//! schema-validator /path/to/custom/schemas/
701//! ```
702//!
703//! ### Features
704//!
705//! - **Schema File Validation**: Validates JSON structure and SCIM schema format
706//! - **Directory Processing**: Batch validation of multiple schema files
707//! - **Schema Registry Testing**: Tests loading schemas into the registry
708//! - **Detailed Error Reporting**: Clear error messages for debugging
709//! - **Schema Summary**: Displays attribute counts and types for valid schemas
710//!
711//! ### Example Output
712//!
713//! ```text
714//! Validating schema file: path/to/User.json
715//! ✓ Schema is valid!
716//!
717//! Schema Summary:
718//!   ID: urn:ietf:params:scim:schemas:core:2.0:User
719//!   Name: User
720//!   Attributes: 15
721//!   Required attributes: 2
722//!   Multi-valued attributes: 4
723//!   Required attribute names: id, userName
724//! ```
725//!
726//! The validator performs comprehensive checks including:
727//! - JSON syntax validation
728//! - Required field presence (id, name, attributes)
729//! - Schema ID URI format validation
730//! - Attribute structure validation
731//! - Complex attribute sub-attribute validation
732//! - Canonical values format checking
733//!
734//! ## Performance
735//!
736//! The library is designed for high performance with:
737//! - Zero-copy JSON processing where possible
738//! - Async-first architecture for high concurrency
739//! - Efficient value object system with minimal allocations
740//! - Type-safe operations that compile to efficient code
741//!
742//! Benchmarks show 40,000+ operations/second on modern hardware.
743//!
744//! ## SCIM 2.0 Compliance
745//!
746//! This library implements the following SCIM 2.0 specifications:
747//! - **RFC 7643**: SCIM Core Schema (User, Group, Schema definitions)
748//! - **RFC 7644**: SCIM Protocol (HTTP operations, filtering, pagination)
749//!
750//! ### Supported Operations
751//! - Resource CRUD (Create, Read, Update, Delete)
752//! - Resource listing with pagination
753//! - Attribute-based search and filtering
754//! - Schema discovery and introspection
755//! - Service provider configuration
756//! - Multi-valued attribute handling
757//! - Complex attribute validation
758//! - Conditional operations with ETag support
759//! - Optimistic concurrency control
760//!
761//! ### Standards Compliance
762//! - Full User schema implementation
763//! - Group schema support
764//! - Extension schema framework
765//! - JSON Schema validation
766//! - HTTP status code compliance
767//! - Error response formatting
768//! - **RFC 7232 weak ETag support for conditional operations**
769//! - RFC 7644 versioning requirements
770//!
771//! ## Contributing
772//!
773//! Contributions are welcome! Please see the repository for:
774//! - Issue reporting and feature requests
775//! - Pull request guidelines
776//! - Development setup instructions
777//! - Testing requirements
778//!
779//! ## License
780//!
781//! This project is licensed under the MIT License - see the LICENSE file for details.
782pub mod auth;
783pub mod error;
784#[cfg(feature = "mcp")]
785pub mod mcp_integration;
786pub mod multi_tenant;
787pub mod operation_handler;
788pub mod provider_capabilities;
789pub mod providers;
790pub mod resource;
791pub mod resource_handlers;
792pub mod schema;
793pub mod schema_discovery;
794pub mod scim_server;
795pub mod storage;
796
797// Core re-exports for library users
798pub use error::{BuildError, ScimError, ValidationError};
799
800// Provider capability discovery system
801pub use provider_capabilities::{
802    AuthenticationCapabilities, BulkCapabilities, CapabilityDiscovery, CapabilityIntrospectable,
803    ExtendedCapabilities, FilterCapabilities, FilterOperator, PaginationCapabilities,
804    ProviderCapabilities,
805};
806// SCIM-focused multi-tenant configuration (recommended)
807pub use multi_tenant::{
808    RateLimit, ScimAuditConfig, ScimAuthScheme, ScimClientAuth, ScimClientConfig,
809    ScimConfigurationError, ScimCustomAttribute, ScimEndpointConfig, ScimOperation, ScimRateLimits,
810    ScimSchemaConfig, ScimSchemaExtension, ScimSearchConfig, ScimTenantConfiguration,
811};
812
813// Standard provider implementations
814pub use providers::{InMemoryError, InMemoryProvider, InMemoryStats, StandardResourceProvider};
815
816// Storage layer - pluggable backend implementations
817pub use storage::{
818    InMemoryStorage, InMemoryStorageStats, StorageError, StorageKey, StoragePrefix, StorageProvider,
819};
820
821// Multi-tenant provider and resolver components
822pub use multi_tenant::{
823    SingleTenantAdapter, StaticTenantResolver, TenantResolver, TenantValidator, ToSingleTenant,
824};
825
826pub use resource::{
827    Address, DatabaseMapper, DynamicResource, EmailAddress, IsolationLevel, ListQuery, Meta, Name,
828    PhoneNumber, RequestContext, Resource, ResourceBuilder, ResourceProvider, ResourceProviderExt,
829    SchemaResourceBuilder, TenantContext, TenantPermissions,
830};
831pub use resource_handlers::{create_group_resource_handler, create_user_resource_handler};
832pub use schema::{
833    AttributeDefinition, AttributeType, Mutability, Schema, SchemaRegistry, Uniqueness,
834};
835pub use schema_discovery::{SchemaDiscovery, ServiceProviderConfig};
836pub use scim_server::ScimServer;
837
838// Operation handler foundation for framework-agnostic integration
839pub use operation_handler::{
840    OperationMetadata, ScimOperationHandler, ScimOperationRequest, ScimOperationResponse,
841    ScimOperationType, ScimQuery,
842};
843
844// MCP (Model Context Protocol) integration for AI agents (optional feature)
845#[cfg(feature = "mcp")]
846pub use mcp_integration::{McpServerInfo, ScimMcpServer, ScimToolResult};