fix(admin): resolve High code-review findings (Admin-003, Admin-004, Admin-005)
Admin-003 — SignalR hubs were anonymously reachable: an unauthenticated client could open /hubs/fleet, /hubs/alerts and /hubs/script-log and stream fleet state, alert detail text and server script-log contents. Added [Authorize] to FleetStatusHub, AlertHub and ScriptLogHub, and chained .RequireAuthorization() onto all three MapHub() calls as a belt-and-braces backstop. Admin-004 — appsettings.json committed live-looking secrets (the `sa` ConfigDb password and the LDAP ServiceAccountPassword) in plaintext. Replaced both with empty placeholders sourced from user-secrets (dev) or the ConnectionStrings__ConfigDb / Authentication__Ldap__ServiceAccountPassword environment variables (prod); added a UserSecretsId to the Admin csproj and a fail-fast guard in Program.cs when ConfigDb is empty/missing. Admin-005 — Login.razor performed SignInAsync from an interactive Blazor circuit, where the original HTTP response has long completed so the auth cookie was not emitted. Rewrote it as a static-rendered plain HTML form (data-enhance="false") posting to a new AuthEndpoints.MapAuthEndpoints() minimal-API handler (/auth/login, /auth/logout) that does the LDAP bind, grant resolution, cookie SignInAsync and redirect while the endpoint still owns the response. Includes an open-redirect guard on returnUrl. Added xUnit + Shouldly regression tests: AuthEndpointsTests (login cookie issuance, failed-bind redirect, open-redirect rejection, logout, anonymous hub negotiate rejection) and AppSettingsSecretHygieneTests (no committed secrets). All 26 auth-related tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Admin.Hubs;
|
||||
@@ -7,6 +8,11 @@ namespace ZB.MOM.WW.OtOpcUa.Admin.Hubs;
|
||||
/// anomalies) to subscribed admin clients. Alerts don't auto-clear — the operator acks them
|
||||
/// from the UI via <see cref="AcknowledgeAsync"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="AuthorizeAttribute"/> gates the hub so failure-detail alert text is not pushed
|
||||
/// to anonymous connections (Admin-003).
|
||||
/// </remarks>
|
||||
[Authorize]
|
||||
public sealed class AlertHub : Hub
|
||||
{
|
||||
public const string AllAlertsGroup = "__alerts__";
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Admin.Hubs;
|
||||
@@ -8,6 +9,12 @@ namespace ZB.MOM.WW.OtOpcUa.Admin.Hubs;
|
||||
/// scope notifications; the server sends <c>NodeStateChanged</c> messages whenever the poller
|
||||
/// observes a delta.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="AuthorizeAttribute"/> gates the hub: an unauthenticated client cannot open the
|
||||
/// connection, so the fleet generation/role/resilience stream is not an anonymous
|
||||
/// information-disclosure channel (Admin-003).
|
||||
/// </remarks>
|
||||
[Authorize]
|
||||
public sealed class FleetStatusHub : Hub
|
||||
{
|
||||
public Task SubscribeCluster(string clusterId)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Admin.Hubs;
|
||||
@@ -26,7 +27,12 @@ namespace ZB.MOM.WW.OtOpcUa.Admin.Hubs;
|
||||
/// first, then new lines are emitted as they are appended. The stream is cancelled when
|
||||
/// the client disconnects.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <see cref="AuthorizeAttribute"/> gates the hub: only an authenticated operator can
|
||||
/// open the connection and tail the server <c>scripts-*.log</c> contents (Admin-003).
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[Authorize]
|
||||
public sealed class ScriptLogHub(IConfiguration configuration, ILogger<ScriptLogHub> logger) : Hub
|
||||
{
|
||||
/// <summary>Number of existing lines to replay from the end of the file before live-tailing.</summary>
|
||||
|
||||
Reference in New Issue
Block a user