From 755be599ee6f07f4b38527b58ec8689d012a926e Mon Sep 17 00:00:00 2001 From: Artemis Date: Mon, 27 Jan 2025 20:17:54 +0100 Subject: [PATCH] implemented audit logs to syslog --- src/routes/account.rs | 51 +++++++++++++++++++++++++++++++-- src/routes/form/accounts.rs | 43 ++++++++++++++++++--------- src/routes/form/register_tag.rs | 27 +++++++++++++---- 3 files changed, 101 insertions(+), 20 deletions(-) diff --git a/src/routes/account.rs b/src/routes/account.rs index 3dc9fdc..7ee5ff1 100644 --- a/src/routes/account.rs +++ b/src/routes/account.rs @@ -1,3 +1,5 @@ +use std::net::IpAddr; + use rocket::{ form::{self, Contextual, Error, Form}, http::CookieJar, @@ -64,6 +66,7 @@ pub async fn change_settings( user: User, meta: CommonTemplateState, mut form: Form>>, + client_ip: IpAddr, ) -> PageResult { let values = match form.value { Some(ref v) => v, @@ -110,6 +113,13 @@ pub async fn change_settings( user::update_info(&mut *db, &user.id, new_username, new_email).await?; + info!( + "[audit|{}] [{}] changed username/email (from {})", + client_ip, + user.id.to_string(), + user.username, + ); + Ok(Redirect::to(uri!("/account", show_settings)).into()) } @@ -138,6 +148,7 @@ pub async fn change_password( user: User, meta: CommonTemplateState, mut form: Form>>, + client_ip: IpAddr, ) -> PageResult { let values = match form.value { Some(ref v) => v, @@ -178,6 +189,12 @@ pub async fn change_password( let new_password_hash = task::spawn_blocking(move || pw::hash(&new_password)).await??; user::update_password(&mut *db, &user.id, &new_password_hash).await?; + info!( + "[audit|{}] [{}] changed password", + client_ip, + user.id.to_string() + ); + Ok(Redirect::to(uri!("/account", show_settings)).into()) } @@ -190,10 +207,20 @@ pub struct DataDump { } #[get("/data_dump")] -pub async fn export_data(mut db: DollTagsDb, user: User) -> RawResult> { +pub async fn export_data( + mut db: DollTagsDb, + user: User, + client_ip: IpAddr, +) -> RawResult> { let tags = doll::list(&mut *db, &user.id).await?; let reserved_tags = doll::list_archived(&mut *db, &user.id).await?; + info!( + "[audit|{}] [{}] exported data", + client_ip, + user.id.to_string() + ); + Ok(Json(DataDump { account: user, tags, @@ -225,10 +252,23 @@ pub async fn ask_delete( } #[get("/yes_delete_this/")] -pub async fn confirm_delete(mut db: DollTagsDb, id: i32, _user: User) -> PageResult { +pub async fn confirm_delete( + mut db: DollTagsDb, + id: i32, + user: User, + client_ip: IpAddr, +) -> PageResult { let mut trx = db.begin().await?; doll::delete(&mut trx, id).await?; trx.commit().await?; + + info!( + "[audit|{}] [{}] deleted tag {:0>6}", + client_ip, + user.id.to_string(), + id + ); + Ok(Redirect::to(uri!("/account", index)).into()) } @@ -242,6 +282,7 @@ pub async fn confirm_terminate_account( mut db: DollTagsDb, user: User, cookies: &CookieJar<'_>, + client_ip: IpAddr, ) -> PageResult { let mut trx = db.begin().await?; doll::delete_all_from_account(&mut trx, &user.id).await?; @@ -249,5 +290,11 @@ pub async fn confirm_terminate_account( session::logout(cookies); trx.commit().await?; + info!( + "[audit|{}] [{}] deleted account", + client_ip, + user.id.to_string() + ); + Ok(Redirect::to("/").into()) } diff --git a/src/routes/form/accounts.rs b/src/routes/form/accounts.rs index 5aaddca..d424960 100644 --- a/src/routes/form/accounts.rs +++ b/src/routes/form/accounts.rs @@ -1,3 +1,5 @@ +use std::net::IpAddr; + use regex::Regex; use rocket::{ form::{self, Contextual, Form}, @@ -49,24 +51,32 @@ pub async fn handle_login( cookies: &CookieJar<'_>, maybe_loggedin: Option, meta: CommonTemplateState, + client_ip: IpAddr, ) -> PageResult { if maybe_loggedin.is_some() { let next = String::from(next.unwrap_or("/")); return Ok(Redirect::to(next).into()); } - let miss = || { - PageResponse::Page(Template::render( - "account/login", - context! {failure: true, meta}, - )) - }; - let values = match &form.value { - None => return Ok(miss()), + None => { + return Ok(Template::render("account/login", context! {failure: true, meta}).into()) + } Some(v) => v, }; + let miss = (|username: String| { + move || { + info!("[audit|{}] login failure ({})", client_ip, username); + PageResponse::Page(Template::render( + "account/login", + context! {failure: true, meta}, + )) + } + })(String::from(values.username)); + + info!("[audit|{}] login attempt ({})", client_ip, &values.username); + let user_in_db = user::get(db, &values.username).await?; let user = match user_in_db { None => return Ok(miss()), @@ -79,6 +89,12 @@ pub async fn handle_login( if right_password && user.enabled { session::login(cookies, &user.id); + info!( + "[audit|{}] [{}] login successful ({})", + client_ip, + user.id.to_string(), + &values.username + ); let next_url = String::from(next.unwrap_or("/")); Ok(Redirect::to(next_url).into()) } else { @@ -137,6 +153,7 @@ pub async fn handle_register( cookies: &CookieJar<'_>, maybe_loggedin: Option, meta: CommonTemplateState, + client_ip: IpAddr, ) -> PageResult { if maybe_loggedin.is_some() { return Ok(Redirect::to("/").into()); @@ -161,11 +178,6 @@ pub async fn handle_register( } }; - debug!( - "registering account {} ({:?})", - values.username, values.email - ); - let password = String::from(values.password); let hashed_password = task::spawn_blocking(move || pw::hash(&password)).await??; @@ -181,6 +193,11 @@ pub async fn handle_register( ) .await?; + info!( + "[audit|{}] [{}] account creation", + client_ip, + &account_id.to_string() + ); session::login(cookies, &account_id); Ok(Redirect::to(uri!("/")).into()) diff --git a/src/routes/form/register_tag.rs b/src/routes/form/register_tag.rs index ef38856..43016bc 100644 --- a/src/routes/form/register_tag.rs +++ b/src/routes/form/register_tag.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{collections::HashMap, net::IpAddr}; use rocket::{ form::{self, Contextual, Form}, @@ -172,6 +172,7 @@ pub async fn handle_register( tag: Form>>, user: User, meta: CommonTemplateState, + client_ip: IpAddr, ) -> PageResult { let tag = match tag.value { Some(ref values) => values, @@ -194,7 +195,6 @@ pub async fn handle_register( } }; - debug!("registering tag: {:?}", tag); fn normalize_opt(opt: &str) -> Option<&str> { if opt.len() != 0 { Some(opt) @@ -208,10 +208,11 @@ pub async fn handle_register( let microchip_id = normalize_opt(&normalized_microchip_id); if doll::id_exists(&mut *db, id, microchip_id.unwrap_or("")).await? { + // TODO: that's weird... what was i expecting to do here? return Ok(Redirect::found(uri!("/account", show_register)).into()); } - doll::create( + let creation_status = doll::create( &mut *db, CreateDollProfile { id, @@ -232,7 +233,24 @@ pub async fn handle_register( bound_to_id: &user.id, }, ) - .await?; + .await; + + if creation_status.is_err() { + info!( + "[audit|{}] [{}] failed to register tag {}", + client_ip, + user.id.to_string(), + tag.ident + ); + } + creation_status?; + + info!( + "[audit|{}] [{}] registered tag {}", + client_ip, + user.id.to_string(), + tag.ident + ); Ok(Redirect::to(uri!(public::show_profile(Some(tag.ident), microchip_id))).into()) } @@ -267,7 +285,6 @@ pub async fn handle_edit_tag( } }; - debug!("editing tag: {:?}", tag); fn normalize_opt(opt: &str) -> Option<&str> { if opt.len() != 0 { Some(opt)