Skip to main content

reinhardt_middleware/session/
id.rs

1//! Session-ID newtypes and the request-scoped active ID holder.
2//!
3//! These types are stored in request extensions by `SessionMiddleware`
4//! so downstream handlers and `Injectable` implementations can read the
5//! current session ID and configured cookie name without parsing cookies
6//! manually.
7
8use std::sync::{Arc, RwLock};
9
10/// Newtype wrapper for session ID stored in request extensions.
11///
12/// Handlers can retrieve the current session ID from the request
13/// extensions without parsing cookies manually.
14///
15/// # Example
16///
17/// ```rust,ignore
18/// fn handle(&self, request: Request) -> Result<Response> {
19///     if let Some(session_id) = request.extensions.get::<SessionId>() {
20///         println!("Session: {}", session_id.as_str());
21///     }
22///     // ...
23/// }
24/// ```
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct SessionId(String);
27
28impl SessionId {
29	/// Create a new `SessionId` from the given string.
30	pub fn new(id: String) -> Self {
31		Self(id)
32	}
33
34	/// Returns the session ID as a string slice.
35	pub fn as_str(&self) -> &str {
36		&self.0
37	}
38}
39
40impl AsRef<str> for SessionId {
41	fn as_ref(&self) -> &str {
42		self.as_str()
43	}
44}
45
46impl std::fmt::Display for SessionId {
47	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48		f.write_str(self.as_str())
49	}
50}
51
52/// Shared, mutable handle to the session ID that the middleware will write
53/// to the response `Set-Cookie` header.
54///
55/// Stored in request extensions by `SessionMiddleware`. Handlers that rotate
56/// the session ID (e.g., for session-fixation prevention on login) MUST
57/// either call `SessionData::regenerate_id` (which updates this holder
58/// transparently) or write to it directly via `set`. Otherwise the cookie
59/// returned to the client points at a session ID that no longer exists in
60/// the store. See #3827.
61#[derive(Debug, Clone)]
62pub struct ActiveSessionId(Arc<RwLock<String>>);
63
64impl ActiveSessionId {
65	/// Create an `ActiveSessionId` initialised to `id`.
66	pub fn new(id: String) -> Self {
67		Self(Arc::new(RwLock::new(id)))
68	}
69
70	/// Read the current session ID.
71	pub fn get(&self) -> String {
72		self.0.read().unwrap_or_else(|e| e.into_inner()).clone()
73	}
74
75	/// Replace the session ID. Call after rotating the underlying
76	/// `SessionData::id` so the middleware's `Set-Cookie` matches the
77	/// store entry.
78	pub fn set(&self, id: String) {
79		*self.0.write().unwrap_or_else(|e| e.into_inner()) = id;
80	}
81}
82
83/// Newtype wrapper for the configured session cookie name.
84///
85/// Stored in request extensions by `SessionMiddleware` so that
86/// `Injectable` implementations can retrieve the configured cookie name
87/// instead of hardcoding it.
88#[derive(Debug, Clone, PartialEq, Eq)]
89pub struct SessionCookieName(String);
90
91impl SessionCookieName {
92	/// Create a new `SessionCookieName`.
93	pub fn new(name: String) -> Self {
94		Self(name)
95	}
96
97	/// Returns the cookie name as a string slice.
98	pub fn as_str(&self) -> &str {
99		&self.0
100	}
101}