alibi_core/session/cookie_cache/runtime/
mod.rs1use crate::utils::LockUnpoisoned;
3use crate::{AdapterOutput, SessionView, UserView};
4mod cookies;
5mod issuance;
6mod read;
7
8use crate::session::SessionRequest;
9use crate::{AuthContext, AuthSchema};
10pub(super) use cookies::{browser_preference, chunk_index};
11use cookies::{cache_value, cookie_values, cookies, existing_names};
12pub use cookies::{chunked_cookie_headers, chunked_cookie_value, session_cleanup_headers};
13use issuance::{build_headers, stored_read_headers};
14pub use issuance::{
15 emit_issuance, emit_issuance_in_transaction, emit_issuance_snapshot, stored_headers,
16 take_issuance,
17};
18pub(crate) use read::{CacheDecoding, authenticated_with, renew_cache};
19pub use read::{authenticated, cleanup as clear_session_cookies, read};
20use std::sync::{Arc, Mutex};
21
22#[derive(Debug)]
23struct IssuancePreference(bool);
24
25#[derive(Clone, Debug)]
28pub struct SessionHookCacheMetadata {
29 pub updated_at: f64,
30 pub version: Option<String>,
31}
32
33#[derive(Debug)]
34struct SessionHookCache(Option<SessionHookCacheMetadata>);
35
36#[derive(Debug)]
37struct PublishedSession(Option<PublishedSessionSnapshot>);
38
39#[derive(Clone, Debug)]
42pub struct PublishedSessionSnapshot {
43 user: UserView,
44 session: SessionView,
45 user_output: Option<AdapterOutput>,
46 session_output: Option<AdapterOutput>,
47}
48impl PublishedSessionSnapshot {
49 #[must_use]
50 pub const fn user(&self) -> &UserView {
51 &self.user
52 }
53 #[must_use]
54 pub const fn session(&self) -> &SessionView {
55 &self.session
56 }
57 #[must_use]
58 pub const fn user_output(&self) -> Option<&AdapterOutput> {
59 self.user_output.as_ref()
60 }
61 #[must_use]
62 pub const fn session_output(&self) -> Option<&AdapterOutput> {
63 self.session_output.as_ref()
64 }
65}
66
67#[must_use]
70pub fn published_session(request: &impl SessionRequest) -> Option<(UserView, SessionView)> {
71 published_session_snapshot(request).map(|snapshot| (snapshot.user, snapshot.session))
72}
73
74#[must_use]
76pub fn published_session_snapshot(
77 request: &impl SessionRequest,
78) -> Option<PublishedSessionSnapshot> {
79 request
80 .extensions()
81 .get::<PublishedSession>()
82 .and_then(|session| session.0.clone())
83}
84
85pub fn retain_publication(source: &impl SessionRequest, target: &impl SessionRequest) {
88 if target
91 .extensions()
92 .get::<PublishedSession>()
93 .is_some_and(|session| session.0.is_none())
94 {
95 return;
96 }
97 target
98 .extensions()
99 .insert(PublishedSession(published_session_snapshot(source)));
100}
101
102pub fn discard_issuance(request: &impl SessionRequest) {
104 request.extensions().insert(PublishedSession(None));
105 if let Some(pending) = request.extensions().get::<PendingIssuance>() {
106 *pending.0.lock_unpoisoned() = PendingData::default();
107 }
108}
109
110fn record_publication(snapshot: PublishedSessionSnapshot) {
111 if let Some(endpoint) = crate::endpoint::current_endpoint_call_context() {
112 endpoint
113 .extensions()
114 .insert(PublishedSession(Some(snapshot)));
115 } else if let Some(request) = crate::hooks::current_request_hook_context() {
116 request.extensions.insert(PublishedSession(Some(snapshot)));
117 }
118}
119
120#[must_use]
122pub fn session_hook_cache_metadata(
123 request: &impl SessionRequest,
124) -> Option<SessionHookCacheMetadata> {
125 request
126 .extensions()
127 .get::<SessionHookCache>()
128 .and_then(|context| context.0.clone())
129}
130
131#[derive(Debug, Default)]
132struct PendingIssuance(Mutex<PendingData>);
133
134#[derive(Debug, Default)]
135struct PendingData {
136 prior_headers: Vec<String>,
137 cache_headers: Vec<String>,
138 ordinary_error: bool,
139}
140
141pub struct AuthenticatedRead<S: AuthSchema> {
144 pub user: crate::AuthenticatedUser<S>,
145 pub session: SessionView,
146 pub needs_refresh: Option<bool>,
147}
148
149struct EstablishedSession<S: AuthSchema>(Option<EstablishedRead<S>>);
150
151struct EstablishedRead<S: AuthSchema> {
152 read: AuthenticatedRead<S>,
153 headers: std::collections::HashMap<String, String>,
154 virtual_session: Option<SessionView>,
155 config: Arc<crate::AuthConfig>,
156 database: Arc<dyn crate::AuthStore<S>>,
157}
158
159pub fn clear_established_session<S: AuthSchema>(request: &impl SessionRequest) {
161 request.extensions().insert(EstablishedSession::<S>(None));
162}
163
164fn establish<S: AuthSchema>(
165 ctx: &AuthContext<S>,
166 request: &impl SessionRequest,
167 read: AuthenticatedRead<S>,
168) -> AuthenticatedRead<S> {
169 request
170 .extensions()
171 .insert(EstablishedSession::<S>(Some(EstablishedRead {
172 read: AuthenticatedRead {
173 user: read.user.clone(),
174 session: read.session.clone(),
175 needs_refresh: read.needs_refresh,
176 },
177 headers: request.session_headers().clone(),
178 virtual_session: request.virtual_session(ctx),
179 config: Arc::clone(&ctx.config),
180 database: Arc::clone(&ctx.database),
181 })));
182 read
183}
184
185impl<S: AuthSchema> std::fmt::Debug for AuthenticatedRead<S> {
186 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187 f.debug_struct("AuthenticatedRead").finish_non_exhaustive()
188 }
189}
190
191#[doc(hidden)]
194pub fn set_issuance_preference(request: &impl SessionRequest, dont_remember: bool) {
195 request
196 .extensions()
197 .insert(IssuancePreference(dont_remember));
198}