use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityAnalysis {
pub schemes: HashMap<String, SecuritySchemeDetails>,
pub global_requirements: SecurityRequirement,
pub operation_requirements: HashMap<String, SecurityRequirement>,
pub summary: AuthSummary,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecuritySchemeDetails {
pub scheme_type: SchemeType,
pub location: Option<AuthLocation>,
pub name: Option<String>,
pub bearer_format: Option<String>,
pub flows: Option<OAuth2Flows>,
pub openid_connect_url: Option<String>,
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum SchemeType {
ApiKey,
Http,
OAuth2,
OpenIdConnect,
MutualTls,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum AuthLocation {
Query,
Header,
Cookie,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OAuth2Flows {
pub implicit: Option<OAuth2Flow>,
pub password: Option<OAuth2Flow>,
pub client_credentials: Option<OAuth2Flow>,
pub authorization_code: Option<OAuth2Flow>,
pub device_code: Option<OAuth2Flow>, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OAuth2Flow {
pub authorization_url: Option<String>,
pub token_url: Option<String>,
pub refresh_url: Option<String>,
pub scopes: HashMap<String, String>,
pub device_url: Option<String>, }
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SecurityRequirement {
pub options: Vec<RequirementOption>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RequirementOption {
pub schemes: Vec<SchemeRequirement>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SchemeRequirement {
pub name: String,
pub scopes: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthSummary {
pub total_schemes: usize,
pub required_schemes: HashSet<String>,
pub optional_schemes: HashSet<String>,
pub operations_with_custom_auth: usize,
pub operations_without_auth: usize,
pub most_common_scheme: Option<String>,
pub complexity_score: AuthComplexity,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AuthComplexity {
None,
Simple,
Moderate,
Complex,
}
impl SecurityRequirement {
#[allow(dead_code)]
pub fn is_satisfied_by(&self, available_schemes: &HashSet<String>) -> bool {
self.options.iter().any(|option| {
option
.schemes
.iter()
.all(|scheme| available_schemes.contains(&scheme.name))
})
}
pub fn simplest_option(&self) -> Option<&RequirementOption> {
self.options.iter().min_by_key(|opt| opt.schemes.len())
}
pub fn is_optional(&self) -> bool {
self.options.is_empty() || self.options.iter().any(|opt| opt.schemes.is_empty())
}
}