100 lines
4.6 KiB
C#
100 lines
4.6 KiB
C#
using System.Collections.Concurrent;
|
|
using System.Security.Claims;
|
|
using Akka.Actor;
|
|
using Bunit;
|
|
using Microsoft.AspNetCore.Components.Authorization;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using Microsoft.Extensions.Options;
|
|
using NSubstitute;
|
|
using ZB.MOM.WW.ScadaBridge.CentralUI.Auth;
|
|
using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Pages.Monitoring;
|
|
using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared;
|
|
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Sites;
|
|
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
|
using ZB.MOM.WW.ScadaBridge.Commons.Messages.RemoteQuery;
|
|
using ZB.MOM.WW.ScadaBridge.Communication;
|
|
using ZB.MOM.WW.ScadaBridge.Security;
|
|
|
|
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Auth;
|
|
|
|
public class ParkedMessagesMutationRegressionTests : BunitContext
|
|
{
|
|
[Theory]
|
|
[InlineData(false)]
|
|
[InlineData(true)]
|
|
public async Task NonnumericSiteIdentifier_RelaysOnlyWhileCurrentSiteGrantAllows(bool revoke)
|
|
{
|
|
using var system = ActorSystem.Create("parked-mutation-" + Guid.NewGuid().ToString("N"));
|
|
var dispatched = new ConcurrentQueue<SiteEnvelope>();
|
|
var comms = new CommunicationService(Options.Create(new CommunicationOptions()), NullLogger<CommunicationService>.Instance);
|
|
comms.SetCommunicationActor(system.ActorOf(Props.Create(() => new ReplyActor(dispatched))));
|
|
Services.AddSingleton(comms);
|
|
var sites = Substitute.For<ISiteRepository>();
|
|
sites.GetAllSitesAsync(Arg.Any<CancellationToken>()).Returns(new List<Site> { new("Plant A", "plant-a") { Id = 42 } });
|
|
Services.AddSingleton(sites);
|
|
var auth = new MutableAuth("42");
|
|
Services.AddSingleton<AuthenticationStateProvider>(auth);
|
|
Services.AddScoped<SiteScopeService>();
|
|
Services.AddAuthorizationCore();
|
|
Services.AddLogging();
|
|
Services.AddSingleton(Substitute.For<IDialogService>());
|
|
JSInterop.Mode = JSRuntimeMode.Loose;
|
|
|
|
var page = Render<ParkedMessages>();
|
|
await page.Find("select").ChangeAsync(new() { Value = "plant-a" });
|
|
page.WaitForAssertion(() => Assert.Contains("ERP", page.Markup));
|
|
await page.FindAll("tr").Single(row => row.TextContent.Contains("ERP")).ClickAsync(new());
|
|
page.WaitForAssertion(() => Assert.Contains("Retry", page.Markup));
|
|
if (revoke) auth.SiteId = "99";
|
|
// The same rendered page still contains the old site and row. The click must
|
|
// resolve SiteIdentifier through Site.Id and consult the live grant.
|
|
await page.FindAll("button").Single(button => button.TextContent.Trim() == "Retry").ClickAsync(new());
|
|
|
|
if (revoke)
|
|
{
|
|
Assert.Empty(dispatched);
|
|
Assert.Contains("Site access was revoked", page.Markup);
|
|
}
|
|
else
|
|
{
|
|
var envelope = Assert.Single(dispatched);
|
|
Assert.Equal("plant-a", envelope.SiteId);
|
|
var retry = Assert.IsType<ParkedMessageRetryRequest>(envelope.Message);
|
|
Assert.Equal("parked-message-1", retry.MessageId);
|
|
Assert.Equal("plant-a", retry.SiteId);
|
|
}
|
|
await system.Terminate();
|
|
}
|
|
|
|
private sealed class MutableAuth(string siteId) : AuthenticationStateProvider
|
|
{
|
|
public string SiteId { get; set; } = siteId;
|
|
public override Task<AuthenticationState> GetAuthenticationStateAsync() => Task.FromResult(new AuthenticationState(
|
|
new ClaimsPrincipal(new ClaimsIdentity(new[] {
|
|
new Claim(JwtTokenService.UsernameClaimType, "operator"),
|
|
new Claim(JwtTokenService.RoleClaimType, Roles.Deployer),
|
|
new Claim(JwtTokenService.SiteIdClaimType, SiteId)
|
|
}, "test"))));
|
|
}
|
|
|
|
private sealed class ReplyActor : ReceiveActor
|
|
{
|
|
public ReplyActor(ConcurrentQueue<SiteEnvelope> dispatched)
|
|
{
|
|
Receive<SiteEnvelope>(envelope =>
|
|
{
|
|
if (envelope.Message is ParkedMessageQueryRequest query)
|
|
Sender.Tell(new ParkedMessageQueryResponse(query.CorrelationId, envelope.SiteId,
|
|
new[] { new ParkedMessageEntry("parked-message-1", "ERP", "Send", "Offline", 2, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow) },
|
|
1, 1, 25, true, null, DateTimeOffset.UtcNow));
|
|
else if (envelope.Message is ParkedMessageRetryRequest retry)
|
|
{
|
|
dispatched.Enqueue(envelope);
|
|
Sender.Tell(new ParkedMessageRetryResponse(retry.CorrelationId, true));
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|