diff options
Diffstat (limited to 'src/domain/queries.rs')
| -rw-r--r-- | src/domain/queries.rs | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/src/domain/queries.rs b/src/domain/queries.rs new file mode 100644 index 0000000..d4eb19e --- /dev/null +++ b/src/domain/queries.rs @@ -0,0 +1,307 @@ +use crate::domain::models::*; +use anyhow::Result; +use chrono::{DateTime, Utc}; + +/// Query Object pattern for complex database queries +pub trait Query<T> { + fn execute(&self) -> Result<Vec<T>>; +} + +/// Base query criteria +#[derive(Debug, Clone)] +pub struct QueryCriteria { + pub limit: Option<u32>, + pub offset: Option<u32>, + pub order_by: Option<String>, + pub order_direction: Option<OrderDirection>, +} + +#[derive(Debug, Clone)] +pub enum OrderDirection { + Asc, + Desc, +} + +impl Default for QueryCriteria { + fn default() -> Self { + Self { + limit: Some(100), + offset: None, + order_by: None, + order_direction: Some(OrderDirection::Desc), + } + } +} + +/// Audit Events Query Object +#[derive(Debug, Clone)] +pub struct AuditEventsQuery { + pub criteria: QueryCriteria, + pub client_id: Option<String>, + pub user_id: Option<String>, + pub event_type: Option<String>, + pub success: Option<bool>, + pub date_from: Option<DateTime<Utc>>, + pub date_to: Option<DateTime<Utc>>, + pub ip_address: Option<String>, +} + +impl AuditEventsQuery { + pub fn new() -> Self { + Self { + criteria: QueryCriteria::default(), + client_id: None, + user_id: None, + event_type: None, + success: None, + date_from: None, + date_to: None, + ip_address: None, + } + } + + pub fn for_client(mut self, client_id: &str) -> Self { + self.client_id = Some(client_id.to_string()); + self + } + + pub fn for_user(mut self, user_id: &str) -> Self { + self.user_id = Some(user_id.to_string()); + self + } + + pub fn event_type(mut self, event_type: &str) -> Self { + self.event_type = Some(event_type.to_string()); + self + } + + pub fn successful_only(mut self) -> Self { + self.success = Some(true); + self + } + + pub fn failed_only(mut self) -> Self { + self.success = Some(false); + self + } + + pub fn date_range(mut self, from: DateTime<Utc>, to: DateTime<Utc>) -> Self { + self.date_from = Some(from); + self.date_to = Some(to); + self + } + + pub fn from_ip(mut self, ip_address: &str) -> Self { + self.ip_address = Some(ip_address.to_string()); + self + } + + pub fn limit(mut self, limit: u32) -> Self { + self.criteria.limit = Some(limit); + self + } + + pub fn offset(mut self, offset: u32) -> Self { + self.criteria.offset = Some(offset); + self + } +} + +/// OAuth Clients Query Object +#[derive(Debug, Clone)] +pub struct OAuthClientsQuery { + pub criteria: QueryCriteria, + pub is_active: Option<bool>, + pub client_name_contains: Option<String>, + pub has_scope: Option<String>, + pub grant_type: Option<String>, +} + +impl OAuthClientsQuery { + pub fn new() -> Self { + Self { + criteria: QueryCriteria::default(), + is_active: None, + client_name_contains: None, + has_scope: None, + grant_type: None, + } + } + + pub fn active_only(mut self) -> Self { + self.is_active = Some(true); + self + } + + pub fn inactive_only(mut self) -> Self { + self.is_active = Some(false); + self + } + + pub fn name_contains(mut self, name_part: &str) -> Self { + self.client_name_contains = Some(name_part.to_string()); + self + } + + pub fn with_scope(mut self, scope: &str) -> Self { + self.has_scope = Some(scope.to_string()); + self + } + + pub fn with_grant_type(mut self, grant_type: &str) -> Self { + self.grant_type = Some(grant_type.to_string()); + self + } +} + +/// Token Usage Analytics Query +#[derive(Debug, Clone)] +pub struct TokenUsageQuery { + pub criteria: QueryCriteria, + pub client_id: Option<String>, + pub date_from: Option<DateTime<Utc>>, + pub date_to: Option<DateTime<Utc>>, + pub group_by: TokenUsageGroupBy, +} + +#[derive(Debug, Clone)] +pub enum TokenUsageGroupBy { + Hour, + Day, + Week, + Month, + Client, +} + +impl TokenUsageQuery { + pub fn new(group_by: TokenUsageGroupBy) -> Self { + Self { + criteria: QueryCriteria::default(), + client_id: None, + date_from: None, + date_to: None, + group_by, + } + } + + pub fn for_client(mut self, client_id: &str) -> Self { + self.client_id = Some(client_id.to_string()); + self + } + + pub fn date_range(mut self, from: DateTime<Utc>, to: DateTime<Utc>) -> Self { + self.date_from = Some(from); + self.date_to = Some(to); + self + } +} + +/// Token Usage Statistics Result +#[derive(Debug, Clone)] +pub struct TokenUsageStats { + pub period: String, + pub client_id: Option<String>, + pub token_count: u32, + pub unique_users: u32, + pub success_rate: f64, +} + +/// Failed Authorization Attempts Query +#[derive(Debug, Clone)] +pub struct FailedAuthQuery { + pub criteria: QueryCriteria, + pub client_id: Option<String>, + pub ip_address: Option<String>, + pub date_from: Option<DateTime<Utc>>, + pub date_to: Option<DateTime<Utc>>, + pub min_attempts: u32, +} + +impl FailedAuthQuery { + pub fn new() -> Self { + Self { + criteria: QueryCriteria::default(), + client_id: None, + ip_address: None, + date_from: None, + date_to: None, + min_attempts: 5, // Minimum failed attempts to be considered suspicious + } + } + + pub fn for_client(mut self, client_id: &str) -> Self { + self.client_id = Some(client_id.to_string()); + self + } + + pub fn from_ip(mut self, ip_address: &str) -> Self { + self.ip_address = Some(ip_address.to_string()); + self + } + + pub fn min_attempts(mut self, attempts: u32) -> Self { + self.min_attempts = attempts; + self + } + + pub fn last_24_hours(mut self) -> Self { + let now = Utc::now(); + let yesterday = now - chrono::Duration::hours(24); + self.date_from = Some(yesterday); + self.date_to = Some(now); + self + } +} + +/// Failed Authorization Result +#[derive(Debug, Clone)] +pub struct FailedAuthResult { + pub client_id: Option<String>, + pub ip_address: Option<String>, + pub attempt_count: u32, + pub first_attempt: DateTime<Utc>, + pub last_attempt: DateTime<Utc>, +} + +/// Query executor trait +pub trait QueryExecutor { + fn execute_audit_query(&self, query: &AuditEventsQuery) -> Result<Vec<AuditEvent>>; + fn execute_client_query(&self, query: &OAuthClientsQuery) -> Result<Vec<OAuthClient>>; + fn execute_token_usage_query(&self, query: &TokenUsageQuery) -> Result<Vec<TokenUsageStats>>; + fn execute_failed_auth_query(&self, query: &FailedAuthQuery) -> Result<Vec<FailedAuthResult>>; +} + +/// Predefined queries for common use cases +pub struct CommonQueries; + +impl CommonQueries { + /// Get recent failed login attempts (security monitoring) + pub fn recent_failed_logins() -> FailedAuthQuery { + FailedAuthQuery::new() + .last_24_hours() + .min_attempts(3) + } + + /// Get audit trail for a specific client + pub fn client_audit_trail(client_id: &str) -> AuditEventsQuery { + AuditEventsQuery::new() + .for_client(client_id) + .limit(1000) + } + + /// Get token usage statistics for the last 30 days + pub fn monthly_token_usage() -> TokenUsageQuery { + let now = Utc::now(); + let thirty_days_ago = now - chrono::Duration::days(30); + + TokenUsageQuery::new(TokenUsageGroupBy::Day) + .date_range(thirty_days_ago, now) + } + + /// Get all active clients with OpenID scope + pub fn openid_clients() -> OAuthClientsQuery { + OAuthClientsQuery::new() + .active_only() + .with_scope("openid") + } +}
\ No newline at end of file |
