//----------------------------------------------------------------------- // // Copyright (c) aliasvault. All rights reserved. // Licensed under the AGPLv3 license. See LICENSE.md file in the project root for full license information. // //----------------------------------------------------------------------- namespace AliasVault.Client.Main.Pages; using AliasVault.Client.Auth.Pages.Base; using AliasVault.Client.Services; using AliasVault.Client.Services.Auth; using AliasVault.RazorComponents.Models; using Blazored.LocalStorage; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Authorization; using Microsoft.Extensions.Localization; /// /// Base authorized page that all pages that are part of the logged in website should inherit from. /// All pages that inherit from this class will receive default injected components that are used globally. /// Also, a default set of breadcrumbs is added in the parent OnInitialized method. /// public abstract class MainBase : OwningComponentBase { private bool _parametersInitialSet; /// /// Gets or sets the NavigationManager. /// [Inject] public NavigationManager NavigationManager { get; set; } = null!; /// /// Gets or sets the AuthenticationStateProvider. /// [Inject] public AuthenticationStateProvider AuthStateProvider { get; set; } = null!; /// /// Gets or sets the GlobalNotificationService. /// [Inject] public GlobalNotificationService GlobalNotificationService { get; set; } = null!; /// /// Gets or sets the GlobalLoadingService in order to manipulate the global loading spinner animation. /// [Inject] public GlobalLoadingService GlobalLoadingSpinner { get; set; } = null!; /// /// Gets or sets the LocalizerFactory. /// [Inject] public IStringLocalizerFactory LocalizerFactory { get; set; } = null!; /// /// Gets or sets the JsInteropService. /// [Inject] public JsInteropService JsInteropService { get; set; } = null!; /// /// Gets or sets the DbService. /// [Inject] public DbService DbService { get; set; } = null!; /// /// Gets or sets the EmailService. /// [Inject] public EmailService EmailService { get; set; } = null!; /// /// Gets or sets the KeyboardShortcutService. /// [Inject] public KeyboardShortcutService KeyboardShortcutService { get; set; } = null!; /// /// Gets or sets the AuthService. /// [Inject] public AuthService AuthService { get; set; } = null!; /// /// Gets or sets the Config instance with values from appsettings.json. /// [Inject] public Config Config { get; set; } = null!; /// /// Gets or sets the LocalStorage. /// [Inject] public ILocalStorageService LocalStorage { get; set; } = null!; /// /// Gets the SharedLocalizer. This is used to access shared resource translations like buttons, etc. /// protected IStringLocalizer SharedLocalizer => LocalizerFactory.Create("SharedResources", "AliasVault.Client"); /// /// Gets or sets the breadcrumb items for the page. A default set of breadcrumbs is added in the parent OnInitialized method. /// protected List BreadcrumbItems { get; set; } = []; /// /// Initializes the component asynchronously. /// /// A representing the asynchronous operation. protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); _parametersInitialSet = false; // Add base breadcrumbs BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = SharedLocalizer["Home"], Url = NavigationManager.BaseUri, ShowHomeIcon = true }); bool willRedirect = await RedirectIfNoEncryptionKey(); if (willRedirect) { // Keep the page from loading if a redirect is imminent. while (true) { await Task.Delay(200); } } // Check if DB is initialized, if not, redirect to sync page. if (!DbService.GetState().CurrentState.IsInitialized()) { var currentRelativeUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri); await LocalStorage.SetItemAsync(LoginBase.ReturnUrlKey, currentRelativeUrl); NavigationManager.NavigateTo("/sync"); while (true) { await Task.Delay(200); } } } /// protected override async Task OnAfterRenderAsync(bool firstRender) { await base.OnAfterRenderAsync(firstRender); bool willRedirect = await RedirectIfNoEncryptionKey(); if (willRedirect) { // Keep the page from loading if a redirect is imminent. while (true) { await Task.Delay(200); } } // Check if DB is initialized, if not, redirect to setup page. if (!DbService.GetState().CurrentState.IsInitialized()) { var currentRelativeUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri); await LocalStorage.SetItemAsync(LoginBase.ReturnUrlKey, currentRelativeUrl); NavigationManager.NavigateTo("/sync"); while (true) { await Task.Delay(200); } } } /// /// Gets the username from the authentication state asynchronously. /// /// The username. protected async Task GetUsernameAsync() { var authState = await AuthStateProvider.GetAuthenticationStateAsync(); return authState.User.Identity?.Name ?? "[Unknown]"; } /// /// Sets the parameters asynchronously. /// /// A representing the asynchronous operation. protected override async Task OnParametersSetAsync() { await base.OnParametersSetAsync(); // This is to prevent the OnParametersSetAsync method from running together with OnInitialized on initial page load. if (!_parametersInitialSet) { _parametersInitialSet = true; } } /// /// Checks if the encryption key is set. If not, redirect to the unlock screen /// where the user can re-enter the master password so the encryption key gets refreshed. /// /// This method should be called on every authenticated page load. /// private async Task RedirectIfNoEncryptionKey() { // If not logged in, let the normal login process handle it. var authState = await AuthStateProvider.GetAuthenticationStateAsync(); if (!authState.User.Identity?.IsAuthenticated ?? true) { return true; } // Check that encryption key is set. If not, redirect to unlock screen. if (!AuthService.IsEncryptionKeySet()) { // If returnUrl is not set and current URL is not unlock page, set it to the current URL. var localStorageReturnUrl = await LocalStorage.GetItemAsync(LoginBase.ReturnUrlKey); if (string.IsNullOrEmpty(localStorageReturnUrl)) { var currentUrl = NavigationManager.Uri; if (!currentUrl.Contains("unlock", StringComparison.OrdinalIgnoreCase)) { var currentRelativeUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri); await LocalStorage.SetItemAsync(LoginBase.ReturnUrlKey, currentRelativeUrl); } } NavigationManager.NavigateTo("/unlock"); return true; } return false; } }