mirror of
https://github.com/actix/actix-extras.git
synced 2025-06-26 10:27:42 +02:00
Add block_on_origin_mismatch option to middleware (#287)
Co-authored-by: CapableWeb <capableweb@domain.com> Co-authored-by: Rob Ede <robjtede@icloud.com>
This commit is contained in:
@ -102,6 +102,7 @@ impl Cors {
|
||||
send_wildcard: false,
|
||||
supports_credentials: true,
|
||||
vary_header: true,
|
||||
block_on_origin_mismatch: true,
|
||||
};
|
||||
|
||||
Cors {
|
||||
@ -448,6 +449,24 @@ impl Cors {
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Configures whether requests should be pre-emptively blocked on mismatched origin.
|
||||
///
|
||||
/// If `true`, a 400 Bad Request is returned immediately when a request fails origin validation.
|
||||
///
|
||||
/// If `false`, the request will be processed as normal but relevant CORS headers will not be
|
||||
/// appended to the response. In this case, the browser is trusted to validate CORS headers and
|
||||
/// and block requests based on pre-flight requests. Use this setting to allow cURL and other
|
||||
/// non-browser HTTP clients to function as normal, no matter what `Origin` the request has.
|
||||
///
|
||||
/// Defaults to `true`.
|
||||
pub fn block_on_origin_mismatch(mut self, block: bool) -> Cors {
|
||||
if let Some(cors) = cors(&mut self.inner, &self.error) {
|
||||
cors.block_on_origin_mismatch = block
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Cors {
|
||||
@ -474,6 +493,7 @@ impl Default for Cors {
|
||||
send_wildcard: false,
|
||||
supports_credentials: false,
|
||||
vary_header: true,
|
||||
block_on_origin_mismatch: true,
|
||||
};
|
||||
|
||||
Cors {
|
||||
|
@ -65,16 +65,19 @@ pub(crate) struct Inner {
|
||||
pub(crate) send_wildcard: bool,
|
||||
pub(crate) supports_credentials: bool,
|
||||
pub(crate) vary_header: bool,
|
||||
pub(crate) block_on_origin_mismatch: bool,
|
||||
}
|
||||
|
||||
static EMPTY_ORIGIN_SET: Lazy<HashSet<HeaderValue>> = Lazy::new(HashSet::new);
|
||||
|
||||
impl Inner {
|
||||
pub(crate) fn validate_origin(&self, req: &RequestHead) -> Result<(), CorsError> {
|
||||
/// The bool returned in Ok(_) position indicates whether the `Access-Control-Allow-Origin`
|
||||
/// header should be added to the response or not.
|
||||
pub(crate) fn validate_origin(&self, req: &RequestHead) -> Result<bool, CorsError> {
|
||||
// return early if all origins are allowed or get ref to allowed origins set
|
||||
#[allow(clippy::mutable_key_type)]
|
||||
let allowed_origins = match &self.allowed_origins {
|
||||
AllOrSome::All if self.allowed_origins_fns.is_empty() => return Ok(()),
|
||||
AllOrSome::All if self.allowed_origins_fns.is_empty() => return Ok(true),
|
||||
AllOrSome::Some(allowed_origins) => allowed_origins,
|
||||
// only function origin validators are defined
|
||||
_ => &EMPTY_ORIGIN_SET,
|
||||
@ -85,9 +88,11 @@ impl Inner {
|
||||
// origin header exists and is a string
|
||||
Some(origin) => {
|
||||
if allowed_origins.contains(origin) || self.validate_origin_fns(origin, req) {
|
||||
Ok(())
|
||||
} else {
|
||||
Ok(true)
|
||||
} else if self.block_on_origin_mismatch {
|
||||
Err(CorsError::OriginNotAllowed)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ use log::debug;
|
||||
use crate::{
|
||||
builder::intersperse_header_values,
|
||||
inner::{add_vary_header, header_value_try_into_method},
|
||||
AllOrSome, Inner,
|
||||
AllOrSome, CorsError, Inner,
|
||||
};
|
||||
|
||||
/// Service wrapper for Cross-Origin Resource Sharing support.
|
||||
@ -60,9 +60,14 @@ impl<S> CorsMiddleware<S> {
|
||||
fn handle_preflight(&self, req: ServiceRequest) -> ServiceResponse {
|
||||
let inner = Rc::clone(&self.inner);
|
||||
|
||||
match inner.validate_origin(req.head()) {
|
||||
Ok(true) => {}
|
||||
Ok(false) => return req.error_response(CorsError::OriginNotAllowed),
|
||||
Err(err) => return req.error_response(err),
|
||||
};
|
||||
|
||||
if let Err(err) = inner
|
||||
.validate_origin(req.head())
|
||||
.and_then(|_| inner.validate_allowed_method(req.head()))
|
||||
.validate_allowed_method(req.head())
|
||||
.and_then(|_| inner.validate_allowed_headers(req.head()))
|
||||
{
|
||||
return req.error_response(err);
|
||||
@ -108,11 +113,17 @@ impl<S> CorsMiddleware<S> {
|
||||
req.into_response(res)
|
||||
}
|
||||
|
||||
fn augment_response<B>(inner: &Inner, mut res: ServiceResponse<B>) -> ServiceResponse<B> {
|
||||
if let Some(origin) = inner.access_control_allow_origin(res.request().head()) {
|
||||
res.headers_mut()
|
||||
.insert(header::ACCESS_CONTROL_ALLOW_ORIGIN, origin);
|
||||
};
|
||||
fn augment_response<B>(
|
||||
inner: &Inner,
|
||||
origin_allowed: bool,
|
||||
mut res: ServiceResponse<B>,
|
||||
) -> ServiceResponse<B> {
|
||||
if origin_allowed {
|
||||
if let Some(origin) = inner.access_control_allow_origin(res.request().head()) {
|
||||
res.headers_mut()
|
||||
.insert(header::ACCESS_CONTROL_ALLOW_ORIGIN, origin);
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(ref expose) = inner.expose_headers_baked {
|
||||
log::trace!("exposing selected headers: {:?}", expose);
|
||||
@ -182,8 +193,10 @@ where
|
||||
}
|
||||
|
||||
// only check actual requests with a origin header
|
||||
if origin.is_some() {
|
||||
if let Err(err) = self.inner.validate_origin(req.head()) {
|
||||
let origin_allowed = match (origin, self.inner.validate_origin(req.head())) {
|
||||
(None, _) => false,
|
||||
(_, Ok(origin_allowed)) => origin_allowed,
|
||||
(_, Err(err)) => {
|
||||
debug!("origin validation failed; inner service is not called");
|
||||
let mut res = req.error_response(err);
|
||||
|
||||
@ -193,14 +206,14 @@ where
|
||||
|
||||
return ok(res.map_into_right_body()).boxed_local();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let inner = Rc::clone(&self.inner);
|
||||
let fut = self.service.call(req);
|
||||
|
||||
Box::pin(async move {
|
||||
let res = fut.await;
|
||||
Ok(Self::augment_response(&inner, res?).map_into_left_body())
|
||||
Ok(Self::augment_response(&inner, origin_allowed, res?).map_into_left_body())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user