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() {
585//! let discovery = SchemaDiscovery::new().unwrap();
586//! // Get available schemas
587//! let schemas = discovery.get_schemas().await.unwrap();
588//! println!("Available schemas: {}", schemas.len());
589//!
590//! // Get service provider configuration
591//! let config = discovery.get_service_provider_config().await.unwrap();
592//! println!("Bulk operations supported: {}", config.bulk_supported);
593//! # }
594//! ```
595//!
596//! ## Provider Implementation
597//!
598//! Implement the `ResourceProvider` trait for your storage backend:
599//!
600//! ```rust,no_run
601//! use scim_server::{ResourceProvider, Resource, RequestContext, ListQuery};
602//! use serde_json::Value;
603//! use std::future::Future;
604//!
605//! struct MyDatabaseProvider {
606//!     // Your database connection, etc.
607//! }
608//!
609//! #[derive(Debug, thiserror::Error)]
610//! #[error("Database error")]
611//! struct MyError;
612//!
613//! impl ResourceProvider for MyDatabaseProvider {
614//!     type Error = MyError;
615//!
616//!     fn create_resource(&self, resource_type: &str, data: Value, context: &RequestContext) -> impl Future<Output = Result<Resource, Self::Error>> + Send {
617//!         async move {
618//!             // Implement your database creation logic here
619//!             Resource::from_json(resource_type.to_string(), data)
620//!                 .map_err(|_| MyError)
621//!         }
622//!     }
623//!
624//!     // Implement other required methods...
625//!     # fn get_resource(&self, _resource_type: &str, _id: &str, _context: &RequestContext) -> impl Future<Output = Result<Option<Resource>, Self::Error>> + Send {
626//!     #     async move { Ok(None) }
627//!     # }
628//!     # fn update_resource(&self, resource_type: &str, _id: &str, data: Value, _context: &RequestContext) -> impl Future<Output = Result<Resource, Self::Error>> + Send {
629//!     #     async move { Resource::from_json(resource_type.to_string(), data).map_err(|_| MyError) }
630//!     # }
631//!     # fn delete_resource(&self, _resource_type: &str, _id: &str, _context: &RequestContext) -> impl Future<Output = Result<(), Self::Error>> + Send {
632//!     #     async move { Ok(()) }
633//!     # }
634//!     # fn list_resources(&self, _resource_type: &str, _query: Option<&ListQuery>, _context: &RequestContext) -> impl Future<Output = Result<Vec<Resource>, Self::Error>> + Send {
635//!     #     async move { Ok(vec![]) }
636//!     # }
637//!     # fn find_resource_by_attribute(&self, _resource_type: &str, _attribute: &str, _value: &Value, _context: &RequestContext) -> impl Future<Output = Result<Option<Resource>, Self::Error>> + Send {
638//!     #     async move { Ok(None) }
639//!     # }
640//!     # fn resource_exists(&self, _resource_type: &str, _id: &str, _context: &RequestContext) -> impl Future<Output = Result<bool, Self::Error>> + Send {
641//!     #     async move { Ok(false) }
642//!     # }
643//! }
644//! ```
645//!
646//! ## Examples
647//!
648//! This crate includes comprehensive examples in the `examples/` directory:
649//!
650//! - **`basic_usage.rs`** - Simple SCIM server setup
651//! - **`etag_concurrency_example.rs`** - ETag concurrency control and conditional operations
652//! - **`mcp_etag_example.rs`** - AI agent ETag usage with Model Context Protocol
653//! - **`multi_tenant_demo.rs`** - Multi-tenant operations
654//! - **`logging_example.rs`** - Comprehensive logging configuration
655//! - **`operation_handler_example.rs`** - Framework-agnostic operation handling
656//! - **`provider_capabilities.rs`** - Automatic capability discovery
657//!
658//! Run examples with:
659//! ```bash
660//! cargo run --example basic_usage
661//! cargo run --example etag_concurrency_example
662//! cargo run --example mcp_etag_example --features mcp
663//! ```
664//!
665//! ## Schema Validation Utility
666//!
667//! This crate includes a command-line schema validation utility for testing and validating
668//! SCIM schema files. The `schema-validator` binary helps ensure your schemas conform to
669//! the expected format before using them in production.
670//!
671//! ### Usage
672//!
673//! #### During Development (with Cargo)
674//! ```bash
675//! # Validate a single schema file
676//! cargo run --bin schema-validator schemas/User.json
677//!
678//! # Validate all schemas in a directory
679//! cargo run --bin schema-validator ./schemas/
680//! ```
681//!
682//! #### Standalone Installation
683//! ```bash
684//! # Install the binary globally
685//! cargo install --path . --bin schema-validator
686//!
687//! # Then use directly
688//! schema-validator schemas/User.json
689//! schema-validator ./schemas/
690//! ```
691//!
692//! #### From Published Crate (when available)
693//! ```bash
694//! # Install from crates.io
695//! cargo install scim-server --bin schema-validator
696//!
697//! # Use anywhere
698//! schema-validator /path/to/schemas/
699//! ```
700//!
701//! ### Features
702//!
703//! - **Schema File Validation**: Validates JSON structure and SCIM schema format
704//! - **Directory Processing**: Batch validation of multiple schema files
705//! - **Schema Registry Testing**: Tests loading schemas into the registry
706//! - **Detailed Error Reporting**: Clear error messages for debugging
707//! - **Schema Summary**: Displays attribute counts and types for valid schemas
708//!
709//! ### Example Output
710//!
711//! ```text
712//! Validating schema file: schemas/User.json
713//! ✓ Schema is valid!
714//!
715//! Schema Summary:
716//!   ID: urn:ietf:params:scim:schemas:core:2.0:User
717//!   Name: User
718//!   Attributes: 15
719//!   Required attributes: 2
720//!   Multi-valued attributes: 4
721//!   Required attribute names: id, userName
722//! ```
723//!
724//! The validator performs comprehensive checks including:
725//! - JSON syntax validation
726//! - Required field presence (id, name, attributes)
727//! - Schema ID URI format validation
728//! - Attribute structure validation
729//! - Complex attribute sub-attribute validation
730//! - Canonical values format checking
731//!
732//! ## Performance
733//!
734//! The library is designed for high performance with:
735//! - Zero-copy JSON processing where possible
736//! - Async-first architecture for high concurrency
737//! - Efficient value object system with minimal allocations
738//! - Type-safe operations that compile to efficient code
739//!
740//! Benchmarks show 40,000+ operations/second on modern hardware.
741//!
742//! ## SCIM 2.0 Compliance
743//!
744//! This library implements the following SCIM 2.0 specifications:
745//! - **RFC 7643**: SCIM Core Schema (User, Group, Schema definitions)
746//! - **RFC 7644**: SCIM Protocol (HTTP operations, filtering, pagination)
747//!
748//! ### Supported Operations
749//! - Resource CRUD (Create, Read, Update, Delete)
750//! - Resource listing with pagination
751//! - Attribute-based search and filtering
752//! - Schema discovery and introspection
753//! - Service provider configuration
754//! - Multi-valued attribute handling
755//! - Complex attribute validation
756//! - Conditional operations with ETag support
757//! - Optimistic concurrency control
758//!
759//! ### Standards Compliance
760//! - Full User schema implementation
761//! - Group schema support
762//! - Extension schema framework
763//! - JSON Schema validation
764//! - HTTP status code compliance
765//! - Error response formatting
766//! - **RFC 7232 weak ETag support for conditional operations**
767//! - RFC 7644 versioning requirements
768//!
769//! ## Contributing
770//!
771//! Contributions are welcome! Please see the repository for:
772//! - Issue reporting and feature requests
773//! - Pull request guidelines
774//! - Development setup instructions
775//! - Testing requirements
776//!
777//! ## License
778//!
779//! This project is licensed under the MIT License - see the LICENSE file for details.
780pub mod auth;
781pub mod error;
782#[cfg(feature = "mcp")]
783pub mod mcp_integration;
784pub mod multi_tenant;
785pub mod operation_handler;
786pub mod provider_capabilities;
787pub mod providers;
788pub mod resource;
789pub mod resource_handlers;
790pub mod schema;
791pub mod schema_discovery;
792pub mod scim_server;
793pub mod storage;
794
795// Core re-exports for library users
796pub use error::{BuildError, ScimError, ValidationError};
797
798// Provider capability discovery system
799pub use provider_capabilities::{
800    AuthenticationCapabilities, BulkCapabilities, CapabilityDiscovery, CapabilityIntrospectable,
801    ExtendedCapabilities, FilterCapabilities, FilterOperator, PaginationCapabilities,
802    ProviderCapabilities,
803};
804// SCIM-focused multi-tenant configuration (recommended)
805pub use multi_tenant::{
806    RateLimit, ScimAuditConfig, ScimAuthScheme, ScimClientAuth, ScimClientConfig,
807    ScimConfigurationError, ScimCustomAttribute, ScimEndpointConfig, ScimOperation, ScimRateLimits,
808    ScimSchemaConfig, ScimSchemaExtension, ScimSearchConfig, ScimTenantConfiguration,
809};
810
811// Standard provider implementations
812pub use providers::{InMemoryError, InMemoryProvider, InMemoryStats};
813
814// Storage layer - pluggable backend implementations
815pub use storage::{
816    InMemoryStorage, InMemoryStorageStats, StorageError, StorageKey, StoragePrefix, StorageProvider,
817};
818
819// Multi-tenant provider and resolver components
820pub use multi_tenant::{
821    SingleTenantAdapter, StaticTenantResolver, TenantResolver, TenantValidator, ToSingleTenant,
822};
823
824pub use resource::{
825    Address, DatabaseMapper, DynamicResource, EmailAddress, IsolationLevel, ListQuery, Meta, Name,
826    PhoneNumber, RequestContext, Resource, ResourceBuilder, ResourceProvider, ResourceProviderExt,
827    SchemaResourceBuilder, TenantContext, TenantPermissions,
828};
829pub use resource_handlers::{create_group_resource_handler, create_user_resource_handler};
830pub use schema::{
831    AttributeDefinition, AttributeType, Mutability, Schema, SchemaRegistry, Uniqueness,
832};
833pub use schema_discovery::{SchemaDiscovery, ServiceProviderConfig};
834pub use scim_server::ScimServer;
835
836// Operation handler foundation for framework-agnostic integration
837pub use operation_handler::{
838    OperationMetadata, ScimOperationHandler, ScimOperationRequest, ScimOperationResponse,
839    ScimOperationType, ScimQuery,
840};
841
842// MCP (Model Context Protocol) integration for AI agents (optional feature)
843#[cfg(feature = "mcp")]
844pub use mcp_integration::{McpServerInfo, ScimMcpServer, ScimToolResult};