diff --git a/src/ScriptRunner/ScriptRunner.GUI/ParamsPanelFactory.cs b/src/ScriptRunner/ScriptRunner.GUI/ParamsPanelFactory.cs index a210de0..d778114 100644 --- a/src/ScriptRunner/ScriptRunner.GUI/ParamsPanelFactory.cs +++ b/src/ScriptRunner/ScriptRunner.GUI/ParamsPanelFactory.cs @@ -63,12 +63,11 @@ private static IControlRecord CreateControlRecord(ScriptParam p, string? value) } }; case PromptType.Password: - return new TextControl + return new PasswordControl { - Control = new TextBox + Control = new PasswordBox() { - PasswordChar = '*', - Text = value + Password = value }, MaskingRequired = true }; @@ -283,6 +282,18 @@ public string GetFormattedValue() public string Name { get; set; } public bool MaskingRequired { get; set; } } +public class PasswordControl : IControlRecord +{ + public IControl Control { get; set; } + + public string GetFormattedValue() + { + return ((PasswordBox)Control).Password; + } + + public string Name { get; set; } + public bool MaskingRequired { get; set; } +} public class FileContent : IControlRecord { public IControl Control { get; set; } diff --git a/src/ScriptRunner/ScriptRunner.GUI/ViewModels/VaultViewModel.cs b/src/ScriptRunner/ScriptRunner.GUI/ViewModels/VaultViewModel.cs index 64f5b80..866344d 100644 --- a/src/ScriptRunner/ScriptRunner.GUI/ViewModels/VaultViewModel.cs +++ b/src/ScriptRunner/ScriptRunner.GUI/ViewModels/VaultViewModel.cs @@ -1,7 +1,10 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; +using System.Runtime.Versioning; +using System.Security.Cryptography; using System.Text; using System.Text.Json; using ReactiveUI; @@ -9,6 +12,30 @@ namespace ScriptRunner.GUI.ViewModels { + public static class VaultProvider + { + public static IReadOnlyList ReadFromVault() + { + var vaultPath = AppSettingsService.GetSettingsPathFor("Vault.dat"); + if (File.Exists(vaultPath)) + { + File.Decrypt(vaultPath); + var content = File.ReadAllText(vaultPath); + File.Encrypt(vaultPath); + var data = JsonSerializer.Deserialize>(content); + return data ?? new List(); + } + return Array.Empty(); + } + + public static void UpdateVault(List date) + { + var vaultPath = AppSettingsService.GetSettingsPathFor("Vault.dat"); + File.WriteAllText(vaultPath, JsonSerializer.Serialize(date), Encoding.UTF8); + File.Encrypt(vaultPath); + } + } + public class VaultViewModel : ViewModelBase { public ObservableCollection Entries @@ -22,23 +49,7 @@ public ObservableCollection Entries public VaultViewModel() { - var vaultPath = AppSettingsService.GetSettingsPathFor("Vault.dat"); - if (File.Exists(vaultPath)) - { - File.Decrypt(vaultPath); - var content = File.ReadAllText(vaultPath); - File.Encrypt(vaultPath); - var data = JsonSerializer.Deserialize>(content); - Entries = new ObservableCollection(data?? new List()); - } - else - { - Entries = new ObservableCollection() - { - - }; - } - + Entries = new ObservableCollection(VaultProvider.ReadFromVault()); } public void AddNewVaultEntry() @@ -49,10 +60,9 @@ public void AddNewVaultEntry() public void SaveVault() { var date = Entries.ToList(); - var vaultPath = AppSettingsService.GetSettingsPathFor("Vault.dat"); - File.WriteAllText(vaultPath, JsonSerializer.Serialize(date), Encoding.UTF8); - File.Encrypt(vaultPath); + VaultProvider.UpdateVault(date); } + } public class VaultEntry diff --git a/src/ScriptRunner/ScriptRunner.GUI/Views/PasswordBox.axaml b/src/ScriptRunner/ScriptRunner.GUI/Views/PasswordBox.axaml new file mode 100644 index 0000000..a8c6a4f --- /dev/null +++ b/src/ScriptRunner/ScriptRunner.GUI/Views/PasswordBox.axaml @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/src/ScriptRunner/ScriptRunner.GUI/Views/PasswordBox.axaml.cs b/src/ScriptRunner/ScriptRunner.GUI/Views/PasswordBox.axaml.cs new file mode 100644 index 0000000..e65bcea --- /dev/null +++ b/src/ScriptRunner/ScriptRunner.GUI/Views/PasswordBox.axaml.cs @@ -0,0 +1,57 @@ +using System; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Interactivity; +using Avalonia.Markup.Xaml; +using Avalonia.Threading; +using Avalonia.VisualTree; + +namespace ScriptRunner.GUI.Views +{ + public partial class PasswordBox : UserControl + { + public PasswordBox() + { + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + private async void PickFromVault(object? sender, RoutedEventArgs e) + { + var pickerDialog = new VaultPicker(); + if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + var sourceWindow = (sender as IControl)?.GetVisualRoot() as Window ?? desktop.MainWindow; + var selectedPassword = await pickerDialog.ShowDialog(sourceWindow); + if (selectedPassword != null) + { + Dispatcher.UIThread.Post(() => + { + Password = selectedPassword; + }); + + } + } + } + + public static readonly DirectProperty PasswordProperty = AvaloniaProperty.RegisterDirect + ( + name: nameof(Password), + getter: picker => picker.FindControl("PasswordTextBox").Text, + setter: (picker, s) => picker.FindControl("PasswordTextBox").Text = s + ); + + + public string? Password + { + get => GetValue(PasswordProperty); + set => SetValue(PasswordProperty, value); + } + } +} diff --git a/src/ScriptRunner/ScriptRunner.GUI/Views/VaultPicker.axaml b/src/ScriptRunner/ScriptRunner.GUI/Views/VaultPicker.axaml new file mode 100644 index 0000000..2e16b1a --- /dev/null +++ b/src/ScriptRunner/ScriptRunner.GUI/Views/VaultPicker.axaml @@ -0,0 +1,20 @@ + + + + + + + + + + + + diff --git a/src/ScriptRunner/ScriptRunner.GUI/Views/VaultPicker.axaml.cs b/src/ScriptRunner/ScriptRunner.GUI/Views/VaultPicker.axaml.cs new file mode 100644 index 0000000..99b0de5 --- /dev/null +++ b/src/ScriptRunner/ScriptRunner.GUI/Views/VaultPicker.axaml.cs @@ -0,0 +1,28 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Markup.Xaml; +using ScriptRunner.GUI.ViewModels; + +namespace ScriptRunner.GUI.Views +{ + public partial class VaultPicker : Window + { + public VaultPicker() + { + + DataContext = VaultProvider.ReadFromVault(); + InitializeComponent(); +#if DEBUG + this.AttachDevTools(); +#endif + } + + + + private void Accept(object? sender, RoutedEventArgs e) + { + Close((SecretsCombo.SelectedItem as VaultEntry)?.Secret); + } + } +}