| 1 |
|
| 2 |
|
| 3 |
use axum::{ |
| 4 |
extract::{Path, State}, |
| 5 |
response::IntoResponse, |
| 6 |
}; |
| 7 |
use tower_sessions::Session; |
| 8 |
|
| 9 |
use sqlx::PgPool; |
| 10 |
|
| 11 |
use crate::{ |
| 12 |
AppCaches, |
| 13 |
auth::{AuthUser, SESSION_TRACKING_KEY}, |
| 14 |
db::{self, UserSessionId}, |
| 15 |
error::{AppError, Result}, |
| 16 |
templates::UserSessionsPartialTemplate, |
| 17 |
}; |
| 18 |
|
| 19 |
|
| 20 |
#[tracing::instrument(skip_all, name = "api::revoke_session")] |
| 21 |
pub(in crate::routes::api) async fn revoke_session( |
| 22 |
State(db): State<PgPool>, |
| 23 |
State(caches): State<AppCaches>, |
| 24 |
session: Session, |
| 25 |
AuthUser(user): AuthUser, |
| 26 |
Path(session_id): Path<UserSessionId>, |
| 27 |
) -> Result<impl IntoResponse> { |
| 28 |
|
| 29 |
if let Ok(Some(current_id)) = session.get::<UserSessionId>(SESSION_TRACKING_KEY).await |
| 30 |
&& current_id == session_id |
| 31 |
{ |
| 32 |
return Err(AppError::BadRequest( |
| 33 |
"Use logout to end your current session".to_string(), |
| 34 |
)); |
| 35 |
} |
| 36 |
|
| 37 |
db::sessions::delete_user_session(&db, session_id, user.id).await?; |
| 38 |
caches.session_cache.remove(&session_id); |
| 39 |
|
| 40 |
|
| 41 |
let sessions = db::sessions::get_user_sessions(&db, user.id).await?; |
| 42 |
let current_tracking_id = session |
| 43 |
.get::<UserSessionId>(SESSION_TRACKING_KEY) |
| 44 |
.await |
| 45 |
.ok() |
| 46 |
.flatten(); |
| 47 |
|
| 48 |
Ok(UserSessionsPartialTemplate { |
| 49 |
sessions, |
| 50 |
current_session_id: current_tracking_id, |
| 51 |
}) |
| 52 |
} |
| 53 |
|
| 54 |
|
| 55 |
#[tracing::instrument(skip_all, name = "api::revoke_other_sessions")] |
| 56 |
pub(in crate::routes::api) async fn revoke_other_sessions( |
| 57 |
State(db): State<PgPool>, |
| 58 |
State(caches): State<AppCaches>, |
| 59 |
session: Session, |
| 60 |
AuthUser(user): AuthUser, |
| 61 |
) -> Result<impl IntoResponse> { |
| 62 |
let current_tracking_id = session |
| 63 |
.get::<UserSessionId>(SESSION_TRACKING_KEY) |
| 64 |
.await |
| 65 |
.ok() |
| 66 |
.flatten(); |
| 67 |
|
| 68 |
if let Some(current_id) = current_tracking_id { |
| 69 |
let revoked_ids = db::sessions::delete_other_sessions(&db, current_id, user.id).await?; |
| 70 |
for id in &revoked_ids { |
| 71 |
caches.session_cache.remove(id); |
| 72 |
} |
| 73 |
tracing::info!(user_id = %user.id, revoked = revoked_ids.len(), event = "revoke_other_sessions", "Revoked other sessions"); |
| 74 |
} |
| 75 |
|
| 76 |
|
| 77 |
let sessions = db::sessions::get_user_sessions(&db, user.id).await?; |
| 78 |
|
| 79 |
Ok(UserSessionsPartialTemplate { |
| 80 |
sessions, |
| 81 |
current_session_id: current_tracking_id, |
| 82 |
}) |
| 83 |
} |
| 84 |
|