use crate::utils::LockUnpoisoned;
use crate::{AdapterOutput, SessionView, UserView};
mod cookies;
mod issuance;
mod read;
use crate::session::SessionRequest;
use crate::{AuthContext, AuthSchema};
pub(super) use cookies::{browser_preference, chunk_index};
use cookies::{cache_value, cookie_values, cookies, existing_names};
pub use cookies::{chunked_cookie_headers, chunked_cookie_value, session_cleanup_headers};
use issuance::{build_headers, stored_read_headers};
pub use issuance::{
emit_issuance, emit_issuance_in_transaction, emit_issuance_snapshot, stored_headers,
take_issuance,
};
pub(crate) use read::{CacheDecoding, authenticated_with, renew_cache};
pub use read::{authenticated, cleanup as clear_session_cookies, read};
use std::sync::{Arc, Mutex};
#[derive(Debug)]
struct IssuancePreference(bool);
#[derive(Clone, Debug)]
pub struct SessionHookCacheMetadata {
pub updated_at: f64,
pub version: Option<String>,
}
#[derive(Debug)]
struct SessionHookCache(Option<SessionHookCacheMetadata>);
#[derive(Debug)]
struct PublishedSession(Option<PublishedSessionSnapshot>);
#[derive(Clone, Debug)]
pub struct PublishedSessionSnapshot {
user: UserView,
session: SessionView,
user_output: Option<AdapterOutput>,
session_output: Option<AdapterOutput>,
}
impl PublishedSessionSnapshot {
#[must_use]
pub const fn user(&self) -> &UserView {
&self.user
}
#[must_use]
pub const fn session(&self) -> &SessionView {
&self.session
}
#[must_use]
pub const fn user_output(&self) -> Option<&AdapterOutput> {
self.user_output.as_ref()
}
#[must_use]
pub const fn session_output(&self) -> Option<&AdapterOutput> {
self.session_output.as_ref()
}
}
#[must_use]
pub fn published_session(request: &impl SessionRequest) -> Option<(UserView, SessionView)> {
published_session_snapshot(request).map(|snapshot| (snapshot.user, snapshot.session))
}
#[must_use]
pub fn published_session_snapshot(
request: &impl SessionRequest,
) -> Option<PublishedSessionSnapshot> {
request
.extensions()
.get::<PublishedSession>()
.and_then(|session| session.0.clone())
}
pub fn discard_issuance(request: &impl SessionRequest) {
request.extensions().insert(PublishedSession(None));
if let Some(pending) = request.extensions().get::<PendingIssuance>() {
*pending.0.lock_unpoisoned() = PendingData::default();
}
}
fn record_publication(snapshot: PublishedSessionSnapshot) {
if let Some(endpoint) = crate::endpoint::current_endpoint_call_context() {
endpoint
.extensions()
.insert(PublishedSession(Some(snapshot)));
} else if let Some(request) = crate::hooks::current_request_hook_context() {
request.extensions.insert(PublishedSession(Some(snapshot)));
}
}
#[must_use]
pub fn session_hook_cache_metadata(
request: &impl SessionRequest,
) -> Option<SessionHookCacheMetadata> {
request
.extensions()
.get::<SessionHookCache>()
.and_then(|context| context.0.clone())
}
#[derive(Debug, Default)]
struct PendingIssuance(Mutex<PendingData>);
#[derive(Debug, Default)]
struct PendingData {
prior_headers: Vec<String>,
cache_headers: Vec<String>,
ordinary_error: bool,
}
pub struct AuthenticatedRead<S: AuthSchema> {
pub user: crate::AuthenticatedUser<S>,
pub session: SessionView,
pub needs_refresh: Option<bool>,
}
struct EstablishedSession<S: AuthSchema>(Option<EstablishedRead<S>>);
struct EstablishedRead<S: AuthSchema> {
read: AuthenticatedRead<S>,
headers: std::collections::HashMap<String, String>,
virtual_session: Option<SessionView>,
config: Arc<crate::AuthConfig>,
database: Arc<dyn crate::AuthStore<S>>,
}
pub fn clear_established_session<S: AuthSchema>(request: &impl SessionRequest) {
request.extensions().insert(EstablishedSession::<S>(None));
}
fn establish<S: AuthSchema>(
ctx: &AuthContext<S>,
request: &impl SessionRequest,
read: AuthenticatedRead<S>,
) -> AuthenticatedRead<S> {
request
.extensions()
.insert(EstablishedSession::<S>(Some(EstablishedRead {
read: AuthenticatedRead {
user: read.user.clone(),
session: read.session.clone(),
needs_refresh: read.needs_refresh,
},
headers: request.session_headers().clone(),
virtual_session: request.virtual_session(ctx),
config: Arc::clone(&ctx.config),
database: Arc::clone(&ctx.database),
})));
read
}
impl<S: AuthSchema> std::fmt::Debug for AuthenticatedRead<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AuthenticatedRead").finish_non_exhaustive()
}
}
#[doc(hidden)]
pub fn set_issuance_preference(request: &impl SessionRequest, dont_remember: bool) {
request
.extensions()
.insert(IssuancePreference(dont_remember));
}