tower_http/cors/
allow_credentials.rs

1use std::{fmt, sync::Arc};
2
3use http::{
4    header::{self, HeaderName, HeaderValue},
5    request::Parts as RequestParts,
6};
7
8/// Holds configuration for how to set the [`Access-Control-Allow-Credentials`][mdn] header.
9///
10/// See [`CorsLayer::allow_credentials`] for more details.
11///
12/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
13/// [`CorsLayer::allow_credentials`]: super::CorsLayer::allow_credentials
14#[derive(Clone, Default)]
15#[must_use]
16pub struct AllowCredentials(AllowCredentialsInner);
17
18impl AllowCredentials {
19    /// Allow credentials for all requests
20    ///
21    /// See [`CorsLayer::allow_credentials`] for more details.
22    ///
23    /// [`CorsLayer::allow_credentials`]: super::CorsLayer::allow_credentials
24    pub fn yes() -> Self {
25        Self(AllowCredentialsInner::Yes)
26    }
27
28    /// Allow credentials for some requests, based on a given predicate
29    ///
30    /// The first argument to the predicate is the request origin.
31    ///
32    /// See [`CorsLayer::allow_credentials`] for more details.
33    ///
34    /// [`CorsLayer::allow_credentials`]: super::CorsLayer::allow_credentials
35    pub fn predicate<F>(f: F) -> Self
36    where
37        F: Fn(&HeaderValue, &RequestParts) -> bool + Send + Sync + 'static,
38    {
39        Self(AllowCredentialsInner::Predicate(Arc::new(f)))
40    }
41
42    pub(super) fn is_true(&self) -> bool {
43        matches!(&self.0, AllowCredentialsInner::Yes)
44    }
45
46    pub(super) fn to_header(
47        &self,
48        origin: Option<&HeaderValue>,
49        parts: &RequestParts,
50    ) -> Option<(HeaderName, HeaderValue)> {
51        #[allow(clippy::declare_interior_mutable_const)]
52        const TRUE: HeaderValue = HeaderValue::from_static("true");
53
54        let allow_creds = match &self.0 {
55            AllowCredentialsInner::Yes => true,
56            AllowCredentialsInner::No => false,
57            AllowCredentialsInner::Predicate(c) => c(origin?, parts),
58        };
59
60        allow_creds.then_some((header::ACCESS_CONTROL_ALLOW_CREDENTIALS, TRUE))
61    }
62}
63
64impl From<bool> for AllowCredentials {
65    fn from(v: bool) -> Self {
66        match v {
67            true => Self(AllowCredentialsInner::Yes),
68            false => Self(AllowCredentialsInner::No),
69        }
70    }
71}
72
73impl fmt::Debug for AllowCredentials {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        match self.0 {
76            AllowCredentialsInner::Yes => f.debug_tuple("Yes").finish(),
77            AllowCredentialsInner::No => f.debug_tuple("No").finish(),
78            AllowCredentialsInner::Predicate(_) => f.debug_tuple("Predicate").finish(),
79        }
80    }
81}
82
83#[derive(Clone)]
84enum AllowCredentialsInner {
85    Yes,
86    No,
87    Predicate(
88        Arc<dyn for<'a> Fn(&'a HeaderValue, &'a RequestParts) -> bool + Send + Sync + 'static>,
89    ),
90}
91
92impl Default for AllowCredentialsInner {
93    fn default() -> Self {
94        Self::No
95    }
96}