mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-03-03 14:28:14 -05:00
Rename login to credentials, fixed warnings and bugs (#58)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
|
||||
|
||||
<PropertyGroup>
|
||||
<RootNamespace>AliasVault.WebApp</RootNamespace>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
@@ -60,12 +60,6 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="Pages\Logins\AddEdit.razor" />
|
||||
<AdditionalFiles Include="Pages\Logins\Delete.razor" />
|
||||
<AdditionalFiles Include="Pages\Logins\View.razor" />
|
||||
<Content Update="Layout\DbStatusIndicator.razor">
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
</Content>
|
||||
<Content Update="wwwroot\appsettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
||||
@@ -9,9 +9,9 @@ namespace AliasVault.WebApp.Auth.Pages.Base;
|
||||
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using AliasVault.Shared.Models.WebApi;
|
||||
using AliasVault.Shared.Models.WebApi.Auth;
|
||||
using AliasVault.WebApp.Auth.Services;
|
||||
using AliasVault.WebApp.Components;
|
||||
using AliasVault.WebApp.Services;
|
||||
using AliasVault.WebApp.Services.Database;
|
||||
using Blazored.LocalStorage;
|
||||
@@ -75,14 +75,34 @@ public class LoginBase : OwningComponentBase
|
||||
[Inject]
|
||||
public ILocalStorageService LocalStorage { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Parses the response content and displays the server validation errors.
|
||||
/// </summary>
|
||||
/// <param name="responseContent">Response content.</param>
|
||||
/// <returns>List of errors if something went wrong.</returns>
|
||||
public static List<string> ParseResponse(string responseContent)
|
||||
{
|
||||
var returnErrors = new List<string>();
|
||||
|
||||
var errorResponse = System.Text.Json.JsonSerializer.Deserialize<ServerValidationErrorResponse>(responseContent);
|
||||
if (errorResponse is not null)
|
||||
{
|
||||
foreach (var error in errorResponse.Errors)
|
||||
{
|
||||
returnErrors.AddRange(error.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return returnErrors;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the username from the authentication state asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="email">Email address.</param>
|
||||
/// <param name="password">Password.</param>
|
||||
/// <param name="serverValidationErrors">ServerValidationErrors Blazor component reference.</param>
|
||||
/// <returns>The username.</returns>
|
||||
protected async Task ProcessLoginAsync(string email, string password, ServerValidationErrors serverValidationErrors)
|
||||
/// <returns>List of errors if something went wrong.</returns>
|
||||
protected async Task<List<string>> ProcessLoginAsync(string email, string password)
|
||||
{
|
||||
// Send request to server with email to get server ephemeral public key.
|
||||
var result = await Http.PostAsJsonAsync("api/v1/Auth/login", new LoginRequest(email));
|
||||
@@ -90,15 +110,16 @@ public class LoginBase : OwningComponentBase
|
||||
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
serverValidationErrors.ParseResponse(responseContent);
|
||||
return;
|
||||
return ParseResponse(responseContent);
|
||||
}
|
||||
|
||||
var loginResponse = JsonSerializer.Deserialize<LoginResponse>(responseContent);
|
||||
if (loginResponse == null)
|
||||
{
|
||||
serverValidationErrors.AddError("An error occurred while processing the login request.");
|
||||
return;
|
||||
return new List<string>
|
||||
{
|
||||
"An error occurred while processing the login request.",
|
||||
};
|
||||
}
|
||||
|
||||
// 3. Client derives shared session key.
|
||||
@@ -120,15 +141,16 @@ public class LoginBase : OwningComponentBase
|
||||
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
serverValidationErrors.ParseResponse(responseContent);
|
||||
return;
|
||||
return ParseResponse(responseContent);
|
||||
}
|
||||
|
||||
var validateLoginResponse = JsonSerializer.Deserialize<ValidateLoginResponse>(responseContent);
|
||||
if (validateLoginResponse == null)
|
||||
{
|
||||
serverValidationErrors.AddError("An error occurred while processing the login request.");
|
||||
return;
|
||||
return new List<string>
|
||||
{
|
||||
"An error occurred while processing the login request.",
|
||||
};
|
||||
}
|
||||
|
||||
// 5. Client verifies proof.
|
||||
@@ -155,5 +177,7 @@ public class LoginBase : OwningComponentBase
|
||||
{
|
||||
NavigationManager.NavigateTo("/");
|
||||
}
|
||||
|
||||
return new List<string>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,11 @@
|
||||
|
||||
try
|
||||
{
|
||||
await ProcessLoginAsync(_loginModel.Email, _loginModel.Password, _serverValidationErrors);
|
||||
var errors = await ProcessLoginAsync(_loginModel.Email, _loginModel.Password);
|
||||
foreach (var error in errors)
|
||||
{
|
||||
_serverValidationErrors.AddError(error);
|
||||
}
|
||||
}
|
||||
#if DEBUG
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
@using System.Text.Json
|
||||
@using AliasVault.Shared.Models
|
||||
@using AliasVault.WebApp.Auth.Components
|
||||
@using AliasVault.WebApp.Auth.Pages.Base
|
||||
@using AliasVault.WebApp.Auth.Services
|
||||
@using Cryptography
|
||||
@using SecureRemotePassword
|
||||
@@ -77,7 +78,7 @@
|
||||
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
_serverValidationErrors.ParseResponse(responseContent);
|
||||
LoginBase.ParseResponse(responseContent);
|
||||
StateHasChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
|
||||
try
|
||||
{
|
||||
await ProcessLoginAsync(Email, _unlockModel.Password, _serverValidationErrors);
|
||||
await ProcessLoginAsync(Email, _unlockModel.Password);
|
||||
StateHasChanged();
|
||||
}
|
||||
#if DEBUG
|
||||
|
||||
@@ -107,6 +107,7 @@ public class AuthService(HttpClient httpClient, ILocalStorageService localStorag
|
||||
/// <returns>Encryption key as base64 string.</returns>
|
||||
public string GetEncryptionKeyAsBase64Async()
|
||||
{
|
||||
// Enable this line for debugging to skip unlock screen.
|
||||
return Convert.ToBase64String(GetEncryptionKeyAsync());
|
||||
}
|
||||
|
||||
|
||||
@@ -11,14 +11,14 @@
|
||||
|
||||
@code {
|
||||
/// <summary>
|
||||
/// Gets or sets the alias object to show.
|
||||
/// Gets or sets the credentials object to show.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public AliasVault.Shared.Models.WebApi.AliasListEntry Obj { get; set; } = null!;
|
||||
public AliasVault.WebApp.Models.CredentialListEntry Obj { get; set; } = null!;
|
||||
|
||||
private void ShowDetails()
|
||||
{
|
||||
// Redirect to view page instead for now.
|
||||
NavigationManager.NavigateTo($"/login/{Obj.Id}");
|
||||
NavigationManager.NavigateTo($"/credentials/{Obj.Id}");
|
||||
}
|
||||
}
|
||||
@@ -11,24 +11,6 @@
|
||||
@code {
|
||||
private readonly List<string> _errors = [];
|
||||
|
||||
/// <summary>
|
||||
/// Parses the response content and displays the server validation errors.
|
||||
/// </summary>
|
||||
public void ParseResponse(string responseContent)
|
||||
{
|
||||
_errors.Clear();
|
||||
var errorResponse = System.Text.Json.JsonSerializer.Deserialize<ServerValidationErrorResponse>(responseContent);
|
||||
if (errorResponse is not null)
|
||||
{
|
||||
foreach (var error in errorResponse.Errors)
|
||||
{
|
||||
_errors.AddRange(error.Value);
|
||||
}
|
||||
}
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a server validation error.
|
||||
/// </summary>
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
<NavLink href="/" class="block rounded text-gray-700 hover:text-primary-700 dark:text-gray-400 dark:hover:text-white" ActiveClass="text-primary-700 dark:text-primary-500" Match="NavLinkMatch.All">
|
||||
Home
|
||||
</NavLink>
|
||||
<NavLink href="/aliases" class="block text-gray-700 hover:text-primary-700 dark:text-gray-400 dark:hover:text-white" ActiveClass="text-primary-700 dark:text-primary-500" Match="NavLinkMatch.All">
|
||||
Aliases
|
||||
<NavLink href="/credentials" class="block text-gray-700 hover:text-primary-700 dark:text-gray-400 dark:hover:text-white" ActiveClass="text-primary-700 dark:text-primary-500" Match="NavLinkMatch.All">
|
||||
Credentials
|
||||
</NavLink>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -74,8 +74,8 @@
|
||||
</NavLink>
|
||||
</li>
|
||||
<li class="block border-b dark:border-gray-700">
|
||||
<NavLink href="/aliases" class="block py-3 px-4 text-gray-900 lg:py-0 dark:text-white lg:hover:underline lg:px-0" ActiveClass="text-primary-700 dark:text-primary-500" Match="NavLinkMatch.All">
|
||||
Aliases
|
||||
<NavLink href="/credentials" class="block py-3 px-4 text-gray-900 lg:py-0 dark:text-white lg:hover:underline lg:px-0" ActiveClass="text-primary-700 dark:text-primary-500" Match="NavLinkMatch.All">
|
||||
Credentials
|
||||
</NavLink>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// <copyright file="LoginEdit.cs" company="lanedirt">
|
||||
// <copyright file="CredentialEdit.cs" company="lanedirt">
|
||||
// Copyright (c) lanedirt. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE.md file in the project root for full license information.
|
||||
// </copyright>
|
||||
@@ -11,9 +11,9 @@ using System.ComponentModel.DataAnnotations;
|
||||
using AliasClientDb;
|
||||
|
||||
/// <summary>
|
||||
/// Login model.
|
||||
/// Credential edit model.
|
||||
/// </summary>
|
||||
public class LoginEdit
|
||||
public class CredentialEdit
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the Id of the login.
|
||||
34
src/AliasVault.WebApp/Models/CredentialListEntry.cs
Normal file
34
src/AliasVault.WebApp/Models/CredentialListEntry.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// <copyright file="CredentialListEntry.cs" company="lanedirt">
|
||||
// Copyright (c) lanedirt. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE.md file in the project root for full license information.
|
||||
// </copyright>
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
namespace AliasVault.WebApp.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Alias list entry model. This model is used to represent an alias in a list with simplified properties.
|
||||
/// </summary>
|
||||
public class CredentialListEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the alias id.
|
||||
/// </summary>
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the alias logo byte array.
|
||||
/// </summary>
|
||||
public byte[]? Logo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the alias service name.
|
||||
/// </summary>
|
||||
public string Service { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the alias create date.
|
||||
/// </summary>
|
||||
public DateTime CreateDate { get; set; }
|
||||
}
|
||||
@@ -1,21 +1,20 @@
|
||||
@page "/add-login"
|
||||
@page "/login/{id:guid}/edit"
|
||||
@page "/add-credentials"
|
||||
@page "/credentials/{id:guid}/edit"
|
||||
@inherits PageBase
|
||||
@inject NavigationManager Navigation
|
||||
@inject AliasService AliasService
|
||||
@inject CredentialService CredentialService
|
||||
@using AliasGenerators.Implementations
|
||||
@using AliasGenerators.Password.Implementations
|
||||
@using AliasVault.WebApp.Models
|
||||
@using Alias = AliasClientDb.Alias
|
||||
@using Password = AliasClientDb.Password
|
||||
@using Service = AliasClientDb.Service
|
||||
|
||||
@if (EditMode)
|
||||
{
|
||||
<LayoutPageTitle>Edit login</LayoutPageTitle>
|
||||
<LayoutPageTitle>Edit credentials</LayoutPageTitle>
|
||||
}
|
||||
else {
|
||||
<LayoutPageTitle>Add login</LayoutPageTitle>
|
||||
<LayoutPageTitle>Add credentials</LayoutPageTitle>
|
||||
}
|
||||
|
||||
<div class="grid grid-cols-1 px-4 pt-6 xl:grid-cols-3 xl:gap-4 dark:bg-gray-900">
|
||||
@@ -24,18 +23,18 @@ else {
|
||||
<div class="flex items-center justify-between">
|
||||
@if (EditMode)
|
||||
{
|
||||
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl dark:text-white">Edit login</h1>
|
||||
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl dark:text-white">Edit credentials</h1>
|
||||
}
|
||||
else {
|
||||
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl dark:text-white">Add login</h1>
|
||||
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl dark:text-white">Add credentials</h1>
|
||||
}
|
||||
</div>
|
||||
@if (EditMode)
|
||||
{
|
||||
<p>Edit the existing login below.</p>
|
||||
<p>Edit the existing credentials entry below.</p>
|
||||
}
|
||||
else {
|
||||
<p>Create a new login below.</p>
|
||||
<p>Create a new credentials entry below.</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@@ -68,7 +67,7 @@ else
|
||||
<h3 class="mb-4 text-xl font-semibold dark:text-white">Login credentials</h3>
|
||||
<div class="mb-4">
|
||||
<button type="button" class="px-4 py-2 text-white bg-blue-600 rounded-lg hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 dark:bg-blue-500 dark:hover:bg-blue-600 dark:focus:ring-blue-800" @onclick="GenerateRandomIdentity">Generate Random Identity</button>
|
||||
<button type="submit" class="px-4 py-2 text-white bg-green-600 rounded-lg hover:bg-green-700 focus:ring-4 focus:ring-green-300 dark:bg-green-500 dark:hover:bg-green-600 dark:focus:ring-green-800">Save Login</button>
|
||||
<button type="submit" class="px-4 py-2 text-white bg-green-600 rounded-lg hover:bg-green-700 focus:ring-4 focus:ring-green-300 dark:bg-green-500 dark:hover:bg-green-600 dark:focus:ring-green-800">Save Credentials</button>
|
||||
@if (IsIdentityLoading)
|
||||
{
|
||||
<p>Loading...</p>
|
||||
@@ -140,7 +139,7 @@ else
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="px-4 py-2 text-white bg-green-600 rounded-lg hover:bg-green-700 focus:ring-4 focus:ring-green-300 dark:bg-green-500 dark:hover:bg-green-600 dark:focus:ring-green-800">Save Login</button>
|
||||
<button type="submit" class="px-4 py-2 text-white bg-green-600 rounded-lg hover:bg-green-700 focus:ring-4 focus:ring-green-300 dark:bg-green-500 dark:hover:bg-green-600 dark:focus:ring-green-800">Save Credentials</button>
|
||||
</div>
|
||||
</EditForm>
|
||||
|
||||
@@ -153,14 +152,14 @@ else
|
||||
|
||||
@code {
|
||||
/// <summary>
|
||||
/// Gets or sets the login ID.
|
||||
/// Gets or sets the Credentials ID.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Guid? Id { get; set; }
|
||||
|
||||
private bool EditMode { get; set; }
|
||||
private bool Loading { get; set; } = true;
|
||||
private LoginEdit Obj { get; set; } = new();
|
||||
private CredentialEdit Obj { get; set; } = new();
|
||||
private bool IsIdentityLoading { get; set; }
|
||||
private bool IsSaving { get; set; }
|
||||
|
||||
@@ -186,11 +185,11 @@ else
|
||||
|
||||
if (EditMode)
|
||||
{
|
||||
BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = "Edit login" });
|
||||
BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = "Edit credential" });
|
||||
}
|
||||
else
|
||||
{
|
||||
BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = "Add new login" });
|
||||
BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = "Add new credential" });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,32 +205,32 @@ else
|
||||
if (Id is null)
|
||||
{
|
||||
// Error loading alias.
|
||||
GlobalNotificationService.AddErrorMessage("This login does not exist (anymore). Please try again.");
|
||||
GlobalNotificationService.AddErrorMessage("This credential does not exist (anymore). Please try again.");
|
||||
NavigationManager.NavigateTo("/", false, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Load existing Obj, retrieve from service
|
||||
var alias = await AliasService.LoadEntryAsync(Id.Value);
|
||||
var alias = await CredentialService.LoadEntryAsync(Id.Value);
|
||||
if (alias is null)
|
||||
{
|
||||
// Error loading alias.
|
||||
GlobalNotificationService.AddErrorMessage("This login does not exist (anymore). Please try again.");
|
||||
GlobalNotificationService.AddErrorMessage("This credential does not exist (anymore). Please try again.");
|
||||
NavigationManager.NavigateTo("/", false, true);
|
||||
return;
|
||||
}
|
||||
|
||||
Obj = LoginToLoginEdit(alias);
|
||||
Obj = CredentialToCredentialEdit(alias);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create new Obj
|
||||
var alias = new Login();
|
||||
var alias = new Credential();
|
||||
alias.Alias = new Alias();
|
||||
alias.Service = new Service();
|
||||
alias.Passwords = new List<Password> { new Password() };
|
||||
|
||||
Obj = LoginToLoginEdit(alias);
|
||||
Obj = CredentialToCredentialEdit(alias);
|
||||
}
|
||||
|
||||
// Hide loading spinner
|
||||
@@ -247,7 +246,7 @@ else
|
||||
StateHasChanged();
|
||||
|
||||
// Generate a random identity using the IIdentityGenerator implementation.
|
||||
var identity = await AliasService.GenerateRandomIdentityAsync();
|
||||
var identity = await CredentialService.GenerateRandomIdentityAsync();
|
||||
|
||||
// Generate random values for the Identity properties
|
||||
Obj.Alias.FirstName = identity.FirstName;
|
||||
@@ -297,12 +296,12 @@ else
|
||||
{
|
||||
if (Id is not null)
|
||||
{
|
||||
Id = await AliasService.UpdateLoginAsync(LoginEditToLogin(Obj));
|
||||
Id = await CredentialService.UpdateEntryAsync(CredentialEditToCredential(Obj));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Id = await AliasService.InsertAliasAsync(LoginEditToLogin(Obj));
|
||||
Id = await CredentialService.InsertEntryAsync(CredentialEditToCredential(Obj));
|
||||
}
|
||||
|
||||
IsSaving = false;
|
||||
@@ -311,40 +310,46 @@ else
|
||||
if (Id is null || Id == Guid.Empty)
|
||||
{
|
||||
// Error saving.
|
||||
GlobalNotificationService.AddErrorMessage("Error saving alias. Please try again.", true);
|
||||
GlobalNotificationService.AddErrorMessage("Error saving credentials. Please try again.", true);
|
||||
return;
|
||||
}
|
||||
|
||||
// No error, add success message.
|
||||
if (EditMode)
|
||||
{
|
||||
GlobalNotificationService.AddSuccessMessage("Login updated successfully.");
|
||||
GlobalNotificationService.AddSuccessMessage("Credentials updated successfully.");
|
||||
}
|
||||
else
|
||||
{
|
||||
GlobalNotificationService.AddSuccessMessage("Login created successfully.");
|
||||
GlobalNotificationService.AddSuccessMessage("Credentials created successfully.");
|
||||
}
|
||||
|
||||
Navigation.NavigateTo("/login/" + Id);
|
||||
Navigation.NavigateTo("/credentials/" + Id);
|
||||
}
|
||||
|
||||
private LoginEdit LoginToLoginEdit(Login alias)
|
||||
private CredentialEdit CredentialToCredentialEdit(Credential alias)
|
||||
{
|
||||
return new LoginEdit
|
||||
Console.WriteLine("passwordCount: " + alias.Passwords.Count);
|
||||
return new CredentialEdit
|
||||
{
|
||||
Id = alias.Id,
|
||||
ServiceName = alias.Service.Name ?? string.Empty,
|
||||
ServiceUrl = alias.Service.Url,
|
||||
Password = alias.Passwords.FirstOrDefault() ?? new Password(),
|
||||
Password = alias.Passwords.FirstOrDefault() ?? new Password
|
||||
{
|
||||
Value = string.Empty,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow,
|
||||
},
|
||||
Alias = alias.Alias,
|
||||
CreateDate = alias.CreatedAt,
|
||||
LastUpdate = alias.UpdatedAt
|
||||
};
|
||||
}
|
||||
|
||||
private Login LoginEditToLogin(LoginEdit alias)
|
||||
private Credential CredentialEditToCredential(CredentialEdit alias)
|
||||
{
|
||||
return new Login()
|
||||
return new Credential()
|
||||
{
|
||||
Id = alias.Id,
|
||||
Service = new Service
|
||||
@@ -354,12 +359,7 @@ else
|
||||
},
|
||||
Passwords = new List<Password>
|
||||
{
|
||||
new Password
|
||||
{
|
||||
Value = alias.Password.Value ?? string.Empty,
|
||||
CreatedAt = alias.CreateDate,
|
||||
UpdatedAt = alias.LastUpdate,
|
||||
},
|
||||
alias.Password,
|
||||
},
|
||||
Alias = alias.Alias,
|
||||
};
|
||||
@@ -1,16 +1,16 @@
|
||||
@page "/login/{id:guid}/delete"
|
||||
@page "/credentials/{id:guid}/delete"
|
||||
@inherits PageBase
|
||||
@inject AliasService AliasService
|
||||
@inject CredentialService CredentialService
|
||||
|
||||
<LayoutPageTitle>Delete login</LayoutPageTitle>
|
||||
<LayoutPageTitle>Delete credentials entry</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" />
|
||||
<div class="flex items-center justify-between">
|
||||
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl dark:text-white">Delete login</h1>
|
||||
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl dark:text-white">Delete credentials</h1>
|
||||
</div>
|
||||
<p>You can delete a login below.</p>
|
||||
<p>You can delete a credentials entry below.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,7 +22,7 @@ else
|
||||
{
|
||||
<div class="p-4 mb-4 bg-white border border-gray-200 rounded-lg shadow-sm 2xl:col-span-2 dark:border-gray-700 sm:p-6 dark:bg-gray-800">
|
||||
<AlertMessageError Message="Note: removing this login entry is permanent and cannot be undone." />
|
||||
<h3 class="mb-4 text-xl font-semibold dark:text-white">Login</h3>
|
||||
<h3 class="mb-4 text-xl font-semibold dark:text-white">Credential entry</h3>
|
||||
<div class="mb-4">
|
||||
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Id</label>
|
||||
<div>@Id</div>
|
||||
@@ -49,14 +49,14 @@ else
|
||||
public Guid Id { get; set; }
|
||||
|
||||
private bool IsLoading { get; set; } = true;
|
||||
private Login? Obj { get; set; }
|
||||
private Credential? Obj { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
BreadcrumbItems.Add(new BreadcrumbItem { Url = "login/" + Id, DisplayName = "View Login" });
|
||||
BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = "Delete login" });
|
||||
BreadcrumbItems.Add(new BreadcrumbItem { Url = "credentials/" + Id, DisplayName = "View Credentials Entry" });
|
||||
BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = "Delete credentials" });
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -67,7 +67,7 @@ else
|
||||
if (firstRender)
|
||||
{
|
||||
// Load existing Obj, retrieve from service
|
||||
Obj = await AliasService.LoadEntryAsync(Id);
|
||||
Obj = await CredentialService.LoadEntryAsync(Id);
|
||||
|
||||
// Hide loading spinner
|
||||
IsLoading = false;
|
||||
@@ -81,17 +81,17 @@ else
|
||||
{
|
||||
if (Obj is null)
|
||||
{
|
||||
GlobalNotificationService.AddErrorMessage("Error deleting. Login not found.", true);
|
||||
GlobalNotificationService.AddErrorMessage("Error deleting. Credentials entry not found.", true);
|
||||
return;
|
||||
}
|
||||
|
||||
await AliasService.DeleteEntryAsync(Id);
|
||||
GlobalNotificationService.AddSuccessMessage("Login successfully deleted.");
|
||||
await CredentialService.DeleteEntryAsync(Id);
|
||||
GlobalNotificationService.AddSuccessMessage("Credentials entry successfully deleted.");
|
||||
NavigationManager.NavigateTo("/");
|
||||
}
|
||||
|
||||
private void Cancel()
|
||||
{
|
||||
NavigationManager.NavigateTo("/login/" + Id);
|
||||
NavigationManager.NavigateTo("/credentials/" + Id);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
@page "/login/{id:guid}"
|
||||
@page "/credentials/{id:guid}"
|
||||
@inherits PageBase
|
||||
@using AliasVault.WebApp.Components.Email
|
||||
@inject AliasService AliasService
|
||||
@inject CredentialService CredentialService
|
||||
|
||||
<LayoutPageTitle>View login</LayoutPageTitle>
|
||||
<LayoutPageTitle>View credentials</LayoutPageTitle>
|
||||
|
||||
@if (IsLoading || Alias == null)
|
||||
{
|
||||
@@ -17,13 +17,13 @@ else
|
||||
<Breadcrumb BreadcrumbItems="BreadcrumbItems"/>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl dark:text-white">View login</h1>
|
||||
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl dark:text-white">View credentials entry</h1>
|
||||
<div class="flex">
|
||||
<a href="/login/@Id/edit" class="mr-3 px-4 py-2 text-white bg-primary-600 rounded-lg hover:bg-primary-700 focus:ring-4 focus:ring-primary-300 dark:bg-primary-500 dark:hover:bg-primary-600 dark:focus:ring-primary-800">
|
||||
Edit login
|
||||
<a href="/credentials/@Id/edit" class="mr-3 px-4 py-2 text-white bg-primary-600 rounded-lg hover:bg-primary-700 focus:ring-4 focus:ring-primary-300 dark:bg-primary-500 dark:hover:bg-primary-600 dark:focus:ring-primary-800">
|
||||
Edit credentials entry
|
||||
</a>
|
||||
<a href="/login/@Id/delete" class="px-4 py-2 text-white bg-red-600 rounded-lg hover:bg-red-700 focus:ring-4 focus:ring-red-300 dark:bg-red-500 dark:hover:bg-red-600 dark:focus:ring-red-800">
|
||||
Delete login
|
||||
<a href="/credentials/@Id/delete" class="px-4 py-2 text-white bg-red-600 rounded-lg hover:bg-red-700 focus:ring-4 focus:ring-red-300 dark:bg-red-500 dark:hover:bg-red-600 dark:focus:ring-red-800">
|
||||
Delete credentials entry
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -119,19 +119,19 @@ else
|
||||
|
||||
@code {
|
||||
/// <summary>
|
||||
/// Gets or sets the login ID.
|
||||
/// Gets or sets the credentials ID.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Guid Id { get; set; }
|
||||
private bool IsLoading { get; set; } = true;
|
||||
private Login? Alias { get; set; } = new();
|
||||
private Credential? Alias { get; set; } = new();
|
||||
private string AliasEmail { get; set; } = string.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = "View login" });
|
||||
BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = "View credentials entry" });
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -151,12 +151,12 @@ else
|
||||
StateHasChanged();
|
||||
|
||||
// Load the aliases from the webapi via AliasService.
|
||||
Alias = await AliasService.LoadEntryAsync(Id);
|
||||
Alias = await CredentialService.LoadEntryAsync(Id);
|
||||
|
||||
if (Alias is null)
|
||||
{
|
||||
// Error loading alias.
|
||||
GlobalNotificationService.AddErrorMessage("This login does not exist (anymore). Please try again.");
|
||||
GlobalNotificationService.AddErrorMessage("This credentials entry does not exist (anymore). Please try again.");
|
||||
NavigationManager.NavigateTo("/", false, true);
|
||||
return;
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
@page "/"
|
||||
@page "/aliases"
|
||||
@page "/credentials"
|
||||
@inherits PageBase
|
||||
@using AliasVault.WebApp.Components.Alias
|
||||
@inject AliasService AliasService
|
||||
@using AliasVault.WebApp.Components.Credentials
|
||||
@inject CredentialService CredentialService
|
||||
|
||||
<LayoutPageTitle>Home</LayoutPageTitle>
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
<div class="mb-4 col-span-full xl:mb-2">
|
||||
<Breadcrumb BreadcrumbItems="BreadcrumbItems" />
|
||||
<div class="flex items-center justify-between">
|
||||
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl dark:text-white">Logins</h1>
|
||||
<a href="/add-login" class="px-4 py-2 text-white bg-primary-600 rounded-lg hover:bg-primary-700 focus:ring-4 focus:ring-primary-300 dark:bg-primary-500 dark:hover:bg-primary-600 dark:focus:ring-primary-800">
|
||||
+ Add new login
|
||||
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl dark:text-white">Credentials</h1>
|
||||
<a href="/add-credentials" class="px-4 py-2 text-white bg-primary-600 rounded-lg hover:bg-primary-700 focus:ring-4 focus:ring-primary-300 dark:bg-primary-500 dark:hover:bg-primary-600 dark:focus:ring-primary-800">
|
||||
+ Add new credential
|
||||
</a>
|
||||
</div>
|
||||
<p>Find all of your logins below.</p>
|
||||
<p>Find all of your credentials below.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -25,16 +25,16 @@
|
||||
}
|
||||
|
||||
<div class="grid gap-4 px-4 mb-4 md:grid-cols-4 xl:grid-cols-6">
|
||||
@foreach (var alias in Aliases)
|
||||
@foreach (var credential in Credentials)
|
||||
{
|
||||
<Alias Obj="@alias"/>
|
||||
<Credential Obj="@credential"/>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
@code {
|
||||
private bool IsLoading { get; set; } = true;
|
||||
private List<Shared.Models.WebApi.AliasListEntry> Aliases { get; set; } = new();
|
||||
private List<CredentialListEntry> Credentials { get; set; } = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
@@ -53,15 +53,15 @@
|
||||
StateHasChanged();
|
||||
|
||||
// Load the aliases from the webapi via AliasService.
|
||||
var aliasListEntries = await AliasService.GetListAsync();
|
||||
if (aliasListEntries is null)
|
||||
var credentialListEntries = await CredentialService.GetListAsync();
|
||||
if (credentialListEntries is null)
|
||||
{
|
||||
// Error loading aliases.
|
||||
GlobalNotificationService.AddErrorMessage("Failed to load aliases.", true);
|
||||
GlobalNotificationService.AddErrorMessage("Failed to load credentials.", true);
|
||||
return;
|
||||
}
|
||||
|
||||
Aliases = aliasListEntries;
|
||||
Credentials = credentialListEntries;
|
||||
IsLoading = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ builder.Services.AddScoped(sp =>
|
||||
builder.Services.AddTransient<AliasVaultApiHandlerService>();
|
||||
builder.Services.AddScoped<AuthService>();
|
||||
builder.Services.AddScoped<AuthenticationStateProvider, AuthStateProvider>();
|
||||
builder.Services.AddScoped<AliasService>();
|
||||
builder.Services.AddScoped<CredentialService>();
|
||||
builder.Services.AddScoped<DbService>();
|
||||
builder.Services.AddScoped<GlobalNotificationService>();
|
||||
builder.Services.AddSingleton<ClipboardCopyService>();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// <copyright file="AliasService.cs" company="lanedirt">
|
||||
// <copyright file="CredentialService.cs" company="lanedirt">
|
||||
// Copyright (c) lanedirt. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE.md file in the project root for full license information.
|
||||
// </copyright>
|
||||
@@ -9,7 +9,7 @@ namespace AliasVault.WebApp.Services;
|
||||
|
||||
using System.Net.Http.Json;
|
||||
using AliasClientDb;
|
||||
using AliasVault.Shared.Models.WebApi;
|
||||
using AliasVault.WebApp.Models;
|
||||
using AliasVault.WebApp.Services.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Identity = AliasGenerators.Identity.Models.Identity;
|
||||
@@ -17,7 +17,7 @@ using Identity = AliasGenerators.Identity.Models.Identity;
|
||||
/// <summary>
|
||||
/// Service class for alias operations.
|
||||
/// </summary>
|
||||
public class AliasService(HttpClient httpClient, DbService dbService)
|
||||
public class CredentialService(HttpClient httpClient, DbService dbService)
|
||||
{
|
||||
/// <summary>
|
||||
/// Generate random identity by calling the IdentityGenerator API.
|
||||
@@ -39,11 +39,11 @@ public class AliasService(HttpClient httpClient, DbService dbService)
|
||||
/// </summary>
|
||||
/// <param name="loginObject">Login object to insert.</param>
|
||||
/// <returns>Guid of inserted entry.</returns>
|
||||
public async Task<Guid> InsertAliasAsync(Login loginObject)
|
||||
public async Task<Guid> InsertEntryAsync(Credential loginObject)
|
||||
{
|
||||
var context = await dbService.GetDbContextAsync();
|
||||
|
||||
var login = new Login
|
||||
var login = new Credential
|
||||
{
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow,
|
||||
@@ -76,19 +76,18 @@ public class AliasService(HttpClient httpClient, DbService dbService)
|
||||
},
|
||||
};
|
||||
|
||||
await context.Logins.AddAsync(login);
|
||||
login.Passwords.Add(new Password()
|
||||
{
|
||||
Value = loginObject.Passwords.First().Value,
|
||||
});
|
||||
|
||||
await context.Credentials.AddAsync(login);
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
Console.WriteLine("Inserted new alias without password.");
|
||||
|
||||
// Add password.
|
||||
context.Passwords.Add(new AliasClientDb.Password()
|
||||
{
|
||||
Value = loginObject.Passwords.FirstOrDefault()?.Value,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow,
|
||||
Login = login,
|
||||
});
|
||||
login.Passwords.Add(loginObject.Passwords.First());
|
||||
|
||||
await dbService.SaveDatabaseAsync();
|
||||
|
||||
@@ -102,12 +101,12 @@ public class AliasService(HttpClient httpClient, DbService dbService)
|
||||
/// </summary>
|
||||
/// <param name="loginObject">Login object to update.</param>
|
||||
/// <returns>Guid of updated entry.</returns>
|
||||
public async Task<Guid> UpdateLoginAsync(Login loginObject)
|
||||
public async Task<Guid> UpdateEntryAsync(Credential loginObject)
|
||||
{
|
||||
var context = await dbService.GetDbContextAsync();
|
||||
|
||||
// Get the existing entry.
|
||||
var login = await context.Logins
|
||||
var login = await context.Credentials
|
||||
.Include(x => x.Alias)
|
||||
.Include(x => x.Service)
|
||||
.Include(x => x.Passwords)
|
||||
@@ -148,16 +147,16 @@ public class AliasService(HttpClient httpClient, DbService dbService)
|
||||
/// </summary>
|
||||
/// <param name="loginId">Id of login to load.</param>
|
||||
/// <returns>Alias object.</returns>
|
||||
public async Task<Login> LoadEntryAsync(Guid loginId)
|
||||
public async Task<Credential?> LoadEntryAsync(Guid loginId)
|
||||
{
|
||||
var context = await dbService.GetDbContextAsync();
|
||||
|
||||
var loginObject = await context.Logins
|
||||
var loginObject = await context.Credentials
|
||||
.Include(x => x.Passwords)
|
||||
.Include(x => x.Alias)
|
||||
.Include(x => x.Service)
|
||||
.Where(x => x.Id == loginId)
|
||||
.FirstAsync();
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
return loginObject;
|
||||
}
|
||||
@@ -165,16 +164,16 @@ public class AliasService(HttpClient httpClient, DbService dbService)
|
||||
/// <summary>
|
||||
/// Get list with all login entries.
|
||||
/// </summary>
|
||||
/// <returns>List of AliasListEntry objects.</returns>
|
||||
public async Task<List<AliasListEntry>?> GetListAsync()
|
||||
/// <returns>List of CredentialListEntry objects.</returns>
|
||||
public async Task<List<CredentialListEntry>?> GetListAsync()
|
||||
{
|
||||
var context = await dbService.GetDbContextAsync();
|
||||
|
||||
// Retrieve all aliases from client DB.
|
||||
return await context.Logins
|
||||
return await context.Credentials
|
||||
.Include(x => x.Alias)
|
||||
.Include(x => x.Service)
|
||||
.Select(x => new AliasListEntry
|
||||
.Select(x => new CredentialListEntry
|
||||
{
|
||||
Id = x.Id,
|
||||
Logo = x.Service.Logo,
|
||||
@@ -193,11 +192,11 @@ public class AliasService(HttpClient httpClient, DbService dbService)
|
||||
{
|
||||
var context = await dbService.GetDbContextAsync();
|
||||
|
||||
var login = await context.Logins
|
||||
var login = await context.Credentials
|
||||
.Where(x => x.Id == id)
|
||||
.FirstAsync();
|
||||
|
||||
context.Logins.Remove(login);
|
||||
context.Credentials.Remove(login);
|
||||
await context.SaveChangesAsync();
|
||||
await dbService.SaveDatabaseAsync();
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
@using AliasVault.WebApp.Components.Models
|
||||
@using AliasVault.WebApp.Components.Alerts
|
||||
@using AliasVault.WebApp.Components.Loading
|
||||
@using AliasVault.WebApp.Models
|
||||
@using AliasVault.WebApp.Pages.Base
|
||||
@using AliasVault.WebApp.Services
|
||||
@using AliasVault.WebApp.Services.Database
|
||||
|
||||
@@ -121,4 +121,9 @@ public class Alias
|
||||
/// Gets or sets the updated timestamp.
|
||||
/// </summary>
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the credential objects.
|
||||
/// </summary>
|
||||
public virtual ICollection<Credential> Credentials { get; set; } = new List<Credential>();
|
||||
}
|
||||
|
||||
@@ -46,27 +46,27 @@ public class AliasClientDbContext : DbContext
|
||||
/// <summary>
|
||||
/// Gets or sets the Alias DbSet.
|
||||
/// </summary>
|
||||
public DbSet<Alias> Aliases { get; set; }
|
||||
public DbSet<Alias> Aliases { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Attachment DbSet.
|
||||
/// </summary>
|
||||
public DbSet<Attachment> Attachment { get; set; }
|
||||
public DbSet<Attachment> Attachment { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Login DbSet.
|
||||
/// Gets or sets the Credential DbSet.
|
||||
/// </summary>
|
||||
public DbSet<Login> Logins { get; set; }
|
||||
public DbSet<Credential> Credentials { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Password DbSet.
|
||||
/// </summary>
|
||||
public DbSet<Password> Passwords { get; set; }
|
||||
public DbSet<Password> Passwords { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Service DbSet.
|
||||
/// </summary>
|
||||
public DbSet<Service> Services { get; set; }
|
||||
public DbSet<Service> Services { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The OnModelCreating method.
|
||||
@@ -88,32 +88,32 @@ public class AliasClientDbContext : DbContext
|
||||
}
|
||||
}
|
||||
|
||||
// Configure Login - Alias relationship
|
||||
modelBuilder.Entity<Login>()
|
||||
// Configure Credential - Alias relationship
|
||||
modelBuilder.Entity<Credential>()
|
||||
.HasOne(l => l.Alias)
|
||||
.WithMany()
|
||||
.WithMany(c => c.Credentials)
|
||||
.HasForeignKey(l => l.AliasId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
// Configure Login - Service relationship
|
||||
modelBuilder.Entity<Login>()
|
||||
// Configure Credential - Service relationship
|
||||
modelBuilder.Entity<Credential>()
|
||||
.HasOne(l => l.Service)
|
||||
.WithMany()
|
||||
.WithMany(c => c.Credentials)
|
||||
.HasForeignKey(l => l.ServiceId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
// Configure Attachment - Login relationship
|
||||
// Configure Attachment - Credential relationship
|
||||
modelBuilder.Entity<Attachment>()
|
||||
.HasOne(l => l.Login)
|
||||
.WithMany()
|
||||
.HasForeignKey(l => l.LoginId)
|
||||
.HasOne(l => l.Credential)
|
||||
.WithMany(c => c.Attachments)
|
||||
.HasForeignKey(l => l.CredentialId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
// Configure Password - Login relationship
|
||||
// Configure Password - Credential relationship
|
||||
modelBuilder.Entity<Password>()
|
||||
.HasOne(l => l.Login)
|
||||
.WithMany()
|
||||
.HasForeignKey(l => l.LoginId)
|
||||
.HasOne(l => l.Credential)
|
||||
.WithMany(c => c.Passwords)
|
||||
.HasForeignKey(l => l.CredentialId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,13 +43,13 @@ public class Attachment
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the login foreign key.
|
||||
/// Gets or sets the credential foreign key.
|
||||
/// </summary>
|
||||
public Guid LoginId { get; set; }
|
||||
public Guid CredentialId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the login navigation property.
|
||||
/// Gets or sets the credential navigation property.
|
||||
/// </summary>
|
||||
[ForeignKey("LoginId")]
|
||||
public virtual Login Login { get; set; } = null!;
|
||||
[ForeignKey("CredentialId")]
|
||||
public virtual Credential Credential { get; set; } = null!;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// <copyright file="Login.cs" company="lanedirt">
|
||||
// <copyright file="Credential.cs" company="lanedirt">
|
||||
// Copyright (c) lanedirt. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE.md file in the project root for full license information.
|
||||
// </copyright>
|
||||
@@ -12,7 +12,7 @@ using System.ComponentModel.DataAnnotations.Schema;
|
||||
/// <summary>
|
||||
/// Login object.
|
||||
/// </summary>
|
||||
public class Login
|
||||
public class Credential
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets Login ID.
|
||||
@@ -38,13 +38,13 @@ public class Password
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the login foreign key.
|
||||
/// Gets or sets the credential foreign key.
|
||||
/// </summary>
|
||||
public Guid LoginId { get; set; }
|
||||
public Guid CredentialId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the login navigation property.
|
||||
/// Gets or sets the credential navigation property.
|
||||
/// </summary>
|
||||
[ForeignKey("LoginId")]
|
||||
public virtual Login Login { get; set; } = null!;
|
||||
[ForeignKey("CredentialId")]
|
||||
public virtual Credential Credential { get; set; } = null!;
|
||||
}
|
||||
|
||||
@@ -45,4 +45,9 @@ public class Service
|
||||
/// Gets or sets the updated timestamp.
|
||||
/// </summary>
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the credential objects.
|
||||
/// </summary>
|
||||
public virtual ICollection<Credential> Credentials { get; set; } = new List<Credential>();
|
||||
}
|
||||
|
||||
@@ -56,6 +56,6 @@ public class AuthTests : PlaywrightTest
|
||||
|
||||
// Check if the login was successful by verifying content.
|
||||
var pageContent = await Page.TextContentAsync("body");
|
||||
Assert.That(pageContent, Does.Contain("Find all of your logins below"), "No index content after logging in.");
|
||||
Assert.That(pageContent, Does.Contain("Find all of your credentials below"), "No index content after logging in.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// <copyright file="AliasTests.cs" company="lanedirt">
|
||||
// <copyright file="CredentialTests.cs" company="lanedirt">
|
||||
// Copyright (c) lanedirt. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE.md file in the project root for full license information.
|
||||
// </copyright>
|
||||
@@ -8,97 +8,95 @@
|
||||
namespace AliasVault.E2ETests.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// End-to-end tests for the alias management.
|
||||
/// End-to-end tests for the credential management.
|
||||
/// </summary>
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
[TestFixture]
|
||||
public class AliasTests : PlaywrightTest
|
||||
public class CredentialTests : PlaywrightTest
|
||||
{
|
||||
private static readonly Random Random = new();
|
||||
|
||||
/// <summary>
|
||||
/// Test if the alias listing index page works.
|
||||
/// Test if the credential listing index page works.
|
||||
/// </summary>
|
||||
/// <returns>Async task.</returns>
|
||||
[Test]
|
||||
public async Task AliasListingTest()
|
||||
public async Task CredentialListingTest()
|
||||
{
|
||||
await NavigateUsingBlazorRouter("aliases");
|
||||
await WaitForURLAsync("**/aliases", "AliasVault");
|
||||
await NavigateUsingBlazorRouter("credentials");
|
||||
await WaitForURLAsync("**/credentials", "AliasVault");
|
||||
|
||||
// Check if the expected content is present.
|
||||
var pageContent = await Page.TextContentAsync("body");
|
||||
Assert.That(pageContent, Does.Contain("Find all of your logins below"), "No index content after logging in.");
|
||||
Assert.That(pageContent, Does.Contain("Find all of your credentials below"), "No index content after logging in.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test if creating a new alias works.
|
||||
/// Test if creating a new credential entry works.
|
||||
/// </summary>
|
||||
/// <returns>Async task.</returns>
|
||||
[Test]
|
||||
public async Task CreateAliasTest()
|
||||
public async Task CreateCredentialTest()
|
||||
{
|
||||
// Create a new alias with service name = "Test Service".
|
||||
var serviceName = "Test Service";
|
||||
await CreateAlias(new Dictionary<string, string>
|
||||
await CreateCredentialEntry(new Dictionary<string, string>
|
||||
{
|
||||
{ "service-name", serviceName },
|
||||
});
|
||||
|
||||
// Check that the service name is present in the content.
|
||||
var pageContent = await Page.TextContentAsync("body");
|
||||
Assert.That(pageContent, Does.Contain(serviceName), "Created alias service name does not appear on alias page.");
|
||||
Assert.That(pageContent, Does.Contain(serviceName), "Created credential service name does not appear on alias page.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test if editing a created alias works.
|
||||
/// Test if editing a created credential entry works.
|
||||
/// </summary>
|
||||
/// <returns>Async task.</returns>
|
||||
[Test]
|
||||
public async Task EditAliasTest()
|
||||
public async Task EditCredentialTest()
|
||||
{
|
||||
// Create a new alias with service name = "Alias service before".
|
||||
var serviceNameBefore = "Login service before";
|
||||
await CreateAlias(new Dictionary<string, string>
|
||||
var serviceNameBefore = "Credential service before";
|
||||
await CreateCredentialEntry(new Dictionary<string, string>
|
||||
{
|
||||
{ "service-name", serviceNameBefore },
|
||||
});
|
||||
|
||||
// Check that the service name is present in the content.
|
||||
var pageContent = await Page.TextContentAsync("body");
|
||||
Assert.That(pageContent, Does.Contain(serviceNameBefore), "Created login service name does not appear on login page.");
|
||||
Assert.That(pageContent, Does.Contain(serviceNameBefore), "Created credential service name does not appear on login page.");
|
||||
|
||||
// Click the edit button.
|
||||
var editButton = Page.Locator("text=Edit login").First;
|
||||
var editButton = Page.Locator("text=Edit credentials entry").First;
|
||||
await editButton.ClickAsync();
|
||||
await WaitForURLAsync("**/edit", "Save Login");
|
||||
await WaitForURLAsync("**/edit", "Save Credentials");
|
||||
|
||||
// Replace the service name with "Alias service after".
|
||||
var serviceNameAfter = "Login service after";
|
||||
var serviceNameAfter = "Credential service after";
|
||||
await InputHelper.FillInputFields(
|
||||
fieldValues: new Dictionary<string, string>
|
||||
{
|
||||
{ "service-name", serviceNameAfter },
|
||||
});
|
||||
|
||||
var submitButton = Page.Locator("text=Save Login").First;
|
||||
var submitButton = Page.Locator("text=Save Credentials").First;
|
||||
await submitButton.ClickAsync();
|
||||
await WaitForURLAsync("**/login/**", "View Login");
|
||||
await WaitForURLAsync("**/credentials/**", "View credentials entry");
|
||||
|
||||
pageContent = await Page.TextContentAsync("body");
|
||||
Assert.That(pageContent, Does.Contain("Login updated"), "Login update confirmation message not shown.");
|
||||
Assert.That(pageContent, Does.Contain(serviceNameAfter), "Login not updated correctly.");
|
||||
Assert.That(pageContent, Does.Contain("Credentials updated"), "Credential update confirmation message not shown.");
|
||||
Assert.That(pageContent, Does.Contain(serviceNameAfter), "Credential not updated correctly.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create new alias.
|
||||
/// Create new credential entry.
|
||||
/// </summary>
|
||||
/// <param name="formValues">Dictionary with html element ids and values to input as field value.</param>
|
||||
/// <returns>Async task.</returns>
|
||||
private async Task CreateAlias(Dictionary<string, string>? formValues = null)
|
||||
private async Task CreateCredentialEntry(Dictionary<string, string>? formValues = null)
|
||||
{
|
||||
await NavigateUsingBlazorRouter("add-login");
|
||||
await WaitForURLAsync("**/add-login", "Add login");
|
||||
await NavigateUsingBlazorRouter("add-credentials");
|
||||
await WaitForURLAsync("**/add-credentials", "Add credentials");
|
||||
|
||||
// Check if a button with text "Generate Random Identity" appears
|
||||
var generateButton = Page.Locator("text=Generate Random Identity");
|
||||
@@ -108,12 +106,12 @@ public class AliasTests : PlaywrightTest
|
||||
await InputHelper.FillInputFields(formValues);
|
||||
await InputHelper.FillEmptyInputFieldsWithRandom();
|
||||
|
||||
var submitButton = Page.Locator("text=Save Login").First;
|
||||
var submitButton = Page.Locator("text=Save Credentials").First;
|
||||
await submitButton.ClickAsync();
|
||||
await WaitForURLAsync("**/login/**", "Login credentials");
|
||||
await WaitForURLAsync("**/credentials/**", "Login credentials");
|
||||
|
||||
// Check if the alias was created
|
||||
// Check if the credential was created
|
||||
var pageContent = await Page.TextContentAsync("body");
|
||||
Assert.That(pageContent, Does.Contain("Login credentials"), "Alias not created.");
|
||||
Assert.That(pageContent, Does.Contain("Login credentials"), "Credential not created.");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user