Skip to content

Commit

Permalink
Merge pull request #695 from immense/tech/refactor-authcomponentbase
Browse files Browse the repository at this point in the history
Refactor AuthComponentBase.
  • Loading branch information
bitbound authored Aug 3, 2023
2 parents 4171d26 + ce9d65a commit 03879a1
Show file tree
Hide file tree
Showing 22 changed files with 228 additions and 106 deletions.
16 changes: 10 additions & 6 deletions Server/Auth/OrganizationAdminRequirementHandler.cs
Original file line number Diff line number Diff line change
@@ -1,29 +1,33 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Remotely.Server.Services;
using Remotely.Shared.Entities;
using System.Threading.Tasks;

namespace Remotely.Server.Auth;

public class OrganizationAdminRequirementHandler : AuthorizationHandler<OrganizationAdminRequirement>
{
private readonly UserManager<RemotelyUser> _userManager;
private readonly IDataService _dataService;

public OrganizationAdminRequirementHandler(UserManager<RemotelyUser> 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;
Expand Down
18 changes: 11 additions & 7 deletions Server/Auth/ServerAdminRequirementHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,39 @@

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Remotely.Server.Services;
using Remotely.Shared.Entities;
using System.Threading.Tasks;

namespace Remotely.Server.Auth;

public class ServerAdminRequirementHandler : AuthorizationHandler<ServerAdminRequirement>
{
private readonly UserManager<RemotelyUser> _userManager;
private readonly IDataService _dataService;

public ServerAdminRequirementHandler(UserManager<RemotelyUser> 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);
}
}
16 changes: 10 additions & 6 deletions Server/Auth/TwoFactorRequiredHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,25 @@ namespace Remotely.Server.Auth;

public class TwoFactorRequiredHandler : AuthorizationHandler<TwoFactorRequiredRequirement>
{
private readonly UserManager<RemotelyUser> _userManager;
private readonly IDataService _dataService;
private readonly IApplicationConfig _appConfig;

public TwoFactorRequiredHandler(UserManager<RemotelyUser> 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;
Expand Down
8 changes: 4 additions & 4 deletions Server/Components/AlertsFrame.razor
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,8 @@
{
await base.OnInitializedAsync();

if (IsAuthenticated)
{
GetAlerts();
}
EnsureUserSet();
GetAlerts();
}

private async Task ClearAlert(Alert alert)
Expand All @@ -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())
Expand Down
51 changes: 12 additions & 39 deletions Server/Components/AuthComponentBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Remotely.Server.Services;
using Remotely.Shared.Entities;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;

Expand All @@ -12,62 +13,34 @@ namespace Remotely.Server.Components;
[Authorize]
public class AuthComponentBase : ComponentBase
{
private readonly ManualResetEventSlim _initSignal = new();
private RemotelyUser? _user;
private string? _userName;
[Inject]
protected IAuthService AuthService { get; set; } = null!;

public bool IsAuthenticated { get; private set; }
protected RemotelyUser? User { get; private set; }

public bool IsUserSet => _user is not null;
protected string? UserName => User?.UserName;

public RemotelyUser User
[MemberNotNull(nameof(User), nameof(UserName))]
protected void EnsureUserSet()
{
get
if (User is null)
{
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.");
throw new InvalidOperationException("User has not been set.");
}
private set => _user = value;
}

public string UserName
{
get
if (UserName is null)
{
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.");
throw new InvalidOperationException("UserName has not been set.");
}
private set => _userName = value;
}

[Inject]
protected IAuthService AuthService { get; set; } = null!;

[Inject]
private ILogger<AuthComponentBase> Logger { get; init; } = null!;


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();
}
}
12 changes: 8 additions & 4 deletions Server/Components/AuthorizedIndex.razor
Original file line number Diff line number Diff line change
Expand Up @@ -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("/");
Expand Down
4 changes: 4 additions & 0 deletions Server/Components/Devices/DeviceCard.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ public void Dispose()
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
EnsureUserSet();
_theme = await AppState.GetEffectiveTheme();
_currentVersion = UpgradeService.GetCurrentVersion();
_deviceGroups = DataService.GetDeviceGroups(UserName);
Expand Down Expand Up @@ -180,6 +181,7 @@ private void HandleHeaderClick()
}
private async Task HandleValidSubmit()
{
EnsureUserSet();
if (!DataService.DoesUserHaveAccessToDevice(Device.ID, User))
{
ToastService.ShowToast("Unauthorized.", classString: "bg-warning");
Expand All @@ -199,6 +201,8 @@ await DataService.UpdateDevice(Device.ID,

private async Task OnFileInputChanged(InputFileChangeEventArgs args)
{
EnsureUserSet();

ToastService.ShowToast("File upload started.");

var fileId = await DataService.AddSharedFile(args.File, User.OrganizationID, OnFileInputProgress);
Expand Down
6 changes: 6 additions & 0 deletions Server/Components/Devices/DevicesFrame.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();

EnsureUserSet();

CircuitConnection.MessageReceived += CircuitConnection_MessageReceived;
AppState.PropertyChanged += AppState_PropertyChanged;

Expand Down Expand Up @@ -283,6 +285,8 @@ private void HandleRefreshClicked()

private void LoadDevices()
{
EnsureUserSet();

lock (_devicesLock)
{
_allDevices.Clear();
Expand Down Expand Up @@ -346,6 +350,8 @@ private void ToggleSortDirection()

private async Task WakeDevices()
{
EnsureUserSet();

var offlineDevices = DataService
.GetDevicesForUser(UserName)
.Where(x => !x.IsOnline);
Expand Down
6 changes: 6 additions & 0 deletions Server/Components/Devices/Terminal.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ private string InputText
private EventCallback<SavedScript> RunQuickScript =>
EventCallback.Factory.Create<SavedScript>(this, async script =>
{
EnsureUserSet();

var scriptRun = new ScriptRun
{
OrganizationID = User.OrganizationID,
Expand Down Expand Up @@ -285,6 +287,8 @@ private async Task ShowAllCompletions()

private async Task ShowQuickScripts()
{
EnsureUserSet();

var quickScripts = await DataService.GetQuickScripts(User.Id);
if (quickScripts?.Any() != true)
{
Expand Down Expand Up @@ -344,6 +348,8 @@ private void ToggleTerminalOpen()
}
private bool TryMatchShellShortcuts()
{
EnsureUserSet();

var currentText = InputText?.Trim()?.ToLower();

if (string.IsNullOrWhiteSpace(currentText))
Expand Down
6 changes: 5 additions & 1 deletion Server/Components/Scripts/RunScript.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ protected override void OnAfterRender(bool firstRender)
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();

EnsureUserSet();
_deviceGroups = DataService.GetDeviceGroups(UserName);
_devices = DataService
.GetDevicesForUser(UserName)
Expand Down Expand Up @@ -107,6 +107,8 @@ private void DeviceSelectedChanged(ChangeEventArgs args, Device device)

private async Task ExecuteScript()
{
EnsureUserSet();

if (_selectedScript is null)
{
ToastService.ShowToast("You must select a script.", classString: "bg-warning");
Expand Down Expand Up @@ -163,6 +165,8 @@ private async Task ExecuteScript()

private async Task ScriptSelected(ScriptTreeNode viewModel)
{
EnsureUserSet();

if (viewModel.Script is not null)
{
var scriptResult = await DataService.GetSavedScript(User.Id, viewModel.Script.Id);
Expand Down
Loading

0 comments on commit 03879a1

Please sign in to comment.