From ce9d65a2364016542522d8d1b77610deeedb48cb Mon Sep 17 00:00:00 2001 From: Jared Goodwin Date: Tue, 1 Aug 2023 15:52:08 -0700 Subject: [PATCH] Make User nullable in AuthComponentBase. Add utility method to make sure it's populated. Refactor as necessary. --- .../OrganizationAdminRequirementHandler.cs | 16 +++-- Server/Auth/ServerAdminRequirementHandler.cs | 18 +++--- Server/Auth/TwoFactorRequiredHandler.cs | 16 +++-- Server/Components/AlertsFrame.razor | 8 +-- Server/Components/AuthComponentBase.cs | 63 ++++++------------- Server/Components/AuthorizedIndex.razor | 12 ++-- Server/Components/Devices/DeviceCard.razor.cs | 4 ++ .../Components/Devices/DevicesFrame.razor.cs | 6 ++ Server/Components/Devices/Terminal.razor.cs | 6 ++ Server/Components/Scripts/RunScript.razor.cs | 6 +- .../Components/Scripts/SavedScripts.razor.cs | 37 +++++++++-- .../Scripts/ScriptSchedules.razor.cs | 46 +++++++++----- Server/Pages/ApiKeys.razor | 19 ++++-- Server/Pages/Branding.razor | 3 + Server/Pages/DeviceDetails.razor | 8 ++- Server/Pages/DeviceDetails.razor.cs | 10 +++ Server/Pages/ManageOrganization.razor | 14 ++--- Server/Pages/ManageOrganization.razor.cs | 23 +++++++ Server/Pages/ScriptsPage.razor.cs | 9 +++ Server/Pages/ServerConfig.razor | 2 +- Server/Pages/ServerConfig.razor.cs | 16 ++++- Server/Pages/UserOptions.razor | 4 +- 22 files changed, 234 insertions(+), 112 deletions(-) diff --git a/Server/Auth/OrganizationAdminRequirementHandler.cs b/Server/Auth/OrganizationAdminRequirementHandler.cs index f0697b9f..48789517 100644 --- a/Server/Auth/OrganizationAdminRequirementHandler.cs +++ b/Server/Auth/OrganizationAdminRequirementHandler.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; +using Remotely.Server.Services; using Remotely.Shared.Entities; using System.Threading.Tasks; @@ -7,23 +8,26 @@ namespace Remotely.Server.Auth; public class OrganizationAdminRequirementHandler : AuthorizationHandler { - private readonly UserManager _userManager; + private readonly IDataService _dataService; - public OrganizationAdminRequirementHandler(UserManager userManager) + public OrganizationAdminRequirementHandler(IDataService dataService) { - _userManager = userManager; + _dataService = dataService; } protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, OrganizationAdminRequirement requirement) { - if (context.User.Identity?.IsAuthenticated != true) + if (context.User.Identity?.IsAuthenticated != true || + string.IsNullOrWhiteSpace(context.User.Identity.Name)) { context.Fail(); return; } - var user = await _userManager.GetUserAsync(context.User); - if (user?.IsAdministrator != true) + var userResult = await _dataService.GetUserByName(context.User.Identity.Name); + + if (!userResult.IsSuccess || + !userResult.Value.IsAdministrator) { context.Fail(); return; diff --git a/Server/Auth/ServerAdminRequirementHandler.cs b/Server/Auth/ServerAdminRequirementHandler.cs index a5c060cc..ee5be6aa 100644 --- a/Server/Auth/ServerAdminRequirementHandler.cs +++ b/Server/Auth/ServerAdminRequirementHandler.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; +using Remotely.Server.Services; using Remotely.Shared.Entities; using System.Threading.Tasks; @@ -9,28 +10,31 @@ namespace Remotely.Server.Auth; public class ServerAdminRequirementHandler : AuthorizationHandler { - private readonly UserManager _userManager; + private readonly IDataService _dataService; - public ServerAdminRequirementHandler(UserManager userManager) + public ServerAdminRequirementHandler(IDataService dataService) { - _userManager = userManager; + _dataService = dataService; } protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ServerAdminRequirement requirement) { - if (context.User.Identity?.IsAuthenticated != true) + if (context.User.Identity?.IsAuthenticated != true || + string.IsNullOrWhiteSpace(context.User.Identity.Name)) { context.Fail(); return; } - var user = await _userManager.GetUserAsync(context.User); - if (user?.IsServerAdmin != true) + var userResult = await _dataService.GetUserByName(context.User.Identity.Name); + + if (!userResult.IsSuccess || + !userResult.Value.IsServerAdmin) { context.Fail(); return; } - + context.Succeed(requirement); } } diff --git a/Server/Auth/TwoFactorRequiredHandler.cs b/Server/Auth/TwoFactorRequiredHandler.cs index ebeccff9..73aa8d65 100644 --- a/Server/Auth/TwoFactorRequiredHandler.cs +++ b/Server/Auth/TwoFactorRequiredHandler.cs @@ -11,21 +11,25 @@ namespace Remotely.Server.Auth; public class TwoFactorRequiredHandler : AuthorizationHandler { - private readonly UserManager _userManager; + private readonly IDataService _dataService; private readonly IApplicationConfig _appConfig; - public TwoFactorRequiredHandler(UserManager userManager, IApplicationConfig appConfig) + public TwoFactorRequiredHandler(IDataService dataService, IApplicationConfig appConfig) { - _userManager = userManager; + _dataService = dataService; _appConfig = appConfig; } protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, TwoFactorRequiredRequirement requirement) { - if (context.User.Identity?.IsAuthenticated == true && _appConfig.Require2FA) + if (context.User.Identity?.IsAuthenticated == true && + context.User.Identity.Name is not null && + _appConfig.Require2FA) { - var user = await _userManager.GetUserAsync(context.User); - if (user?.TwoFactorEnabled != true) + var userResult = await _dataService.GetUserByName(context.User.Identity.Name); + + if (!userResult.IsSuccess || + !userResult.Value.TwoFactorEnabled) { context.Fail(); return; diff --git a/Server/Components/AlertsFrame.razor b/Server/Components/AlertsFrame.razor index 922ae35a..b76b564b 100644 --- a/Server/Components/AlertsFrame.razor +++ b/Server/Components/AlertsFrame.razor @@ -63,10 +63,8 @@ { await base.OnInitializedAsync(); - if (IsAuthenticated) - { - GetAlerts(); - } + EnsureUserSet(); + GetAlerts(); } private async Task ClearAlert(Alert alert) @@ -77,12 +75,14 @@ private async Task ClearAllAlerts() { + EnsureUserSet(); await DataService.DeleteAllAlerts(User.OrganizationID, User.UserName); _alerts.Clear(); } private void GetAlerts() { + EnsureUserSet(); _alerts.Clear(); var alerts = DataService.GetAlerts(User.Id); if (alerts.Any()) diff --git a/Server/Components/AuthComponentBase.cs b/Server/Components/AuthComponentBase.cs index 42b922ed..52a8fefb 100644 --- a/Server/Components/AuthComponentBase.cs +++ b/Server/Components/AuthComponentBase.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging; using Remotely.Server.Services; using Remotely.Shared.Entities; using System; +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; @@ -12,62 +13,34 @@ namespace Remotely.Server.Components; [Authorize] public class AuthComponentBase : ComponentBase { - private readonly ManualResetEventSlim _initSignal = new(); - private RemotelyUser? _user; - private string? _userName; - - public bool IsAuthenticated { get; private set; } - - public bool IsUserSet => _user is not null; - - public RemotelyUser User - { - get - { - if (_initSignal.Wait(TimeSpan.FromSeconds(5)) && _user is not null) - { - return _user; - } - // This should never happen, since AuthBasedComponent is only - // used on components that require authentication. This was easier - // than making this explicitly nullable and refactoring everywhere. - Logger.LogError("Failed to resolve user."); - throw new InvalidOperationException("Failed to resolve user."); - } - private set => _user = value; - } - - public string UserName - { - get - { - if (_initSignal.Wait(TimeSpan.FromSeconds(5)) && _userName is not null) - { - return _userName; - } - Logger.LogError("Failed to resolve user."); - throw new InvalidOperationException("Failed to resolve user."); - } - private set => _userName = value; - } - [Inject] protected IAuthService AuthService { get; set; } = null!; - [Inject] - private ILogger Logger { get; init; } = null!; + protected RemotelyUser? User { get; private set; } + protected string? UserName => User?.UserName; + + [MemberNotNull(nameof(User), nameof(UserName))] + protected void EnsureUserSet() + { + if (User is null) + { + throw new InvalidOperationException("User has not been set."); + } + + if (UserName is null) + { + throw new InvalidOperationException("UserName has not been set."); + } + } protected override async Task OnInitializedAsync() { - IsAuthenticated = await AuthService.IsAuthenticated(); var userResult = await AuthService.GetUser(); if (userResult.IsSuccess) { - _user = userResult.Value; - _userName = userResult.Value.UserName ?? string.Empty; + User = userResult.Value; } - _initSignal.Set(); await base.OnInitializedAsync(); } } diff --git a/Server/Components/AuthorizedIndex.razor b/Server/Components/AuthorizedIndex.razor index ba0e9936..de7f0595 100644 --- a/Server/Components/AuthorizedIndex.razor +++ b/Server/Components/AuthorizedIndex.razor @@ -18,23 +18,27 @@ @code { - protected override void OnAfterRender(bool firstRender) + protected override async Task OnAfterRenderAsync(bool firstRender) { - if (AppConfig.Require2FA && !User.TwoFactorEnabled) + if (User is not null && + AppConfig.Require2FA && + !User.TwoFactorEnabled) { NavManager.NavigateTo("/TwoFactorRequired"); } - base.OnAfterRender(firstRender); + await base.OnAfterRenderAsync(firstRender); } protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); + var isAuthenticated = await AuthService.IsAuthenticated(); + var userResult = await AuthService.GetUser(); // This handles a weird edge case when the user has been // deleted but still has an authentication cookie in their // browser. - if (IsAuthenticated == true && !IsUserSet) + if (isAuthenticated == true && !userResult.IsSuccess) { await SignInManager.SignOutAsync(); NavManager.NavigateTo("/"); diff --git a/Server/Components/Devices/DeviceCard.razor.cs b/Server/Components/Devices/DeviceCard.razor.cs index 8a5f677e..cf3267ba 100644 --- a/Server/Components/Devices/DeviceCard.razor.cs +++ b/Server/Components/Devices/DeviceCard.razor.cs @@ -79,6 +79,7 @@ public partial class DeviceCard : AuthComponentBase, IDisposable protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); + EnsureUserSet(); _theme = await AppState.GetEffectiveTheme(); _currentVersion = UpgradeService.GetCurrentVersion(); _deviceGroups = DataService.GetDeviceGroups(UserName); @@ -180,6 +181,7 @@ public partial class DeviceCard : AuthComponentBase, IDisposable } private async Task HandleValidSubmit() { + EnsureUserSet(); if (!DataService.DoesUserHaveAccessToDevice(Device.ID, User)) { ToastService.ShowToast("Unauthorized.", classString: "bg-warning"); @@ -199,6 +201,8 @@ public partial class DeviceCard : AuthComponentBase, IDisposable private async Task OnFileInputChanged(InputFileChangeEventArgs args) { + EnsureUserSet(); + ToastService.ShowToast("File upload started."); var fileId = await DataService.AddSharedFile(args.File, User.OrganizationID, OnFileInputProgress); diff --git a/Server/Components/Devices/DevicesFrame.razor.cs b/Server/Components/Devices/DevicesFrame.razor.cs index cefa89fe..677010e6 100644 --- a/Server/Components/Devices/DevicesFrame.razor.cs +++ b/Server/Components/Devices/DevicesFrame.razor.cs @@ -74,6 +74,8 @@ public partial class DevicesFrame : AuthComponentBase, IDisposable { await base.OnInitializedAsync(); + EnsureUserSet(); + CircuitConnection.MessageReceived += CircuitConnection_MessageReceived; AppState.PropertyChanged += AppState_PropertyChanged; @@ -283,6 +285,8 @@ public partial class DevicesFrame : AuthComponentBase, IDisposable private void LoadDevices() { + EnsureUserSet(); + lock (_devicesLock) { _allDevices.Clear(); @@ -346,6 +350,8 @@ public partial class DevicesFrame : AuthComponentBase, IDisposable private async Task WakeDevices() { + EnsureUserSet(); + var offlineDevices = DataService .GetDevicesForUser(UserName) .Where(x => !x.IsOnline); diff --git a/Server/Components/Devices/Terminal.razor.cs b/Server/Components/Devices/Terminal.razor.cs index 8ba370e5..f907029e 100644 --- a/Server/Components/Devices/Terminal.razor.cs +++ b/Server/Components/Devices/Terminal.razor.cs @@ -64,6 +64,8 @@ public partial class Terminal : AuthComponentBase, IDisposable private EventCallback RunQuickScript => EventCallback.Factory.Create(this, async script => { + EnsureUserSet(); + var scriptRun = new ScriptRun { OrganizationID = User.OrganizationID, @@ -285,6 +287,8 @@ public partial class Terminal : AuthComponentBase, IDisposable private async Task ShowQuickScripts() { + EnsureUserSet(); + var quickScripts = await DataService.GetQuickScripts(User.Id); if (quickScripts?.Any() != true) { @@ -344,6 +348,8 @@ public partial class Terminal : AuthComponentBase, IDisposable } private bool TryMatchShellShortcuts() { + EnsureUserSet(); + var currentText = InputText?.Trim()?.ToLower(); if (string.IsNullOrWhiteSpace(currentText)) diff --git a/Server/Components/Scripts/RunScript.razor.cs b/Server/Components/Scripts/RunScript.razor.cs index 72961f4c..6bb66ea0 100644 --- a/Server/Components/Scripts/RunScript.razor.cs +++ b/Server/Components/Scripts/RunScript.razor.cs @@ -63,7 +63,7 @@ public partial class RunScript : AuthComponentBase protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - + EnsureUserSet(); _deviceGroups = DataService.GetDeviceGroups(UserName); _devices = DataService .GetDevicesForUser(UserName) @@ -107,6 +107,8 @@ public partial class RunScript : AuthComponentBase private async Task ExecuteScript() { + EnsureUserSet(); + if (_selectedScript is null) { ToastService.ShowToast("You must select a script.", classString: "bg-warning"); @@ -163,6 +165,8 @@ public partial class RunScript : AuthComponentBase private async Task ScriptSelected(ScriptTreeNode viewModel) { + EnsureUserSet(); + if (viewModel.Script is not null) { var scriptResult = await DataService.GetSavedScript(User.Id, viewModel.Script.Id); diff --git a/Server/Components/Scripts/SavedScripts.razor.cs b/Server/Components/Scripts/SavedScripts.razor.cs index 9bf2609d..552580e1 100644 --- a/Server/Components/Scripts/SavedScripts.razor.cs +++ b/Server/Components/Scripts/SavedScripts.razor.cs @@ -36,11 +36,35 @@ public partial class SavedScripts : AuthComponentBase [Inject] public IModalService ModalService { get; set; } = null!; - private bool CanModifyScript => _selectedScript.Id == Guid.Empty || - _selectedScript.CreatorId == User.Id || User.IsAdministrator; + private bool CanModifyScript + { + get + { + if (User is null) + { + return false; + } - private bool CanDeleteScript => !string.IsNullOrWhiteSpace(_selectedScript.CreatorId) && - (_selectedScript.CreatorId == User.Id || User.IsAdministrator); + return + _selectedScript.Id == Guid.Empty || + _selectedScript.CreatorId == User.Id || User.IsAdministrator; + } + } + + private bool CanDeleteScript + { + get + { + if (User is null) + { + return false; + } + + return + !string.IsNullOrWhiteSpace(_selectedScript.CreatorId) && + (_selectedScript.CreatorId == User.Id || User.IsAdministrator); + } + } protected override void OnAfterRender(bool firstRender) { @@ -53,6 +77,8 @@ public partial class SavedScripts : AuthComponentBase private async Task OnValidSubmit(EditContext context) { + EnsureUserSet(); + if (_selectedScript is null) { return; @@ -63,7 +89,7 @@ public partial class SavedScripts : AuthComponentBase ToastService.ShowToast("You can't modify other people's scripts.", classString: "bg-warning"); return; } - + await DataService.AddOrUpdateSavedScript(_selectedScript, User.Id); await ParentPage.RefreshScripts(); ToastService.ShowToast("Script saved."); @@ -102,6 +128,7 @@ public partial class SavedScripts : AuthComponentBase private async Task ScriptSelected(ScriptTreeNode viewModel) { + EnsureUserSet(); if (viewModel.Script is not null) { var result = await DataService.GetSavedScript(User.Id, viewModel.Script.Id); diff --git a/Server/Components/Scripts/ScriptSchedules.razor.cs b/Server/Components/Scripts/ScriptSchedules.razor.cs index 74fb3ca0..561a5ae6 100644 --- a/Server/Components/Scripts/ScriptSchedules.razor.cs +++ b/Server/Components/Scripts/ScriptSchedules.razor.cs @@ -48,25 +48,39 @@ public partial class ScriptSchedules : AuthComponentBase [Inject] private IToastService ToastService { get; set; } = null!; - private bool CanModifySchedule => - _selectedSchedule.CreatorId == User.Id || - User.IsAdministrator; + private bool CanModifySchedule + { + get + { + EnsureUserSet(); + return + _selectedSchedule.CreatorId == User.Id || + User.IsAdministrator; + } + } - private bool CanDeleteSchedule => - _selectedSchedule.CreatorId == User.Id || - User.IsAdministrator; + private bool CanDeleteSchedule + { + get + { + EnsureUserSet(); + return + _selectedSchedule.CreatorId == User.Id || + User.IsAdministrator; + } + } protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - if (IsAuthenticated) - { - _deviceGroups = DataService.GetDeviceGroups(UserName); - _devices = DataService - .GetDevicesForUser(UserName) - .OrderBy(x => x.DeviceName) - .ToArray(); - } + + EnsureUserSet(); + + _deviceGroups = DataService.GetDeviceGroups(UserName); + _devices = DataService + .GetDevicesForUser(UserName) + .OrderBy(x => x.DeviceName) + .ToArray(); await RefreshSchedules(); } @@ -140,6 +154,8 @@ public partial class ScriptSchedules : AuthComponentBase private async Task OnValidSubmit(EditContext context) { + EnsureUserSet(); + if (_selectedSchedule is null) { return; @@ -226,6 +242,8 @@ public partial class ScriptSchedules : AuthComponentBase private async Task ScriptSelected(ScriptTreeNode viewModel) { + EnsureUserSet(); + if (viewModel.Script is not null) { var result = await DataService.GetSavedScript(User.Id, viewModel.Script.Id); diff --git a/Server/Pages/ApiKeys.razor b/Server/Pages/ApiKeys.razor index 31d14f31..0128855f 100644 --- a/Server/Pages/ApiKeys.razor +++ b/Server/Pages/ApiKeys.razor @@ -18,7 +18,11 @@ } -@if (User?.IsAdministrator == true) +@if (_isLoading) +{ +
Loading...
+} +else if (User is not null) { if (!string.IsNullOrWhiteSpace(_newKeySecret)) { @@ -71,26 +75,25 @@ } -else -{ -
Only organization administrators can view this page.
-} - @code { private readonly List _apiTokens = new(); private string _alertMessage = string.Empty; private string _createKeyName = string.Empty; private string _newKeySecret = string.Empty; + private bool _isLoading = true; protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); RefreshData(); + _isLoading = false; } private async Task CreateNewKey() { + EnsureUserSet(); + var secret = RandomGenerator.GenerateString(48); var secretHash = new PasswordHasher().HashPassword(string.Empty, secret); @@ -107,6 +110,8 @@ else private async Task DeleteKey(string keyId) { + EnsureUserSet(); + var result = await JsInterop.Confirm("Are you sure you want to delete this key?"); if (!result) { @@ -125,6 +130,7 @@ else private void RefreshData() { + EnsureUserSet(); _apiTokens.Clear(); _apiTokens.AddRange(DataService.GetAllApiTokens(User.Id)); _createKeyName = string.Empty; @@ -135,6 +141,7 @@ else private async Task RenameKey(string keyId) { + EnsureUserSet(); var newName = await JsInterop.Prompt("New key name"); if (!string.IsNullOrWhiteSpace(newName)) { diff --git a/Server/Pages/Branding.razor b/Server/Pages/Branding.razor index f0ae890d..f70bba18 100644 --- a/Server/Pages/Branding.razor +++ b/Server/Pages/Branding.razor @@ -92,6 +92,8 @@ private async Task HandleValidSubmit(EditContext context) { + EnsureUserSet(); + await DataService.UpdateBrandingInfo( User.OrganizationID, _inputModel.ProductName, @@ -168,6 +170,7 @@ private async Task ResetBranding() { + EnsureUserSet(); var result = await JsInterop.Confirm("Are you sure you want to reset branding to default?"); if (result) { diff --git a/Server/Pages/DeviceDetails.razor b/Server/Pages/DeviceDetails.razor index f8979bc2..9d1b3b39 100644 --- a/Server/Pages/DeviceDetails.razor +++ b/Server/Pages/DeviceDetails.razor @@ -29,11 +29,15 @@ } +else if (_isLoading) +{ +
Loading...
+} else if (_device is null) {

Device not found.

} -else if (!DataService.DoesUserHaveAccessToDevice(_device.ID, User)) +else if (!_userHasAccess) {

Unauthorized.

} @@ -168,7 +172,7 @@ else - @foreach (var group in DataService.GetDeviceGroups(UserName)) + @foreach (var group in _deviceGroups) { } diff --git a/Server/Pages/DeviceDetails.razor.cs b/Server/Pages/DeviceDetails.razor.cs index 1e87451a..9fb5149e 100644 --- a/Server/Pages/DeviceDetails.razor.cs +++ b/Server/Pages/DeviceDetails.razor.cs @@ -22,7 +22,10 @@ public partial class DeviceDetails : AuthComponentBase private string? _alertMessage; private Device? _device; + private bool _userHasAccess; private string? _inputDeviceId; + private bool _isLoading = true; + private DeviceGroup[] _deviceGroups = Array.Empty(); [Parameter] public string ActiveTab { get; set; } = string.Empty; @@ -54,12 +57,15 @@ public partial class DeviceDetails : AuthComponentBase { await base.OnInitializedAsync(); + EnsureUserSet(); + if (!string.IsNullOrWhiteSpace(DeviceId)) { var deviceResult = await DataService.GetDevice(DeviceId); if (deviceResult.IsSuccess) { _device = deviceResult.Value; + _userHasAccess = DataService.DoesUserHaveAccessToDevice(_device.ID, User); } else { @@ -67,7 +73,9 @@ public partial class DeviceDetails : AuthComponentBase } } + _deviceGroups = DataService.GetDeviceGroups(UserName); CircuitConnection.MessageReceived += CircuitConnection_MessageReceived; + _isLoading = false; } private void CircuitConnection_MessageReceived(object? sender, Models.CircuitEvent e) @@ -130,6 +138,8 @@ public partial class DeviceDetails : AuthComponentBase return; } + EnsureUserSet(); + _scriptResults.Clear(); if (User.IsAdministrator) diff --git a/Server/Pages/ManageOrganization.razor b/Server/Pages/ManageOrganization.razor index 717f206f..68ff734d 100644 --- a/Server/Pages/ManageOrganization.razor +++ b/Server/Pages/ManageOrganization.razor @@ -4,8 +4,11 @@

Manage Organization

- -@if (User?.IsAdministrator == true) +@if (_isLoading) +{ +
Loading...
+} +else if (User is not null) {
@@ -195,9 +198,4 @@
-} -else -{ -
Only organization administrators can view this page.
-} - +} \ No newline at end of file diff --git a/Server/Pages/ManageOrganization.razor.cs b/Server/Pages/ManageOrganization.razor.cs index eea0a6d4..78f3f1ab 100644 --- a/Server/Pages/ManageOrganization.razor.cs +++ b/Server/Pages/ManageOrganization.razor.cs @@ -25,6 +25,7 @@ public partial class ManageOrganization : AuthComponentBase private readonly List _orgUsers = new(); private bool _inviteAsAdmin; private string _inviteEmail = string.Empty; + private bool _isLoading = true; private string _newDeviceGroupName = string.Empty; private Organization? _organization; private string _selectedDeviceGroupId = string.Empty; @@ -55,10 +56,14 @@ public partial class ManageOrganization : AuthComponentBase await base.OnInitializedAsync(); await RefreshData(); + + _isLoading = false; } private async Task CreateNewDeviceGroup() { + EnsureUserSet(); + if (!User.IsAdministrator) { return; @@ -93,6 +98,8 @@ public partial class ManageOrganization : AuthComponentBase return; } + EnsureUserSet(); + if (!User.IsServerAdmin) { return; @@ -109,6 +116,8 @@ public partial class ManageOrganization : AuthComponentBase private async Task DeleteInvite(InviteLink invite) { + EnsureUserSet(); + if (!User.IsAdministrator) { return; @@ -127,6 +136,8 @@ public partial class ManageOrganization : AuthComponentBase private async Task DeleteSelectedDeviceGroup() { + EnsureUserSet(); + if (!User.IsAdministrator) { return; @@ -150,6 +161,8 @@ public partial class ManageOrganization : AuthComponentBase private async Task DeleteUser(RemotelyUser user) { + EnsureUserSet(); + if (!User.IsAdministrator) { return; @@ -208,6 +221,8 @@ public partial class ManageOrganization : AuthComponentBase return; } + EnsureUserSet(); + if (!User.IsAdministrator) { return; @@ -237,6 +252,8 @@ public partial class ManageOrganization : AuthComponentBase private async Task RefreshData() { + EnsureUserSet(); + var orgResult = await DataService.GetOrganizationByUserName(UserName); if (!orgResult.IsSuccess) { @@ -256,6 +273,8 @@ public partial class ManageOrganization : AuthComponentBase } private async Task ResetPassword(RemotelyUser user) { + EnsureUserSet(); + if (!User.IsAdministrator) { return; @@ -275,6 +294,8 @@ public partial class ManageOrganization : AuthComponentBase private async Task SendInvite() { + EnsureUserSet(); + if (!User.IsAdministrator) { return; @@ -352,6 +373,8 @@ public partial class ManageOrganization : AuthComponentBase private async Task SetUserIsAdmin(ChangeEventArgs args, RemotelyUser orgUser) { + EnsureUserSet(); + if (!User.IsAdministrator) { return; diff --git a/Server/Pages/ScriptsPage.razor.cs b/Server/Pages/ScriptsPage.razor.cs index 68e3aca7..bec83f47 100644 --- a/Server/Pages/ScriptsPage.razor.cs +++ b/Server/Pages/ScriptsPage.razor.cs @@ -62,6 +62,8 @@ public partial class ScriptsPage : AuthComponentBase public async Task RefreshScripts() { + EnsureUserSet(); + _treeNodes.Clear(); _allScripts = await DataService.GetSavedScriptsWithoutContent(User.Id, User.OrganizationID); @@ -119,6 +121,13 @@ public partial class ScriptsPage : AuthComponentBase private void RefreshTreeNodes() { + if (User is null) + { + return; + } + + EnsureUserSet(); + _treeNodes.Clear(); foreach (var script in _allScripts) diff --git a/Server/Pages/ServerConfig.razor b/Server/Pages/ServerConfig.razor index 89c575f2..5b9f4e6e 100644 --- a/Server/Pages/ServerConfig.razor +++ b/Server/Pages/ServerConfig.razor @@ -66,7 +66,7 @@ @foreach (var user in UserList) {
- + @user.UserName
} diff --git a/Server/Pages/ServerConfig.razor.cs b/Server/Pages/ServerConfig.razor.cs index dec2fb5d..0f1cccee 100644 --- a/Server/Pages/ServerConfig.razor.cs +++ b/Server/Pages/ServerConfig.razor.cs @@ -188,6 +188,13 @@ public partial class ServerConfig : AuthComponentBase { get { + if (User is null) + { + return Enumerable.Empty(); + } + + EnsureUserSet(); + return _userList.Where(x => (!_showAdminsOnly || x.IsServerAdmin) && (!_showMyOrgAdminsOnly || x.OrganizationID == User.OrganizationID)); @@ -197,7 +204,7 @@ public partial class ServerConfig : AuthComponentBase protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - + EnsureUserSet(); if (!User.IsServerAdmin) { return; @@ -338,8 +345,8 @@ public partial class ServerConfig : AuthComponentBase private async Task SaveAndTestSmtpSettings() { + EnsureUserSet(); await SaveInputToAppSettings(); - if (string.IsNullOrWhiteSpace(User.Email)) { ToastService.ShowToast2("User email is not set.", Enums.ToastType.Warning); @@ -417,6 +424,9 @@ public partial class ServerConfig : AuthComponentBase { return; } + + EnsureUserSet(); + DataService.SetIsServerAdmin(user.Id, isAdmin, User.Id); ToastService.ShowToast("Server admins updated."); } @@ -443,6 +453,8 @@ public partial class ServerConfig : AuthComponentBase private async Task UpdateAllDevices() { + EnsureUserSet(); + if (!User.IsServerAdmin) { return; diff --git a/Server/Pages/UserOptions.razor b/Server/Pages/UserOptions.razor index 3f5fa6ae..3d5d5e9b 100644 --- a/Server/Pages/UserOptions.razor +++ b/Server/Pages/UserOptions.razor @@ -97,11 +97,13 @@ protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - _options = User.UserOptions ?? new(); + _options = User?.UserOptions ?? new(); } private Task HandleValidSubmit() { + EnsureUserSet(); + if (!_options.CommandModeShortcutBash.StartsWith("/")) { _options.CommandModeShortcutBash = "/" + _options.CommandModeShortcutBash;