scim_server/resource/
context.rs

1//! Request context and query structures for SCIM operations.
2//!
3//! This module provides request tracking, tenant context, and query parameters
4//! for SCIM operations with support for multi-tenant environments.
5
6use crate::resource::tenant::{IsolationLevel, TenantContext};
7use uuid::Uuid;
8
9/// Request context for SCIM operations.
10///
11/// Provides request tracking for logging and auditing purposes.
12/// Optionally includes tenant context for multi-tenant operations.
13#[derive(Debug, Clone)]
14pub struct RequestContext {
15    /// Unique identifier for this request
16    pub request_id: String,
17    /// Optional tenant context for multi-tenant operations
18    pub tenant_context: Option<TenantContext>,
19}
20
21impl RequestContext {
22    /// Create a new request context with a specific request ID.
23    pub fn new(request_id: String) -> Self {
24        Self {
25            request_id,
26            tenant_context: None,
27        }
28    }
29
30    /// Create a new request context with a generated request ID.
31    pub fn with_generated_id() -> Self {
32        Self {
33            request_id: Uuid::new_v4().to_string(),
34            tenant_context: None,
35        }
36    }
37
38    /// Create a new request context with tenant information.
39    pub fn with_tenant(request_id: String, tenant_context: TenantContext) -> Self {
40        Self {
41            request_id,
42            tenant_context: Some(tenant_context),
43        }
44    }
45
46    /// Create a new request context with generated ID and tenant information.
47    pub fn with_tenant_generated_id(tenant_context: TenantContext) -> Self {
48        Self {
49            request_id: Uuid::new_v4().to_string(),
50            tenant_context: Some(tenant_context),
51        }
52    }
53
54    /// Get the tenant ID if this is a multi-tenant request.
55    pub fn tenant_id(&self) -> Option<&str> {
56        self.tenant_context.as_ref().map(|t| t.tenant_id.as_str())
57    }
58
59    /// Get the client ID if this is a multi-tenant request.
60    pub fn client_id(&self) -> Option<&str> {
61        self.tenant_context.as_ref().map(|t| t.client_id.as_str())
62    }
63
64    /// Check if this is a multi-tenant request.
65    pub fn is_multi_tenant(&self) -> bool {
66        self.tenant_context.is_some()
67    }
68
69    /// Get the isolation level for this request.
70    pub fn isolation_level(&self) -> Option<&IsolationLevel> {
71        self.tenant_context.as_ref().map(|t| &t.isolation_level)
72    }
73
74    /// Check if the tenant has permission for a specific operation.
75    pub fn can_perform_operation(&self, operation: &str) -> bool {
76        match &self.tenant_context {
77            Some(tenant) => tenant.can_perform_operation(operation),
78            None => true, // Single-tenant operations are always allowed
79        }
80    }
81
82    /// Validate that this context can perform the requested operation.
83    pub fn validate_operation(&self, operation: &str) -> Result<(), String> {
84        if self.can_perform_operation(operation) {
85            Ok(())
86        } else {
87            Err(format!(
88                "Operation '{}' not permitted for tenant",
89                operation
90            ))
91        }
92    }
93}
94
95impl Default for RequestContext {
96    fn default() -> Self {
97        Self::with_generated_id()
98    }
99}
100
101/// Query parameters for listing resources.
102///
103/// This structure supports pagination, filtering, and attribute selection
104/// for SCIM list operations.
105#[derive(Debug, Clone, Default)]
106pub struct ListQuery {
107    /// Maximum number of results to return
108    pub count: Option<usize>,
109    /// Starting index for pagination
110    pub start_index: Option<usize>,
111    /// Filter expression
112    pub filter: Option<String>,
113    /// Attributes to include in results
114    pub attributes: Vec<String>,
115    /// Attributes to exclude from results
116    pub excluded_attributes: Vec<String>,
117}
118
119impl ListQuery {
120    /// Create a new empty query.
121    pub fn new() -> Self {
122        Self::default()
123    }
124
125    /// Set the maximum count.
126    pub fn with_count(mut self, count: usize) -> Self {
127        self.count = Some(count);
128        self
129    }
130
131    /// Set the starting index.
132    pub fn with_start_index(mut self, start_index: usize) -> Self {
133        self.start_index = Some(start_index);
134        self
135    }
136
137    /// Set a filter expression.
138    pub fn with_filter(mut self, filter: String) -> Self {
139        self.filter = Some(filter);
140        self
141    }
142
143    /// Add an attribute to include in results.
144    pub fn with_attribute(mut self, attribute: String) -> Self {
145        self.attributes.push(attribute);
146        self
147    }
148
149    /// Add multiple attributes to include in results.
150    pub fn with_attributes(mut self, attributes: Vec<String>) -> Self {
151        self.attributes.extend(attributes);
152        self
153    }
154
155    /// Add an attribute to exclude from results.
156    pub fn with_excluded_attribute(mut self, attribute: String) -> Self {
157        self.excluded_attributes.push(attribute);
158        self
159    }
160
161    /// Add multiple attributes to exclude from results.
162    pub fn with_excluded_attributes(mut self, attributes: Vec<String>) -> Self {
163        self.excluded_attributes.extend(attributes);
164        self
165    }
166}