diff --git a/.gitignore b/.gitignore
index f7eb152..2ac8ff8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,6 +37,8 @@ MongoDB/
.gitea-token
master.key
openrouter.key
+# #567: Hash des Einstellungs-Passworts (PBKDF2) - gehoert zur Installation, nicht ins Repository
+settings-password.json
# Packager-Zugangsdaten (FTP, updateservice:publish-Token) – Vorlage in deploy/packager.config.example.json
deploy/packager.config.json
diff --git a/src/PolyTrader.App.Avalonia/Controls/SettingsEditor.axaml.cs b/src/PolyTrader.App.Avalonia/Controls/SettingsEditor.axaml.cs
index 5dd24d9..92d8f68 100644
--- a/src/PolyTrader.App.Avalonia/Controls/SettingsEditor.axaml.cs
+++ b/src/PolyTrader.App.Avalonia/Controls/SettingsEditor.axaml.cs
@@ -17,10 +17,11 @@ namespace PolyTrader.App.Avalonia.Controls
public SettingsEditor() => AvaloniaXamlLoader.Load(this);
/// Setzt das anzuzeigende Objekt (oder null, um zu leeren).
- public void Show(object? target)
+ /// false blendet Geheimnisfelder aus (#567).
+ public void Show(object? target, bool includeSensitive = true)
{
var list = this.FindControl("sections")!;
- list.ItemsSource = target == null ? null : SettingsModelBuilder.Build(target);
+ list.ItemsSource = target == null ? null : SettingsModelBuilder.Build(target, includeSensitive);
}
}
}
diff --git a/src/PolyTrader.App.Avalonia/Program.cs b/src/PolyTrader.App.Avalonia/Program.cs
index 1318629..90cbf1f 100644
--- a/src/PolyTrader.App.Avalonia/Program.cs
+++ b/src/PolyTrader.App.Avalonia/Program.cs
@@ -293,6 +293,14 @@ namespace PolyTrader.App.Avalonia
// Einstellungsdatei zur Laufzeit fortzuschreiben - gemeinsam fuer Desktop und WebUI.
services.AddSingleton(new KnownModules(allModules));
services.AddSingleton(new ServerSettingsFile(ServerSettingsPath));
+
+ // #567: Passwortschutz fuer Konten, Geheimnisse und WebUI-Zugang in der Desktop-Oberflaeche.
+ // Eigene Datei neben master.key - nicht in server_settings.xml, die das WebUI mitschreibt.
+ services.AddSingleton(sp => new SettingsLock(
+ new SettingsPasswordStore(
+ Path.Combine(AppContext.BaseDirectory, "settings-password.json"),
+ onWarning: m => sp.GetRequiredService().Warning(m)),
+ log: m => sp.GetRequiredService().Info(m)));
services.AddSingleton(logger);
services.AddSingleton();
services.AddSingleton();
diff --git a/src/PolyTrader.App.Avalonia/Shell/CoreViews.cs b/src/PolyTrader.App.Avalonia/Shell/CoreViews.cs
index c650fc3..2fbc3d0 100644
--- a/src/PolyTrader.App.Avalonia/Shell/CoreViews.cs
+++ b/src/PolyTrader.App.Avalonia/Shell/CoreViews.cs
@@ -64,7 +64,8 @@ namespace PolyTrader.App.Avalonia.Shell
services.GetService(),
services.GetService(),
services.GetService(),
- services.GetService())
+ services.GetService(),
+ services.GetRequiredService())
});
host.RegisterView(new ModuleView
diff --git a/src/PolyTrader.App.Avalonia/ViewModels/SettingsFields.cs b/src/PolyTrader.App.Avalonia/ViewModels/SettingsFields.cs
index 9046c13..ff610df 100644
--- a/src/PolyTrader.App.Avalonia/ViewModels/SettingsFields.cs
+++ b/src/PolyTrader.App.Avalonia/ViewModels/SettingsFields.cs
@@ -244,12 +244,17 @@ namespace PolyTrader.App.Avalonia.ViewModels
///
public static class SettingsModelBuilder
{
- public static IReadOnlyList Build(object target)
+ ///
+ /// false blendet Felder mit aus –
+ /// solange die geschützten Einstellungen gesperrt sind (#567).
+ ///
+ public static IReadOnlyList Build(object target, bool includeSensitive = true)
{
var fields = target.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.GetIndexParameters().Length == 0)
.Where(p => p.GetCustomAttribute()?.Browsable != false)
+ .Where(p => includeSensitive || p.GetCustomAttribute() == null)
.Select(p => new
{
Property = p,
diff --git a/src/PolyTrader.App.Avalonia/Views/PasswordDialog.axaml b/src/PolyTrader.App.Avalonia/Views/PasswordDialog.axaml
new file mode 100644
index 0000000..bc317cc
--- /dev/null
+++ b/src/PolyTrader.App.Avalonia/Views/PasswordDialog.axaml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/PolyTrader.App.Avalonia/Views/PasswordDialog.axaml.cs b/src/PolyTrader.App.Avalonia/Views/PasswordDialog.axaml.cs
new file mode 100644
index 0000000..7506abb
--- /dev/null
+++ b/src/PolyTrader.App.Avalonia/Views/PasswordDialog.axaml.cs
@@ -0,0 +1,76 @@
+using System.Threading.Tasks;
+using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Markup.Xaml;
+using PolyTrader.Core.Security;
+
+namespace PolyTrader.App.Avalonia.Views
+{
+ ///
+ /// Passwortabfrage vor geschützten Einstellungen (#567). Layout in PasswordDialog.axaml.
+ /// Der Dialog prüft nichts selbst – das tut . Er nimmt nur die Eingabe entgegen
+ /// und zeigt, was beim letzten Versuch schiefging.
+ ///
+ public partial class PasswordDialog : Window
+ {
+ public PasswordDialog() => AvaloniaXamlLoader.Load(this);
+
+ private void Setup(string title, string message, bool withConfirmation, string okText, string? error)
+ {
+ Title = title;
+ this.FindControl("lblTitle")!.Text = title;
+ this.FindControl("lblMessage")!.Text = message;
+ this.FindControl("txtConfirm")!.IsVisible = withConfirmation;
+ this.FindControl