mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-05-24 08:17:57 -04:00
Localize form model validations (#1006)
This commit is contained in:
committed by
Leendert de Borst
parent
c90c5a9f2f
commit
1e3e542f92
@@ -0,0 +1,60 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// <copyright file="RegisterFormModel.cs" company="lanedirt">
|
||||
// Copyright (c) lanedirt. All rights reserved.
|
||||
// Licensed under the AGPLv3 license. See LICENSE.md file in the project root for full license information.
|
||||
// </copyright>
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
namespace AliasVault.Client.Auth.Models;
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using AliasVault.Client.Resources;
|
||||
using AliasVault.Shared.Models.Validation;
|
||||
using AliasVault.Shared.Models.WebApi.Auth;
|
||||
|
||||
/// <summary>
|
||||
/// Register form model with validation.
|
||||
/// </summary>
|
||||
public class RegisterFormModel : RegisterModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the username.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public new string Username { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the password.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MinLength(10, ErrorMessageResourceType = typeof(ValidationMessages), ErrorMessageResourceName = nameof(ValidationMessages.PasswordMinLengthGeneric))]
|
||||
public new string Password { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the password confirmation.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Compare("Password", ErrorMessageResourceType = typeof(ValidationMessages), ErrorMessageResourceName = nameof(ValidationMessages.PasswordsDoNotMatchGeneric))]
|
||||
public new string PasswordConfirm { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the terms and conditions are accepted or not.
|
||||
/// </summary>
|
||||
[MustBeTrue(ErrorMessageResourceType = typeof(ValidationMessages), ErrorMessageResourceName = nameof(ValidationMessages.MustAcceptTerms))]
|
||||
public new bool AcceptTerms { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Converts the form model to the base model.
|
||||
/// </summary>
|
||||
/// <returns>The base RegisterModel.</returns>
|
||||
public RegisterModel ToBaseModel()
|
||||
{
|
||||
return new RegisterModel
|
||||
{
|
||||
Username = Username,
|
||||
Password = Password,
|
||||
PasswordConfirm = PasswordConfirm,
|
||||
AcceptTerms = AcceptTerms,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
@inherits AliasVault.Client.Auth.Pages.Base.LoginBase
|
||||
@attribute [AllowAnonymous]
|
||||
@layout Auth.Layout.MainLayout
|
||||
@using AliasVault.Shared.Models.WebApi.Auth
|
||||
@using AliasVault.Client.Auth.Models
|
||||
@using AliasVault.Client.Auth.Components
|
||||
@using Microsoft.Extensions.Localization
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
</EditForm>
|
||||
|
||||
@code {
|
||||
private readonly RegisterModel _registerModel = new();
|
||||
private readonly RegisterFormModel _registerModel = new();
|
||||
private FullScreenLoadingIndicator _loadingIndicator = new();
|
||||
private ServerValidationErrors _serverValidationErrors = new();
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace AliasVault.Client.Main.Models;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using AliasClientDb;
|
||||
using AliasVault.Client.Resources;
|
||||
|
||||
/// <summary>
|
||||
/// Credential edit model.
|
||||
@@ -29,7 +30,7 @@ public sealed class TotpCodeEdit
|
||||
/// <summary>
|
||||
/// Gets or sets the secret key of the TOTP code.
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "Secret key is required")]
|
||||
[Required(ErrorMessageResourceType = typeof(ValidationMessages), ErrorMessageResourceName = nameof(ValidationMessages.SecretKeyRequired))]
|
||||
public string SecretKey { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// <copyright file="PasswordChangeFormModel.cs" company="lanedirt">
|
||||
// Copyright (c) lanedirt. All rights reserved.
|
||||
// Licensed under the AGPLv3 license. See LICENSE.md file in the project root for full license information.
|
||||
// </copyright>
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
namespace AliasVault.Client.Main.Models.Validation;
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using AliasVault.Client.Resources;
|
||||
using AliasVault.Shared.Models.WebApi.PasswordChange;
|
||||
|
||||
/// <summary>
|
||||
/// Password change form model with validation.
|
||||
/// </summary>
|
||||
public class PasswordChangeFormModel : PasswordChangeModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the current password.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public new string CurrentPassword { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the new password.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MinLength(10, ErrorMessageResourceType = typeof(ValidationMessages), ErrorMessageResourceName = nameof(ValidationMessages.PasswordMinLength))]
|
||||
public new string NewPassword { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the password confirmation.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Compare("NewPassword", ErrorMessageResourceType = typeof(ValidationMessages), ErrorMessageResourceName = nameof(ValidationMessages.PasswordsDoNotMatch))]
|
||||
public new string NewPasswordConfirm { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Converts the form model to the base model.
|
||||
/// </summary>
|
||||
/// <returns>The base PasswordChangeModel.</returns>
|
||||
public PasswordChangeModel ToBaseModel()
|
||||
{
|
||||
return new PasswordChangeModel
|
||||
{
|
||||
CurrentPassword = CurrentPassword,
|
||||
NewPassword = NewPassword,
|
||||
NewPasswordConfirm = NewPasswordConfirm,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,21 @@
|
||||
@page "/settings/security/change-password"
|
||||
@using AliasVault.Client.Utilities
|
||||
@using AliasVault.Client.Main.Models.Validation
|
||||
@using AliasVault.Shared.Models.WebApi.PasswordChange
|
||||
@using AliasVault.Shared.Models.WebApi.Vault;
|
||||
@using AliasVault.Cryptography.Client
|
||||
@using SecureRemotePassword
|
||||
@using Microsoft.Extensions.Localization
|
||||
@inherits MainBase
|
||||
@inject HttpClient Http
|
||||
|
||||
<LayoutPageTitle>Change password</LayoutPageTitle>
|
||||
<LayoutPageTitle>@Localizer["PageTitle"]</LayoutPageTitle>
|
||||
|
||||
<div class="grid grid-cols-1 px-4 pt-6 xl:grid-cols-3 xl:gap-4 dark:bg-gray-900">
|
||||
<div class="mb-4 col-span-full xl:mb-2">
|
||||
<Breadcrumb BreadcrumbItems="BreadcrumbItems"/>
|
||||
<H1>Change password</H1>
|
||||
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">Changing your master password also changes the vault encryption keys. It is advised to periodically change your master password to keep your vaults secure.</p>
|
||||
<H1>@Localizer["PageTitle"]</H1>
|
||||
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">@Localizer["PageDescription"]</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -24,34 +26,34 @@
|
||||
else
|
||||
{
|
||||
<div class="p-4 mb-4 mx-4 bg-white border border-gray-200 rounded-lg shadow-sm dark:border-gray-700 sm:p-6 dark:bg-gray-800">
|
||||
<EditForm Model="@PasswordChangeModel" OnValidSubmit="@InitiatePasswordChange" class="space-y-4">
|
||||
<EditForm Model="@PasswordChangeFormModel" OnValidSubmit="@InitiatePasswordChange" class="space-y-4">
|
||||
<DataAnnotationsValidator />
|
||||
<ValidationSummary />
|
||||
|
||||
<div>
|
||||
<label for="currentPassword" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Current Password</label>
|
||||
<InputText type="password" id="currentPassword" @bind-Value="PasswordChangeModel.CurrentPassword"
|
||||
<label for="currentPassword" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">@Localizer["CurrentPasswordLabel"]</label>
|
||||
<InputText type="password" id="currentPassword" @bind-Value="PasswordChangeFormModel.CurrentPassword"
|
||||
class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
|
||||
required />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="newPassword" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">New Password</label>
|
||||
<InputText type="password" id="newPassword" @bind-Value="PasswordChangeModel.NewPassword"
|
||||
<label for="newPassword" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">@Localizer["NewPasswordLabel"]</label>
|
||||
<InputText type="password" id="newPassword" @bind-Value="PasswordChangeFormModel.NewPassword"
|
||||
class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
|
||||
required />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="newPasswordConfirm" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Confirm New Password</label>
|
||||
<InputText type="password" id="newPasswordConfirm" @bind-Value="PasswordChangeModel.NewPasswordConfirm"
|
||||
<label for="newPasswordConfirm" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">@Localizer["ConfirmNewPasswordLabel"]</label>
|
||||
<InputText type="password" id="newPasswordConfirm" @bind-Value="PasswordChangeFormModel.NewPasswordConfirm"
|
||||
class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
|
||||
required />
|
||||
</div>
|
||||
|
||||
<button type="submit"
|
||||
class="w-full bg-primary-500 text-white py-2 px-4 rounded-md hover:bg-primary-600 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 transition duration-150 ease-in-out">
|
||||
Change Password
|
||||
@Localizer["ChangePasswordButton"]
|
||||
</button>
|
||||
</EditForm>
|
||||
</div>
|
||||
@@ -64,9 +66,12 @@ else
|
||||
private bool IsLoading { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the password change model.
|
||||
/// Gets or sets the password change form model.
|
||||
/// </summary>
|
||||
private PasswordChangeModel PasswordChangeModel { get; set; } = new();
|
||||
private PasswordChangeFormModel PasswordChangeFormModel { get; set; } = new();
|
||||
|
||||
private IStringLocalizer Localizer => LocalizerFactory.Create("Components.Main.Pages.Settings.Security.ChangePassword", "AliasVault.Client");
|
||||
private IStringLocalizer ApiErrorLocalizer => LocalizerFactory.Create("ApiErrors", "AliasVault.Client");
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current user's password salt.
|
||||
@@ -96,8 +101,8 @@ else
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
|
||||
BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = "Security settings", Url = "/settings/security" });
|
||||
BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = "Change password" });
|
||||
BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = Localizer["BreadcrumbSecuritySettings"], Url = "/settings/security" });
|
||||
BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = Localizer["BreadcrumbChangePassword"] });
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -126,7 +131,7 @@ else
|
||||
|
||||
if (response == null)
|
||||
{
|
||||
GlobalNotificationService.AddErrorMessage("Failed to initiate the password change process.", true);
|
||||
GlobalNotificationService.AddErrorMessage(Localizer["FailedToInitiatePasswordChange"], true);
|
||||
IsLoading = false;
|
||||
StateHasChanged();
|
||||
return;
|
||||
@@ -143,12 +148,12 @@ else
|
||||
/// </summary>
|
||||
private async Task InitiatePasswordChange()
|
||||
{
|
||||
GlobalLoadingSpinner.Show("Changing password...");
|
||||
GlobalLoadingSpinner.Show(Localizer["ChangingPasswordMessage"]);
|
||||
GlobalNotificationService.ClearMessages();
|
||||
StateHasChanged();
|
||||
|
||||
// Generate ephemeral for current password to verify it.
|
||||
var currentPasswordHash = await Encryption.DeriveKeyFromPasswordAsync(PasswordChangeModel.CurrentPassword, CurrentSalt, CurrentEncryptionType, CurrentEncryptionSettings);
|
||||
var currentPasswordHash = await Encryption.DeriveKeyFromPasswordAsync(PasswordChangeFormModel.CurrentPassword, CurrentSalt, CurrentEncryptionType, CurrentEncryptionSettings);
|
||||
var currentPasswordHashString = BitConverter.ToString(currentPasswordHash).Replace("-", string.Empty);
|
||||
|
||||
ClientEphemeral = Srp.GenerateEphemeralClient();
|
||||
@@ -165,7 +170,7 @@ else
|
||||
var client = new SrpClient();
|
||||
var newSalt = client.GenerateSalt();
|
||||
|
||||
byte[] newPasswordHash = await Encryption.DeriveKeyFromPasswordAsync(PasswordChangeModel.NewPassword, newSalt);
|
||||
byte[] newPasswordHash = await Encryption.DeriveKeyFromPasswordAsync(PasswordChangeFormModel.NewPassword, newSalt);
|
||||
var newPasswordHashString = BitConverter.ToString(newPasswordHash).Replace("-", string.Empty);
|
||||
|
||||
// Backup current password hash in case of failure.
|
||||
@@ -201,7 +206,7 @@ else
|
||||
};
|
||||
|
||||
// Clear form.
|
||||
PasswordChangeModel = new PasswordChangeModel();
|
||||
PasswordChangeFormModel = new PasswordChangeFormModel();
|
||||
|
||||
// 4. Client sends proof of session key to server.
|
||||
try {
|
||||
@@ -210,7 +215,7 @@ else
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
foreach (var error in ApiResponseUtility.ParseErrorResponse(responseContent))
|
||||
foreach (var error in ApiResponseUtility.ParseErrorResponse(responseContent, ApiErrorLocalizer))
|
||||
{
|
||||
GlobalNotificationService.AddErrorMessage(error, true);
|
||||
}
|
||||
@@ -233,7 +238,7 @@ else
|
||||
}
|
||||
catch
|
||||
{
|
||||
GlobalNotificationService.AddErrorMessage("Failed to change password. Please refresh the page and try again.", true);
|
||||
GlobalNotificationService.AddErrorMessage(Localizer["FailedToChangePassword"], true);
|
||||
|
||||
// Set currentPasswordHash back to original, so we're back to the original state.
|
||||
await AuthService.StoreEncryptionKeyAsync(backupPasswordHash);
|
||||
@@ -244,7 +249,7 @@ else
|
||||
}
|
||||
|
||||
// Set success message.
|
||||
GlobalNotificationService.AddSuccessMessage("Password changed successfully.", true);
|
||||
GlobalNotificationService.AddSuccessMessage(Localizer["PasswordChangedSuccessfully"], true);
|
||||
|
||||
// Get the new password ephemeral and salt from the server, which is required if the usre
|
||||
// wants to change the password again.
|
||||
|
||||
@@ -6,14 +6,16 @@
|
||||
@using AliasVault.Cryptography.Client
|
||||
@using SecureRemotePassword
|
||||
@using System.ComponentModel.DataAnnotations
|
||||
@using Microsoft.Extensions.Localization
|
||||
@using AliasVault.Client.Resources
|
||||
@inject HttpClient Http
|
||||
|
||||
<LayoutPageTitle>Delete Account</LayoutPageTitle>
|
||||
<LayoutPageTitle>@Localizer["PageTitle"]</LayoutPageTitle>
|
||||
|
||||
<div class="grid grid-cols-1 px-4 pt-6 xl:grid-cols-3 xl:gap-4 dark:bg-gray-900">
|
||||
<div class="mb-4 col-span-full xl:mb-2">
|
||||
<Breadcrumb BreadcrumbItems="BreadcrumbItems"/>
|
||||
<H1>Delete Account</H1>
|
||||
<H1>@Localizer["PageTitle"]</H1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -21,25 +23,25 @@
|
||||
@if (!_showPasswordConfirm)
|
||||
{
|
||||
<div class="mb-6">
|
||||
<MessageWarning Message="Warning: This action is permanent and cannot be undone. All your data will be permanently deleted." />
|
||||
<MessageWarning Message="@Localizer["PermanentActionWarning"]" />
|
||||
|
||||
<div class="mt-4 mb-6 text-gray-600 dark:text-gray-400">
|
||||
<p class="mb-2">Please note:</p>
|
||||
<p class="mb-2">@Localizer["PleaseNote"]</p>
|
||||
<ul class="list-disc list-inside space-y-2">
|
||||
<li>All encrypted vaults which includes all of your credentials will be permanently deleted</li>
|
||||
<li>Your email aliases will be orphaned and cannot be claimed by other users</li>
|
||||
<li>Your account cannot be recovered after deletion</li>
|
||||
<li>@Localizer["VaultsDeletedNote"]</li>
|
||||
<li>@Localizer["EmailAliasesOrphanedNote"]</li>
|
||||
<li>@Localizer["AccountCannotBeRecoveredNote"]</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<EditForm Model="@_usernameModel" OnSubmit="@ConfirmUsername">
|
||||
<div class="mb-4">
|
||||
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Confirm your username</label>
|
||||
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">@Localizer["ConfirmUsernameLabel"]</label>
|
||||
<InputText id="username" @bind-Value="_usernameModel.Username" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500" />
|
||||
</div>
|
||||
|
||||
<div class="flex space-x-3">
|
||||
<Button Type="submit" Color="danger">Continue with Account Deletion</Button>
|
||||
<Button Type="submit" Color="danger">@Localizer["ContinueWithAccountDeletion"]</Button>
|
||||
<Button Type="button" Color="secondary" OnClick="Cancel">Cancel</Button>
|
||||
</div>
|
||||
</EditForm>
|
||||
@@ -48,12 +50,12 @@
|
||||
else
|
||||
{
|
||||
<div class="mb-6">
|
||||
<MessageWarning Message="Final warning: Enter your password to permanently delete your account." />
|
||||
<MessageWarning Message="@Localizer["FinalWarning"]" />
|
||||
|
||||
<div class="mt-4 mb-6 text-gray-600 dark:text-gray-400">
|
||||
<p class="mb-2">Please note:</p>
|
||||
<p class="mb-2">@Localizer["PleaseNote"]</p>
|
||||
<ul class="list-disc list-inside space-y-2">
|
||||
<li>Account deletion is irreversible and cannot be undone. Pressing the button below will delete your account immmediately and permanently.</li>
|
||||
<li>@Localizer["DeletionIrreversibleNote"]</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -62,12 +64,12 @@
|
||||
<ValidationSummary />
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Enter your password</label>
|
||||
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">@Localizer["EnterPasswordLabel"]</label>
|
||||
<InputText id="password" type="password" @bind-Value="_passwordModel.Password" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500" />
|
||||
</div>
|
||||
|
||||
<div class="flex space-x-3">
|
||||
<Button Type="submit" Color="danger">Delete My Account</Button>
|
||||
<Button Type="submit" Color="danger">@Localizer["DeleteMyAccount"]</Button>
|
||||
<Button Type="button" Color="secondary" OnClick="Cancel">Cancel</Button>
|
||||
</div>
|
||||
</EditForm>
|
||||
@@ -86,6 +88,9 @@
|
||||
/// </summary>
|
||||
private readonly DeleteAccountPasswordModel _passwordModel = new();
|
||||
|
||||
private IStringLocalizer Localizer => LocalizerFactory.Create("Components.Main.Pages.Settings.Security.DeleteAccount", "AliasVault.Client");
|
||||
private IStringLocalizer ApiErrorLocalizer => LocalizerFactory.Create("ApiErrors", "AliasVault.Client");
|
||||
|
||||
/// <summary>
|
||||
/// Whether to show the password confirmation step.
|
||||
/// </summary>
|
||||
@@ -106,8 +111,8 @@
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
|
||||
BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = "Security settings", Url = "/settings/security" });
|
||||
BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = "Delete Account" });
|
||||
BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = Localizer["BreadcrumbSecuritySettings"], Url = "/settings/security" });
|
||||
BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = Localizer["BreadcrumbDeleteAccount"] });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -119,7 +124,7 @@
|
||||
|
||||
if (string.IsNullOrEmpty(_usernameModel.Username))
|
||||
{
|
||||
GlobalNotificationService.AddErrorMessage("Username is required", true);
|
||||
GlobalNotificationService.AddErrorMessage(Localizer["UsernameRequired"], true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -127,7 +132,7 @@
|
||||
var usernameMatches = string.Equals(_usernameModel.Username.Trim(), username.Trim(), StringComparison.OrdinalIgnoreCase);
|
||||
if (!usernameMatches)
|
||||
{
|
||||
GlobalNotificationService.AddErrorMessage("The username you entered does not match your current username. Please try again.", true);
|
||||
GlobalNotificationService.AddErrorMessage(Localizer["UsernameDoesNotMatch"], true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -140,7 +145,7 @@
|
||||
/// </summary>
|
||||
private async Task DeleteAccountConfirmed()
|
||||
{
|
||||
GlobalLoadingSpinner.Show("Deleting account...");
|
||||
GlobalLoadingSpinner.Show(Localizer["DeletingAccountMessage"]);
|
||||
GlobalNotificationService.ClearMessages();
|
||||
|
||||
try
|
||||
@@ -152,7 +157,7 @@
|
||||
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
foreach (var error in ApiResponseUtility.ParseErrorResponse(responseContent))
|
||||
foreach (var error in ApiResponseUtility.ParseErrorResponse(responseContent, ApiErrorLocalizer))
|
||||
{
|
||||
GlobalNotificationService.AddErrorMessage(error, true);
|
||||
}
|
||||
@@ -162,7 +167,7 @@
|
||||
var loginResponse = JsonSerializer.Deserialize<LoginInitiateResponse>(responseContent);
|
||||
if (loginResponse == null)
|
||||
{
|
||||
GlobalNotificationService.AddErrorMessage("An error occurred while processing the request.", true);
|
||||
GlobalNotificationService.AddErrorMessage(Localizer["ErrorProcessingRequest"], true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -185,7 +190,7 @@
|
||||
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
foreach (var error in ApiResponseUtility.ParseErrorResponse(responseContent))
|
||||
foreach (var error in ApiResponseUtility.ParseErrorResponse(responseContent, ApiErrorLocalizer))
|
||||
{
|
||||
GlobalNotificationService.AddErrorMessage(error, true);
|
||||
}
|
||||
@@ -217,7 +222,7 @@
|
||||
/// <summary>
|
||||
/// Gets or sets the username.
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "Username is required")]
|
||||
[Required(ErrorMessageResourceType = typeof(ValidationMessages), ErrorMessageResourceName = nameof(ValidationMessages.UsernameRequired))]
|
||||
public string Username { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
@@ -229,7 +234,7 @@
|
||||
/// <summary>
|
||||
/// Gets or sets the password.
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "Password is required")]
|
||||
[Required(ErrorMessageResourceType = typeof(ValidationMessages), ErrorMessageResourceName = nameof(ValidationMessages.PasswordRequired))]
|
||||
public string Password { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
|
||||
<!-- Page title and breadcrumbs -->
|
||||
<data name="PageTitle" xml:space="preserve">
|
||||
<value>Change password</value>
|
||||
<comment>Page title for the change password page</comment>
|
||||
</data>
|
||||
<data name="BreadcrumbSecuritySettings" xml:space="preserve">
|
||||
<value>Security settings</value>
|
||||
<comment>Breadcrumb text for security settings</comment>
|
||||
</data>
|
||||
<data name="BreadcrumbChangePassword" xml:space="preserve">
|
||||
<value>Change password</value>
|
||||
<comment>Breadcrumb text for change password</comment>
|
||||
</data>
|
||||
|
||||
<!-- Page description -->
|
||||
<data name="PageDescription" xml:space="preserve">
|
||||
<value>Changing your master password also changes the vault encryption keys. It is advised to periodically change your master password to keep your vaults secure.</value>
|
||||
<comment>Description text explaining the password change process</comment>
|
||||
</data>
|
||||
|
||||
<!-- Form labels -->
|
||||
<data name="CurrentPasswordLabel" xml:space="preserve">
|
||||
<value>Current Password</value>
|
||||
<comment>Label for current password input field</comment>
|
||||
</data>
|
||||
<data name="NewPasswordLabel" xml:space="preserve">
|
||||
<value>New Password</value>
|
||||
<comment>Label for new password input field</comment>
|
||||
</data>
|
||||
<data name="ConfirmNewPasswordLabel" xml:space="preserve">
|
||||
<value>Confirm New Password</value>
|
||||
<comment>Label for confirm new password input field</comment>
|
||||
</data>
|
||||
|
||||
<!-- Button text -->
|
||||
<data name="ChangePasswordButton" xml:space="preserve">
|
||||
<value>Change Password</value>
|
||||
<comment>Button text for changing password</comment>
|
||||
</data>
|
||||
|
||||
<!-- Loading and status messages -->
|
||||
<data name="ChangingPasswordMessage" xml:space="preserve">
|
||||
<value>Changing password...</value>
|
||||
<comment>Loading message displayed while changing password</comment>
|
||||
</data>
|
||||
<data name="PasswordChangedSuccessfully" xml:space="preserve">
|
||||
<value>Password changed successfully.</value>
|
||||
<comment>Success message after password change</comment>
|
||||
</data>
|
||||
<data name="FailedToInitiatePasswordChange" xml:space="preserve">
|
||||
<value>Failed to initiate the password change process.</value>
|
||||
<comment>Error message when password change initiation fails</comment>
|
||||
</data>
|
||||
<data name="FailedToChangePassword" xml:space="preserve">
|
||||
<value>Failed to change password. Please refresh the page and try again.</value>
|
||||
<comment>Error message when password change fails</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -0,0 +1,145 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
|
||||
<!-- Page title and breadcrumbs -->
|
||||
<data name="PageTitle" xml:space="preserve">
|
||||
<value>Delete Account</value>
|
||||
<comment>Page title for the delete account page</comment>
|
||||
</data>
|
||||
<data name="BreadcrumbSecuritySettings" xml:space="preserve">
|
||||
<value>Security settings</value>
|
||||
<comment>Breadcrumb text for security settings</comment>
|
||||
</data>
|
||||
<data name="BreadcrumbDeleteAccount" xml:space="preserve">
|
||||
<value>Delete Account</value>
|
||||
<comment>Breadcrumb text for delete account</comment>
|
||||
</data>
|
||||
|
||||
<!-- Warning messages -->
|
||||
<data name="PermanentActionWarning" xml:space="preserve">
|
||||
<value>Warning: This action is permanent and cannot be undone. All your data will be permanently deleted.</value>
|
||||
<comment>Warning message about permanent deletion</comment>
|
||||
</data>
|
||||
<data name="FinalWarning" xml:space="preserve">
|
||||
<value>Final warning: Enter your password to permanently delete your account.</value>
|
||||
<comment>Final warning message before account deletion</comment>
|
||||
</data>
|
||||
|
||||
<!-- Note sections -->
|
||||
<data name="PleaseNote" xml:space="preserve">
|
||||
<value>Please note:</value>
|
||||
<comment>Header for note section</comment>
|
||||
</data>
|
||||
<data name="VaultsDeletedNote" xml:space="preserve">
|
||||
<value>All encrypted vaults which includes all of your credentials will be permanently deleted</value>
|
||||
<comment>Note about vault deletion</comment>
|
||||
</data>
|
||||
<data name="EmailAliasesOrphanedNote" xml:space="preserve">
|
||||
<value>Your email aliases will be orphaned and cannot be claimed by other users</value>
|
||||
<comment>Note about email aliases being orphaned</comment>
|
||||
</data>
|
||||
<data name="AccountCannotBeRecoveredNote" xml:space="preserve">
|
||||
<value>Your account cannot be recovered after deletion</value>
|
||||
<comment>Note about account recovery</comment>
|
||||
</data>
|
||||
<data name="DeletionIrreversibleNote" xml:space="preserve">
|
||||
<value>Account deletion is irreversible and cannot be undone. Pressing the button below will delete your account immmediately and permanently.</value>
|
||||
<comment>Note about deletion being irreversible</comment>
|
||||
</data>
|
||||
|
||||
<!-- Form labels -->
|
||||
<data name="ConfirmUsernameLabel" xml:space="preserve">
|
||||
<value>Confirm your username</value>
|
||||
<comment>Label for username confirmation input field</comment>
|
||||
</data>
|
||||
<data name="EnterPasswordLabel" xml:space="preserve">
|
||||
<value>Enter your password</value>
|
||||
<comment>Label for password input field</comment>
|
||||
</data>
|
||||
|
||||
<!-- Button text -->
|
||||
<data name="ContinueWithAccountDeletion" xml:space="preserve">
|
||||
<value>Continue with Account Deletion</value>
|
||||
<comment>Button text to continue with account deletion</comment>
|
||||
</data>
|
||||
<data name="DeleteMyAccount" xml:space="preserve">
|
||||
<value>Delete My Account</value>
|
||||
<comment>Button text to delete account</comment>
|
||||
</data>
|
||||
|
||||
<!-- Loading and status messages -->
|
||||
<data name="DeletingAccountMessage" xml:space="preserve">
|
||||
<value>Deleting account...</value>
|
||||
<comment>Loading message displayed while deleting account</comment>
|
||||
</data>
|
||||
<data name="UsernameRequired" xml:space="preserve">
|
||||
<value>Username is required</value>
|
||||
<comment>Error message when username is not provided</comment>
|
||||
</data>
|
||||
<data name="UsernameDoesNotMatch" xml:space="preserve">
|
||||
<value>The username you entered does not match your current username. Please try again.</value>
|
||||
<comment>Error message when username doesn't match</comment>
|
||||
</data>
|
||||
<data name="ErrorProcessingRequest" xml:space="preserve">
|
||||
<value>An error occurred while processing the request.</value>
|
||||
<comment>Generic error message for request processing</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -259,4 +259,38 @@
|
||||
<value>If loading seems stuck, you can click the button below to refresh the page.</value>
|
||||
<comment>Text shown above refresh button on loading screen</comment>
|
||||
</data>
|
||||
|
||||
<!-- Validation error messages for forms -->
|
||||
<data name="ErrorPasswordMinLength" xml:space="preserve">
|
||||
<value>The new password must be at least 10 characters long.</value>
|
||||
<comment>Error message for password minimum length validation</comment>
|
||||
</data>
|
||||
<data name="ErrorPasswordsDoNotMatch" xml:space="preserve">
|
||||
<value>The new passwords do not match.</value>
|
||||
<comment>Error message when password confirmation doesn't match</comment>
|
||||
</data>
|
||||
<data name="ErrorPasswordMinLengthGeneric" xml:space="preserve">
|
||||
<value>Password must be at least 10 characters long.</value>
|
||||
<comment>Generic error message for password minimum length validation</comment>
|
||||
</data>
|
||||
<data name="ErrorPasswordsDoNotMatchGeneric" xml:space="preserve">
|
||||
<value>Passwords do not match.</value>
|
||||
<comment>Generic error message when passwords don't match</comment>
|
||||
</data>
|
||||
<data name="ErrorMustAcceptTerms" xml:space="preserve">
|
||||
<value>You must accept the terms and conditions.</value>
|
||||
<comment>Error message for terms and conditions acceptance</comment>
|
||||
</data>
|
||||
<data name="ErrorSecretKeyRequired" xml:space="preserve">
|
||||
<value>Secret key is required</value>
|
||||
<comment>Error message when secret key is required</comment>
|
||||
</data>
|
||||
<data name="ErrorUsernameRequired" xml:space="preserve">
|
||||
<value>Username is required</value>
|
||||
<comment>Error message when username is required</comment>
|
||||
</data>
|
||||
<data name="ErrorPasswordRequired" xml:space="preserve">
|
||||
<value>Password is required</value>
|
||||
<comment>Error message when password is required</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -0,0 +1,80 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// <copyright file="ValidationMessages.cs" company="lanedirt">
|
||||
// Copyright (c) lanedirt. All rights reserved.
|
||||
// Licensed under the AGPLv3 license. See LICENSE.md file in the project root for full license information.
|
||||
// </copyright>
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
namespace AliasVault.Client.Resources;
|
||||
|
||||
using System.Globalization;
|
||||
using System.Resources;
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to validation message resources.
|
||||
/// </summary>
|
||||
public static class ValidationMessages
|
||||
{
|
||||
/// <summary>
|
||||
/// The resource manager for accessing validation messages.
|
||||
/// </summary>
|
||||
private static readonly ResourceManager ResourceManager = new("AliasVault.Client.Resources.ValidationMessages", typeof(ValidationMessages).Assembly);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the error message for password minimum length validation.
|
||||
/// </summary>
|
||||
public static string PasswordMinLength => GetResourceValue("PasswordMinLength");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the error message when password confirmation doesn't match.
|
||||
/// </summary>
|
||||
public static string PasswordsDoNotMatch => GetResourceValue("PasswordsDoNotMatch");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the generic error message for password minimum length validation.
|
||||
/// </summary>
|
||||
public static string PasswordMinLengthGeneric => GetResourceValue("PasswordMinLengthGeneric");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the generic error message when passwords don't match.
|
||||
/// </summary>
|
||||
public static string PasswordsDoNotMatchGeneric => GetResourceValue("PasswordsDoNotMatchGeneric");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the error message when username is required.
|
||||
/// </summary>
|
||||
public static string UsernameRequired => GetResourceValue("UsernameRequired");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the error message when password is required.
|
||||
/// </summary>
|
||||
public static string PasswordRequired => GetResourceValue("PasswordRequired");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the error message when secret key is required.
|
||||
/// </summary>
|
||||
public static string SecretKeyRequired => GetResourceValue("SecretKeyRequired");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the error message for terms and conditions acceptance.
|
||||
/// </summary>
|
||||
public static string MustAcceptTerms => GetResourceValue("MustAcceptTerms");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the resource value for the specified key.
|
||||
/// </summary>
|
||||
/// <param name="key">The resource key.</param>
|
||||
/// <returns>The localized resource value.</returns>
|
||||
private static string GetResourceValue(string key)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ResourceManager.GetString(key, CultureInfo.CurrentUICulture) ?? key;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Return the key as fallback if resource loading fails
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
|
||||
<!-- Password validation messages -->
|
||||
<data name="PasswordMinLength" xml:space="preserve">
|
||||
<value>The new password must be at least 10 characters long.</value>
|
||||
<comment>Error message for password minimum length validation</comment>
|
||||
</data>
|
||||
<data name="PasswordsDoNotMatch" xml:space="preserve">
|
||||
<value>The new passwords do not match.</value>
|
||||
<comment>Error message when password confirmation doesn't match</comment>
|
||||
</data>
|
||||
<data name="PasswordMinLengthGeneric" xml:space="preserve">
|
||||
<value>Password must be at least 10 characters long.</value>
|
||||
<comment>Generic error message for password minimum length validation</comment>
|
||||
</data>
|
||||
<data name="PasswordsDoNotMatchGeneric" xml:space="preserve">
|
||||
<value>Passwords do not match.</value>
|
||||
<comment>Generic error message when passwords don't match</comment>
|
||||
</data>
|
||||
|
||||
<!-- Required field validation messages -->
|
||||
<data name="UsernameRequired" xml:space="preserve">
|
||||
<value>Username is required</value>
|
||||
<comment>Error message when username is required</comment>
|
||||
</data>
|
||||
<data name="PasswordRequired" xml:space="preserve">
|
||||
<value>Password is required</value>
|
||||
<comment>Error message when password is required</comment>
|
||||
</data>
|
||||
<data name="SecretKeyRequired" xml:space="preserve">
|
||||
<value>Secret key is required</value>
|
||||
<comment>Error message when secret key is required</comment>
|
||||
</data>
|
||||
|
||||
<!-- Terms and conditions -->
|
||||
<data name="MustAcceptTerms" xml:space="preserve">
|
||||
<value>You must accept the terms and conditions.</value>
|
||||
<comment>Error message for terms and conditions acceptance</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -13,6 +13,7 @@ using AliasVault.Client.Utilities;
|
||||
using AliasVault.Cryptography.Client;
|
||||
using AliasVault.Shared.Models.WebApi.Auth;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using SecureRemotePassword;
|
||||
|
||||
/// <summary>
|
||||
@@ -22,8 +23,11 @@ using SecureRemotePassword;
|
||||
/// <param name="authStateProvider">The provider that manages authentication state.</param>
|
||||
/// <param name="authService">The service handling authentication operations.</param>
|
||||
/// <param name="config">The application configuration.</param>
|
||||
public class UserRegistrationService(HttpClient httpClient, AuthenticationStateProvider authStateProvider, AuthService authService, Config config)
|
||||
/// <param name="localizerFactory">The string localizer factory for localization.</param>
|
||||
public class UserRegistrationService(HttpClient httpClient, AuthenticationStateProvider authStateProvider, AuthService authService, Config config, IStringLocalizerFactory localizerFactory)
|
||||
{
|
||||
private readonly IStringLocalizer _apiErrorLocalizer = localizerFactory.Create("ApiErrors", "AliasVault.Client");
|
||||
|
||||
/// <summary>
|
||||
/// Registers a new user asynchronously.
|
||||
/// </summary>
|
||||
@@ -55,7 +59,7 @@ public class UserRegistrationService(HttpClient httpClient, AuthenticationStateP
|
||||
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
var errors = ApiResponseUtility.ParseErrorResponse(responseContent);
|
||||
var errors = ApiResponseUtility.ParseErrorResponse(responseContent, _apiErrorLocalizer);
|
||||
return (false, string.Join(", ", errors));
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
|
||||
namespace AliasVault.Shared.Models.WebApi.Auth;
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using AliasVault.Shared.Models.Validation;
|
||||
|
||||
/// <summary>
|
||||
/// Register model.
|
||||
/// </summary>
|
||||
@@ -18,26 +15,20 @@ public class RegisterModel
|
||||
/// <summary>
|
||||
/// Gets or sets the username.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Username { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the password.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MinLength(8, ErrorMessage = "Password must be at least 8 characters long.")]
|
||||
public string Password { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the password confirmation.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Compare("Password", ErrorMessage = "Passwords do not match.")]
|
||||
public string PasswordConfirm { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the terms and conditions are accepted or not.
|
||||
/// </summary>
|
||||
[MustBeTrue(ErrorMessage = "You must accept the terms and conditions.")]
|
||||
public bool AcceptTerms { get; set; } = false;
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
|
||||
namespace AliasVault.Shared.Models.WebApi.PasswordChange;
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
/// <summary>
|
||||
/// Password change model.
|
||||
/// </summary>
|
||||
@@ -17,20 +15,15 @@ public class PasswordChangeModel
|
||||
/// <summary>
|
||||
/// Gets or sets the current password.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string CurrentPassword { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the password.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[MinLength(8, ErrorMessage = "The new password must be at least 8 characters long.")]
|
||||
public string NewPassword { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the password confirmation.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Compare("NewPassword", ErrorMessage = "The new passwords do not match.")]
|
||||
public string NewPasswordConfirm { get; set; } = null!;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user