Skip to content

Commit

Permalink
Add option to remember vault choice for a given action and parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
cezarypiatek committed Aug 27, 2022
1 parent 3e6d1ea commit 26c6590
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 31 deletions.
52 changes: 40 additions & 12 deletions src/ScriptRunner/ScriptRunner.GUI/ParamsPanelFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,28 @@
using Avalonia.Data.Converters;
using Avalonia.Media;
using ScriptRunner.GUI.ScriptConfigs;
using ScriptRunner.GUI.Settings;
using ScriptRunner.GUI.ViewModels;
using ScriptRunner.GUI.Views;

namespace ScriptRunner.GUI;

public class ParamsPanelFactory
{
public ParamsPanel Create(IEnumerable<ScriptParam> parameters, Dictionary<string, string> values)
public ParamsPanel Create(ScriptConfig action, Dictionary<string, string> values)
{
var paramsPanel = new StackPanel
{
Classes = new Classes("paramsPanel"),
};

var controlRecords = new List<IControlRecord>();

foreach (var (param,i) in parameters.Select((x,i)=>(x,i)))
var appSettings = AppSettingsService.Load();
var secretBindings = appSettings.VaultBindings ?? new List<VaultBinding>();
foreach (var (param,i) in action.Params.Select((x,i)=>(x,i)))
{
values.TryGetValue(param.Name, out var value);
var controlRecord = CreateControlRecord(param, value, i);
var controlRecord = CreateControlRecord(param, value, i, action, secretBindings);
controlRecord.Name = param.Name;
var actionPanel = new StackPanel
{
Expand All @@ -55,7 +57,8 @@ public ParamsPanel Create(IEnumerable<ScriptParam> parameters, Dictionary<string
};
}

private static IControlRecord CreateControlRecord(ScriptParam p, string? value, int index)
private static IControlRecord CreateControlRecord(ScriptParam p, string? value, int index,
ScriptConfig scriptConfig, List<VaultBinding> secretBindings)
{
switch (p.Prompt)
{
Expand All @@ -70,15 +73,40 @@ private static IControlRecord CreateControlRecord(ScriptParam p, string? value,
}
};
case PromptType.Password:
return new PasswordControl

var passwordBox = new PasswordBox
{
Password = value,
TabIndex = index,
IsTabStop = true
};

if (secretBindings.FirstOrDefault(x => x.ActionName == scriptConfig.Name && x.ParameterName == p.Name) is { } binding)
{
Control = new PasswordBox()
var vaultEntries = VaultProvider.ReadFromVault();
if (vaultEntries.FirstOrDefault(x => x.Name == binding.VaultKey) is { } vaultEntry)
{
Password = value,
TabIndex = index,
IsTabStop = true
},
MaskingRequired = true
passwordBox.VaultKey = vaultEntry.Name;
passwordBox.Password = vaultEntry.Secret;
}
}

passwordBox.VaultBindingChanged += (sender, args) =>
{
if (args.VaultEntryChoice.RememberBinding)
{
AppSettingsService.UpdateVaultBindings(new VaultBinding
{
ActionName = scriptConfig.Name,
ParameterName = p.Name,
VaultKey = args.VaultEntryChoice.SelectedEntry.Name
});
}
};
return new PasswordControl
{
Control = passwordBox,
MaskingRequired = true,
};
case PromptType.Dropdown:
return new DropdownControl
Expand Down
17 changes: 17 additions & 0 deletions src/ScriptRunner/ScriptRunner.GUI/Settings/AppSettingsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,23 @@ public static void UpdateScriptConfigs(IEnumerable<ConfigScriptEntry> configScri
Save(allSettings);
}

public static void UpdateVaultBindings(VaultBinding binding)
{
var allSettings = Load();
allSettings.VaultBindings ??= new List<VaultBinding>();
var existingBinding = allSettings.VaultBindings.FirstOrDefault(x => x.ActionName == binding.ActionName && x.ParameterName == x.ParameterName);
if (existingBinding != null)
{
existingBinding.VaultKey = binding.VaultKey;
}
else
{
allSettings.VaultBindings.Add(binding);
}

Save(allSettings);
}

private static string GetSettingsPath()
{
return GetSettingsPathFor("settings.json");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public class ScriptRunnerAppSettings
public LayoutSettings? Layout { get; set; }
public Dictionary<string, CommandInstallationStatus> InstalledActions { get; set; }
public List<ConfigScriptEntry>? ConfigScripts { get; set; }
public List<VaultBinding> VaultBindings { get; set; }
}

public record ConfigScriptEntry
Expand All @@ -22,4 +23,11 @@ public record ConfigScriptEntry
public class CommandInstallationStatus
{
public bool IsInstalled { get; set; }
}

public class VaultBinding
{
public string ActionName { get; set; }
public string ParameterName { get; set; }
public string VaultKey { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ private void RenderParameterForm(ScriptConfig action, Dictionary<string, string>
//var actionPanel = new StackPanel();

// Create IPanel with controls for all parameters
var paramsPanel = new ParamsPanelFactory().Create(action.Params, parameterValues);
var paramsPanel = new ParamsPanelFactory().Create(action, parameterValues);

// Add panel with param controls to action panel
//actionPanel.Children.Add(paramsPanel.Panel);
Expand Down
30 changes: 26 additions & 4 deletions src/ScriptRunner/ScriptRunner.GUI/Views/PasswordBox.axaml.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
Expand All @@ -22,24 +23,45 @@ private void InitializeComponent()
AvaloniaXamlLoader.Load(this);
}

public string VaultKey { get; set; }

private async void PickFromVault(object? sender, RoutedEventArgs e)
{
var pickerDialog = new VaultPicker();
if (string.IsNullOrWhiteSpace(VaultKey) == false)
{
pickerDialog.ViewModel.SelectedEntry = pickerDialog.ViewModel.Entries.FirstOrDefault(x => x.Name == VaultKey);
}
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
var sourceWindow = (sender as IControl)?.GetVisualRoot() as Window ?? desktop.MainWindow;
var selectedPassword = await pickerDialog.ShowDialog<string?>(sourceWindow);
if (selectedPassword != null)
if (await pickerDialog.ShowDialog<VaultEntryChoice>(sourceWindow) is { } choice)
{
VaultKey = choice.SelectedEntry.Name;
OnVaultBindingChanged(new VaultBindingChangedEventArgs(choice));
Dispatcher.UIThread.Post(() =>
{
Password = selectedPassword;
Password = choice.SelectedEntry.Secret;
});

}
}
}

public event EventHandler<VaultBindingChangedEventArgs> VaultBindingChanged;

public class VaultBindingChangedEventArgs : EventArgs
{
public VaultEntryChoice VaultEntryChoice { get; }

public VaultBindingChangedEventArgs(VaultEntryChoice vaultEntryChoice)
{
VaultEntryChoice = vaultEntryChoice;
}
}

private void OnVaultBindingChanged(VaultBindingChangedEventArgs e) => VaultBindingChanged?.Invoke(this, e);


public static readonly DirectProperty<PasswordBox, string?> PasswordProperty = AvaloniaProperty.RegisterDirect<PasswordBox, string?>
(
name: nameof(Password),
Expand Down
29 changes: 18 additions & 11 deletions src/ScriptRunner/ScriptRunner.GUI/Views/VaultPicker.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,26 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:ScriptRunner.GUI.Views"
mc:Ignorable="d" Width="400"
SizeToContent="WidthAndHeight"
x:Class="ScriptRunner.GUI.Views.VaultPicker"
WindowStartupLocation="CenterOwner"
Title="Pick secret">
<StackPanel Margin="40">
<ComboBox Name="SecretsCombo" Items="{Binding}" HorizontalAlignment="Center" Margin="10,0,0,20" Width="250">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontFamily="{Binding}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button HorizontalAlignment="Center" Click="Accept">Use selected secret</Button>
</StackPanel>
</Window>
<Design.DataContext>
<vm:VaultPickerViewModel />
</Design.DataContext>
<Border BorderBrush="#A1A1A1" BorderThickness="1">
<StackPanel Margin="40, 40, 40, 20" >
<ComboBox Name="SecretsCombo" Items="{Binding Entries}" SelectedItem="{Binding SelectedEntry, Mode=TwoWay}" HorizontalAlignment="Center" Margin="10,0,0,20" Width="300">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<CheckBox Name="Remember" Margin="0,0,0,20">Remember binding</CheckBox>
<Button HorizontalAlignment="Center" Click="Accept">Use selected secret</Button>
</StackPanel>
</Border>
</Window>
41 changes: 38 additions & 3 deletions src/ScriptRunner/ScriptRunner.GUI/Views/VaultPicker.axaml.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,63 @@
using System.Collections.Generic;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using ReactiveUI;
using ScriptRunner.GUI.ViewModels;

namespace ScriptRunner.GUI.Views
{
public class VaultPickerViewModel:ViewModelBase
{
public IReadOnlyList<VaultEntry> Entries { get; set; }

public VaultEntry? SelectedEntry
{
get => _selectedEntry;
set => this.RaiseAndSetIfChanged(ref _selectedEntry, value);
}

private VaultEntry? _selectedEntry;



}

public partial class VaultPicker : Window
{
public VaultPicker()
{

DataContext = VaultProvider.ReadFromVault();
DataContext = this.ViewModel = new VaultPickerViewModel
{
Entries = VaultProvider.ReadFromVault()
};
InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
}

public VaultPickerViewModel ViewModel { get; set; }


private void Accept(object? sender, RoutedEventArgs e)
{
Close((SecretsCombo.SelectedItem as VaultEntry)?.Secret);
if(SecretsCombo.SelectedItem is VaultEntry selectedEntry)
{
Close(new VaultEntryChoice()
{
RememberBinding = this.Remember.IsChecked ?? false,
SelectedEntry = selectedEntry
});
}

}
}

public class VaultEntryChoice
{
public bool RememberBinding { get; set; }
public VaultEntry SelectedEntry { get; set; }
}
}

0 comments on commit 26c6590

Please sign in to comment.