about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorVika <vika@fireburn.ru>2025-01-01 23:17:56 +0300
committerVika <vika@fireburn.ru>2025-01-01 23:17:56 +0300
commitd10710326da703f69eaa06723dc66e330fd32745 (patch)
treee4a32c2a7f2e4a61d1f3f6237977de64e47470ae /src
parent675141379067858376698d5f75ab163977d33e3a (diff)
downloadkittybox-d10710326da703f69eaa06723dc66e330fd32745.tar.zst
axum: 0.7.9 → 0.8.1
Some breaking changes. For better or for worse. The optional extractor
breaking change is a double-edged sword, since not all extractors can
be used with `Option<T>` now, and you have to use
`Result<T, T::Rejection>` even when you want to ignore an error coming
from an extractor, such as `Query`.

However, this allows catching errors on authorization extractors even
in places where authorization is optional.

Change-Id: I35f809d3adf27dbef0e7ee93dc1a7af178b7d014
Diffstat (limited to 'src')
-rw-r--r--src/frontend/mod.rs4
-rw-r--r--src/frontend/onboarding.rs3
-rw-r--r--src/indieauth/mod.rs19
-rw-r--r--src/indieauth/webauthn.rs4
-rw-r--r--src/lib.rs37
-rw-r--r--src/login.rs4
-rw-r--r--src/main.rs2
-rw-r--r--src/media/mod.rs5
-rw-r--r--src/micropub/mod.rs12
9 files changed, 47 insertions, 43 deletions
diff --git a/src/frontend/mod.rs b/src/frontend/mod.rs
index 9abbcf1..8338ac6 100644
--- a/src/frontend/mod.rs
+++ b/src/frontend/mod.rs
@@ -1,10 +1,10 @@
 use crate::database::{Storage, StorageError};
 use axum::{
-    extract::{Host, Query, State},
+    extract::{Query, State},
     http::{StatusCode, Uri},
     response::IntoResponse,
 };
-use axum_extra::headers::HeaderMapExt;
+use axum_extra::{extract::Host, headers::HeaderMapExt};
 use futures_util::FutureExt;
 use serde::Deserialize;
 use std::convert::TryInto;
diff --git a/src/frontend/onboarding.rs b/src/frontend/onboarding.rs
index a8d2ae6..4588157 100644
--- a/src/frontend/onboarding.rs
+++ b/src/frontend/onboarding.rs
@@ -2,11 +2,12 @@ use std::sync::Arc;
 
 use crate::database::{settings, Storage};
 use axum::{
-    extract::{FromRef, Host, State},
+    extract::{FromRef, State},
     http::StatusCode,
     response::{Html, IntoResponse},
     Json,
 };
+use axum_extra::extract::Host;
 use kittybox_frontend_renderer::{ErrorPage, OnboardingPage, Template};
 use serde::Deserialize;
 use tokio::{task::JoinSet, sync::Mutex};
diff --git a/src/indieauth/mod.rs b/src/indieauth/mod.rs
index 0ac3dfd..ab38715 100644
--- a/src/indieauth/mod.rs
+++ b/src/indieauth/mod.rs
@@ -3,10 +3,10 @@ use microformats::types::Class;
 use tracing::error;
 use serde::Deserialize;
 use axum::{
-    extract::{Form, FromRef, Host, Json, Query, State}, http::StatusCode, response::{Html, IntoResponse, Response}
+    extract::{Form, FromRef, Json, Query, State}, http::StatusCode, response::{Html, IntoResponse, Response}
 };
 #[cfg_attr(not(feature = "webauthn"), allow(unused_imports))]
-use axum_extra::extract::cookie::{CookieJar, Cookie};
+use axum_extra::extract::{Host, cookie::{CookieJar, Cookie}};
 use axum_extra::{headers::{authorization::Bearer, Authorization, ContentType, HeaderMapExt}, TypedHeader};
 use crate::database::Storage;
 use kittybox_indieauth::{
@@ -65,7 +65,20 @@ impl axum::response::IntoResponse for IndieAuthResourceError {
     }
 }
 
