Fail closed when SpacetimeDB auth restore is unavailable

This commit is contained in:
kdletters
2026-05-27 20:58:37 +08:00
parent 948d5a698c
commit 418fcb0548
24 changed files with 595 additions and 601 deletions

View File

@@ -12,8 +12,6 @@ pub use events::*;
use std::{
collections::HashMap,
fs,
path::{Path, PathBuf},
sync::{Arc, Mutex},
};
@@ -33,7 +31,6 @@ use tracing::{info, warn};
#[derive(Clone, Debug)]
pub struct InMemoryAuthStore {
inner: Arc<Mutex<InMemoryAuthStoreState>>,
persistence_path: Option<Arc<PathBuf>>,
}
#[derive(Debug)]
@@ -887,7 +884,6 @@ impl Default for InMemoryAuthStore {
fn default() -> Self {
Self {
inner: Arc::new(Mutex::new(InMemoryAuthStoreState::default())),
persistence_path: None,
}
}
}
@@ -936,14 +932,6 @@ impl InMemoryAuthStoreState {
}
}
fn build_temp_persistence_path(path: &Path) -> PathBuf {
let file_name = path
.file_name()
.and_then(|value| value.to_str())
.unwrap_or("auth-store.json");
path.with_file_name(format!("{file_name}.tmp"))
}
impl InMemoryAuthStore {
pub fn from_snapshot_json(snapshot_json: &str) -> Result<Self, String> {
let snapshot = serde_json::from_str::<PersistentAuthStoreSnapshot>(snapshot_json)
@@ -952,25 +940,6 @@ impl InMemoryAuthStore {
inner: Arc::new(Mutex::new(
InMemoryAuthStoreState::from_persistent_snapshot(snapshot),
)),
persistence_path: None,
})
}
pub fn from_persistence_path(path: impl Into<PathBuf>) -> Result<Self, String> {
let path = path.into();
let state = if path.is_file() {
let raw_text =
fs::read_to_string(&path).map_err(|error| format!("读取认证快照失败:{error}"))?;
let snapshot = serde_json::from_str::<PersistentAuthStoreSnapshot>(&raw_text)
.map_err(|error| format!("解析认证快照失败:{error}"))?;
InMemoryAuthStoreState::from_persistent_snapshot(snapshot)
} else {
InMemoryAuthStoreState::default()
};
Ok(Self {
inner: Arc::new(Mutex::new(state)),
persistence_path: Some(Arc::new(path)),
})
}
@@ -985,30 +954,8 @@ impl InMemoryAuthStore {
}
fn persist_state(&self, state: &InMemoryAuthStoreState) -> Result<(), String> {
let Some(path) = self.persistence_path.as_deref() else {
return Ok(());
};
if let Some(parent_dir) = path.parent() {
fs::create_dir_all(parent_dir).map_err(|error| {
format!(
"创建认证快照目录失败:{},路径:{}",
error,
parent_dir.display()
)
})?;
}
let snapshot = state.to_persistent_snapshot();
let raw_text = serde_json::to_string_pretty(&snapshot)
.map_err(|error| format!("序列化认证快照失败:{error}"))?;
let temp_path = build_temp_persistence_path(path);
fs::write(&temp_path, raw_text)
.map_err(|error| format!("写入认证快照临时文件失败:{error}"))?;
fs::rename(&temp_path, path).map_err(|error| {
let _ = fs::remove_file(&temp_path);
format!("替换认证快照文件失败:{error}")
})
let _ = state;
Ok(())
}
fn persist_password_state(
@@ -2545,15 +2492,8 @@ mod tests {
}
#[tokio::test]
async fn persistent_store_restores_user_and_refresh_session_after_restart() {
let store_path = std::env::temp_dir().join(format!(
"genarrative-auth-store-{}.json",
new_uuid_simple_string()
));
let _ = std::fs::remove_file(&store_path);
let store = InMemoryAuthStore::from_persistence_path(store_path.clone())
.expect("persistent store should initialize");
async fn snapshot_json_restores_user_and_refresh_session_after_roundtrip() {
let store = InMemoryAuthStore::default();
let user = create_phone_login_user(store.clone(), "13800138003").await;
let password_service = build_password_service(store.clone());
let refresh_service = build_refresh_service(store.clone());
@@ -2576,10 +2516,12 @@ mod tests {
OffsetDateTime::now_utc(),
)
.expect("refresh session should be persisted");
drop(store);
let restored_store = InMemoryAuthStore::from_persistence_path(store_path.clone())
.expect("persistent store should restore");
let snapshot_json = store
.export_snapshot_json()
.expect("snapshot export should succeed");
let restored_store = InMemoryAuthStore::from_snapshot_json(&snapshot_json)
.expect("snapshot json should restore");
let restored_user = build_password_service(restored_store.clone())
.get_user_by_id(&user.id)
.expect("restored user query should succeed")
@@ -2597,8 +2539,6 @@ mod tests {
)
.expect("restored refresh session should rotate");
assert_eq!(rotated.user.id, user.id);
let _ = std::fs::remove_file(&store_path);
}
#[tokio::test]