use std::future::{ready, Future, Ready};
use std::pin::Pin;
use actix_web::body::{BoxBody, MessageBody};
use actix_web::dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform};
use actix_web::Error;
use actix_web::HttpMessage;
use crate::headers::{security_header_pairs, CspNonce, SecurityHeadersLayer};
#[derive(Clone, Debug, Default)]
pub struct SecurityHeadersTransform {
layer: SecurityHeadersLayer,
}
impl SecurityHeadersTransform {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_csp(mut self, csp: impl Into<String>) -> Self {
self.layer = self.layer.with_csp(csp);
self
}
#[must_use]
pub fn with_hsts(mut self, hsts: impl Into<String>) -> Self {
self.layer = self.layer.with_hsts(hsts);
self
}
#[must_use]
pub fn with_csp_nonce(mut self) -> Self {
self.layer = self.layer.with_csp_nonce();
self
}
#[must_use]
pub fn with_permissions_policy(mut self, pp: impl Into<String>) -> Self {
self.layer = self.layer.with_permissions_policy(pp);
self
}
}
impl<S, B> Transform<S, ServiceRequest> for SecurityHeadersTransform
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
S::Future: 'static,
B: MessageBody + 'static,
{
type Response = ServiceResponse<BoxBody>;
type Error = Error;
type Transform = SecurityHeadersMiddleware<S>;
type InitError = ();
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(SecurityHeadersMiddleware {
service,
layer: self.layer.clone(),
}))
}
}
pub struct SecurityHeadersMiddleware<S> {
service: S,
layer: SecurityHeadersLayer,
}
impl<S, B> Service<ServiceRequest> for SecurityHeadersMiddleware<S>
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
S::Future: 'static,
B: MessageBody + 'static,
{
type Response = ServiceResponse<BoxBody>;
type Error = Error;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
forward_ready!(service);
fn call(&self, req: ServiceRequest) -> Self::Future {
let layer = self.layer.clone();
let nonce = if layer.include_csp_nonce {
let nonce = CspNonce::generate();
req.extensions_mut().insert(nonce.clone());
Some(nonce)
} else {
None
};
let fut = self.service.call(req);
Box::pin(async move {
let res: ServiceResponse<B> = fut.await?;
let res = res.map_into_boxed_body();
let (req, mut http_res) = res.into_parts();
let headers = http_res.headers_mut();
for (name, value) in security_header_pairs(&layer, nonce.as_ref()) {
let Ok(name_v02) =
actix_http::header::HeaderName::from_bytes(name.as_str().as_bytes())
else {
continue;
};
let Ok(value_v02) = actix_http::header::HeaderValue::from_bytes(value.as_bytes())
else {
continue;
};
headers.insert(name_v02, value_v02);
}
Ok(ServiceResponse::new(req, http_res))
})
}
}