fix(central-ui): resolve CentralUI-002/003/004 — site-scope enforcement, per-circuit console capture, cached auth state
This commit is contained in:
@@ -65,6 +65,10 @@ public class TopologyPageTests : BunitContext
|
||||
// DI registration still has to satisfy the [Inject].
|
||||
Services.AddScoped<IDialogService, DialogService>();
|
||||
|
||||
// Site scoping (CentralUI-002): Topology injects SiteScopeService to
|
||||
// filter the tree by the user's permitted sites.
|
||||
Services.AddScoped<ScadaLink.CentralUI.Auth.SiteScopeService>();
|
||||
|
||||
// TreeView persists expansion state via JS interop. Stub the calls so render doesn't throw.
|
||||
JSInterop.Setup<string?>("treeviewStorage.load", _ => true).SetResult(null);
|
||||
JSInterop.SetupVoid("treeviewStorage.save", _ => true);
|
||||
@@ -194,6 +198,52 @@ public class TopologyPageTests : BunitContext
|
||||
Assert.Contains(dimmedNodes, n => n.TextContent.Contains("Boilers"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SiteScoping_ScopedDeploymentUser_OnlySeesPermittedSites()
|
||||
{
|
||||
// Regression test for CentralUI-002. The SiteId claims issued at login were
|
||||
// never read, so a Deployment user scoped to one site could view (and act
|
||||
// on) every site's topology. Topology now filters the tree by the user's
|
||||
// permitted sites via SiteScopeService.
|
||||
var scopedUser = new ClaimsPrincipal(new ClaimsIdentity(new[]
|
||||
{
|
||||
new Claim("Username", "scoped-tester"),
|
||||
new Claim(ScadaLink.Security.JwtTokenService.RoleClaimType, "Deployment"),
|
||||
// Permitted on site 1 only.
|
||||
new Claim(ScadaLink.Security.JwtTokenService.SiteIdClaimType, "1"),
|
||||
}, "TestAuth"));
|
||||
// Last AuthenticationStateProvider registration wins on resolution.
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(scopedUser));
|
||||
|
||||
SeedRepos(sites: new[]
|
||||
{
|
||||
new Site("Plant-A", "plant-a") { Id = 1 },
|
||||
new Site("Plant-B", "plant-b") { Id = 2 },
|
||||
});
|
||||
|
||||
var cut = Render<TopologyPage>();
|
||||
|
||||
// The permitted site is rendered; the non-permitted site is not.
|
||||
Assert.Contains("Plant-A", cut.Markup);
|
||||
Assert.DoesNotContain("Plant-B", cut.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SiteScoping_SystemWideDeploymentUser_SeesAllSites()
|
||||
{
|
||||
// A Deployment user with no SiteId claims is system-wide and sees every site.
|
||||
SeedRepos(sites: new[]
|
||||
{
|
||||
new Site("Plant-A", "plant-a") { Id = 1 },
|
||||
new Site("Plant-B", "plant-b") { Id = 2 },
|
||||
});
|
||||
|
||||
var cut = Render<TopologyPage>();
|
||||
|
||||
Assert.Contains("Plant-A", cut.Markup);
|
||||
Assert.Contains("Plant-B", cut.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DoubleClick_OnAreaLabel_EntersRenameMode()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user