diff options
Diffstat (limited to 'src/lib.rs')
-rw-r--r-- | src/lib.rs | 79 |
1 files changed, 73 insertions, 6 deletions
diff --git a/src/lib.rs b/src/lib.rs index 495591d..f1a563e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,13 +3,13 @@ use std::sync::Arc; -use axum::extract::FromRef; -use axum_extra::extract::cookie::Key; +use axum::{extract::{FromRef, FromRequestParts}, response::IntoResponse}; +use axum_extra::extract::{cookie::Key, SignedCookieJar}; use database::{FileStorage, PostgresStorage, Storage}; use indieauth::backend::{AuthBackend, FileBackend as FileAuthBackend}; use kittybox_util::queue::JobQueue; use media::storage::{MediaStore, file::FileStore as FileMediaStore}; -use tokio::{sync::Mutex, task::JoinSet}; +use tokio::{sync::{Mutex, RwLock}, task::JoinSet}; use webmentions::queue::PostgresJobQueue; /// Database abstraction layer for Kittybox, allowing the CMS to work with any kind of database. @@ -22,6 +22,8 @@ pub mod webmentions; pub mod login; //pub mod admin; +const OAUTH2_SOFTWARE_ID: &str = "6f2eee84-c22c-4c9e-b900-10d4e97273c8"; + #[derive(Clone)] pub struct AppState<A, S, M, Q> where @@ -36,7 +38,63 @@ Q: JobQueue<webmentions::Webmention> + Sized pub job_queue: Q, pub http: reqwest::Client, pub background_jobs: Arc<Mutex<JoinSet<()>>>, - pub cookie_key: Key + pub cookie_key: Key, + pub session_store: SessionStore +} + +pub type SessionStore = Arc<RwLock<std::collections::HashMap<uuid::Uuid, Session>>>; + +#[derive(Debug, Clone)] +#[repr(transparent)] +pub struct Session(kittybox_indieauth::ProfileUrl); + +impl std::ops::Deref for Session { + type Target = kittybox_indieauth::ProfileUrl; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +pub struct NoSessionError; +impl axum::response::IntoResponse for NoSessionError { + fn into_response(self) -> axum::response::Response { + // TODO: prettier error message + (axum::http::StatusCode::UNAUTHORIZED, "You are not logged in, but this page requires a session.").into_response() + } +} + +#[async_trait::async_trait] +impl<S> FromRequestParts<S> for Session +where + SessionStore: FromRef<S>, + Key: FromRef<S>, + S: Send + Sync, +{ + type Rejection = NoSessionError; + + async fn from_request_parts(parts: &mut axum::http::request::Parts, state: &S) -> Result<Self, Self::Rejection> { + let jar = SignedCookieJar::<Key>::from_request_parts(parts, state).await.unwrap(); + let session_store = SessionStore::from_ref(state).read_owned().await; + + tracing::debug!("Cookie jar: {:#?}", jar); + let cookie = match jar.get("session_id") { + Some(cookie) => { + tracing::debug!("Session ID cookie: {}", cookie); + cookie + }, + None => { return Err(NoSessionError) } + }; + + session_store.get( + &dbg!(cookie.value_trimmed()) + .parse() + .map_err(|err| { + tracing::error!("Error parsing cookie: {}", err); + NoSessionError + })? + ).cloned().ok_or(NoSessionError) + } } // This is really regrettable, but I can't write: @@ -58,7 +116,6 @@ Q: JobQueue<webmentions::Webmention> + Sized // have to repeat this magic invocation. impl<S, M, Q> FromRef<AppState<Self, S, M, Q>> for FileAuthBackend -// where S: Storage, M: MediaStore where S: Storage, M: MediaStore, Q: JobQueue<webmentions::Webmention> { fn from_ref(input: &AppState<Self, S, M, Q>) -> Self { @@ -67,7 +124,6 @@ where S: Storage, M: MediaStore, Q: JobQueue<webmentions::Webmention> } impl<A, M, Q> FromRef<AppState<A, Self, M, Q>> for PostgresStorage -// where A: AuthBackend, M: MediaStore where A: AuthBackend, M: MediaStore, Q: JobQueue<webmentions::Webmention> { fn from_ref(input: &AppState<A, Self, M, Q>) -> Self { @@ -125,6 +181,14 @@ where A: AuthBackend, S: Storage, M: MediaStore } } +impl<A, S, M, Q> FromRef<AppState<A, S, M, Q>> for SessionStore +where A: AuthBackend, S: Storage, M: MediaStore, Q: JobQueue<webmentions::Webmention> +{ + fn from_ref(input: &AppState<A, S, M, Q>) -> Self { + input.session_store.clone() + } +} + pub mod companion { use std::{collections::HashMap, sync::Arc}; use axum::{ @@ -229,6 +293,8 @@ M: MediaStore + 'static + FromRef<St>, Q: kittybox_util::queue::JobQueue<crate::webmentions::Webmention> + FromRef<St>, reqwest::Client: FromRef<St>, Arc<Mutex<JoinSet<()>>>: FromRef<St>, +crate::SessionStore: FromRef<St>, +axum_extra::extract::cookie::Key: FromRef<St>, St: Clone + Send + Sync + 'static { use axum::routing::get; @@ -241,6 +307,7 @@ St: Clone + Send + Sync + 'static .merge(crate::indieauth::router::<St, A, S>()) .merge(crate::webmentions::router::<St, Q>()) .route("/.kittybox/health", get(health_check::<S>)) + .nest("/.kittybox/login", crate::login::router::<St, S>()) .route( "/.kittybox/static/:path", axum::routing::get(crate::frontend::statics) |