-#[async_trait::async_trait]
+impl <A: AuthBackend + FromRef<St>, St: Clone + Send + Sync + 'static> axum::extract::OptionalFromRequestParts<St> for User<A> {
+    type Rejection = <Self as axum::extract::FromRequestParts<St>>::Rejection;
+
+    async fn from_request_parts(req: &mut axum::http::request::Parts, state: &St) -> Result<Option<Self>, Self::Rejection> {
+        let res = <Self as axum::extract::FromRequestParts<St>>::from_request_parts(req, state).await;
+
+        match res {
+            Ok(user) => Ok(Some(user)),
+            Err(IndieAuthResourceError::Unauthorized) => Ok(None),
+            Err(err) => Err(err),
+        }
+    }
+}
+
 impl <A: AuthBackend + FromRef<St>, St: Clone + Send + Sync + 'static> axum::extract::FromRequestParts<St> for User<A> {
     type Rejection = IndieAuthResourceError;
 
diff --git a/src/indieauth/webauthn.rs b/src/indieauth/webauthn.rs
index b7d8c71..0757e72 100644
--- a/src/indieauth/webauthn.rs
+++ b/src/indieauth/webauthn.rs
@@ -1,9 +1,9 @@
 use axum::{
-    extract::{Json, Host},
+    extract::Json,
     response::{IntoResponse, Response},
     http::StatusCode, Extension
 };
-use axum_extra::extract::cookie::{CookieJar, Cookie};
+use axum_extra::extract::{Host, cookie::{CookieJar, Cookie}};
 use axum_extra::{TypedHeader, headers::{authorization::Bearer, Authorization}};
 
 use super::backend::AuthBackend;
diff --git a/src/lib.rs b/src/lib.rs
index 5fe3b18..e6bc24c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,8 +3,8 @@
 
 use std::sync::Arc;
 
-use axum::extract::{FromRef, FromRequestParts};
-use axum_extra::extract::{cookie::Key, SignedCookieJar};
+use axum::extract::{FromRef, FromRequestParts, OptionalFromRequestParts};
+use axum_extra::extract::{cookie::{Cookie, Key}, SignedCookieJar};
 use database::{FileStorage, PostgresStorage, Storage};
 use indieauth::backend::{AuthBackend, FileBackend as FileAuthBackend};
 use kittybox_util::queue::JobQueue;
@@ -64,36 +64,23 @@ impl axum::response::IntoResponse for NoSessionError {
     }
 }
 
-#[async_trait::async_trait]
-impl<S> FromRequestParts<S> for Session
+impl<S> OptionalFromRequestParts<S> for Session
 where
     SessionStore: FromRef<S>,
     Key: FromRef<S>,
     S: Send + Sync,
 {
-    type Rejection = NoSessionError;
+    type Rejection = std::convert::Infallible;
 
-    async fn from_request_parts(parts: &mut axum::http::request::Parts, state: &S) ->  Result<Self, Self::Rejection> {
+    async fn from_request_parts(parts: &mut axum::http::request::Parts, state: &S) ->  Result<Option<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)
+        Ok(jar.get("session_id")
+            .as_ref()
+            .map(Cookie::value_trimmed)
+            .and_then(|id| uuid::Uuid::parse_str(id).ok())
+            .and_then(|id| session_store.get(&id).cloned()))
     }
 }
 
@@ -264,7 +251,7 @@ pub mod companion {
 
         axum::Router::new()
             .route(
-                "/:filename",
+                "/{filename}",
                 axum::routing::get(map_to_static)
                     .layer(Extension(resources))
             )
@@ -309,7 +296,7 @@ St: Clone + Send + Sync + 'static
         .route("/.kittybox/health", get(health_check::<S>))
         .nest("/.kittybox/login", crate::login::router::<St, S>())
         .route(
-            "/.kittybox/static/:path",
+            "/.kittybox/static/{path}",
             axum::routing::get(crate::frontend::statics)
         )
         .route("/.kittybox/coffee", get(teapot_route))
diff --git a/src/login.rs b/src/login.rs
index 19bfaf7..eaa787c 100644
--- a/src/login.rs
+++ b/src/login.rs
@@ -1,8 +1,8 @@
 use std::{borrow::Cow, str::FromStr};
 
 use futures_util::FutureExt;
-use axum::{extract::{FromRef, Host, Query, State}, http::HeaderValue, response::IntoResponse, Form};
-use axum_extra::{extract::{cookie::{self, Cookie}, SignedCookieJar}, headers::HeaderMapExt, TypedHeader};
+use axum::{extract::{FromRef, Query, State}, http::HeaderValue, response::IntoResponse, Form};
+use axum_extra::{extract::{Host, cookie::{self, Cookie}, SignedCookieJar}, headers::HeaderMapExt, TypedHeader};
 use hyper::{header::{CACHE_CONTROL, LOCATION}, StatusCode};
 use kittybox_frontend_renderer::{Template, LoginPage, LogoutPage};
 use kittybox_indieauth::{AuthorizationResponse, Error, GrantType, PKCEVerifier, Scope, Scopes};
diff --git a/src/main.rs b/src/main.rs
index f57b0be..bd3684e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -224,7 +224,7 @@ async fn main() {
         }
     };
 
