fix(adminui): capture audit username at click time, not at panel init
v2-ci / build (push) Failing after 48s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped

DriverStatusPanel previously cached the username in a field at
OnInitializedAsync and forwarded the cached value into RestartDriver
/ ReconnectDriver messages. A token refresh or claim change mid-
circuit would land the stale name in the audit ConfigEdit row.
Re-reads AuthenticationStateProvider at button-click time so the
audit entry reflects the current principal.
This commit is contained in:
Joseph Doherty
2026-05-28 11:58:12 -04:00
parent 662f3f9f5c
commit 0d3ec46c14
@@ -149,7 +149,6 @@
// Authorization
private bool _canOperate;
private string? _currentUserName;
// Action state
private bool _busyReconnect;
@@ -162,8 +161,9 @@
protected override async Task OnInitializedAsync()
{
// Check DriverOperator authorization so buttons only render for permitted users.
// The username for audit logging is re-read at button-click time (not captured here)
// so token-refreshes mid-session land in audit entries accurately.
var auth = await AuthState.GetAuthenticationStateAsync();
_currentUserName = auth.User.Identity?.Name ?? auth.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value ?? "unknown";
var authResult = await AuthorizationService.AuthorizeAsync(auth.User, null, "DriverOperator");
_canOperate = authResult.Succeeded;
@@ -213,8 +213,9 @@
StateHasChanged();
try
{
var userName = await GetCurrentUserNameAsync();
var result = await AdminOps.AskAsync<ReconnectDriverResult>(
new ReconnectDriver(ClusterId, DriverInstanceId, _currentUserName ?? "unknown", Guid.NewGuid()),
new ReconnectDriver(ClusterId, DriverInstanceId, userName, Guid.NewGuid()),
new System.Threading.CancellationTokenSource(TimeSpan.FromSeconds(15)).Token);
ShowOpResult(result.Ok, result.Ok ? "Reconnect dispatched" : (result.Message ?? "Failed"));
}
@@ -237,8 +238,9 @@
StateHasChanged();
try
{
var userName = await GetCurrentUserNameAsync();
var result = await AdminOps.AskAsync<RestartDriverResult>(
new RestartDriver(ClusterId, DriverInstanceId, _currentUserName ?? "unknown", Guid.NewGuid()),
new RestartDriver(ClusterId, DriverInstanceId, userName, Guid.NewGuid()),
new System.Threading.CancellationTokenSource(TimeSpan.FromSeconds(15)).Token);
ShowOpResult(result.Ok, result.Ok ? "Restart dispatched" : (result.Message ?? "Failed"));
}
@@ -253,6 +255,20 @@
}
}
/// <summary>
/// Re-reads the AuthenticationState at call time so the username forwarded to the
/// audit log reflects the current claims-principal — survives token refresh / role
/// change during a long-lived Blazor circuit. Returns "unknown" if no Name claim is
/// present (auth requirements upstream should normally prevent this).
/// </summary>
private async Task<string> GetCurrentUserNameAsync()
{
var auth = await AuthState.GetAuthenticationStateAsync();
return auth.User.Identity?.Name
?? auth.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value
?? "unknown";
}
private void ShowOpResult(bool ok, string message)
{
_opResultOk = ok;