diff --git a/crates/axum-utils/src/session.rs b/crates/axum-utils/src/session.rs index bac41b78..e8c95e44 100644 --- a/crates/axum-utils/src/session.rs +++ b/crates/axum-utils/src/session.rs @@ -38,6 +38,13 @@ impl SessionInfo { } } + /// Mark the session as ended + #[must_use] + pub fn mark_session_ended(mut self) -> Self { + self.current = None; + self + } + /// Load the [`BrowserSession`] from database pub async fn load_session( &self, diff --git a/crates/handlers/src/lib.rs b/crates/handlers/src/lib.rs index 8e93d3aa..da261838 100644 --- a/crates/handlers/src/lib.rs +++ b/crates/handlers/src/lib.rs @@ -21,7 +21,12 @@ use std::sync::Arc; -use axum::{body::HttpBody, extract::Extension, routing::get, Router}; +use axum::{ + body::HttpBody, + extract::Extension, + routing::{get, post}, + Router, +}; use mas_axum_utils::UrlBuilder; use mas_config::{Encrypter, RootConfig}; use mas_email::Mailer; @@ -81,6 +86,7 @@ where "/login", get(self::views::login::get).post(self::views::login::post), ) + .route("/logout", post(self::views::logout::post)) .fallback(mas_static_files::Assets) .layer(Extension(pool.clone())) .layer(Extension(templates.clone())) diff --git a/crates/handlers/src/views/login.rs b/crates/handlers/src/views/login.rs index befde971..3f47ffff 100644 --- a/crates/handlers/src/views/login.rs +++ b/crates/handlers/src/views/login.rs @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#![allow(clippy::trait_duplication_in_bounds)] - use axum::{ extract::{Extension, Form, Query}, response::{Html, IntoResponse, Redirect, Response}, diff --git a/crates/handlers/src/views/logout.rs b/crates/handlers/src/views/logout.rs index e07acd20..883e1e7e 100644 --- a/crates/handlers/src/views/logout.rs +++ b/crates/handlers/src/views/logout.rs @@ -1,4 +1,4 @@ -// Copyright 2021 The Matrix.org Foundation C.I.C. +// Copyright 2021, 2022 The Matrix.org Foundation C.I.C. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,34 +12,48 @@ // See the License for the specific language governing permissions and // limitations under the License. -use mas_config::Encrypter; -use mas_data_model::BrowserSession; -use mas_storage::{user::end_session, PostgresqlBackend}; -use mas_warp_utils::{ - errors::WrapError, - filters::{self, csrf::protected_form, database::transaction, session::session}, +use axum::{ + extract::{Extension, Form}, + response::{IntoResponse, Redirect}, }; -use sqlx::{PgPool, Postgres, Transaction}; -use warp::{filters::BoxedFilter, hyper::Uri, Filter, Rejection, Reply}; +use hyper::Uri; +use mas_axum_utils::{ + csrf::{CsrfExt, ProtectedForm}, + fancy_error, FancyError, PrivateCookieJar, SessionInfoExt, +}; +use mas_config::Encrypter; +use mas_storage::user::end_session; +use mas_templates::Templates; +use sqlx::PgPool; -pub(super) fn filter(pool: &PgPool, encrypter: &Encrypter) -> BoxedFilter<(Box,)> { - warp::path!("logout") - .and(filters::trace::name("POST /logout")) - .and(warp::post()) - .and(session(pool, encrypter)) - .and(transaction(pool)) - .and(protected_form(encrypter)) - .and_then(post) - .boxed() -} - -async fn post( - session: BrowserSession, - mut txn: Transaction<'_, Postgres>, - _form: (), -) -> Result, Rejection> { - end_session(&mut txn, &session).await.wrap_error()?; - txn.commit().await.wrap_error()?; - - Ok(Box::new(warp::redirect(Uri::from_static("/login")))) +pub(crate) async fn post( + Extension(templates): Extension, + Extension(pool): Extension, + cookie_jar: PrivateCookieJar, + Form(form): Form>, +) -> Result { + let mut txn = pool.begin().await.map_err(fancy_error(templates.clone()))?; + + cookie_jar + .verify_form(form) + .map_err(fancy_error(templates.clone()))?; + + let (session_info, mut cookie_jar) = cookie_jar.session_info(); + + let maybe_session = session_info + .load_session(&mut txn) + .await + .map_err(fancy_error(templates.clone()))?; + + if let Some(session) = maybe_session { + end_session(&mut txn, &session) + .await + .map_err(fancy_error(templates.clone()))?; + cookie_jar = cookie_jar.update_session_info(&session_info.mark_session_ended()); + } + + txn.commit().await.map_err(fancy_error(templates))?; + + let to = Uri::from_static("/login"); + Ok((cookie_jar.headers(), Redirect::to(to))) } diff --git a/crates/handlers/src/views/mod.rs b/crates/handlers/src/views/mod.rs index 6a6b7fe2..5a2d78aa 100644 --- a/crates/handlers/src/views/mod.rs +++ b/crates/handlers/src/views/mod.rs @@ -28,8 +28,8 @@ pub mod shared; pub mod verify; use self::{ - account::filter as account, logout::filter as logout, reauth::filter as reauth, - register::filter as register, verify::filter as verify, + account::filter as account, reauth::filter as reauth, register::filter as register, + verify::filter as verify, }; pub(crate) use self::{ login::LoginRequest, reauth::ReauthRequest, register::RegisterRequest, shared::PostAuthAction, @@ -45,15 +45,12 @@ pub(super) fn filter( ) -> BoxedFilter<(Box,)> { let account = account(pool, templates, mailer, encrypter, http_config, csrf_config); let register = register(pool, templates, encrypter, csrf_config); - let logout = logout(pool, encrypter); let reauth = reauth(pool, templates, encrypter, csrf_config); let verify = verify(pool, templates, encrypter, csrf_config); account .or(register) .unify() - .or(logout) - .unify() .or(reauth) .unify() .or(verify)