use crate::call::Call;
use crate::pipeline::{Middleware, Next};
use crate::response::Response;
use async_trait::async_trait;
use http::header::{
HeaderName, CONTENT_SECURITY_POLICY, REFERRER_POLICY, STRICT_TRANSPORT_SECURITY,
X_CONTENT_TYPE_OPTIONS, X_FRAME_OPTIONS,
};
use http::HeaderValue;
#[derive(Debug, Clone)]
pub struct SecurityHeaders {
content_type_options: Option<String>,
frame_options: Option<String>,
referrer_policy: Option<String>,
hsts: Option<String>,
csp: Option<String>,
}
impl Default for SecurityHeaders {
fn default() -> Self {
Self {
content_type_options: Some("nosniff".into()),
frame_options: Some("DENY".into()),
referrer_policy: Some("no-referrer".into()),
hsts: Some("max-age=31536000".into()),
csp: None,
}
}
}
impl SecurityHeaders {
pub fn new() -> Self {
Self::default()
}
pub fn content_type_options(mut self, v: Option<&str>) -> Self {
self.content_type_options = v.map(Into::into);
self
}
pub fn frame_options(mut self, v: Option<&str>) -> Self {
self.frame_options = v.map(Into::into);
self
}
pub fn referrer_policy(mut self, v: Option<&str>) -> Self {
self.referrer_policy = v.map(Into::into);
self
}
pub fn hsts(mut self, v: Option<&str>) -> Self {
self.hsts = v.map(Into::into);
self
}
pub fn content_security_policy(mut self, v: Option<&str>) -> Self {
self.csp = v.map(Into::into);
self
}
pub(crate) fn apply_to(&self, headers: &mut http::HeaderMap, over_tls: bool) {
let mut set = |name: HeaderName, value: &Option<String>| {
let Some(v) = value else { return };
if headers.contains_key(&name) {
return;
}
if let Ok(hv) = HeaderValue::from_str(v) {
headers.insert(name, hv);
}
};
set(X_CONTENT_TYPE_OPTIONS, &self.content_type_options);
set(X_FRAME_OPTIONS, &self.frame_options);
set(REFERRER_POLICY, &self.referrer_policy);
set(CONTENT_SECURITY_POLICY, &self.csp);
if over_tls {
set(STRICT_TRANSPORT_SECURITY, &self.hsts);
}
}
pub(crate) fn into_middleware(self, tls_enabled: bool) -> SecurityHeadersMiddleware {
SecurityHeadersMiddleware {
cfg: self,
tls_enabled,
}
}
}
pub(crate) struct SecurityHeadersMiddleware {
cfg: SecurityHeaders,
tls_enabled: bool,
}
#[async_trait]
impl Middleware for SecurityHeadersMiddleware {
async fn handle(&self, call: Call, next: Next) -> Response {
let mut res = next.run(call).await;
self.cfg.apply_to(&mut res.headers, self.tls_enabled);
res
}
}