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}