use std::collections::HashMap;
use serde::de::{self, Deserializer};
use serde::ser::{SerializeMap, Serializer};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SecurityScheme {
ApiKey(ApiKeySecurityScheme),
Http(HttpAuthSecurityScheme),
OAuth2(Box<OAuth2SecurityScheme>),
OpenIdConnect(OpenIdConnectSecurityScheme),
MutualTLS(MutualTlsSecurityScheme),
}
impl Serialize for SecurityScheme {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(Some(1))?;
match self {
Self::ApiKey(v) => map.serialize_entry("apiKey", v)?,
Self::Http(v) => map.serialize_entry("http", v)?,
Self::OAuth2(v) => map.serialize_entry("oauth2", v.as_ref())?,
Self::OpenIdConnect(v) => map.serialize_entry("openIdConnect", v)?,
Self::MutualTLS(v) => map.serialize_entry("mutualTLS", v)?,
}
map.end()
}
}
impl<'de> Deserialize<'de> for SecurityScheme {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let raw: HashMap<String, serde_json::Value> = HashMap::deserialize(deserializer)?;
if let Some(v) = raw.get("apiKey") {
Ok(Self::ApiKey(
serde_json::from_value(v.clone()).map_err(de::Error::custom)?,
))
} else if let Some(v) = raw.get("http") {
Ok(Self::Http(
serde_json::from_value(v.clone()).map_err(de::Error::custom)?,
))
} else if let Some(v) = raw.get("oauth2") {
Ok(Self::OAuth2(Box::new(
serde_json::from_value(v.clone()).map_err(de::Error::custom)?,
)))
} else if let Some(v) = raw.get("openIdConnect") {
Ok(Self::OpenIdConnect(
serde_json::from_value(v.clone()).map_err(de::Error::custom)?,
))
} else if let Some(v) = raw.get("mutualTLS") {
Ok(Self::MutualTLS(
serde_json::from_value(v.clone()).map_err(de::Error::custom)?,
))
} else {
Err(de::Error::custom(
"SecurityScheme must contain one of: apiKey, http, oauth2, openIdConnect, mutualTLS",
))
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ApiKeyLocation {
Query,
Header,
Cookie,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ApiKeySecurityScheme {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub location: ApiKeyLocation,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct HttpAuthSecurityScheme {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub scheme: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bearer_format: Option<String>,
}
impl HttpAuthSecurityScheme {
#[must_use]
pub fn bearer() -> Self {
Self {
description: None,
scheme: "Bearer".into(),
bearer_format: None,
}
}
#[must_use]
pub fn bearer_jwt() -> Self {
Self {
description: None,
scheme: "Bearer".into(),
bearer_format: Some("JWT".into()),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct OAuth2SecurityScheme {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub flows: OAuthFlow,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub oauth2_metadata_url: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct OpenIdConnectSecurityScheme {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub open_id_connect_url: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct MutualTlsSecurityScheme {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OAuthFlow {
AuthorizationCode(AuthorizationCodeOAuthFlow),
ClientCredentials(ClientCredentialsOAuthFlow),
DeviceCode(DeviceCodeOAuthFlow),
#[deprecated = "Use Authorization Code + PKCE instead"]
Implicit(ImplicitOAuthFlow),
#[deprecated = "Use Authorization Code + PKCE or Device Code"]
Password(PasswordOAuthFlow),
}
impl Serialize for OAuthFlow {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(Some(1))?;
match self {
Self::AuthorizationCode(v) => map.serialize_entry("authorizationCode", v)?,
Self::ClientCredentials(v) => map.serialize_entry("clientCredentials", v)?,
Self::DeviceCode(v) => map.serialize_entry("deviceCode", v)?,
#[allow(
deprecated,
reason = "self-deprecated variant still needs serde support"
)]
Self::Implicit(v) => map.serialize_entry("implicit", v)?,
#[allow(
deprecated,
reason = "self-deprecated variant still needs serde support"
)]
Self::Password(v) => map.serialize_entry("password", v)?,
}
map.end()
}
}
impl<'de> Deserialize<'de> for OAuthFlow {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let raw: HashMap<String, serde_json::Value> = HashMap::deserialize(deserializer)?;
if let Some(v) = raw.get("authorizationCode") {
Ok(Self::AuthorizationCode(
serde_json::from_value(v.clone()).map_err(de::Error::custom)?,
))
} else if let Some(v) = raw.get("clientCredentials") {
Ok(Self::ClientCredentials(
serde_json::from_value(v.clone()).map_err(de::Error::custom)?,
))
} else if let Some(v) = raw.get("deviceCode") {
Ok(Self::DeviceCode(
serde_json::from_value(v.clone()).map_err(de::Error::custom)?,
))
} else if let Some(v) = raw.get("implicit") {
#[allow(
deprecated,
reason = "self-deprecated variant still needs serde support"
)]
Ok(Self::Implicit(
serde_json::from_value(v.clone()).map_err(de::Error::custom)?,
))
} else if let Some(v) = raw.get("password") {
#[allow(
deprecated,
reason = "self-deprecated variant still needs serde support"
)]
Ok(Self::Password(
serde_json::from_value(v.clone()).map_err(de::Error::custom)?,
))
} else {
Err(de::Error::custom(
"OAuthFlow must contain one of: authorizationCode, clientCredentials, deviceCode, implicit, password",
))
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct AuthorizationCodeOAuthFlow {
pub authorization_url: String,
pub token_url: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub refresh_url: Option<String>,
pub scopes: HashMap<String, String>,
#[serde(default, skip_serializing_if = "super::is_false")]
pub pkce_required: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct ClientCredentialsOAuthFlow {
pub token_url: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub refresh_url: Option<String>,
pub scopes: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct DeviceCodeOAuthFlow {
pub device_authorization_url: String,
pub token_url: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub refresh_url: Option<String>,
pub scopes: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct ImplicitOAuthFlow {
pub authorization_url: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub refresh_url: Option<String>,
#[serde(default)]
pub scopes: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct PasswordOAuthFlow {
pub token_url: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub refresh_url: Option<String>,
#[serde(default)]
pub scopes: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct SecurityRequirement {
pub schemes: HashMap<String, Vec<String>>,
}