Migrate vault encryption to OS keyring, handle PIN rate limiting, and improve tombstone handling logic.
- Replaced legacy vault key derivation with OS keyring-backed storage for improved security and platform integration. - Introduced PIN rate limiting with configurable lockout durations to mitigate brute-force attacks. - Enhanced tombstone services to use unified error-handling logic via `apply_tombstone`. - Refactored logic for book and series data synchronization, fixing null checks and improving flow consistency. - Added comprehensive error handling to all database and vault operations in key manager services.
This commit is contained in:
@@ -17,10 +17,21 @@ use crate::domains::series_character::service as series_character_service;
|
||||
use crate::domains::series_location::service as series_location_service;
|
||||
use crate::domains::series_world::service as series_world_service;
|
||||
use crate::domains::series_spell::service as series_spell_service;
|
||||
use crate::error::AppError;
|
||||
use crate::error::{AppError, AppResult};
|
||||
use crate::shared::session::SessionState;
|
||||
use crate::shared::types::Lang;
|
||||
|
||||
/// Runs a tombstone deletion, ignoring NotFound errors (entity already deleted).
|
||||
/// Propagates all other errors (DB corruption, lock failure, etc.).
|
||||
fn apply_tombstone<F: FnOnce() -> AppResult<bool>>(action: F) -> AppResult<()> {
|
||||
match action() {
|
||||
Ok(_) => Ok(()),
|
||||
Err(AppError::NotFound(_)) => Ok(()),
|
||||
Err(AppError::Internal(msg)) if msg.contains("non trouvé") || msg.contains("not found") => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_session(session: &State<SessionState>) -> Result<(String, Lang), AppError> {
|
||||
let session_guard = session.lock().map_err(|e| AppError::Internal(format!("Session lock failed: {}", e)))?;
|
||||
let user_id = session_guard.get_user_id().map_err(|e| AppError::Auth(e))?.to_string();
|
||||
@@ -68,22 +79,24 @@ pub fn apply_book_tombstones(tombstones: Vec<TombstoneInput>, db: State<DbManage
|
||||
|
||||
for tombstone in &tombstones {
|
||||
let book_id = tombstone.book_id.as_deref().unwrap_or("");
|
||||
let entity_id = tombstone.entity_id.as_str();
|
||||
let deleted_at = tombstone.deleted_at;
|
||||
match tombstone.table_name.as_str() {
|
||||
"erit_books" => { let _ = book_service::remove_book(conn, &user_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
"book_chapters" => { let _ = chapter_service::remove_chapter(conn, &user_id, book_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
"book_chapter_infos" => { let _ = chapter_service::remove_chapter_information(conn, &user_id, book_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
"book_characters" => { let _ = character_service::delete_character(conn, &user_id, book_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
"book_characters_attributes" => { let _ = character_service::delete_attribute(conn, &user_id, book_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
"book_location" => { let _ = location_service::delete_location_section(conn, &user_id, book_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
"location_element" => { let _ = location_service::delete_location_element(conn, &user_id, book_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
"location_sub_element" => { let _ = location_service::delete_location_sub_element(conn, &user_id, book_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
"book_world_elements" => { let _ = world_service::remove_element_from_world(conn, &user_id, book_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
"book_incidents" => { let _ = incident_service::remove_incident(conn, &user_id, book_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
"book_plot_points" => { let _ = plotpoint_service::remove_plot_point(conn, &user_id, book_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
"book_issues" => { let _ = issue_service::remove_issue(conn, &user_id, book_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
"book_spells" => { let _ = spell_service::delete_spell(conn, &user_id, book_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
"book_spell_tags" => { let _ = spell_service::delete_spell_tag(conn, &user_id, book_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
_ => {}
|
||||
"erit_books" => apply_tombstone(|| book_service::remove_book(conn, &user_id, entity_id, deleted_at, lang))?,
|
||||
"book_chapters" => apply_tombstone(|| chapter_service::remove_chapter(conn, &user_id, book_id, entity_id, deleted_at, lang))?,
|
||||
"book_chapter_infos" => apply_tombstone(|| chapter_service::remove_chapter_information(conn, &user_id, book_id, entity_id, deleted_at, lang))?,
|
||||
"book_characters" => apply_tombstone(|| character_service::delete_character(conn, &user_id, book_id, entity_id, deleted_at, lang))?,
|
||||
"book_characters_attributes" => apply_tombstone(|| character_service::delete_attribute(conn, &user_id, book_id, entity_id, deleted_at, lang))?,
|
||||
"book_location" => apply_tombstone(|| location_service::delete_location_section(conn, &user_id, book_id, entity_id, deleted_at, lang))?,
|
||||
"location_element" => apply_tombstone(|| location_service::delete_location_element(conn, &user_id, book_id, entity_id, deleted_at, lang))?,
|
||||
"location_sub_element" => apply_tombstone(|| location_service::delete_location_sub_element(conn, &user_id, book_id, entity_id, deleted_at, lang))?,
|
||||
"book_world_elements" => apply_tombstone(|| world_service::remove_element_from_world(conn, &user_id, book_id, entity_id, deleted_at, lang))?,
|
||||
"book_incidents" => apply_tombstone(|| incident_service::remove_incident(conn, &user_id, book_id, entity_id, deleted_at, lang))?,
|
||||
"book_plot_points" => apply_tombstone(|| plotpoint_service::remove_plot_point(conn, &user_id, book_id, entity_id, deleted_at, lang))?,
|
||||
"book_issues" => apply_tombstone(|| issue_service::remove_issue(conn, &user_id, book_id, entity_id, deleted_at, lang))?,
|
||||
"book_spells" => apply_tombstone(|| spell_service::delete_spell(conn, &user_id, book_id, entity_id, deleted_at, lang))?,
|
||||
"book_spell_tags" => apply_tombstone(|| spell_service::delete_spell_tag(conn, &user_id, book_id, entity_id, deleted_at, lang))?,
|
||||
_ => return Err(AppError::Validation(format!("Unknown tombstone table: {}", tombstone.table_name))),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,22 +110,24 @@ pub fn apply_series_tombstones(tombstones: Vec<TombstoneInput>, db: State<DbMana
|
||||
let conn = db_manager.get_connection(&user_id)?;
|
||||
|
||||
for tombstone in &tombstones {
|
||||
let entity_id = tombstone.entity_id.as_str();
|
||||
let deleted_at = tombstone.deleted_at;
|
||||
match tombstone.table_name.as_str() {
|
||||
"erit_series" => { let _ = series_service::delete_series(conn, &user_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
"erit_series" => apply_tombstone(|| series_service::delete_series(conn, &user_id, entity_id, deleted_at, lang))?,
|
||||
"series_books" => {
|
||||
if let Some(ref book_id) = tombstone.book_id {
|
||||
let _ = series_service::remove_book_from_series(conn, &user_id, book_id, &tombstone.entity_id, tombstone.deleted_at, lang);
|
||||
apply_tombstone(|| series_service::remove_book_from_series(conn, &user_id, book_id, entity_id, deleted_at, lang))?;
|
||||
}
|
||||
}
|
||||
"series_characters" => { let _ = series_character_service::delete_character(conn, &user_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
"series_characters_attributes" => { let _ = series_character_service::delete_attribute(conn, &user_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
"series_locations" => { let _ = series_location_service::delete_location(conn, &user_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
"series_location_elements" => { let _ = series_location_service::delete_element(conn, &user_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
"series_location_sub_elements" => { let _ = series_location_service::delete_sub_element(conn, &user_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
"series_world_elements" => { let _ = series_world_service::delete_element(conn, &user_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
"series_spells" => { let _ = series_spell_service::delete_spell(conn, &user_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
"series_spell_tags" => { let _ = series_spell_service::delete_tag(conn, &user_id, &tombstone.entity_id, tombstone.deleted_at, lang); }
|
||||
_ => {}
|
||||
"series_characters" => apply_tombstone(|| series_character_service::delete_character(conn, &user_id, entity_id, deleted_at, lang))?,
|
||||
"series_characters_attributes" => apply_tombstone(|| series_character_service::delete_attribute(conn, &user_id, entity_id, deleted_at, lang))?,
|
||||
"series_locations" => apply_tombstone(|| series_location_service::delete_location(conn, &user_id, entity_id, deleted_at, lang))?,
|
||||
"series_location_elements" => apply_tombstone(|| series_location_service::delete_element(conn, &user_id, entity_id, deleted_at, lang))?,
|
||||
"series_location_sub_elements" => apply_tombstone(|| series_location_service::delete_sub_element(conn, &user_id, entity_id, deleted_at, lang))?,
|
||||
"series_world_elements" => apply_tombstone(|| series_world_service::delete_element(conn, &user_id, entity_id, deleted_at, lang))?,
|
||||
"series_spells" => apply_tombstone(|| series_spell_service::delete_spell(conn, &user_id, entity_id, deleted_at, lang))?,
|
||||
"series_spell_tags" => apply_tombstone(|| series_spell_service::delete_tag(conn, &user_id, entity_id, deleted_at, lang))?,
|
||||
_ => return Err(AppError::Validation(format!("Unknown tombstone table: {}", tombstone.table_name))),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user