tower_sesh/
config.rs

1//! Cookie configuration.
2
3use std::fmt;
4
5use cookie::{Cookie, CookieJar, Key};
6
7// Adapted from https://github.com/rwf2/cookie-rs.
8/// The `SameSite` cookie attribute.
9///
10/// A cookie with a `SameSite` attribute is imposed restrictions on when it is
11/// sent to the origin server in a cross-site request:
12///
13/// - `Strict`: The cookie is never sent in cross-site requests.
14/// - `Lax`: The cookie is sent in cross-site top-level navigations.
15/// - `None`: The cookie is sent in all cross-site requests if the `Secure`
16///   flag is also set; otherwise, the cookie is ignored.
17///
18/// **Note:** This cookie attribute is an [HTTP draft]! Its meaning and
19/// definition are subject to change.
20///
21/// [HTTP draft]: https://tools.ietf.org/html/draft-west-cookie-incrementalism-00
22// NOTE: `Copy` should not be implemented in case web standards change in the future.
23#[derive(Clone, Debug, PartialEq, Eq)]
24#[non_exhaustive]
25pub enum SameSite {
26    Strict,
27    Lax,
28    None,
29}
30
31impl SameSite {
32    #[allow(dead_code)]
33    pub(crate) fn from_cookie_same_site(value: cookie::SameSite) -> SameSite {
34        match value {
35            cookie::SameSite::Strict => SameSite::Strict,
36            cookie::SameSite::Lax => SameSite::Lax,
37            cookie::SameSite::None => SameSite::None,
38        }
39    }
40
41    #[allow(dead_code)]
42    pub(crate) fn into_cookie_same_site(self) -> cookie::SameSite {
43        match self {
44            SameSite::Strict => cookie::SameSite::Strict,
45            SameSite::Lax => cookie::SameSite::Lax,
46            SameSite::None => cookie::SameSite::None,
47        }
48    }
49}
50
51impl fmt::Display for SameSite {
52    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53        match self {
54            SameSite::Strict => f.write_str("Strict"),
55            SameSite::Lax => f.write_str("Lax"),
56            SameSite::None => f.write_str("None"),
57        }
58    }
59}
60
61/// Trait used to control how cookies are stored and retrieved.
62#[doc(hidden)]
63pub trait CookieSecurity: Clone + private::Sealed {
64    fn get<'c>(&self, jar: &'c CookieJar, name: &str) -> Option<Cookie<'c>>;
65    fn add(&self, jar: &mut CookieJar, cookie: Cookie<'static>);
66    fn remove(&self, jar: &mut CookieJar, cookie: Cookie<'static>);
67    fn into_key(self) -> Key;
68}
69
70#[doc(hidden)]
71#[derive(Clone, Debug)]
72pub struct SignedCookie {
73    key: Key,
74}
75
76#[doc(hidden)]
77#[derive(Clone, Debug)]
78pub struct PrivateCookie {
79    key: Key,
80}
81
82#[doc(hidden)]
83#[non_exhaustive]
84#[derive(Clone, Debug)]
85pub struct PlainCookie;
86
87impl SignedCookie {
88    pub(crate) fn new(key: Key) -> Self {
89        Self { key }
90    }
91}
92
93impl PrivateCookie {
94    pub(crate) fn new(key: Key) -> Self {
95        Self { key }
96    }
97}
98
99impl CookieSecurity for SignedCookie {
100    #[inline]
101    fn get<'c>(&self, jar: &'c CookieJar, name: &str) -> Option<Cookie<'c>> {
102        jar.signed(&self.key).get(name)
103    }
104
105    #[inline]
106    fn add(&self, jar: &mut CookieJar, cookie: Cookie<'static>) {
107        jar.signed_mut(&self.key).add(cookie)
108    }
109
110    #[inline]
111    fn remove(&self, jar: &mut CookieJar, cookie: Cookie<'static>) {
112        jar.signed_mut(&self.key).remove(cookie)
113    }
114
115    #[inline]
116    fn into_key(self) -> Key {
117        self.key
118    }
119}
120impl private::Sealed for SignedCookie {}
121
122impl CookieSecurity for PrivateCookie {
123    #[inline]
124    fn get<'c>(&self, jar: &'c CookieJar, name: &str) -> Option<Cookie<'c>> {
125        jar.private(&self.key).get(name)
126    }
127
128    #[inline]
129    fn add(&self, jar: &mut CookieJar, cookie: Cookie<'static>) {
130        jar.private_mut(&self.key).add(cookie)
131    }
132
133    #[inline]
134    fn remove(&self, jar: &mut CookieJar, cookie: Cookie<'static>) {
135        jar.private_mut(&self.key).remove(cookie)
136    }
137
138    #[inline]
139    fn into_key(self) -> Key {
140        self.key
141    }
142}
143impl private::Sealed for PrivateCookie {}
144
145impl CookieSecurity for PlainCookie {
146    #[inline]
147    fn get<'c>(&self, jar: &'c CookieJar, name: &str) -> Option<Cookie<'c>> {
148        jar.get(name).cloned()
149    }
150
151    #[inline]
152    fn add(&self, jar: &mut CookieJar, cookie: Cookie<'static>) {
153        jar.add(cookie)
154    }
155
156    #[inline]
157    fn remove(&self, jar: &mut CookieJar, cookie: Cookie<'static>) {
158        jar.remove(cookie)
159    }
160
161    #[inline]
162    #[track_caller]
163    fn into_key(self) -> Key {
164        unimplemented!("use `SessionLayer::new()` to sign or encrypt cookies")
165    }
166}
167impl private::Sealed for PlainCookie {}
168
169mod private {
170    pub trait Sealed {}
171}