//! Extractors for using in routes. See [Wrap] //! //! Extractors in this module: //! - `Wrap` //! - `Option>` use std::ops::Deref; use axum::{ extract::{FromRequestParts, OptionalFromRequestParts}, http::{HeaderValue, header, request::Parts}, }; use somethings::auth::Session; use crate::{RuntimeError, state::SplormState}; /// A wrapped around an external type, allowing us to implement /// traits like [FromRequestParts] on them. /// /// Can be dereferenced to the wrapped type. pub struct Wrap(pub T); impl Deref for Wrap { type Target = T; fn deref(&self) -> &Self::Target { &self.0 } } impl FromRequestParts for Wrap { type Rejection = RuntimeError; async fn from_request_parts( parts: &mut Parts, state: &SplormState, ) -> Result { let sid_str = sid_from_cookie_header(parts.headers.get(header::COOKIE))?; let session = knowing::get_session(state.database(), sid_str) .await .map_err(|_| RuntimeError::InvalidSession)?; Ok(Wrap(session)) } } impl OptionalFromRequestParts for Wrap { type Rejection = RuntimeError; async fn from_request_parts( parts: &mut Parts, state: &SplormState, ) -> Result, Self::Rejection> { match sid_from_cookie_header(parts.headers.get(header::COOKIE)) { Err(_) => return Ok(None), Ok(sid_str) => { let session_result = knowing::get_session(state.database(), sid_str).await; match session_result { Ok(session) => Ok(Some(Wrap(session))), Err(knowing::Error::DatabaseError(dbe)) => Err(dbe.into()), _ => Ok(None), } } } } } fn sid_from_cookie_header(cookie_header: Option<&HeaderValue>) -> Result<&str, RuntimeError> { let cookie = gny::web::cookie::parse_header( cookie_header .ok_or(RuntimeError::InvalidSession)? .to_str() .map_err(|_| RuntimeError::InvalidSession)?, ) .map_err(|_| RuntimeError::InvalidSession)?; Ok(cookie.get("sid").ok_or(RuntimeError::InvalidSession)?) }