use std::fmt;
use anyhow::Result;
use http::HeaderName;
use http::Method;
use super::origin::OriginMatcher;
#[derive(Clone)]
pub struct Config {
pub origins: Vec<String>,
pub origin_matchers: Vec<OriginMatcher>,
pub methods: Vec<Method>,
pub headers: Vec<HeaderName>,
pub allow_credentials: bool,
pub max_age_secs: Option<u32>,
pub allow_private_network: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
origins: Vec::new(),
origin_matchers: Vec::new(),
methods: vec![
Method::GET,
Method::POST,
Method::PUT,
Method::PATCH,
Method::DELETE,
Method::OPTIONS,
],
headers: Vec::new(),
allow_credentials: false,
max_age_secs: Some(3600),
allow_private_network: false,
}
}
}
impl Config {
pub fn validate(&self) -> Result<(), CorsConfigError> {
if self.allow_credentials && self.origins.is_empty() && self.origin_matchers.is_empty() {
return Err(CorsConfigError::CredentialsWithWildcardOrigin);
}
Ok(())
}
pub(crate) fn origin_allowed(&self, origin: &str) -> bool {
self.origins.iter().any(|p| p == origin)
|| self.origin_matchers.iter().any(|m| m.matches(origin))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CorsConfigError {
CredentialsWithWildcardOrigin,
}
impl fmt::Display for CorsConfigError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::CredentialsWithWildcardOrigin => f.write_str(
"CORS misconfiguration: allow_credentials = true requires at least one explicit \
allowed origin; reflecting `*` together with credentials is rejected by browsers",
),
}
}
}
impl std::error::Error for CorsConfigError {}