use actix_web::HttpResponse; use diesel::prelude::*; use crate::auth::extractor::AuthUser; use crate::database::establish_connection; use crate::error::AppError; use crate::schema::users; /// Invalidates every token previously issued for this user by bumping /// `token_version` — the auth middleware rejects tokens whose `tv` claim no /// longer matches the stored value. pub async fn logout(auth_user: AuthUser) -> Result { let mut connection = establish_connection(); diesel::update(users::table.find(auth_user.0)) .set(users::token_version.eq(users::token_version + 1)) .execute(&mut connection)?; Ok(HttpResponse::Ok().finish()) } #[cfg(test)] mod tests { use actix_service::Service; use actix_web::http::StatusCode; use actix_web::{test, web, App, HttpMessage}; use diesel::prelude::*; use super::logout; use crate::auth::extractor::AuthUser; use crate::database::establish_connection; use crate::models::user::rss_user::User; use crate::schema::users; use crate::test_helpers::{delete_user, insert_user}; #[actix_web::test] async fn logout_bumps_token_version() { let mut connection = establish_connection(); let user = insert_user(&mut connection, "secret"); let user_id = user.id; let initial_version = user.token_version; let app = test::init_service( App::new() .wrap_fn(move |req, srv| { req.extensions_mut().insert(AuthUser(user_id)); srv.call(req) }) .route("/logout", web::post().to(logout)), ) .await; let req = test::TestRequest::post().uri("/logout").to_request(); let resp = test::call_service(&app, req).await; assert_eq!(StatusCode::OK, resp.status()); let updated: User = users::table.find(user_id).first(&mut connection).unwrap(); assert_eq!(initial_version + 1, updated.token_version); delete_user(&mut connection, user_id); } }