topcoat_cookie/signed.rs
1use cookie::{Cookie, CookieJar as RawCookieJar, Key};
2
3use crate::Cookies;
4
5/// A [`Cookies`] adapter that signs cookies written through it and verifies
6/// cookies read through it, using a [`Key`].
7///
8/// Signing detects changes to a cookie's value while leaving it readable by
9/// the client. Create a signed jar with [`Cookies::signed`] or
10/// [`signed_cookies`](crate::signed_cookies).
11///
12/// Reads return `None` for missing cookies or invalid signatures. This adapter
13/// can be combined with prefixes and attribute defaults.
14#[derive(Debug, Clone, Copy)]
15pub struct SignedJar<'key, J> {
16 inner: J,
17 key: &'key Key,
18}
19
20impl<'key, J> SignedJar<'key, J> {
21 pub(crate) fn new(inner: J, key: &'key Key) -> Self {
22 Self { inner, key }
23 }
24}
25
26impl<J: Cookies> Cookies for SignedJar<'_, J> {
27 fn get(&self, name: &str) -> Option<Cookie<'static>> {
28 let raw = self.inner.get(name)?;
29 // Reuse the cookie crate's verification by routing the raw cookie
30 // through a throwaway jar seeded with it as an original.
31 let name = raw.name().to_owned();
32 let mut jar = RawCookieJar::new();
33 jar.add_original(raw);
34 jar.signed(self.key).get(&name)
35 }
36
37 fn add<C: Into<Cookie<'static>>>(&self, cookie: C) {
38 let cookie = cookie.into();
39 // Sign via a throwaway jar, then forward the signed cookie inward so
40 // any further layers (prefix, defaults) still apply.
41 let name = cookie.name().to_owned();
42 let mut jar = RawCookieJar::new();
43 jar.signed_mut(self.key).add(cookie);
44 if let Some(signed) = jar.get(&name).cloned() {
45 self.inner.add(signed);
46 }
47 }
48
49 fn remove<C: Into<Cookie<'static>>>(&self, cookie: C) {
50 // Removal cookies match by name only; the signature is irrelevant.
51 self.inner.remove(cookie);
52 }
53}