Ensure server restarts on password change.

This commit is contained in:
Peter Steinberger 2025-06-19 18:15:44 +02:00
parent 99392b53a4
commit 2c276fc67c
3 changed files with 110 additions and 36 deletions

View file

@ -32,7 +32,15 @@ struct LazyBasicAuthMiddleware<Context: RequestContext>: RouterMiddleware where
} }
// Check if password protection is enabled // Check if password protection is enabled
guard UserDefaults.standard.bool(forKey: "dashboardPasswordEnabled") else { let passwordEnabled = UserDefaults.standard.bool(forKey: "dashboardPasswordEnabled")
// Check if enabled state changed and clear cache if needed
if await passwordCache.shouldRecache(currentEnabledState: passwordEnabled) {
await passwordCache.clear()
logger.info("Password enabled state changed, cleared cache")
}
guard passwordEnabled else {
// No password protection, allow request // No password protection, allow request
return try await next(request, context) return try await next(request, context)
} }
@ -112,6 +120,7 @@ struct LazyBasicAuthMiddleware<Context: RequestContext>: RouterMiddleware where
/// Actor to manage password caching in a thread-safe way /// Actor to manage password caching in a thread-safe way
private actor PasswordCache { private actor PasswordCache {
private var cachedPassword: String? private var cachedPassword: String?
private var lastEnabledState: Bool?
func getPassword() -> String? { func getPassword() -> String? {
cachedPassword cachedPassword
@ -123,5 +132,14 @@ private actor PasswordCache {
func clear() { func clear() {
cachedPassword = nil cachedPassword = nil
lastEnabledState = nil
}
func shouldRecache(currentEnabledState: Bool) -> Bool {
if lastEnabledState != currentEnabledState {
lastEnabledState = currentEnabledState
return true
}
return false
} }
} }

View file

@ -43,6 +43,34 @@ struct DashboardSettingsView: View {
DashboardAccessMode(rawValue: accessModeString) ?? .localhost DashboardAccessMode(rawValue: accessModeString) ?? .localhost
} }
// MARK: - Helper Methods
/// Handles server-specific password updates (adding, changing, or removing passwords)
static func updateServerForPasswordChange(action: PasswordAction, logger: Logger) async {
let serverManager = ServerManager.shared
if serverManager.serverMode == .rust {
// Rust server requires restart to apply password changes
logger.info("Restarting Rust server to \(action.logMessage)")
await serverManager.restart()
} else {
// Hummingbird server just needs cache clear
await serverManager.clearAuthCache()
}
}
enum PasswordAction {
case apply
case remove
var logMessage: String {
switch self {
case .apply: "apply new password"
case .remove: "remove password protection"
}
}
}
var body: some View { var body: some View {
NavigationStack { NavigationStack {
Form { Form {
@ -54,7 +82,8 @@ struct DashboardSettingsView: View {
passwordError: $passwordError, passwordError: $passwordError,
passwordSaved: $passwordSaved, passwordSaved: $passwordSaved,
dashboardKeychain: dashboardKeychain, dashboardKeychain: dashboardKeychain,
savePassword: savePassword savePassword: savePassword,
logger: logger
) )
ServerConfigurationSection( ServerConfigurationSection(
@ -156,15 +185,37 @@ struct DashboardSettingsView: View {
password = "" password = ""
confirmPassword = "" confirmPassword = ""
// Clear cached password in LazyBasicAuthMiddleware // Check if we need to switch to network mode
Task { let needsNetworkModeSwitch = accessMode == .localhost
await ServerManager.shared.clearAuthCache()
if needsNetworkModeSwitch {
// Switch to network mode first (this updates ServerManager.bindAddress)
accessModeString = DashboardAccessMode.network.rawValue
} }
// When password is set for the first time, automatically switch to network mode // Handle server-specific password update
if accessMode == .localhost { Task {
accessModeString = DashboardAccessMode.network.rawValue let serverManager = ServerManager.shared
restartServerWithNewBindAddress()
if needsNetworkModeSwitch {
// If switching to network mode, update bind address before restart
serverManager.bindAddress = DashboardAccessMode.network.bindAddress
// Always restart when switching to network mode (both server types need it)
logger.info("Restarting server to apply new password and network mode")
await serverManager.restart()
// Wait for server to be ready
try? await Task.sleep(for: .seconds(1))
await MainActor.run {
SessionMonitor.shared.stopMonitoring()
SessionMonitor.shared.startMonitoring()
}
} else {
// Just password change, no network mode switch
await DashboardSettingsView.updateServerForPasswordChange(action: .apply, logger: logger)
}
} }
} else { } else {
passwordError = "Failed to save password to keychain" passwordError = "Failed to save password to keychain"
@ -302,6 +353,7 @@ private struct SecuritySection: View {
@Binding var passwordSaved: Bool @Binding var passwordSaved: Bool
let dashboardKeychain: DashboardKeychain let dashboardKeychain: DashboardKeychain
let savePassword: () -> Void let savePassword: () -> Void
let logger: Logger
var body: some View { var body: some View {
Section { Section {
@ -315,9 +367,13 @@ private struct SecuritySection: View {
_ = dashboardKeychain.deletePassword() _ = dashboardKeychain.deletePassword()
showPasswordFields = false showPasswordFields = false
passwordSaved = false passwordSaved = false
// Clear cached password in LazyBasicAuthMiddleware
// Handle server-specific password removal
Task { Task {
await ServerManager.shared.clearAuthCache() await DashboardSettingsView.updateServerForPasswordChange(
action: .remove,
logger: logger
)
} }
} }
} }