Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

overhaul this crate's interface #57

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,28 @@ authors = [
"Jacob Rothstein <hi@jbr.me>"
]

[features]
default = ["memory-store", "cookie-store"]
memory-store = ["dashmap", "thiserror", "log"]
cookie-store = ["bincode-json", "thiserror"]

[dependencies]
async-trait = "0.1.59"
async-trait = "0.1.64"
rand = "0.8.5"
base64 = "0.20.0"
sha2 = "0.10.6"
hmac = "0.12.1"
serde_json = "1.0.89"
bincode = "1.3.3"
anyhow = "1.0.66"
base64 = "0.21.0"
serde_json = "1.0.93"
blake3 = "1.3.3"
async-lock = "2.6.0"
log = "0.4.17"
log = { version = "0.4.17", optional = true }
dashmap = { version = "5.4.0", optional = true }
bincode-json = { version = "0.1.5", features = ["json"], optional = true }
thiserror = { version = "1.0.38", optional = true }

[dependencies.serde]
version = "1.0.150"
features = ["rc", "derive"]
version = "1.0.152"
features = ["derive"]

[dependencies.time]
version = "0.3.17"
version = "0.3.18"
features = ["serde"]

[dev-dependencies.async-std]
Expand Down
58 changes: 39 additions & 19 deletions src/cookie_store.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use crate::{async_trait, Result, Session, SessionStore};
use crate::{async_trait, Session, SessionStore};
use base64::{engine::general_purpose::STANDARD as BASE64, Engine};

/// A session store that serializes the entire session into a Cookie.
///
/// # ***This is not recommended for most production deployments.***
///
/// This implementation uses [`bincode`](::bincode) to serialize the
/// Session to decrease the size of the cookie. Note: There is a
/// maximum of 4093 cookie bytes allowed _per domain_, so the cookie
/// store is limited in capacity.
/// This implementation uses [`bincode_json`](::bincode_json) to
/// serialize the Session to decrease the size of the cookie. Note:
/// There is a maximum of 4093 cookie bytes allowed _per domain_, so
/// the cookie store is limited in capacity.
///
/// **Note:** Currently, the data in the cookie is only signed, but *not
/// encrypted*. If the contained session data is sensitive and
Expand All @@ -29,24 +30,43 @@ impl CookieStore {
}
}

#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
/// All errors that can occur in the [`CookieStore`]
pub enum CookieStoreError {
/// A bincode_json error
#[error(transparent)]
Bincode(#[from] bincode_json::Error),

/// A base64 error
#[error(transparent)]
Base64(#[from] base64::DecodeError),

/// A json error
#[error(transparent)]
Json(#[from] serde_json::Error),
}

#[async_trait]
impl SessionStore for CookieStore {
async fn load_session(&self, cookie_value: String) -> Result<Option<Session>> {
let serialized = base64::decode(cookie_value)?;
let session: Session = bincode::deserialize(&serialized)?;
type Error = CookieStoreError;

async fn load_session(&self, cookie_value: String) -> Result<Option<Session>, Self::Error> {
let serialized = BASE64.decode(cookie_value)?;
let session: Session = bincode_json::from_slice(&serialized)?;
Ok(session.validate())
}

async fn store_session(&self, session: Session) -> Result<Option<String>> {
let serialized = bincode::serialize(&session)?;
Ok(Some(base64::encode(serialized)))
async fn store_session(&self, session: Session) -> Result<Option<String>, Self::Error> {
let serialized = bincode_json::to_vec(&session)?;
Ok(Some(BASE64.encode(serialized)))
}

async fn destroy_session(&self, _session: Session) -> Result {
async fn destroy_session(&self, _session: Session) -> Result<(), Self::Error> {
Ok(())
}

async fn clear_store(&self) -> Result {
async fn clear_store(&self) -> Result<(), Self::Error> {
Ok(())
}
}
Expand All @@ -57,7 +77,7 @@ mod tests {
use async_std::task;
use std::time::Duration;
#[async_std::test]
async fn creating_a_new_session_with_no_expiry() -> Result {
async fn creating_a_new_session_with_no_expiry() -> Result<(), CookieStoreError> {
let store = CookieStore::new();
let mut session = Session::new();
session.insert("key", "Hello")?;
Expand All @@ -72,7 +92,7 @@ mod tests {
}

#[async_std::test]
async fn updating_a_session() -> Result {
async fn updating_a_session() -> Result<(), CookieStoreError> {
let store = CookieStore::new();
let mut session = Session::new();

Expand All @@ -90,18 +110,18 @@ mod tests {
}

#[async_std::test]
async fn updating_a_session_extending_expiry() -> Result {
async fn updating_a_session_extending_expiry() -> Result<(), CookieStoreError> {
let store = CookieStore::new();
let mut session = Session::new();
session.expire_in(Duration::from_secs(1));
let original_expires = session.expiry().unwrap().clone();
let original_expires = *session.expiry().unwrap();
let cookie_value = store.store_session(session).await?.unwrap();

let mut session = store.load_session(cookie_value.clone()).await?.unwrap();

assert_eq!(session.expiry().unwrap(), &original_expires);
session.expire_in(Duration::from_secs(3));
let new_expires = session.expiry().unwrap().clone();
let new_expires = *session.expiry().unwrap();
let cookie_value = store.store_session(session).await?.unwrap();

let session = store.load_session(cookie_value.clone()).await?.unwrap();
Expand All @@ -114,7 +134,7 @@ mod tests {
}

#[async_std::test]
async fn creating_a_new_session_with_expiry() -> Result {
async fn creating_a_new_session_with_expiry() -> Result<(), CookieStoreError> {
let store = CookieStore::new();
let mut session = Session::new();
session.expire_in(Duration::from_secs(3));
Expand Down
22 changes: 7 additions & 15 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
//! ```
//! use async_session::{Session, SessionStore, MemoryStore};
//!
//! # fn main() -> async_session::Result {
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # async_std::task::block_on(async {
//! #
//! // Init a new session store we can persist sessions to.
Expand Down Expand Up @@ -46,26 +46,18 @@
unused_qualifications
)]

pub use anyhow::Error;
/// An anyhow::Result with default return type of ()
pub type Result<T = ()> = std::result::Result<T, Error>;

#[cfg(feature = "cookie-store")]
mod cookie_store;
#[cfg(feature = "memory-store")]
mod memory_store;
mod session;
mod session_store;

pub use cookie_store::CookieStore;
pub use memory_store::MemoryStore;
#[cfg(feature = "cookie-store")]
pub use cookie_store::{CookieStore, CookieStoreError};
#[cfg(feature = "memory-store")]
pub use memory_store::{MemoryStore, MemoryStoreError};
pub use session::Session;
pub use session_store::SessionStore;

pub use async_trait::async_trait;
pub use base64;
pub use blake3;
pub use hmac;
pub use log;
pub use serde;
pub use serde_json;
pub use sha2;
pub use time;
Loading