mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-02-02 02:13:48 -05:00
333 lines
15 KiB
Plaintext
333 lines
15 KiB
Plaintext
@page "/credentials/{id:guid}"
|
|
@inherits MainBase
|
|
@inject CredentialService CredentialService
|
|
@implements IAsyncDisposable
|
|
@using Microsoft.Extensions.Localization
|
|
|
|
<LayoutPageTitle>@Localizer["ViewCredentialsPageTitle"]</LayoutPageTitle>
|
|
|
|
@if (IsLoading || Alias == null)
|
|
{
|
|
<LoadingIndicator />
|
|
}
|
|
else
|
|
{
|
|
<PageHeader
|
|
BreadcrumbItems="@BreadcrumbItems"
|
|
Title="@Localizer["ViewCredentialTitle"]">
|
|
<CustomActions>
|
|
<LinkButton
|
|
SmallText="@Localizer["EditButtonMobile"]"
|
|
Text="@Localizer["EditButtonDesktop"]"
|
|
Href="@($"/credentials/{Id}/edit")"
|
|
Color="primary" />
|
|
<LinkButton
|
|
SmallText="@Localizer["DeleteButtonMobile"]"
|
|
Text="@Localizer["DeleteButtonDesktop"]"
|
|
Href="@($"/credentials/{Id}/delete")"
|
|
Color="danger" />
|
|
</CustomActions>
|
|
</PageHeader>
|
|
|
|
<div class="grid grid-cols-1 px-4 pt-6 md:grid-cols-2 lg:grid-cols-3 lg:gap-4 dark:bg-gray-900">
|
|
<div class="col-span-1 md:col-span-2 lg:col-span-1">
|
|
<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">
|
|
<div class="items-center flex space-x-4">
|
|
<DisplayFavicon FaviconBytes="@Alias.Service.Logo" Padding="true" />
|
|
|
|
<div>
|
|
<h3 class="mb-1 text-xl font-bold text-gray-900 dark:text-white">@Alias.Service.Name</h3>
|
|
@if (Alias.Service.Url is not null && Alias.Service.Url.Length > 0)
|
|
{
|
|
@if (Alias.Service.Url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || Alias.Service.Url.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
<a href="@Alias.Service.Url" target="_blank" class="text-blue-500 break-all dark:text-blue-400">@Alias.Service.Url</a>
|
|
}
|
|
else
|
|
{
|
|
<span class="text-gray-700 break-all dark:text-gray-300">@Alias.Service.Url</span>
|
|
}
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<RecentEmails EmailAddress="@Alias.Alias.Email" />
|
|
|
|
@if (Alias.TotpCodes.Count > 0)
|
|
{
|
|
<TotpViewer TotpCodeList="@Alias.TotpCodes" />
|
|
}
|
|
|
|
@if (Alias.Notes != null && Alias.Notes.Length > 0)
|
|
{
|
|
<FormattedNote Notes="@Alias.Notes" />
|
|
}
|
|
|
|
@if (Alias.Attachments.Count > 0)
|
|
{
|
|
<AttachmentViewer Attachments="@Alias.Attachments" />
|
|
}
|
|
</div>
|
|
<div class="col-span-1 md:col-span-2 lg:col-span-2">
|
|
<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">
|
|
<h3 class="mb-2 text-xl font-semibold dark:text-white">@Localizer["LoginCredentialsSection"]</h3>
|
|
<p class="mb-4 text-sm text-gray-600 dark:text-gray-400">
|
|
@if (EmailService.IsAliasVaultSupportedDomain(Alias.Alias.Email ?? string.Empty))
|
|
{
|
|
<span>@Localizer["GeneratedCredentialsDescription"]</span>
|
|
}
|
|
else
|
|
{
|
|
<span>@Localizer["StoredCredentialsDescription"]</span>
|
|
}
|
|
</p>
|
|
<form action="#">
|
|
<div class="grid gap-6">
|
|
@if (Alias.Passkeys != null && Alias.Passkeys.Any())
|
|
{
|
|
var passkey = Alias.Passkeys.First();
|
|
@* With passkey: Username, Passkey, Email, Password *@
|
|
@if (!string.IsNullOrWhiteSpace(Alias.Username))
|
|
{
|
|
<div class="col-span-6">
|
|
<CopyPasteFormRow Id="username" Label="@Localizer["UsernameLabel"]" Value="@(Alias.Username)"></CopyPasteFormRow>
|
|
</div>
|
|
}
|
|
<div class="col-span-6">
|
|
<div class="p-3 rounded-lg bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700">
|
|
<div class="flex items-start gap-3">
|
|
<svg class="w-5 h-5 text-gray-600 dark:text-gray-400 mt-0.5 flex-shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4" />
|
|
</svg>
|
|
<div class="flex-1">
|
|
<div class="mb-1">
|
|
<span class="text-sm font-semibold text-gray-900 dark:text-white">@Localizer["PasskeyLabel"]</span>
|
|
</div>
|
|
<div class="space-y-1 mb-2">
|
|
@if (!string.IsNullOrWhiteSpace(passkey.RpId))
|
|
{
|
|
<div>
|
|
<span class="text-xs text-gray-500 dark:text-gray-400">@Localizer["PasskeySiteLabel"]: </span>
|
|
<span class="text-sm text-gray-900 dark:text-white">@passkey.RpId</span>
|
|
</div>
|
|
}
|
|
@if (!string.IsNullOrWhiteSpace(passkey.DisplayName))
|
|
{
|
|
<div>
|
|
<span class="text-xs text-gray-500 dark:text-gray-400">@Localizer["PasskeyDisplayNameLabel"]: </span>
|
|
<span class="text-sm text-gray-900 dark:text-white">@passkey.DisplayName</span>
|
|
</div>
|
|
}
|
|
</div>
|
|
<p class="text-xs text-gray-600 dark:text-gray-400">
|
|
@Localizer["PasskeyHelpText"]
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@if (!string.IsNullOrWhiteSpace(Alias.Alias.Email))
|
|
{
|
|
<div class="col-span-6">
|
|
<CopyPasteFormRow Id="email" Label="@Localizer["EmailLabel"]" Value="@Alias.Alias.Email"></CopyPasteFormRow>
|
|
</div>
|
|
}
|
|
@if (!string.IsNullOrWhiteSpace(Alias.Passwords.FirstOrDefault()?.Value))
|
|
{
|
|
<div class="col-span-6">
|
|
<CopyPastePasswordFormRow Id="password" Label="@Localizer["PasswordLabel"]" Value="@(Alias.Passwords.FirstOrDefault()?.Value ?? string.Empty)"></CopyPastePasswordFormRow>
|
|
</div>
|
|
}
|
|
}
|
|
else
|
|
{
|
|
@* Without passkey: Email, Username, Password *@
|
|
@if (!string.IsNullOrWhiteSpace(Alias.Alias.Email))
|
|
{
|
|
<div class="col-span-6">
|
|
<CopyPasteFormRow Id="email" Label="@Localizer["EmailLabel"]" Value="@Alias.Alias.Email"></CopyPasteFormRow>
|
|
</div>
|
|
}
|
|
@if (!string.IsNullOrWhiteSpace(Alias.Username))
|
|
{
|
|
<div class="col-span-6">
|
|
<CopyPasteFormRow Id="username" Label="@Localizer["UsernameLabel"]" Value="@(Alias.Username)"></CopyPasteFormRow>
|
|
</div>
|
|
}
|
|
@if (!string.IsNullOrWhiteSpace(Alias.Passwords.FirstOrDefault()?.Value))
|
|
{
|
|
<div class="col-span-6">
|
|
<CopyPastePasswordFormRow Id="password" Label="@Localizer["PasswordLabel"]" Value="@(Alias.Passwords.FirstOrDefault()?.Value ?? string.Empty)"></CopyPastePasswordFormRow>
|
|
</div>
|
|
}
|
|
}
|
|
</div>
|
|
</form>
|
|
</div>
|
|
@if (HasAlias)
|
|
{
|
|
<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">
|
|
<h3 class="mb-4 text-xl font-semibold dark:text-white">@Localizer["AliasSection"]</h3>
|
|
<form action="#">
|
|
<div class="grid grid-cols-6 gap-6">
|
|
@if (!string.IsNullOrWhiteSpace(Alias.Alias.FirstName) && !string.IsNullOrWhiteSpace(Alias.Alias.LastName))
|
|
{
|
|
<div class="col-span-6">
|
|
<CopyPasteFormRow Label="@Localizer["FullNameLabel"]" Value="@(Alias.Alias.FirstName + " " + Alias.Alias.LastName)"></CopyPasteFormRow>
|
|
</div>
|
|
}
|
|
@if (!string.IsNullOrWhiteSpace(Alias.Alias.FirstName))
|
|
{
|
|
<div class="col-span-6 sm:col-span-3">
|
|
<CopyPasteFormRow Label="@Localizer["FirstNameLabel"]" Value="@(Alias.Alias.FirstName)"></CopyPasteFormRow>
|
|
</div>
|
|
}
|
|
@if (!string.IsNullOrWhiteSpace(Alias.Alias.LastName))
|
|
{
|
|
<div class="col-span-6 sm:col-span-3">
|
|
<CopyPasteFormRow Label="@Localizer["LastNameLabel"]" Value="@(Alias.Alias.LastName)"></CopyPasteFormRow>
|
|
</div>
|
|
}
|
|
@if (IsValidDate(Alias.Alias.BirthDate))
|
|
{
|
|
<div class="col-span-6 sm:col-span-3">
|
|
<CopyPasteFormRow Label="@Localizer["BirthdateLabel"]" Value="@(Alias.Alias.BirthDate.ToString("yyyy-MM-dd"))"></CopyPasteFormRow>
|
|
</div>
|
|
}
|
|
@if (!string.IsNullOrWhiteSpace(Alias.Alias.NickName))
|
|
{
|
|
<div class="col-span-6 sm:col-span-3">
|
|
<CopyPasteFormRow Label="@Localizer["NicknameLabel"]" Value="@(Alias.Alias.NickName)"></CopyPasteFormRow>
|
|
</div>
|
|
}
|
|
</div>
|
|
</form>
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
@code {
|
|
private IStringLocalizer Localizer => LocalizerFactory.Create("Pages.Main.Credentials.View", "AliasVault.Client");
|
|
|
|
/// <summary>
|
|
/// Gets or sets the credentials ID.
|
|
/// </summary>
|
|
[Parameter]
|
|
public Guid Id { get; set; }
|
|
private bool IsLoading { get; set; } = true;
|
|
private Credential? Alias { get; set; } = new();
|
|
private bool HasAlias { get; set; } = false;
|
|
|
|
/// <summary>
|
|
/// Checks if a date is valid and not a min value.
|
|
/// </summary>
|
|
/// <param name="date">The date to check.</param>
|
|
/// <returns>True if the date is valid and not a min value, false otherwise.</returns>
|
|
private static bool IsValidDate(DateTime date)
|
|
{
|
|
// Check if date is min value (year 1 or 0001-01-01)
|
|
if (date.Year <= 1 || date.ToString("yyyy-MM-dd") == "0001-01-01")
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if the alias has any valid data.
|
|
/// </summary>
|
|
/// <param name="alias">The credential containing alias information.</param>
|
|
/// <returns>True if the alias has any valid data, false otherwise.</returns>
|
|
private static bool CheckHasAlias(Credential alias)
|
|
{
|
|
if (alias?.Alias == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return !string.IsNullOrWhiteSpace(alias.Alias.FirstName) ||
|
|
!string.IsNullOrWhiteSpace(alias.Alias.LastName) ||
|
|
!string.IsNullOrWhiteSpace(alias.Alias.NickName) ||
|
|
IsValidDate(alias.Alias.BirthDate);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
await base.OnInitializedAsync();
|
|
BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = Localizer["ViewCredentialBreadcrumb"] });
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override async Task OnParametersSetAsync()
|
|
{
|
|
await base.OnParametersSetAsync();
|
|
await LoadEntryAsync();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads the credential.
|
|
/// </summary>
|
|
private async Task LoadEntryAsync()
|
|
{
|
|
IsLoading = true;
|
|
StateHasChanged();
|
|
|
|
// Load the aliases from the webapi via AliasService.
|
|
Alias = await CredentialService.LoadEntryAsync(Id);
|
|
|
|
if (Alias is null)
|
|
{
|
|
// Error loading alias.
|
|
GlobalNotificationService.AddErrorMessage(Localizer["CredentialNotFoundError"]);
|
|
NavigationManager.NavigateTo("/credentials", false, true);
|
|
return;
|
|
}
|
|
|
|
// Check if the alias has any valid data
|
|
HasAlias = CheckHasAlias(Alias);
|
|
|
|
IsLoading = false;
|
|
StateHasChanged();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
async ValueTask IAsyncDisposable.DisposeAsync()
|
|
{
|
|
await KeyboardShortcutService.UnregisterShortcutAsync("ge");
|
|
await KeyboardShortcutService.UnregisterShortcutAsync("gd");
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
{
|
|
await base.OnAfterRenderAsync(firstRender);
|
|
|
|
if (firstRender)
|
|
{
|
|
await KeyboardShortcutService.RegisterShortcutAsync("ge", NavigateToEdit);
|
|
await KeyboardShortcutService.RegisterShortcutAsync("gd", NavigateToDelete);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Navigates to the edit page.
|
|
/// </summary>
|
|
private Task NavigateToEdit()
|
|
{
|
|
NavigationManager.NavigateTo($"/credentials/{Id}/edit");
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Navigates to the delete page.
|
|
/// </summary>
|
|
private Task NavigateToDelete()
|
|
{
|
|
NavigationManager.NavigateTo($"/credentials/{Id}/delete");
|
|
return Task.CompletedTask;
|
|
}
|
|
}
|