Skip to content

Commit

Permalink
Fix AuthComponentBase and tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
bitbound committed Jul 27, 2023
1 parent aa11983 commit a0807cf
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 35 deletions.
58 changes: 42 additions & 16 deletions Server/Components/AuthComponentBase.cs
Original file line number Diff line number Diff line change
@@ -1,47 +1,73 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Identity;
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;

namespace Remotely.Server.Components;

[Authorize]
public class AuthComponentBase : ComponentBase
{
private readonly ManualResetEventSlim _initSignal = new();
private RemotelyUser? _user;
private string? _userName;

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;
}
await base.OnInitializedAsync();
}

public bool IsAuthenticated { get; private set; }

public bool IsUserSet => _user is not null;

public RemotelyUser User
{
get => _user ?? throw new InvalidOperationException("User has not been resolved yet.");
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 => _userName ?? throw new InvalidOperationException("User has not been resolved yet.");
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<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;
}
_initSignal.Set();
await base.OnInitializedAsync();
}
}
5 changes: 4 additions & 1 deletion Server/Components/AuthorizedIndex.razor
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

protected override void OnAfterRender(bool firstRender)
{
if (AppConfig.Require2FA && IsUserSet && !User.TwoFactorEnabled)
if (AppConfig.Require2FA && !User.TwoFactorEnabled)
{
NavManager.NavigateTo("/TwoFactorRequired");
}
Expand All @@ -31,6 +31,9 @@
{
await base.OnInitializedAsync();

// 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)
{
await SignInManager.SignOutAsync();
Expand Down
8 changes: 4 additions & 4 deletions Server/Components/Scripts/ScriptSchedules.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@ public partial class ScriptSchedules : AuthComponentBase
private IToastService ToastService { get; set; } = null!;

private bool CanModifySchedule =>
_selectedSchedule.CreatorId == User?.Id ||
User?.IsAdministrator == true;
_selectedSchedule.CreatorId == User.Id ||
User.IsAdministrator;

private bool CanDeleteSchedule =>
_selectedSchedule.CreatorId == User?.Id ||
User?.IsAdministrator == true;
_selectedSchedule.CreatorId == User.Id ||
User.IsAdministrator;

protected override async Task OnInitializedAsync()
{
Expand Down
8 changes: 4 additions & 4 deletions Server/Components/TabControl/TabHeader.razor
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@
throw new Exception("TabHeader must be contained in a TabControl.");
}

if (!string.IsNullOrWhiteSpace(NavigationUri))
if (Parent.ActiveTab == Name)
{
OnActivated?.Invoke();
}
}
private void SetActiveTab()

private async Task SetActiveTab()
{
if (!string.IsNullOrWhiteSpace(NavigationUri))
{
Expand All @@ -47,7 +47,7 @@
else
{
Parent?.SetActiveTab(this);
StateHasChanged();
await InvokeAsync(StateHasChanged);
OnActivated?.Invoke();
}
}
Expand Down
21 changes: 19 additions & 2 deletions Server/Hubs/CircuitConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public class CircuitConnection : CircuitHandler, ICircuitConnection
private readonly ILogger<CircuitConnection> _logger;
private readonly IAgentHubSessionCache _agentSessionCache;
private readonly IToastService _toastService;
private readonly ManualResetEventSlim _initSignal = new();
private RemotelyUser? _user;

public CircuitConnection(
Expand Down Expand Up @@ -120,8 +121,23 @@ public CircuitConnection(

public RemotelyUser User
{
get => _user ?? throw new InvalidOperationException("User has not been resolved yet.");
internal set => _user = value;
get
{
if (_initSignal.Wait(TimeSpan.FromSeconds(5)) && _user is not null)
{
return _user;
}
_logger.LogError("Failed to resolve user.");
throw new InvalidOperationException("Failed to resolve user.");
}
internal set
{
_user = value;
if (_user is not null)
{
_initSignal.Set();
}
}
}


Expand Down Expand Up @@ -223,6 +239,7 @@ public override async Task OnCircuitOpenedAsync(Circuit circuit, CancellationTok
}
_user = userResult.Value;
_circuitManager.TryAddConnection(ConnectionId, this);
_initSignal.Set();
}
await base.OnCircuitOpenedAsync(circuit, cancellationToken);
}
Expand Down
5 changes: 0 additions & 5 deletions Server/Pages/DeviceDetails.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,6 @@ private string GetTrimmedText(string source, int stringLength)
return source[0..25] + "...";
}

private string GetTrimmedText(string[] source, int stringLength)
{
return GetTrimmedText(string.Join("", source), stringLength);
}

private Task HandleValidSubmit()
{
if (_device is null)
Expand Down
1 change: 1 addition & 0 deletions Server/Services/DataService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1566,6 +1566,7 @@ public async Task<IEnumerable<ScriptRun>> GetPendingScriptRuns(string deviceId)
.AsNoTracking()
.Include(x => x.ScriptRuns)
.ThenInclude(x => x.Results)
.Include(x => x.ScriptResults)
.FirstOrDefaultAsync(x => x.ID == deviceId);

if (device is null)
Expand Down
6 changes: 3 additions & 3 deletions Tests/Server.Tests/DataServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,12 @@ public async Task CreateDevice()
};

// First call should create and return device.
var savedDevice = await _dataService.CreateDevice(deviceOptions);
var savedDevice = (await _dataService.CreateDevice(deviceOptions)).Value!;
Assert.IsInstanceOfType(savedDevice, typeof(Device));

// Second call with same DeviceUuid should return null;
// Second call with same DeviceUuid should fail.
var secondSave = await _dataService.CreateDevice(deviceOptions);
Assert.IsNull(secondSave);
Assert.IsFalse(secondSave.IsSuccess);
}

[TestMethod]
Expand Down

0 comments on commit a0807cf

Please sign in to comment.