|
1 | 1 | //! Async HTTP sessions.
|
2 | 2 | //!
|
3 |
| -//! This crate provides a generic interface between cookies and storage |
4 |
| -//! backends to create a concept of sessions. It provides an interface that |
5 |
| -//! can be used to encode and store sessions, and decode and load sessions |
6 |
| -//! generating cookies in the process. |
| 3 | +//! This crate provides a generic interface between cookie values and |
| 4 | +//! storage backends to create a concept of sessions. It provides an |
| 5 | +//! interface that can be used to encode and store sessions, and |
| 6 | +//! decode and load sessions generating cookies in the process. |
7 | 7 | //!
|
8 |
| -//! # Security |
9 |
| -//! |
10 |
| -//! This module has not been vetted for security purposes, and in particular |
11 |
| -//! the in-memory storage backend is wildly insecure. Please thoroughly |
12 |
| -//! validate whether this crate is a match for your intended use case before |
13 |
| -//! relying on it in any sensitive context. |
14 |
| -//! |
15 |
| -//! # Examples |
| 8 | +//! # Example |
16 | 9 | //!
|
17 | 10 | //! ```
|
18 |
| -//! use async_session::mem::MemoryStore; |
19 |
| -//! use async_session::{Session, SessionStore}; |
20 |
| -//! use cookie::CookieJar; |
| 11 | +//! use async_session::{Session, SessionStore, MemoryStore}; |
21 | 12 | //!
|
22 |
| -//! # fn main() -> std::io::Result<()> { |
| 13 | +//! # fn main() -> async_session::Result { |
23 | 14 | //! # async_std::task::block_on(async {
|
24 | 15 | //! #
|
25 | 16 | //! // Init a new session store we can persist sessions to.
|
26 | 17 | //! let mut store = MemoryStore::new();
|
27 | 18 | //!
|
28 | 19 | //! // Create a new session.
|
29 |
| -//! let sess = store.create_session(); |
| 20 | +//! let mut session = Session::new(); |
| 21 | +//! session.insert("user_id", 1)?; |
| 22 | +//! assert!(session.data_changed()); |
30 | 23 | //!
|
31 |
| -//! // Persist the session to our backend, and store a cookie |
32 |
| -//! // to later access the session. |
33 |
| -//! let mut jar = CookieJar::new(); |
34 |
| -//! let sess = store.store_session(sess, &mut jar).await?; |
| 24 | +//! // retrieve the cookie value to store in a session cookie |
| 25 | +//! let cookie_value = store.store_session(session).await?.unwrap(); |
35 | 26 | //!
|
36 | 27 | //! // Retrieve the session using the cookie.
|
37 |
| -//! let sess = store.load_session(&jar).await?; |
38 |
| -//! println!("session: {:?}", sess); |
| 28 | +//! let session = store.load_session(cookie_value).await?.unwrap(); |
| 29 | +//! assert_eq!(session.get::<usize>("user_id").unwrap(), 1); |
| 30 | +//! assert!(!session.data_changed()); |
39 | 31 | //! #
|
40 | 32 | //! # Ok(()) }) }
|
41 | 33 | //! ```
|
42 | 34 |
|
43 |
| -#![forbid(unsafe_code, future_incompatible, rust_2018_idioms)] |
44 |
| -#![deny(missing_debug_implementations, nonstandard_style)] |
45 |
| -#![warn(missing_docs, missing_doc_code_examples, unreachable_pub)] |
46 |
| - |
47 |
| -use async_trait::async_trait; |
48 |
| -use std::collections::HashMap; |
49 |
| - |
50 |
| -/// An async session backend. |
51 |
| -#[async_trait] |
52 |
| -pub trait SessionStore: Send + Sync + 'static + Clone { |
53 |
| - /// The type of error that can occur when storing and loading errors. |
54 |
| - type Error; |
55 |
| - |
56 |
| - /// Get a session from the storage backend. |
57 |
| - /// |
58 |
| - /// The input should usually be the content of a cookie. This will then be |
59 |
| - /// parsed by the session middleware into a valid session. |
60 |
| - async fn load_session(&self, jar: &cookie::CookieJar) -> Result<Session, Self::Error>; |
61 |
| - |
62 |
| - /// Store a session on the storage backend. |
63 |
| - /// |
64 |
| - /// This method should return a stringified representation of the session so |
65 |
| - /// that it can be sent back to the client through a cookie. |
66 |
| - async fn store_session( |
67 |
| - &mut self, |
68 |
| - session: Session, |
69 |
| - jar: &mut cookie::CookieJar, |
70 |
| - ) -> Result<(), Self::Error>; |
71 |
| -} |
72 |
| - |
73 |
| -/// The main session type. |
74 |
| -#[derive(Clone, Debug)] |
75 |
| -pub struct Session { |
76 |
| - inner: HashMap<String, String>, |
77 |
| -} |
78 |
| - |
79 |
| -impl Session { |
80 |
| - /// Create a new session. |
81 |
| - pub fn new() -> Self { |
82 |
| - Self { |
83 |
| - inner: HashMap::new(), |
84 |
| - } |
85 |
| - } |
86 |
| - |
87 |
| - /// Insert a new value into the Session. |
88 |
| - pub fn insert(&mut self, k: String, v: String) -> Option<String> { |
89 |
| - self.inner.insert(k, v) |
90 |
| - } |
91 |
| - |
92 |
| - /// Get a value from the session. |
93 |
| - pub fn get(&self, k: &str) -> Option<&String> { |
94 |
| - self.inner.get(k) |
95 |
| - } |
96 |
| -} |
97 |
| - |
98 |
| -/// In-memory session store. |
99 |
| -pub mod mem { |
100 |
| - use async_std::io::{Error, ErrorKind}; |
101 |
| - use async_std::sync::{Arc, RwLock}; |
102 |
| - use cookie::Cookie; |
103 |
| - use std::collections::HashMap; |
104 |
| - |
105 |
| - use async_trait::async_trait; |
106 |
| - use uuid::Uuid; |
107 |
| - |
108 |
| - use crate::{Session, SessionStore}; |
109 |
| - |
110 |
| - /// An in-memory session store. |
111 |
| - /// |
112 |
| - /// # Security |
113 |
| - /// |
114 |
| - /// This store *does not* generate secure sessions, and should under no |
115 |
| - /// circumstance be used in production. It's meant only to quickly create |
116 |
| - /// sessions. |
117 |
| - #[derive(Debug)] |
118 |
| - pub struct MemoryStore { |
119 |
| - inner: Arc<RwLock<HashMap<String, Session>>>, |
120 |
| - } |
121 |
| - |
122 |
| - impl MemoryStore { |
123 |
| - /// Create a new instance of MemoryStore. |
124 |
| - pub fn new() -> Self { |
125 |
| - Self { |
126 |
| - inner: Arc::new(RwLock::new(HashMap::new())), |
127 |
| - } |
128 |
| - } |
129 |
| - |
130 |
| - /// Generates a new session by generating a new uuid. |
131 |
| - /// |
132 |
| - /// This is *not* a secure way of generating sessions, and is intended for debug purposes only. |
133 |
| - pub fn create_session(&self) -> Session { |
134 |
| - let mut sess = Session::new(); |
135 |
| - sess.insert("id".to_string(), uuid::Uuid::new_v4().to_string()); |
136 |
| - sess |
137 |
| - } |
138 |
| - } |
139 |
| - |
140 |
| - impl Clone for MemoryStore { |
141 |
| - fn clone(&self) -> Self { |
142 |
| - Self { |
143 |
| - inner: self.inner.clone(), |
144 |
| - } |
145 |
| - } |
146 |
| - } |
147 |
| - |
148 |
| - #[async_trait] |
149 |
| - impl SessionStore for MemoryStore { |
150 |
| - /// The type of error that can occur when storing and loading errors. |
151 |
| - type Error = std::io::Error; |
152 |
| - |
153 |
| - /// Get a session from the storage backend. |
154 |
| - async fn load_session(&self, jar: &cookie::CookieJar) -> Result<Session, Self::Error> { |
155 |
| - let id = match jar.get("session") { |
156 |
| - Some(cookie) => Uuid::parse_str(cookie.value()), |
157 |
| - None => return Err(Error::new(ErrorKind::Other, "No session cookie found")), |
158 |
| - }; |
159 |
| - |
160 |
| - let id = id |
161 |
| - .map_err(|_| Error::new(ErrorKind::Other, "Cookie content was not a valid uuid"))? |
162 |
| - .to_string(); |
163 |
| - |
164 |
| - let inner = self.inner.read().await; |
165 |
| - let sess = inner.get(&id).ok_or(Error::from(ErrorKind::Other))?; |
166 |
| - Ok(sess.clone()) |
167 |
| - } |
168 |
| - |
169 |
| - /// Store a session on the storage backend. |
170 |
| - /// |
171 |
| - /// The data inside the session will be url-encoded so it can be stored |
172 |
| - /// inside a cookie. |
173 |
| - async fn store_session( |
174 |
| - &mut self, |
175 |
| - sess: Session, |
176 |
| - jar: &mut cookie::CookieJar, |
177 |
| - ) -> Result<(), Self::Error> { |
178 |
| - let mut inner = self.inner.write().await; |
179 |
| - let id = sess.get("id").unwrap().to_string(); |
180 |
| - inner.insert(id.clone(), sess); |
181 |
| - jar.add(Cookie::new("session", id)); |
182 |
| - Ok(()) |
183 |
| - } |
184 |
| - } |
185 |
| -} |
| 35 | +// #![forbid(unsafe_code, future_incompatible)] |
| 36 | +// #![deny(missing_debug_implementations, nonstandard_style)] |
| 37 | +// #![warn(missing_docs, missing_doc_code_examples, unreachable_pub)] |
| 38 | +#![forbid(unsafe_code, future_incompatible)] |
| 39 | +#![deny( |
| 40 | + missing_debug_implementations, |
| 41 | + nonstandard_style, |
| 42 | + missing_docs, |
| 43 | + unreachable_pub, |
| 44 | + missing_copy_implementations, |
| 45 | + unused_qualifications |
| 46 | +)] |
| 47 | + |
| 48 | +pub use anyhow::Error; |
| 49 | +/// An anyhow::Result with default return type of () |
| 50 | +pub type Result<T = ()> = std::result::Result<T, Error>; |
| 51 | + |
| 52 | +mod cookie_store; |
| 53 | +mod memory_store; |
| 54 | +mod session; |
| 55 | +mod session_store; |
| 56 | + |
| 57 | +pub use cookie_store::CookieStore; |
| 58 | +pub use memory_store::MemoryStore; |
| 59 | +pub use session::Session; |
| 60 | +pub use session_store::SessionStore; |
| 61 | + |
| 62 | +pub use async_trait::async_trait; |
| 63 | +pub use base64; |
| 64 | +pub use blake3; |
| 65 | +pub use chrono; |
| 66 | +pub use hmac; |
| 67 | +pub use kv_log_macro as log; |
| 68 | +pub use serde; |
| 69 | +pub use serde_json; |
| 70 | +pub use sha2; |
0 commit comments