fix(security): close auth & site-scoping gaps across 8 findings
Resolves the auth-theme batch from the 2026-05-28 baseline review (8 findings across Security/CentralUI/ManagementService/CLI). The most consequential gaps: NotificationReport + SiteCallsReport now route through SiteScopeService so a site-scoped Deployment user cannot see or act on other sites' rows (CUI-028); QueryAuditLogCommand is no longer "any authenticated user" — gated Admin-only to match /api/audit/query's strictness (MS-018); RoleMapper preserves the broader grant when a user is in both an unscoped and scoped Deployment LDAP group, instead of silently narrowing to the scoped set (Sec-016); and the dead SiteScopeRequirement/Handler are deleted so SiteScopeService is unambiguously the sole site-scoping mechanism (Sec-017). Pending findings: 172 → 164.
This commit is contained in:
@@ -93,6 +93,7 @@ public class NotificationReportDetailModalTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddScoped<ScadaLink.CentralUI.Auth.SiteScopeService>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -79,6 +79,10 @@ public class NotificationReportPageTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
// CentralUI-028: the page now injects SiteScopeService — the test user
|
||||
// has no SiteId claims, so this resolves to system-wide and the
|
||||
// pre-existing test expectations hold.
|
||||
Services.AddScoped<ScadaLink.CentralUI.Auth.SiteScopeService>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -94,6 +94,7 @@ public class SiteCallsReportPageTests : BunitContext
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(user));
|
||||
Services.AddAuthorizationCore();
|
||||
Services.AddScoped<ScadaLink.CentralUI.Auth.SiteScopeService>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -484,6 +485,31 @@ public class SiteCallsReportPageTests : BunitContext
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SiteScoping_ScopedDeploymentUser_HidesOutOfScopeRows()
|
||||
{
|
||||
// CentralUI-028: a Deployment user scoped to Plant A only must not see
|
||||
// Plant B rows in the grid, even though the query response carried both.
|
||||
// Last AuthenticationStateProvider registration wins on resolution.
|
||||
var scopedUser = new ClaimsPrincipal(new ClaimsIdentity(new[]
|
||||
{
|
||||
new Claim("Username", "scoped"),
|
||||
new Claim(ClaimTypes.Role, "Deployment"),
|
||||
new Claim(JwtTokenService.SiteIdClaimType, "1"), // Plant A only
|
||||
}, "TestAuth"));
|
||||
Services.AddSingleton<AuthenticationStateProvider>(new TestAuthStateProvider(scopedUser));
|
||||
|
||||
var cut = Render<SiteCallsReportPage>();
|
||||
cut.WaitForState(() => cut.FindAll("table tbody tr").Count > 0,
|
||||
TimeSpan.FromSeconds(2));
|
||||
|
||||
var rows = cut.FindAll("table tbody tr");
|
||||
Assert.Single(rows);
|
||||
// Plant A row only; Plant B (FailedId) row must be filtered out.
|
||||
Assert.Contains(ParkedId.ToString("N")[..12], rows[0].TextContent);
|
||||
Assert.DoesNotContain(FailedId.ToString("N")[..12], cut.Markup);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
|
||||
Reference in New Issue
Block a user