use crate::domain::models::*; use anyhow::Result; use chrono::{DateTime, Utc}; /// Query Object pattern for complex database queries pub trait Query { fn execute(&self) -> Result>; } /// Base query criteria #[derive(Debug, Clone)] pub struct QueryCriteria { pub limit: Option, pub offset: Option, pub order_by: Option, pub order_direction: Option, } #[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, pub user_id: Option, pub event_type: Option, pub success: Option, pub date_from: Option>, pub date_to: Option>, pub ip_address: Option, } 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, to: DateTime) -> 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, pub client_name_contains: Option, pub has_scope: Option, pub grant_type: Option, } 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, pub date_from: Option>, pub date_to: Option>, 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, to: DateTime) -> 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, 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, pub ip_address: Option, pub date_from: Option>, pub date_to: Option>, 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, pub ip_address: Option, pub attempt_count: u32, pub first_attempt: DateTime, pub last_attempt: DateTime, } /// Query executor trait pub trait QueryExecutor { fn execute_audit_query(&self, query: &AuditEventsQuery) -> Result>; fn execute_client_query(&self, query: &OAuthClientsQuery) -> Result>; fn execute_token_usage_query(&self, query: &TokenUsageQuery) -> Result>; fn execute_failed_auth_query(&self, query: &FailedAuthQuery) -> Result>; } /// 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") } }