feat: store user agent with sessions

main
Jef Roosens 2025-03-29 15:37:50 +01:00
parent 5112a6ce35
commit 2c44f788d9
No known key found for this signature in database
GPG Key ID: 21FD3D77D56BAF49
9 changed files with 23 additions and 5 deletions

View File

@ -56,6 +56,7 @@ pub struct EpisodeAction {
pub struct Session { pub struct Session {
pub id: i64, pub id: i64,
pub last_seen: DateTime<Utc>, pub last_seen: DateTime<Utc>,
pub user_agent: Option<String>,
pub user: User, pub user: User,
} }

View File

@ -71,11 +71,16 @@ impl GpodderRepository {
} }
} }
pub fn create_session(&self, user: &models::User) -> Result<models::Session, AuthErr> { pub fn create_session(
&self,
user: &models::User,
user_agent: Option<String>,
) -> Result<models::Session, AuthErr> {
let session = models::Session { let session = models::Session {
id: rand::thread_rng().gen(), id: rand::thread_rng().gen(),
last_seen: Utc::now(), last_seen: Utc::now(),
user: user.clone(), user: user.clone(),
user_agent,
}; };
self.store.insert_session(&session)?; self.store.insert_session(&session)?;

View File

@ -0,0 +1,2 @@
alter table sessions
drop column user_agent;

View File

@ -0,0 +1,2 @@
alter table sessions
add column user_agent text;

View File

@ -10,6 +10,7 @@ pub struct Session {
pub id: i64, pub id: i64,
pub user_id: i64, pub user_id: i64,
pub last_seen: i64, pub last_seen: i64,
pub user_agent: Option<String>,
} }
impl Session { impl Session {

View File

@ -59,6 +59,7 @@ impl gpodder::AuthStore for SqliteRepository {
id: session.id, id: session.id,
last_seen: DateTime::from_timestamp(session.last_seen, 0).unwrap(), last_seen: DateTime::from_timestamp(session.last_seen, 0).unwrap(),
user: user.into(), user: user.into(),
user_agent: session.user_agent.clone(),
})), })),
Ok(None) => Ok(None), Ok(None) => Ok(None),
Err(err) => Err(DbError::from(err).into()), Err(err) => Err(DbError::from(err).into()),
@ -79,6 +80,7 @@ impl gpodder::AuthStore for SqliteRepository {
id: session.id, id: session.id,
user_id: session.user.id, user_id: session.user.id,
last_seen: session.last_seen.timestamp(), last_seen: session.last_seen.timestamp(),
user_agent: session.user_agent.clone(),
} }
.insert_into(sessions::table) .insert_into(sessions::table)
.execute(&mut self.pool.get().map_err(DbError::from)?) .execute(&mut self.pool.get().map_err(DbError::from)?)

View File

@ -43,6 +43,7 @@ diesel::table! {
id -> BigInt, id -> BigInt,
user_id -> BigInt, user_id -> BigInt,
last_seen -> BigInt, last_seen -> BigInt,
user_agent -> Nullable<Text>,
} }
} }

View File

@ -1,7 +1,5 @@
use std::time::Duration; use std::time::Duration;
use tracing_subscriber::util::SubscriberInitExt;
use crate::server; use crate::server;
pub fn serve(config: &crate::config::Config) -> u8 { pub fn serve(config: &crate::config::Config) -> u8 {

View File

@ -5,10 +5,11 @@ use axum::{
}; };
use axum_extra::{ use axum_extra::{
extract::{cookie::Cookie, CookieJar}, extract::{cookie::Cookie, CookieJar},
headers::{authorization::Basic, Authorization}, headers::{authorization::Basic, Authorization, UserAgent},
TypedHeader, TypedHeader,
}; };
use cookie::time::Duration; use cookie::time::Duration;
use gpodder::AuthErr;
use crate::server::{ use crate::server::{
error::{AppError, AppResult}, error::{AppError, AppResult},
@ -27,6 +28,7 @@ async fn post_login(
Path(username): Path<String>, Path(username): Path<String>,
jar: CookieJar, jar: CookieJar,
TypedHeader(auth): TypedHeader<Authorization<Basic>>, TypedHeader(auth): TypedHeader<Authorization<Basic>>,
user_agent: Option<TypedHeader<UserAgent>>,
) -> AppResult<CookieJar> { ) -> AppResult<CookieJar> {
// These should be the same according to the spec // These should be the same according to the spec
if username != auth.username() { if username != auth.username() {
@ -62,7 +64,11 @@ async fn post_login(
let user = ctx let user = ctx
.store .store
.validate_credentials(auth.username(), auth.password())?; .validate_credentials(auth.username(), auth.password())?;
ctx.store.create_session(&user)
let user_agent = user_agent.map(|header| header.to_string());
let session = ctx.store.create_session(&user, user_agent)?;
Ok::<_, AuthErr>(session)
}) })
.await .await
.unwrap()?; .unwrap()?;