Skip to content

Commit

Permalink
Merge pull request #6 from zvkemp/private-jar
Browse files Browse the repository at this point in the history
add PrivateCookies
  • Loading branch information
imbolc authored Jan 3, 2022
2 parents cd942c1 + e13fe0f commit 97b695d
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ version = "0.4.0"
[features]
default = ["axum-core"]
signed = ["cookie/signed"]
private = ["cookie/secure"]

[dependencies]
async-trait = "0.1"
Expand Down
30 changes: 29 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ pub use self::service::{CookieManager, CookieManagerLayer};
#[cfg(feature = "signed")]
pub use self::signed::SignedCookies;

#[cfg(feature = "signed")]
#[cfg(feature = "private")]
pub use self::private::PrivateCookies;

#[cfg(any(feature = "signed", feature = "private"))]
pub use cookie::Key;

pub use cookie::Cookie;
Expand All @@ -65,6 +68,9 @@ mod extract;
#[cfg(feature = "signed")]
mod signed;

#[cfg(feature = "private")]
mod private;

pub mod service;

/// A parsed on-demand cookie jar.
Expand Down Expand Up @@ -135,6 +141,28 @@ impl Cookies {
pub fn signed<'a>(&self, key: &'a cookie::Key) -> SignedCookies<'a> {
SignedCookies::new(self, key)
}

/// Returns a child [`PrivateCookies`] jar for encrypting and decrypting cookies.
///
/// # Example:
/// ```
/// use cookie::{Cookie, Key};
/// use tower_cookies::Cookies;
///
/// let cookies = Cookies::default();
/// let key = Key::generate();
/// let private = cookies.private(&key);
///
/// let foo = Cookie::new("foo", "bar");
/// private.add(foo.clone());
///
/// assert_eq!(private.get("foo"), Some(foo.clone()));
/// assert_ne!(cookies.get("foo"), Some(foo));
/// ```
#[cfg(feature = "private")]
pub fn private<'a>(&self, key: &'a cookie::Key) -> PrivateCookies<'a> {
PrivateCookies::new(self, key)
}
}

#[derive(Debug, Default)]
Expand Down
100 changes: 100 additions & 0 deletions src/private.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use crate::Cookies;
use cookie::{Cookie, Key};

/// A child cookie jar that encrypts its cookies.
pub struct PrivateCookies<'a> {
cookies: Cookies,
key: &'a Key,
}

impl<'a> PrivateCookies<'a> {
/// Creates an instance of `PrivateCookies` with parent `cookies` and key `key`. This method is
/// typically called indirectly via the `private` method of [`Cookies`].
pub(crate) fn new(cookies: &Cookies, key: &'a Key) -> Self {
Self {
cookies: cookies.clone(),
key,
}
}

/// Adds cookie to the parent jar. The cookie’s value is encrypted.
pub fn add(&self, cookie: Cookie<'static>) {
let mut inner = self.cookies.inner.lock();
inner.changed = true;
inner.jar().private_mut(self.key).add(cookie);
}

/// Returns `Cookie` with the `name` and decrypted contents.
pub fn get(&self, name: &str) -> Option<Cookie> {
let mut inner = self.cookies.inner.lock();
inner.jar().private(self.key).get(name)
}

/// Removes the `cookie` from the parent jar.
pub fn remove(&self, cookie: Cookie<'static>) {
self.cookies.remove(cookie);
}
}

#[cfg(all(test, feature = "private"))]
mod tests {
use crate::Cookies;
use cookie::{Cookie, Key};

#[test]
fn get_absent() {
let key = Key::generate();
let cookies = Cookies::new(None);
assert_eq!(cookies.private(&key).get("foo"), None);
}

#[test]
fn add_get_private() {
let key = Key::generate();
let cookies = Cookies::new(None);
let cookie = Cookie::new("foo", "bar");
let private = cookies.private(&key);
private.add(cookie.clone());
assert_eq!(private.get("foo").unwrap(), cookie);
}

#[test]
fn add_private_get_raw() {
let key = Key::generate();
let cookies = Cookies::new(None);
let cookie = Cookie::new("foo", "bar");
cookies.private(&key).add(cookie.clone());
assert_ne!(cookies.get("foo").unwrap(), cookie);
}

#[test]
fn add_raw_get_private() {
let key = Key::generate();
let cookies = Cookies::new(None);
let cookie = Cookie::new("foo", "bar");
cookies.add(cookie);
assert_eq!(cookies.private(&key).get("foo"), None);
}

#[test]
fn messed_keys() {
let key1 = Key::generate();
let key2 = Key::generate();
let cookies = Cookies::new(None);
let cookie = Cookie::new("foo", "bar");
cookies.private(&key1).add(cookie);
assert_eq!(cookies.private(&key2).get("foo"), None);
}

#[test]
fn remove() {
let key = Key::generate();
let cookies = Cookies::new(None);
let cookie = Cookie::new("foo", "bar");
let private = cookies.private(&key);
private.add(cookie.clone());
assert!(private.get("foo").is_some());
private.remove(cookie);
assert!(private.get("foo").is_none());
}
}

0 comments on commit 97b695d

Please sign in to comment.