Fail closed when SpacetimeDB auth restore is unavailable
This commit is contained in:
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user