Security (33 pentest findings addressed): - CRITICAL: backend binds 127.0.0.1, path traversal in tor.rs/dwn fixed - HIGH: federation requires signatures, XSS login redirect, RBAC viewer restricted - HIGH: tar slip prevention, S3 SSRF validation, backup ID validation - MEDIUM: remember-me random secret, TOTP session rotation, password re-auth - LOW: CSP unsafe-inline removed, CORS dev-only, onion/webhook validation Container reliability: - Memory limits on all 37 containers (OOM prevention) - Exited vs stopped state distinction with health-aware status badges - Crash recovery coordination (no more restart cascade) - User-stopped tracking survives reboots - Tiered boot recovery (databases → core → services → apps) UI: - Wallet TransactionsModal, health-aware app status badges - Restart button on containers, exited/crashed red state - Mesh view overhaul, glass button updates, BaseModal/ToggleSwitch - Apps sticky header removed, dev faucet, mutable mock wallet Infrastructure: - LND REST port 8080 exposed over Tor (LND Connect fix) - Nginx cookie_session fix, deploy script Tor config updated - Dev environment: podman auto-start, boot mode simulation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
98 lines
3.4 KiB
Rust
98 lines
3.4 KiB
Rust
use super::{RpcHandler, DEV_DEFAULT_PASSWORD};
|
|
use anyhow::Result;
|
|
|
|
impl RpcHandler {
|
|
pub(super) async fn handle_auth_login(
|
|
&self,
|
|
params: Option<serde_json::Value>,
|
|
) -> Result<serde_json::Value> {
|
|
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
|
|
let password = params
|
|
.get("password")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| anyhow::anyhow!("Missing password"))?;
|
|
|
|
let is_setup = self.auth_manager.is_setup().await?;
|
|
if !is_setup {
|
|
// Dev mode: allow default password so UI can log in without running setup
|
|
if self.config.dev_mode && password == DEV_DEFAULT_PASSWORD {
|
|
return Ok(serde_json::Value::Null);
|
|
}
|
|
return Err(anyhow::anyhow!(
|
|
"User not set up. Please complete setup first."
|
|
));
|
|
}
|
|
|
|
let valid = self.auth_manager.verify_password(password).await?;
|
|
if !valid {
|
|
return Err(anyhow::anyhow!("Password Incorrect"));
|
|
}
|
|
|
|
Ok(serde_json::Value::Null)
|
|
}
|
|
|
|
pub(super) async fn handle_auth_logout(&self) -> Result<serde_json::Value> {
|
|
Ok(serde_json::Value::Null)
|
|
}
|
|
|
|
pub(super) async fn handle_auth_change_password(
|
|
&self,
|
|
params: Option<serde_json::Value>,
|
|
session_token: &Option<String>,
|
|
) -> Result<serde_json::Value> {
|
|
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
|
|
let current_password = params
|
|
.get("currentPassword")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| anyhow::anyhow!("Missing currentPassword"))?;
|
|
let new_password = params
|
|
.get("newPassword")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| anyhow::anyhow!("Missing newPassword"))?;
|
|
let also_change_ssh = params
|
|
.get("alsoChangeSsh")
|
|
.and_then(|v| v.as_bool())
|
|
.unwrap_or(true);
|
|
|
|
self.auth_manager
|
|
.change_password(current_password, new_password, also_change_ssh)
|
|
.await?;
|
|
|
|
// Session rotation: invalidate all other sessions, rotate the caller's session
|
|
if let Some(token) = session_token {
|
|
self.session_store.invalidate_all_except(token).await;
|
|
}
|
|
|
|
Ok(serde_json::json!({ "success": true, "session_rotated": true }))
|
|
}
|
|
|
|
pub(super) async fn handle_auth_onboarding_complete(&self) -> Result<serde_json::Value> {
|
|
self.auth_manager.complete_onboarding().await?;
|
|
Ok(serde_json::json!(true))
|
|
}
|
|
|
|
pub(super) async fn handle_auth_is_onboarding_complete(&self) -> Result<serde_json::Value> {
|
|
let complete = self.auth_manager.is_onboarding_complete().await?;
|
|
Ok(serde_json::json!(complete))
|
|
}
|
|
|
|
pub(super) async fn handle_auth_reset_onboarding(
|
|
&self,
|
|
params: Option<serde_json::Value>,
|
|
) -> Result<serde_json::Value> {
|
|
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
|
|
let password = params
|
|
.get("password")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| anyhow::anyhow!("Missing password — re-authentication required"))?;
|
|
|
|
let valid = self.auth_manager.verify_password(password).await?;
|
|
if !valid {
|
|
return Err(anyhow::anyhow!("Password Incorrect"));
|
|
}
|
|
|
|
self.auth_manager.reset_onboarding().await?;
|
|
Ok(serde_json::json!(true))
|
|
}
|
|
}
|