-    let mut servers: Vec<axum::serve::Serve<_, _>> = vec![];
+    let mut servers: Vec<axum::serve::Serve<_, _, _>> = vec![];
 
     let build_hyper = |tcp: std::net::TcpListener| {
         tracing::info!("Listening on {}", tcp.local_addr().unwrap());
diff --git a/src/media/mod.rs b/src/media/mod.rs
index 3ed6810..199f05f 100644
--- a/src/media/mod.rs
+++ b/src/media/mod.rs
@@ -1,7 +1,8 @@
 use axum::{
-    extract::{multipart::Multipart, FromRef, Host, Path, State}, response::{IntoResponse, Response}
+    extract::{multipart::Multipart, FromRef, Path, State}, response::{IntoResponse, Response}
 };
 use axum_extra::headers::{ContentLength, HeaderMapExt, HeaderValue, IfNoneMatch};
+use axum_extra::extract::Host;
 use axum_extra::TypedHeader;
 use kittybox_util::micropub::{Error as MicropubError, ErrorKind as ErrorType};
 use kittybox_indieauth::Scope;
@@ -132,5 +133,5 @@ pub fn router<St, A, M>() -> axum::Router<St> where
 {
     axum::Router::new()
         .route("/", axum::routing::post(upload::<M, A>))
-        .route("/uploads/*file", axum::routing::get(serve::<M>))
+        .route("/uploads/{*file}", axum::routing::get(serve::<M>))
 }
diff --git a/src/micropub/mod.rs b/src/micropub/mod.rs
index 621d4f9..719fbf0 100644
--- a/src/micropub/mod.rs
+++ b/src/micropub/mod.rs
@@ -6,8 +6,9 @@ use crate::database::{MicropubChannel, Storage, StorageError};
 use crate::indieauth::backend::AuthBackend;
 use crate::indieauth::User;
 use crate::micropub::util::form_to_mf2_json;
-use axum::extract::{FromRef, Host, Query, State};
+use axum::extract::{FromRef, Query, State};
 use axum::body::Body as BodyStream;
+use axum_extra::extract::Host;
 use axum_extra::headers::ContentType;
 use axum::response::{IntoResponse, Response};
 use axum_extra::TypedHeader;
@@ -603,13 +604,13 @@ pub(crate) async fn post<D: Storage + 'static, A: AuthBackend>(
 #[tracing::instrument(skip(db))]
 pub(crate) async fn query<D: Storage, A: AuthBackend>(
     State(db): State<D>,
-    query: Option<Query<MicropubQuery>>,
+    query: Result<Query<MicropubQuery>, <Query<MicropubQuery> as axum::extract::FromRequestParts<()>>::Rejection>,
     Host(host): Host,
     user: User<A>,
 ) -> axum::response::Response {
     // We handle the invalid query case manually to return a
     // MicropubError instead of HTTP 422
-    let query = if let Some(Query(query)) = query {
+    let query = if let Ok(Query(query)) = query {
         query
     } else {
         return MicropubError::from_static(
@@ -771,7 +772,8 @@ mod tests {
 
     use super::FetchedPostContext;
     use kittybox_indieauth::{Scopes, Scope, TokenData};
-    use axum::extract::{Host, State};
+    use axum::extract::State;
+    use axum_extra::extract::Host;
 
     #[test]
     fn test_populate_reply_context() {
@@ -927,7 +929,7 @@ mod tests {
     async fn test_query_foreign_url() {
         let res = super::query(
             State(crate::database::MemoryStorage::default()),
-            Some(axum::extract::Query(super::MicropubQuery::source(
+            Ok(axum::extract::Query(super::MicropubQuery::source(
                 "https://aaronparecki.com/feeds/main",
             ))),
             Host("aaronparecki.com".to_owned()),