Make password settings a separate component (#167)

This commit is contained in:
Leendert de Borst
2025-03-18 10:05:10 +01:00
parent 1de7f831b5
commit 607c0da5b4
5 changed files with 178 additions and 58 deletions

View File

@@ -35,53 +35,11 @@
@if (IsPasswordSettingsVisible)
{
<div class="fixed inset-0 bg-gray-600 bg-opacity-50 z-30 overflow-y-auto h-full w-full" @onclick="ClosePasswordSettings">
<div class="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white dark:bg-gray-800" @onclick:stopPropagation>
<div class="mt-3">
<h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-white">Password Generation Settings</h3>
<div class="mt-4 space-y-4">
<div>
<label for="password-length" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Password Length</label>
<input type="number" id="password-length" min="8" max="64" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-primary-500 focus:ring-primary-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" @bind="PasswordSettings.Length">
</div>
<div class="flex items-center">
<input id="use-lowercase" type="checkbox" class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded dark:bg-gray-700 dark:border-gray-600" @bind="PasswordSettings.UseLowercase">
<label for="use-lowercase" class="ml-2 block text-sm text-gray-700 dark:text-gray-300">Include lowercase letters (a-z)</label>
</div>
<div class="flex items-center">
<input id="use-uppercase" type="checkbox" class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded dark:bg-gray-700 dark:border-gray-600" @bind="PasswordSettings.UseUppercase">
<label for="use-uppercase" class="ml-2 block text-sm text-gray-700 dark:text-gray-300">Include uppercase letters (A-Z)</label>
</div>
<div class="flex items-center">
<input id="use-numbers" type="checkbox" class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded dark:bg-gray-700 dark:border-gray-600" @bind="PasswordSettings.UseNumbers">
<label for="use-numbers" class="ml-2 block text-sm text-gray-700 dark:text-gray-300">Include numbers (0-9)</label>
</div>
<div class="flex items-center">
<input id="use-special-chars" type="checkbox" class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded dark:bg-gray-700 dark:border-gray-600" @bind="PasswordSettings.UseSpecialChars">
<label for="use-special-chars" class="ml-2 block text-sm text-gray-700 dark:text-gray-300">Include special characters (!@@#$%^&amp;*)</label>
</div>
<div class="flex items-center">
<input id="use-non-ambiguous" type="checkbox" class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded dark:bg-gray-700 dark:border-gray-600" @bind="PasswordSettings.UseNonAmbiguousChars">
<label for="use-non-ambiguous" class="ml-2 block text-sm text-gray-700 dark:text-gray-300">Avoid ambiguous characters (1, l, I, 0, O, etc.)</label>
</div>
<div class="flex justify-between pt-4">
<button type="button" class="px-4 py-2 bg-gray-200 text-gray-800 rounded-md hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-gray-400 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-600" @onclick="ClosePasswordSettings">
Cancel
</button>
<button type="button" class="px-4 py-2 bg-primary-600 text-white rounded-md hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-primary-700 dark:hover:bg-primary-600" @onclick="SavePasswordSettings">
Save Settings
</button>
</div>
</div>
</div>
</div>
</div>
<PasswordSettingsPopup
PasswordSettings="@PasswordSettings"
IsTemporary="true"
OnSaveSettings="@HandlePasswordSettingsSaved"
OnClose="@ClosePasswordSettings" />
}
@code {
@@ -146,16 +104,6 @@
}
}
private async Task SavePasswordSettings()
{
var settingsJson = System.Text.Json.JsonSerializer.Serialize(PasswordSettings);
await DbService.Settings.SetSettingAsync("PasswordGenerationSettings", settingsJson);
ClosePasswordSettings();
// Generate a new password with the new settings
await GeneratePassword();
}
private void TogglePasswordVisibility()
{
_internalShowPassword = !_internalShowPassword;
@@ -177,6 +125,12 @@
await ValueChanged.InvokeAsync(Value);
}
private async Task HandlePasswordSettingsSaved(PasswordSettings settings)
{
PasswordSettings = settings;
await GeneratePassword();
}
private async Task GeneratePassword()
{
// Generate the password

View File

@@ -0,0 +1,51 @@
@inject DbService DbService
<div class="bg-white dark:bg-gray-800 shadow rounded-lg p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-medium text-gray-900 dark:text-white">Default Password Generation Settings</h3>
<button type="button" class="px-4 py-2 bg-primary-600 text-white rounded-md hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-primary-700 dark:hover:bg-primary-600" @onclick="OpenSettings">
Configure
</button>
</div>
<div class="text-sm text-gray-500 dark:text-gray-400">
Configure the default settings used when generating new passwords. These settings will be used unless overridden for specific entries.
</div>
</div>
@if (IsSettingsVisible)
{
<PasswordSettingsPopup
PasswordSettings="@PasswordSettings"
IsTemporary="false"
OnSaveSettings="@HandlePasswordSettingsSaved"
OnClose="@CloseSettings" />
}
@code {
private PasswordSettings PasswordSettings { get; set; } = new();
private bool IsSettingsVisible { get; set; }
/// <inheritdoc />
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
PasswordSettings = DbService.Settings.PasswordSettings;
}
private void OpenSettings()
{
IsSettingsVisible = true;
}
private void CloseSettings()
{
IsSettingsVisible = false;
}
private void HandlePasswordSettingsSaved(PasswordSettings settings)
{
PasswordSettings = settings;
// The settings are already saved in the PasswordSettingsPopup component
// We just need to update our local state
}
}

View File

@@ -0,0 +1,113 @@
@inject DbService DbService
<div class="fixed inset-0 bg-gray-600 bg-opacity-50 z-30 overflow-y-auto h-full w-full" @onclick="OnClose">
<div class="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white dark:bg-gray-800" @onclick:stopPropagation>
<div class="mt-3">
<h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-white">Password Generation Settings</h3>
<div class="mt-4 space-y-4">
<div>
<label for="password-length" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Password Length: @PasswordSettings.Length</label>
<input type="range" id="password-length" min="8" max="64"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-primary-500 focus:ring-primary-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white"
@bind="PasswordSettings.Length" @bind:event="oninput">
</div>
<div class="flex items-center">
<input id="use-lowercase" type="checkbox" class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded dark:bg-gray-700 dark:border-gray-600" @bind="PasswordSettings.UseLowercase">
<label for="use-lowercase" class="ml-2 block text-sm text-gray-700 dark:text-gray-300">Include lowercase letters (a-z)</label>
</div>
<div class="flex items-center">
<input id="use-uppercase" type="checkbox" class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded dark:bg-gray-700 dark:border-gray-600" @bind="PasswordSettings.UseUppercase">
<label for="use-uppercase" class="ml-2 block text-sm text-gray-700 dark:text-gray-300">Include uppercase letters (A-Z)</label>
</div>
<div class="flex items-center">
<input id="use-numbers" type="checkbox" class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded dark:bg-gray-700 dark:border-gray-600" @bind="PasswordSettings.UseNumbers">
<label for="use-numbers" class="ml-2 block text-sm text-gray-700 dark:text-gray-300">Include numbers (0-9)</label>
</div>
<div class="flex items-center">
<input id="use-special-chars" type="checkbox" class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded dark:bg-gray-700 dark:border-gray-600" @bind="PasswordSettings.UseSpecialChars">
<label for="use-special-chars" class="ml-2 block text-sm text-gray-700 dark:text-gray-300">Include special characters (!@@#$%^&amp;*)</label>
</div>
<div class="flex items-center">
<input id="use-non-ambiguous" type="checkbox" class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded dark:bg-gray-700 dark:border-gray-600" @bind="PasswordSettings.UseNonAmbiguousChars">
<label for="use-non-ambiguous" class="ml-2 block text-sm text-gray-700 dark:text-gray-300">Avoid ambiguous characters (1, l, I, 0, O, etc.)</label>
</div>
<div class="mt-4">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Preview</label>
<div class="mt-1 flex">
<input type="text" readonly class="flex-1 rounded-md border-gray-300 shadow-sm focus:border-primary-500 focus:ring-primary-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white" value="@_previewPassword">
<button type="button" class="ml-2 px-3 py-2 text-sm text-gray-500 dark:text-white bg-gray-200 hover:bg-gray-300 focus:ring-4 focus:outline-none focus:ring-gray-300 font-medium rounded-md dark:bg-gray-600 dark:hover:bg-gray-700 dark:focus:ring-gray-800" @onclick="RefreshPreview">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
</svg>
</button>
</div>
</div>
<div class="flex justify-between pt-4">
<button type="button" class="px-4 py-2 bg-gray-200 text-gray-800 rounded-md hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-gray-400 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-600" @onclick="OnClose">
Cancel
</button>
<button type="button" class="px-4 py-2 bg-primary-600 text-white rounded-md hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-primary-700 dark:hover:bg-primary-600" @onclick="OnSave">
Save Settings
</button>
</div>
</div>
</div>
</div>
</div>
@code {
/// <summary>
/// The PasswordSettings to use.
/// </summary>
[Parameter]
public PasswordSettings PasswordSettings { get; set; } = new();
/// <summary>
/// Whether change is temporary.
/// </summary>
[Parameter]
public bool IsTemporary { get; set; }
/// <summary>
/// Callback invoked when settings have been changed.
/// </summary>
[Parameter]
public EventCallback<PasswordSettings> OnSaveSettings { get; set; }
/// <summary>
/// Callback invoked when popup is closed.
/// </summary>
[Parameter]
public EventCallback OnClose { get; set; }
private string _previewPassword = string.Empty;
/// <inheritdoc />
protected override void OnInitialized()
{
RefreshPreview();
}
private void RefreshPreview()
{
_previewPassword = CredentialService.GenerateRandomPassword(PasswordSettings);
}
private async Task OnSave()
{
if (!IsTemporary)
{
var settingsJson = System.Text.Json.JsonSerializer.Serialize(PasswordSettings);
await DbService.Settings.SetSettingAsync("PasswordGenerationSettings", settingsJson);
}
await OnSaveSettings.InvokeAsync(PasswordSettings);
await OnClose.InvokeAsync();
}
}

View File

@@ -16,7 +16,8 @@
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "http://localhost:5067",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
"DOTNET_MODIFIABLE_ASSEMBLIES": "debug",
"ASPNETCORE_ENVIRONMENT": "Development",
}
},
"http-release": {

View File

@@ -19,6 +19,7 @@
@using AliasVault.Client.Main.Components.Forms
@using AliasVault.Client.Main.Components.Layout
@using AliasVault.Client.Main.Components.Loading
@using AliasVault.Client.Main.Components.Settings
@using AliasVault.Client.Main.Components.TotpCodes
@using AliasVault.Client.Main.Components.Widgets
@using AliasVault.Client.Main.Models